mirror of
https://github.com/readrops/Readrops.git
synced 2025-02-09 08:28:39 +01:00
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.feeds.FeedScreenModel
|
||||
import com.readrops.app.item.ItemScreenModel
|
||||
import com.readrops.app.more.preferences.PreferencesScreenModel
|
||||
import com.readrops.app.notifications.NotificationsScreenModel
|
||||
import com.readrops.app.repositories.BaseRepository
|
||||
import com.readrops.app.repositories.FreshRSSRepository
|
||||
@ -51,6 +52,8 @@ val composeAppModule = module {
|
||||
|
||||
factory { (account: Account) -> NotificationsScreenModel(account, get()) }
|
||||
|
||||
factory { PreferencesScreenModel(get()) }
|
||||
|
||||
single { GetFoldersWithFeeds(get()) }
|
||||
|
||||
// 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.spacing
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
|
||||
object MoreTab : Tab, KoinComponent {
|
||||
|
||||
@ -140,7 +139,7 @@ object MoreTab : Tab, KoinComponent {
|
||||
spacing = MaterialTheme.spacing.largeSpacing,
|
||||
padding = MaterialTheme.spacing.mediumSpacing,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
onClick = { navigator.push(PreferencesScreen(get())) }
|
||||
onClick = { navigator.push(PreferencesScreen()) }
|
||||
)
|
||||
|
||||
SelectableIconText(
|
||||
|
@ -12,25 +12,30 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
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.currentOrThrow
|
||||
import com.readrops.app.R
|
||||
import com.readrops.app.more.preferences.components.ListPreferenceWidget
|
||||
import com.readrops.app.more.preferences.components.PreferenceHeader
|
||||
import com.readrops.app.more.preferences.components.SwitchPreferenceWidget
|
||||
import com.readrops.app.util.Preferences
|
||||
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)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val screenModel = getScreenModel<PreferencesScreenModel>()
|
||||
|
||||
val state by screenModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@ -52,60 +57,74 @@ class PreferencesScreen(val preferences: Preferences) : AndroidScreen(), KoinCom
|
||||
Box(
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
) {
|
||||
Column {
|
||||
PreferenceHeader(text = stringResource(id = R.string.global))
|
||||
when (state) {
|
||||
is PreferencesScreenState.Loading -> {
|
||||
CenteredProgressIndicator()
|
||||
}
|
||||
else -> {
|
||||
val loadedState = (state as PreferencesScreenState.Loaded)
|
||||
|
||||
ListPreferenceWidget(
|
||||
preference = preferences.theme,
|
||||
entries = mapOf(
|
||||
"light" to stringResource(id = R.string.light),
|
||||
"dark" to stringResource(id = R.string.dark),
|
||||
"system" to stringResource(id = R.string.system)
|
||||
),
|
||||
title = stringResource(id = R.string.theme),
|
||||
onValueChange = {}
|
||||
)
|
||||
Column {
|
||||
PreferenceHeader(text = stringResource(id = R.string.global))
|
||||
|
||||
ListPreferenceWidget(
|
||||
preference = preferences.backgroundSynchronization,
|
||||
entries = mapOf(
|
||||
"manual" to stringResource(id = R.string.manual),
|
||||
"0.30" to stringResource(id = R.string.min_30),
|
||||
"1" to stringResource(id = R.string.hour_1),
|
||||
"2" to stringResource(id = R.string.hour_2),
|
||||
"3" to stringResource(id = R.string.hour_3),
|
||||
"6" to stringResource(id = R.string.hour_6),
|
||||
"12" to stringResource(id = R.string.hour_12),
|
||||
"24" to stringResource(id = R.string.every_day)
|
||||
),
|
||||
title = stringResource(id = R.string.auto_synchro),
|
||||
onValueChange = {}
|
||||
)
|
||||
ListPreferenceWidget(
|
||||
preference = loadedState.themePref.second,
|
||||
selectedKey = loadedState.themePref.first,
|
||||
entries = mapOf(
|
||||
"light" to stringResource(id = R.string.light),
|
||||
"dark" to stringResource(id = R.string.dark),
|
||||
"system" to stringResource(id = R.string.system)
|
||||
),
|
||||
title = stringResource(id = R.string.theme),
|
||||
onValueChange = {}
|
||||
)
|
||||
|
||||
PreferenceHeader(text = "Timeline")
|
||||
ListPreferenceWidget(
|
||||
preference = loadedState.backgroundSyncPref.second,
|
||||
selectedKey = loadedState.backgroundSyncPref.first,
|
||||
entries = mapOf(
|
||||
"manual" to stringResource(id = R.string.manual),
|
||||
"0.30" to stringResource(id = R.string.min_30),
|
||||
"1" to stringResource(id = R.string.hour_1),
|
||||
"2" to stringResource(id = R.string.hour_2),
|
||||
"3" to stringResource(id = R.string.hour_3),
|
||||
"6" to stringResource(id = R.string.hour_6),
|
||||
"12" to stringResource(id = R.string.hour_12),
|
||||
"24" to stringResource(id = R.string.every_day)
|
||||
),
|
||||
title = stringResource(id = R.string.auto_synchro),
|
||||
onValueChange = {}
|
||||
)
|
||||
|
||||
SwitchPreferenceWidget(
|
||||
preference = preferences.hideReadFeeds,
|
||||
title = stringResource(id = R.string.hide_feeds),
|
||||
subtitle = "Feeds with no left unread items will be hidden with their respective folder"
|
||||
)
|
||||
PreferenceHeader(text = stringResource(id = R.string.timeline))
|
||||
|
||||
SwitchPreferenceWidget(
|
||||
preference = preferences.scrollRead,
|
||||
title = stringResource(id = R.string.mark_items_read)
|
||||
)
|
||||
SwitchPreferenceWidget(
|
||||
preference = loadedState.hideReadFeeds.second,
|
||||
isChecked = loadedState.hideReadFeeds.first,
|
||||
title = stringResource(id = R.string.hide_feeds),
|
||||
subtitle = stringResource(R.string.hide_feeds_subtitle)
|
||||
)
|
||||
|
||||
PreferenceHeader(text = "Item view")
|
||||
SwitchPreferenceWidget(
|
||||
preference = loadedState.scrollReadPref.second,
|
||||
isChecked = loadedState.scrollReadPref.first,
|
||||
title = stringResource(id = R.string.mark_items_read)
|
||||
)
|
||||
|
||||
ListPreferenceWidget(
|
||||
preference = preferences.openLinksWith,
|
||||
entries = mapOf(
|
||||
"navigator_view" to stringResource(id = R.string.navigator_view),
|
||||
"external_navigator" to stringResource(id = R.string.external_navigator)
|
||||
),
|
||||
title = stringResource(id = R.string.open_items_in),
|
||||
onValueChange = {}
|
||||
)
|
||||
PreferenceHeader(text = stringResource(id = R.string.item_view))
|
||||
|
||||
ListPreferenceWidget(
|
||||
preference = loadedState.openLinksWith.second,
|
||||
selectedKey = loadedState.openLinksWith.first,
|
||||
entries = mapOf(
|
||||
"navigator_view" to stringResource(id = R.string.navigator_view),
|
||||
"external_navigator" to stringResource(id = R.string.external_navigator)
|
||||
),
|
||||
title = stringResource(id = R.string.open_items_in),
|
||||
onValueChange = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.toMutableStateList
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.readrops.app.util.Preference
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun <T> ListPreferenceWidget(
|
||||
preference: Preference<T>,
|
||||
selectedKey: T,
|
||||
entries: Map<T, String>,
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
@ -22,7 +22,6 @@ fun <T> ListPreferenceWidget(
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
val selectedKey by preference.flow.collectAsStateWithLifecycle(initialValue = preference.default)
|
||||
|
||||
if (showDialog) {
|
||||
val values = remember {
|
||||
|
@ -15,6 +15,9 @@ fun PreferenceHeader(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
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.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.readrops.app.util.Preference
|
||||
@ -12,11 +10,11 @@ import kotlinx.coroutines.launch
|
||||
@Composable
|
||||
fun SwitchPreferenceWidget(
|
||||
preference: Preference<Boolean>,
|
||||
isChecked: Boolean,
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
subtitle: String? = null,
|
||||
) {
|
||||
val isChecked by preference.flow.collectAsState(initial = preference.default)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
BasePreference(
|
||||
|
@ -177,4 +177,7 @@
|
||||
<string name="url">URL</string>
|
||||
<string name="unread">%1$d non-lu(s)</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>
|
@ -186,4 +186,7 @@
|
||||
<string name="url">URL</string>
|
||||
<string name="unread">%1$d unread</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>
|
Loading…
x
Reference in New Issue
Block a user