Improve PreferencesScreen state management
This commit is contained in:
parent
da5b030ab6
commit
965fb2ea67
@ -15,6 +15,7 @@ import com.readrops.app.account.credentials.AccountCredentialsScreenModel
|
|||||||
import com.readrops.app.account.selection.AccountSelectionScreenModel
|
import com.readrops.app.account.selection.AccountSelectionScreenModel
|
||||||
import com.readrops.app.feeds.FeedScreenModel
|
import com.readrops.app.feeds.FeedScreenModel
|
||||||
import com.readrops.app.item.ItemScreenModel
|
import com.readrops.app.item.ItemScreenModel
|
||||||
|
import com.readrops.app.more.preferences.PreferencesScreenModel
|
||||||
import com.readrops.app.notifications.NotificationsScreenModel
|
import com.readrops.app.notifications.NotificationsScreenModel
|
||||||
import com.readrops.app.repositories.BaseRepository
|
import com.readrops.app.repositories.BaseRepository
|
||||||
import com.readrops.app.repositories.FreshRSSRepository
|
import com.readrops.app.repositories.FreshRSSRepository
|
||||||
@ -51,6 +52,8 @@ val composeAppModule = module {
|
|||||||
|
|
||||||
factory { (account: Account) -> NotificationsScreenModel(account, get()) }
|
factory { (account: Account) -> NotificationsScreenModel(account, get()) }
|
||||||
|
|
||||||
|
factory { PreferencesScreenModel(get()) }
|
||||||
|
|
||||||
single { GetFoldersWithFeeds(get()) }
|
single { GetFoldersWithFeeds(get()) }
|
||||||
|
|
||||||
// repositories
|
// repositories
|
||||||
|
@ -36,7 +36,6 @@ import com.readrops.app.util.theme.MediumSpacer
|
|||||||
import com.readrops.app.util.theme.ShortSpacer
|
import com.readrops.app.util.theme.ShortSpacer
|
||||||
import com.readrops.app.util.theme.spacing
|
import com.readrops.app.util.theme.spacing
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.get
|
|
||||||
|
|
||||||
object MoreTab : Tab, KoinComponent {
|
object MoreTab : Tab, KoinComponent {
|
||||||
|
|
||||||
@ -140,7 +139,7 @@ object MoreTab : Tab, KoinComponent {
|
|||||||
spacing = MaterialTheme.spacing.largeSpacing,
|
spacing = MaterialTheme.spacing.largeSpacing,
|
||||||
padding = MaterialTheme.spacing.mediumSpacing,
|
padding = MaterialTheme.spacing.mediumSpacing,
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
onClick = { navigator.push(PreferencesScreen(get())) }
|
onClick = { navigator.push(PreferencesScreen()) }
|
||||||
)
|
)
|
||||||
|
|
||||||
SelectableIconText(
|
SelectableIconText(
|
||||||
|
@ -12,25 +12,30 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import cafe.adriel.voyager.koin.getScreenModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import com.readrops.app.R
|
import com.readrops.app.R
|
||||||
import com.readrops.app.more.preferences.components.ListPreferenceWidget
|
import com.readrops.app.more.preferences.components.ListPreferenceWidget
|
||||||
import com.readrops.app.more.preferences.components.PreferenceHeader
|
import com.readrops.app.more.preferences.components.PreferenceHeader
|
||||||
import com.readrops.app.more.preferences.components.SwitchPreferenceWidget
|
import com.readrops.app.more.preferences.components.SwitchPreferenceWidget
|
||||||
import com.readrops.app.util.Preferences
|
|
||||||
import com.readrops.app.util.components.AndroidScreen
|
import com.readrops.app.util.components.AndroidScreen
|
||||||
import org.koin.core.component.KoinComponent
|
import com.readrops.app.util.components.CenteredProgressIndicator
|
||||||
|
|
||||||
|
|
||||||
class PreferencesScreen(val preferences: Preferences) : AndroidScreen(), KoinComponent {
|
class PreferencesScreen : AndroidScreen() {
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
val screenModel = getScreenModel<PreferencesScreenModel>()
|
||||||
|
|
||||||
|
val state by screenModel.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@ -52,11 +57,19 @@ class PreferencesScreen(val preferences: Preferences) : AndroidScreen(), KoinCom
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier.padding(paddingValues)
|
modifier = Modifier.padding(paddingValues)
|
||||||
) {
|
) {
|
||||||
|
when (state) {
|
||||||
|
is PreferencesScreenState.Loading -> {
|
||||||
|
CenteredProgressIndicator()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val loadedState = (state as PreferencesScreenState.Loaded)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
PreferenceHeader(text = stringResource(id = R.string.global))
|
PreferenceHeader(text = stringResource(id = R.string.global))
|
||||||
|
|
||||||
ListPreferenceWidget(
|
ListPreferenceWidget(
|
||||||
preference = preferences.theme,
|
preference = loadedState.themePref.second,
|
||||||
|
selectedKey = loadedState.themePref.first,
|
||||||
entries = mapOf(
|
entries = mapOf(
|
||||||
"light" to stringResource(id = R.string.light),
|
"light" to stringResource(id = R.string.light),
|
||||||
"dark" to stringResource(id = R.string.dark),
|
"dark" to stringResource(id = R.string.dark),
|
||||||
@ -67,7 +80,8 @@ class PreferencesScreen(val preferences: Preferences) : AndroidScreen(), KoinCom
|
|||||||
)
|
)
|
||||||
|
|
||||||
ListPreferenceWidget(
|
ListPreferenceWidget(
|
||||||
preference = preferences.backgroundSynchronization,
|
preference = loadedState.backgroundSyncPref.second,
|
||||||
|
selectedKey = loadedState.backgroundSyncPref.first,
|
||||||
entries = mapOf(
|
entries = mapOf(
|
||||||
"manual" to stringResource(id = R.string.manual),
|
"manual" to stringResource(id = R.string.manual),
|
||||||
"0.30" to stringResource(id = R.string.min_30),
|
"0.30" to stringResource(id = R.string.min_30),
|
||||||
@ -82,23 +96,26 @@ class PreferencesScreen(val preferences: Preferences) : AndroidScreen(), KoinCom
|
|||||||
onValueChange = {}
|
onValueChange = {}
|
||||||
)
|
)
|
||||||
|
|
||||||
PreferenceHeader(text = "Timeline")
|
PreferenceHeader(text = stringResource(id = R.string.timeline))
|
||||||
|
|
||||||
SwitchPreferenceWidget(
|
SwitchPreferenceWidget(
|
||||||
preference = preferences.hideReadFeeds,
|
preference = loadedState.hideReadFeeds.second,
|
||||||
|
isChecked = loadedState.hideReadFeeds.first,
|
||||||
title = stringResource(id = R.string.hide_feeds),
|
title = stringResource(id = R.string.hide_feeds),
|
||||||
subtitle = "Feeds with no left unread items will be hidden with their respective folder"
|
subtitle = stringResource(R.string.hide_feeds_subtitle)
|
||||||
)
|
)
|
||||||
|
|
||||||
SwitchPreferenceWidget(
|
SwitchPreferenceWidget(
|
||||||
preference = preferences.scrollRead,
|
preference = loadedState.scrollReadPref.second,
|
||||||
|
isChecked = loadedState.scrollReadPref.first,
|
||||||
title = stringResource(id = R.string.mark_items_read)
|
title = stringResource(id = R.string.mark_items_read)
|
||||||
)
|
)
|
||||||
|
|
||||||
PreferenceHeader(text = "Item view")
|
PreferenceHeader(text = stringResource(id = R.string.item_view))
|
||||||
|
|
||||||
ListPreferenceWidget(
|
ListPreferenceWidget(
|
||||||
preference = preferences.openLinksWith,
|
preference = loadedState.openLinksWith.second,
|
||||||
|
selectedKey = loadedState.openLinksWith.first,
|
||||||
entries = mapOf(
|
entries = mapOf(
|
||||||
"navigator_view" to stringResource(id = R.string.navigator_view),
|
"navigator_view" to stringResource(id = R.string.navigator_view),
|
||||||
"external_navigator" to stringResource(id = R.string.external_navigator)
|
"external_navigator" to stringResource(id = R.string.external_navigator)
|
||||||
@ -111,4 +128,6 @@ class PreferencesScreen(val preferences: Preferences) : AndroidScreen(), KoinCom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.readrops.app.more.preferences
|
||||||
|
|
||||||
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
|
import com.readrops.app.util.Preference
|
||||||
|
import com.readrops.app.util.Preferences
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
typealias PreferenceState<T> = Pair<T, Preference<T>>
|
||||||
|
|
||||||
|
class PreferencesScreenModel(
|
||||||
|
preferences: Preferences,
|
||||||
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
|
) : StateScreenModel<PreferencesScreenState>(PreferencesScreenState.Loading) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
screenModelScope.launch(dispatcher) {
|
||||||
|
combine(
|
||||||
|
preferences.theme.flow,
|
||||||
|
preferences.backgroundSynchronization.flow,
|
||||||
|
preferences.scrollRead.flow,
|
||||||
|
preferences.hideReadFeeds.flow,
|
||||||
|
preferences.openLinksWith.flow
|
||||||
|
) { (theme, backgroundSync, scrollRead, hideReadFeeds, openLinksWith) ->
|
||||||
|
PreferencesScreenState.Loaded(
|
||||||
|
themePref = (theme as String) to preferences.theme,
|
||||||
|
backgroundSyncPref = (backgroundSync as String) to preferences.backgroundSynchronization,
|
||||||
|
scrollReadPref = (scrollRead as Boolean) to preferences.scrollRead,
|
||||||
|
hideReadFeeds = (hideReadFeeds as Boolean) to preferences.hideReadFeeds,
|
||||||
|
openLinksWith = (openLinksWith as String) to preferences.openLinksWith
|
||||||
|
)
|
||||||
|
}.collect { theme ->
|
||||||
|
mutableState.update { theme }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class PreferencesScreenState {
|
||||||
|
data object Loading : PreferencesScreenState()
|
||||||
|
data object Error : PreferencesScreenState()
|
||||||
|
|
||||||
|
data class Loaded(
|
||||||
|
val themePref: PreferenceState<String>,
|
||||||
|
val backgroundSyncPref: PreferenceState<String>,
|
||||||
|
val scrollReadPref: PreferenceState<Boolean>,
|
||||||
|
val hideReadFeeds: PreferenceState<Boolean>,
|
||||||
|
val openLinksWith: PreferenceState<String>
|
||||||
|
) : PreferencesScreenState()
|
||||||
|
|
||||||
|
}
|
@ -8,13 +8,13 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.toMutableStateList
|
import androidx.compose.runtime.toMutableStateList
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import com.readrops.app.util.Preference
|
import com.readrops.app.util.Preference
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> ListPreferenceWidget(
|
fun <T> ListPreferenceWidget(
|
||||||
preference: Preference<T>,
|
preference: Preference<T>,
|
||||||
|
selectedKey: T,
|
||||||
entries: Map<T, String>,
|
entries: Map<T, String>,
|
||||||
title: String,
|
title: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@ -22,7 +22,6 @@ fun <T> ListPreferenceWidget(
|
|||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
val selectedKey by preference.flow.collectAsStateWithLifecycle(initialValue = preference.default)
|
|
||||||
|
|
||||||
if (showDialog) {
|
if (showDialog) {
|
||||||
val values = remember {
|
val values = remember {
|
||||||
|
@ -15,6 +15,9 @@ fun PreferenceHeader(
|
|||||||
text = text,
|
text = text,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
modifier = Modifier.padding(MaterialTheme.spacing.shortSpacing)
|
modifier = Modifier.padding(
|
||||||
|
horizontal = MaterialTheme.spacing.mediumSpacing,
|
||||||
|
vertical = MaterialTheme.spacing.shortSpacing
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -2,8 +2,6 @@ package com.readrops.app.more.preferences.components
|
|||||||
|
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.readrops.app.util.Preference
|
import com.readrops.app.util.Preference
|
||||||
@ -12,11 +10,11 @@ import kotlinx.coroutines.launch
|
|||||||
@Composable
|
@Composable
|
||||||
fun SwitchPreferenceWidget(
|
fun SwitchPreferenceWidget(
|
||||||
preference: Preference<Boolean>,
|
preference: Preference<Boolean>,
|
||||||
|
isChecked: Boolean,
|
||||||
title: String,
|
title: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
subtitle: String? = null,
|
subtitle: String? = null,
|
||||||
) {
|
) {
|
||||||
val isChecked by preference.flow.collectAsState(initial = preference.default)
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
BasePreference(
|
BasePreference(
|
||||||
|
@ -177,4 +177,7 @@
|
|||||||
<string name="url">URL</string>
|
<string name="url">URL</string>
|
||||||
<string name="unread">%1$d non-lu(s)</string>
|
<string name="unread">%1$d non-lu(s)</string>
|
||||||
<string name="preferences">Paramètres</string>
|
<string name="preferences">Paramètres</string>
|
||||||
|
<string name="item_view">Vue article</string>
|
||||||
|
<string name="timeline">Timeline</string>
|
||||||
|
<string name="hide_feeds_subtitle">Les flux sans nouveaux articles disparaîtront du menu tiroir</string>
|
||||||
</resources>
|
</resources>
|
@ -186,4 +186,7 @@
|
|||||||
<string name="url">URL</string>
|
<string name="url">URL</string>
|
||||||
<string name="unread">%1$d unread</string>
|
<string name="unread">%1$d unread</string>
|
||||||
<string name="preferences">Preferences</string>
|
<string name="preferences">Preferences</string>
|
||||||
|
<string name="item_view">Article view</string>
|
||||||
|
<string name="timeline">Timeline</string>
|
||||||
|
<string name="hide_feeds_subtitle">Feeds with no left unread items will be hidden in the drawer</string>
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user