Skip to content

Commit

Permalink
Add OPML section to settings screen
Browse files Browse the repository at this point in the history
  • Loading branch information
msasikanth committed Oct 28, 2023
1 parent 8ca11e1 commit 2d20ac2
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ val EnTwineStrings =
moreMenuOptions = "More menu options",
settingsHeaderBehaviour = "Behavior",
settingsHeaderFeedback = "Feedback & bug reports",
settingsHeaderOpml = "OPML",
settingsBrowserTypeTitle = "Use in-app browser",
settingsBrowserTypeSubtitle = "When turned off, links will open in your default browser.",
settingsEnableBlurTitle = "Enable blur in homepage",
Expand All @@ -71,6 +72,11 @@ val EnTwineStrings =
settingsVersion = { versionName, versionCode -> "$versionName ($versionCode)" },
settingsAboutTitle = "About Twine",
settingsAboutSubtitle = "Get to know the authors",
settingsOpmlImport = "Import",
settingsOpmlExport = "Export",
settingsOpmlImporting = { progress -> "Importing..$progress%" },
settingsOpmlExporting = { progress -> "Exporting..$progress%" },
settingsOpmlCancel = "Cancel",
feeds = "Feeds",
editFeeds = "Edit feeds",
comments = "Comments",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ data class TwineStrings(
val settings: String,
val moreMenuOptions: String,
val settingsHeaderBehaviour: String,
val settingsHeaderOpml: String,
val settingsHeaderFeedback: String,
val settingsBrowserTypeTitle: String,
val settingsBrowserTypeSubtitle: String,
Expand All @@ -59,6 +60,11 @@ data class TwineStrings(
val settingsVersion: (String, Int) -> String,
val settingsAboutTitle: String,
val settingsAboutSubtitle: String,
val settingsOpmlImport: String,
val settingsOpmlExport: String,
val settingsOpmlImporting: (Int) -> String,
val settingsOpmlExporting: (Int) -> String,
val settingsOpmlCancel: String,
val feeds: String,
val editFeeds: String,
val comments: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,10 @@ sealed interface SettingsEvent {
data class ToggleFeaturedItemBlur(val value: Boolean) : SettingsEvent

object AboutClicked : SettingsEvent

object ImportOpmlClicked : SettingsEvent

object ExportOpmlClicked : SettingsEvent

object CancelOpmlImportOrExport : SettingsEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate
import dev.sasikanth.rss.reader.app.AppInfo
import dev.sasikanth.rss.reader.opml.OpmlManager
import dev.sasikanth.rss.reader.repository.BrowserType
import dev.sasikanth.rss.reader.repository.RssRepository
import dev.sasikanth.rss.reader.repository.SettingsRepository
Expand All @@ -44,6 +45,7 @@ class SettingsPresenter(
private val settingsRepository: SettingsRepository,
private val rssRepository: RssRepository,
private val appInfo: AppInfo,
private val opmlManager: OpmlManager,
@Assisted componentContext: ComponentContext,
@Assisted private val goBack: () -> Unit,
@Assisted private val openAbout: () -> Unit,
Expand All @@ -55,7 +57,8 @@ class SettingsPresenter(
dispatchersProvider = dispatchersProvider,
appInfo = appInfo,
settingsRepository = settingsRepository,
rssRepository = rssRepository
rssRepository = rssRepository,
opmlManager = opmlManager,
)
}

Expand All @@ -76,8 +79,9 @@ class SettingsPresenter(
private class PresenterInstance(
dispatchersProvider: DispatchersProvider,
appInfo: AppInfo,
rssRepository: RssRepository,
private val rssRepository: RssRepository,
private val settingsRepository: SettingsRepository,
private val opmlManager: OpmlManager,
) : InstanceKeeper.Instance {

private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchersProvider.main)
Expand Down Expand Up @@ -109,6 +113,13 @@ class SettingsPresenter(
}
}
.launchIn(coroutineScope)

opmlManager.result
.onEach { result ->
println(result)
_state.update { it.copy(opmlResult = result) }
}
.launchIn(coroutineScope)
}

fun dispatch(event: SettingsEvent) {
Expand All @@ -121,9 +132,24 @@ class SettingsPresenter(
SettingsEvent.AboutClicked -> {
// no-op
}
SettingsEvent.ImportOpmlClicked -> importOpmlClicked()
SettingsEvent.ExportOpmlClicked -> exportOpmlClicked()
SettingsEvent.CancelOpmlImportOrExport -> cancelOpmlImportOrExport()
}
}

private fun cancelOpmlImportOrExport() {
opmlManager.cancel()
}

private fun exportOpmlClicked() {
coroutineScope.launch { opmlManager.export() }
}

private fun importOpmlClicked() {
coroutineScope.launch { opmlManager.import() }
}

private fun toggleFeaturedItemBlur(value: Boolean) {
coroutineScope.launch { settingsRepository.toggleFeaturedItemBlur(value) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package dev.sasikanth.rss.reader.settings

import androidx.compose.runtime.Immutable
import dev.sasikanth.rss.reader.app.AppInfo
import dev.sasikanth.rss.reader.opml.OpmlResult
import dev.sasikanth.rss.reader.repository.BrowserType

@Immutable
Expand All @@ -25,6 +26,7 @@ internal data class SettingsState(
val enableHomePageBlur: Boolean,
val hasFeeds: Boolean,
val appInfo: AppInfo,
val opmlResult: OpmlResult?,
) {

companion object {
Expand All @@ -34,7 +36,8 @@ internal data class SettingsState(
browserType = BrowserType.Default,
enableHomePageBlur = true,
hasFeeds = false,
appInfo = appInfo
appInfo = appInfo,
opmlResult = null
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package dev.sasikanth.rss.reader.settings.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
Expand All @@ -26,12 +27,14 @@ import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.Divider as MaterialDivider
import androidx.compose.material3.Icon
Expand All @@ -56,10 +59,13 @@ import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import dev.sasikanth.rss.reader.app.AppInfo
import dev.sasikanth.rss.reader.components.OutlinedButton
import dev.sasikanth.rss.reader.components.SubHeader
import dev.sasikanth.rss.reader.components.image.AsyncImage
import dev.sasikanth.rss.reader.opml.OpmlResult
import dev.sasikanth.rss.reader.platform.LocalLinkHandler
import dev.sasikanth.rss.reader.repository.BrowserType
import dev.sasikanth.rss.reader.resources.strings.LocalStrings
Expand Down Expand Up @@ -133,7 +139,7 @@ internal fun SettingsScreen(
}

if (canBlurImage) {
item { InsetDivider() }
item { Divider(horizontalInsets = 24.dp) }

item {
FeaturedItemBlurSettingItem(
Expand All @@ -145,13 +151,22 @@ internal fun SettingsScreen(
}
}

item { Divider(24.dp) }

item {
MaterialDivider(
modifier = Modifier.fillMaxWidth(),
color = AppTheme.colorScheme.surfaceContainer
OPMLSettingItem(
opmlResult = state.opmlResult,
hasFeeds = state.hasFeeds,
onImportClicked = { settingsPresenter.dispatch(SettingsEvent.ImportOpmlClicked) },
onExportClicked = { settingsPresenter.dispatch(SettingsEvent.ExportOpmlClicked) },
onCancelClicked = {
settingsPresenter.dispatch(SettingsEvent.CancelOpmlImportOrExport)
}
)
}

item { Divider() }

item {
SubHeader(
text = LocalStrings.current.settingsHeaderFeedback,
Expand All @@ -167,21 +182,11 @@ internal fun SettingsScreen(
)
}

item {
MaterialDivider(
modifier = Modifier.fillMaxWidth(),
color = AppTheme.colorScheme.surfaceContainer
)
}
item { Divider() }

item { AboutItem { settingsPresenter.dispatch(SettingsEvent.AboutClicked) } }

item {
MaterialDivider(
modifier = Modifier.fillMaxWidth(),
color = AppTheme.colorScheme.surfaceContainer
)
}
item { Divider() }
}
}
},
Expand Down Expand Up @@ -307,6 +312,100 @@ private fun BrowserTypeSettingItem(
}
}

@Composable
private fun OPMLSettingItem(
opmlResult: OpmlResult?,
hasFeeds: Boolean,
onImportClicked: () -> Unit,
onExportClicked: () -> Unit,
onCancelClicked: () -> Unit
) {
Column {
SubHeader(text = LocalStrings.current.settingsHeaderOpml)

when (opmlResult) {
is OpmlResult.InProgress.Importing,
is OpmlResult.InProgress.Exporting -> {
Row(
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
OutlinedButton(
modifier = Modifier.weight(1f),
onClick = {
// no-op
},
enabled = false,
colors =
ButtonDefaults.outlinedButtonColors(
containerColor = AppTheme.colorScheme.tintedSurface,
disabledContainerColor = AppTheme.colorScheme.tintedSurface,
contentColor = AppTheme.colorScheme.tintedForeground,
disabledContentColor = AppTheme.colorScheme.tintedForeground,
),
border = null
) {
val string =
when (opmlResult) {
is OpmlResult.InProgress.Importing -> {
LocalStrings.current.settingsOpmlImporting(opmlResult.progress)
}
is OpmlResult.InProgress.Exporting -> {
LocalStrings.current.settingsOpmlExporting(opmlResult.progress)
}
else -> {
""
}
}

Text(string)
}

OutlinedButton(
modifier = Modifier.weight(1f),
onClick = onCancelClicked,
colors =
ButtonDefaults.outlinedButtonColors(
containerColor = Color.Unspecified,
contentColor = AppTheme.colorScheme.tintedForeground,
),
) {
Text(LocalStrings.current.settingsOpmlCancel)
}
}
}

// TODO: Handle error states
OpmlResult.Idle,
OpmlResult.Error.NoContentInOpmlFile,
is OpmlResult.Error.UnknownFailure, -> {
Row(
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
OutlinedButton(
modifier = Modifier.weight(1f),
onClick = onImportClicked,
) {
Text(LocalStrings.current.settingsOpmlImport)
}

OutlinedButton(
modifier = Modifier.weight(1f),
enabled = hasFeeds,
onClick = onExportClicked,
) {
Text(LocalStrings.current.settingsOpmlExport)
}
}
}
null -> {
Box(Modifier.requiredHeight(64.dp))
}
}
}
}

@Composable
private fun ReportIssueItem(appInfo: AppInfo, onClick: () -> Unit) {
Box(modifier = Modifier.clickable(onClick = onClick)) {
Expand Down Expand Up @@ -398,9 +497,9 @@ private fun AboutProfileImages() {
}

@Composable
private fun InsetDivider() {
private fun Divider(horizontalInsets: Dp = 0.dp) {
MaterialDivider(
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp, horizontal = 24.dp),
modifier = Modifier.padding(vertical = 8.dp, horizontal = horizontalInsets),
color = AppTheme.colorScheme.surfaceContainer
)
}

0 comments on commit 2d20ac2

Please sign in to comment.