Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/src/main/java/de/readeckapp/domain/model/DefaultFilter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.readeckapp.domain.model

enum class DefaultFilter {
ALL,
UNREAD,
ARCHIVED,
FAVORITES
}
3 changes: 3 additions & 0 deletions app/src/main/java/de/readeckapp/io/prefs/SettingsDataStore.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.readeckapp.io.prefs

import de.readeckapp.domain.model.AutoSyncTimeframe
import de.readeckapp.domain.model.DefaultFilter
import de.readeckapp.domain.model.Theme
import kotlinx.coroutines.flow.StateFlow
import kotlinx.datetime.Instant
Expand Down Expand Up @@ -37,4 +38,6 @@ interface SettingsDataStore {
suspend fun getTheme(): Theme
suspend fun getZoomFactor(): Int
suspend fun saveZoomFactor(zoomFactor: Int)
suspend fun getDefaultFilter(): DefaultFilter
suspend fun saveDefaultFilter(defaultFilter: DefaultFilter)
}
14 changes: 14 additions & 0 deletions app/src/main/java/de/readeckapp/io/prefs/SettingsDataStoreImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import dagger.hilt.android.qualifiers.ApplicationContext
import de.readeckapp.domain.model.AutoSyncTimeframe
import de.readeckapp.domain.model.DefaultFilter
import de.readeckapp.domain.model.Theme
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand Down Expand Up @@ -36,6 +37,7 @@ class SettingsDataStoreImpl @Inject constructor(@ApplicationContext private val
private val KEY_ZOOM_FACTOR = intPreferencesKey("zoom_factor")
private val KEY_SYNC_READ_PROGRESS = booleanPreferencesKey("sync_read_progress")
private val KEY_SCROLL_TO_PROGRESS = booleanPreferencesKey("scroll_to_progress")
private val KEY_DEFAULT_FILTER = stringPreferencesKey("default_filter")

override fun saveUsername(username: String) {
Timber.d("saveUsername")
Expand Down Expand Up @@ -164,6 +166,18 @@ class SettingsDataStoreImpl @Inject constructor(@ApplicationContext private val
}
}

override suspend fun getDefaultFilter(): DefaultFilter {
return encryptedSharedPreferences.getString(KEY_DEFAULT_FILTER.name, DefaultFilter.ALL.name)?.let {
DefaultFilter.valueOf(it)
} ?: DefaultFilter.ALL
}

override suspend fun saveDefaultFilter(defaultFilter: DefaultFilter) {
encryptedSharedPreferences.edit {
putString(KEY_DEFAULT_FILTER.name, defaultFilter.name)
}
}

override val tokenFlow = getStringFlow(KEY_TOKEN.name, null)
override val usernameFlow = getStringFlow(KEY_USERNAME.name, null)
override val urlFlow = getStringFlow(KEY_URL.name, null)
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/de/readeckapp/ui/list/BookmarkListScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,18 @@ fun BookmarkListScreen(navHostController: NavHostController) {
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {
TopAppBar(
title = { Text(stringResource(id = R.string.bookmarks)) },
title = {
val titleRes = when {
Comment thread
jensomato marked this conversation as resolved.
filterState.value.unread == true -> R.string.unread
filterState.value.archived == true -> R.string.archive
filterState.value.favorite == true -> R.string.favorites
filterState.value.type == Bookmark.Type.Article -> R.string.articles
filterState.value.type == Bookmark.Type.Video -> R.string.videos
filterState.value.type == Bookmark.Type.Picture -> R.string.pictures
else -> R.string.bookmarks
}
Text(stringResource(id = titleRes))
},
navigationIcon = {
IconButton(
onClick = { scope.launch { drawerState.open() } }
Expand Down
22 changes: 16 additions & 6 deletions app/src/main/java/de/readeckapp/ui/list/BookmarkListViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import de.readeckapp.domain.BookmarkRepository
import de.readeckapp.domain.model.Bookmark
import de.readeckapp.domain.model.BookmarkCounts
import de.readeckapp.domain.model.BookmarkListItem
import de.readeckapp.domain.model.DefaultFilter
import de.readeckapp.domain.usecase.UpdateBookmarkUseCase
import de.readeckapp.io.prefs.SettingsDataStore
import de.readeckapp.util.extractUrlAndTitle
Expand Down Expand Up @@ -102,6 +103,14 @@ class BookmarkListViewModel @Inject constructor(
}

viewModelScope.launch(loadBookmarkExceptionHandler) {
_filterState.value = settingsDataStore.getDefaultFilter().toFilterState()

// Check if the initial sync has been performed
if (!settingsDataStore.isInitialSyncPerformed()) {
Timber.d("loadBookmarks")
loadBookmarks() // Start incremental sync when the ViewModel is created
}

filterState.collectLatest { filterState ->
bookmarkRepository.observeBookmarkListItems(
type = filterState.type,
Expand All @@ -117,12 +126,6 @@ class BookmarkListViewModel @Inject constructor(
}
}
}

// Check if the initial sync has been performed
if (!settingsDataStore.isInitialSyncPerformed()) {
Timber.d("loadBookmarks")
loadBookmarks() // Start incremental sync when the ViewModel is created
}
}
}

Expand Down Expand Up @@ -383,3 +386,10 @@ class BookmarkListViewModel @Inject constructor(
data class Error(val message: String) : UpdateBookmarkState()
}
}

private fun DefaultFilter.toFilterState(): BookmarkListViewModel.FilterState = when (this) {
DefaultFilter.ALL -> BookmarkListViewModel.FilterState()
DefaultFilter.UNREAD -> BookmarkListViewModel.FilterState(unread = true)
DefaultFilter.ARCHIVED -> BookmarkListViewModel.FilterState(archived = true)
DefaultFilter.FAVORITES -> BookmarkListViewModel.FilterState(favorite = true)
}
Comment thread
jensomato marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.readeckapp.ui.settings

import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand All @@ -23,14 +24,21 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import de.readeckapp.R
import de.readeckapp.domain.model.Theme
import de.readeckapp.ui.theme.Typography

data class SelectableOption<T>(
val value: T,
@StringRes
val label: Int,
val selected: Boolean
)

@Composable
fun ThemeDialog(
themeOptions: List<ThemeOption>,
fun <T> SingleChoiceDialog(
@StringRes supportText: Int,
options: List<SelectableOption<T>>,
onDismissRequest: () -> Unit,
onElementSelected: (selected: Theme) -> Unit,
onElementSelected: (selected: T) -> Unit,
) {
Dialog(
onDismissRequest = onDismissRequest
Expand All @@ -48,19 +56,19 @@ fun ThemeDialog(
modifier = Modifier.padding(24.dp)
) {
Text(
text = stringResource(R.string.ui_settings_theme_dialog_support_text),
text = stringResource(supportText),
style = Typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
HorizontalDivider()
themeOptions.forEach { option ->
options.forEach { option ->
Row(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.selectable(
selected = option.selected,
onClick = { onElementSelected(option.theme) },
onClick = { onElementSelected(option.value) },
role = Role.RadioButton
),
verticalAlignment = Alignment.CenterVertically
Expand Down Expand Up @@ -94,24 +102,13 @@ fun ThemeDialog(

@Preview
@Composable
fun ThemeDialogPreview() {
ThemeDialog(
themeOptions = listOf(
ThemeOption(
theme = Theme.SYSTEM,
label = R.string.theme_system,
selected = true
),
ThemeOption(
theme = Theme.LIGHT,
label = R.string.theme_light,
selected = false
),
ThemeOption(
theme = Theme.DARK,
label = R.string.theme_dark,
selected = false
),
private fun SingleChoiceDialogPreview() {
SingleChoiceDialog(
supportText = R.string.ui_settings_theme_dialog_support_text,
options = listOf(
SelectableOption(value = "a", label = R.string.theme_system, selected = true),
SelectableOption(value = "b", label = R.string.theme_light, selected = false),
SelectableOption(value = "c", label = R.string.theme_dark, selected = false),
),
onDismissRequest = {},
onElementSelected = {}
Expand Down
36 changes: 34 additions & 2 deletions app/src/main/java/de/readeckapp/ui/settings/UiSettingsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import androidx.navigation.NavHostController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import de.readeckapp.R
import de.readeckapp.domain.model.AutoSyncTimeframe
import de.readeckapp.domain.model.DefaultFilter
import de.readeckapp.domain.model.Theme
import de.readeckapp.ui.theme.Typography

Expand All @@ -45,6 +46,7 @@ fun UiSettingsScreen(
val navigationEvent = viewModel.navigationEvent.collectAsState()
val onClickBack: () -> Unit = { viewModel.onClickBack() }
val onClickTheme: () -> Unit = { viewModel.onClickTheme() }
val onClickDefaultFilter: () -> Unit = { viewModel.onClickDefaultFilter() }
val onScrollToProgressToggle: (Boolean) -> Unit = { viewModel.onScrollToProgressToggle(it) }
val snackbarHostState = remember { SnackbarHostState() }

Expand All @@ -60,18 +62,29 @@ fun UiSettingsScreen(
}

if (settingsUiState.showDialog) {
ThemeDialog(
themeOptions = settingsUiState.themeOptions,
SingleChoiceDialog(
supportText = R.string.ui_settings_theme_dialog_support_text,
options = settingsUiState.themeOptions,
onDismissRequest = { viewModel.onDismissDialog() },
onElementSelected = { viewModel.onThemeSelected(it) }
)
}

if (settingsUiState.showDefaultFilterDialog) {
SingleChoiceDialog(
supportText = R.string.ui_settings_default_filter_dialog_support_text,
options = settingsUiState.defaultFilterOptions,
onDismissRequest = { viewModel.onDismissDefaultFilterDialog() },
onElementSelected = { viewModel.onDefaultFilterSelected(it) }
)
}

UiSettingsView(
modifier = Modifier,
snackbarHostState = snackbarHostState,
onClickBack = onClickBack,
onClickTheme = onClickTheme,
onClickDefaultFilter = onClickDefaultFilter,
onScrollToProgressToggle = onScrollToProgressToggle,
settingsUiState = settingsUiState
)
Expand All @@ -85,6 +98,7 @@ fun UiSettingsView(
snackbarHostState: SnackbarHostState,
settingsUiState: UiSettingsUiState,
onClickTheme: () -> Unit,
onClickDefaultFilter: () -> Unit,
onScrollToProgressToggle: (Boolean) -> Unit,
onClickBack: () -> Unit,
) {
Expand Down Expand Up @@ -129,6 +143,19 @@ fun UiSettingsView(
)
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable(enabled = true, onClick = onClickDefaultFilter)
) {
Column(modifier = Modifier.weight(1f)) {
Text(stringResource(R.string.ui_settings_default_filter_title))
Text(
text = stringResource(settingsUiState.defaultFilterLabel),
style = Typography.bodySmall
)
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
Expand Down Expand Up @@ -158,12 +185,17 @@ fun UiSettingsScreenViewPreview() {
showDialog = false,
themeLabel = Theme.SYSTEM.toLabelResource(),
scrollToProgressEnabled = true,
defaultFilter = DefaultFilter.ALL,
defaultFilterLabel = DefaultFilter.ALL.toLabelResource(),
defaultFilterOptions = listOf(),
showDefaultFilterDialog = false,
)
UiSettingsView(
modifier = Modifier,
snackbarHostState = SnackbarHostState(),
onClickBack = {},
onClickTheme = {},
onClickDefaultFilter = {},
settingsUiState = settingsUiState,
onScrollToProgressToggle = {},
)
Expand Down
Loading
Loading