diff --git a/app/src/main/java/de/readeckapp/domain/model/DefaultFilter.kt b/app/src/main/java/de/readeckapp/domain/model/DefaultFilter.kt new file mode 100644 index 0000000..532ba47 --- /dev/null +++ b/app/src/main/java/de/readeckapp/domain/model/DefaultFilter.kt @@ -0,0 +1,8 @@ +package de.readeckapp.domain.model + +enum class DefaultFilter { + ALL, + UNREAD, + ARCHIVED, + FAVORITES +} diff --git a/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStore.kt b/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStore.kt index c90d7c1..260772c 100644 --- a/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStore.kt +++ b/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStore.kt @@ -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 @@ -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) } diff --git a/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStoreImpl.kt b/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStoreImpl.kt index 1609ff5..12fc805 100644 --- a/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStoreImpl.kt +++ b/app/src/main/java/de/readeckapp/io/prefs/SettingsDataStoreImpl.kt @@ -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 @@ -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") @@ -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) diff --git a/app/src/main/java/de/readeckapp/ui/list/BookmarkListScreen.kt b/app/src/main/java/de/readeckapp/ui/list/BookmarkListScreen.kt index 2336ab9..0c33043 100644 --- a/app/src/main/java/de/readeckapp/ui/list/BookmarkListScreen.kt +++ b/app/src/main/java/de/readeckapp/ui/list/BookmarkListScreen.kt @@ -332,7 +332,18 @@ fun BookmarkListScreen(navHostController: NavHostController) { snackbarHost = { SnackbarHost(snackbarHostState) }, topBar = { TopAppBar( - title = { Text(stringResource(id = R.string.bookmarks)) }, + title = { + val titleRes = when { + 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() } } diff --git a/app/src/main/java/de/readeckapp/ui/list/BookmarkListViewModel.kt b/app/src/main/java/de/readeckapp/ui/list/BookmarkListViewModel.kt index 6b7bcdc..3bf0ca2 100644 --- a/app/src/main/java/de/readeckapp/ui/list/BookmarkListViewModel.kt +++ b/app/src/main/java/de/readeckapp/ui/list/BookmarkListViewModel.kt @@ -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 @@ -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, @@ -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 - } } } @@ -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) +} diff --git a/app/src/main/java/de/readeckapp/ui/settings/ThemeDialog.kt b/app/src/main/java/de/readeckapp/ui/settings/SingleChoiceDialog.kt similarity index 79% rename from app/src/main/java/de/readeckapp/ui/settings/ThemeDialog.kt rename to app/src/main/java/de/readeckapp/ui/settings/SingleChoiceDialog.kt index 3f02c04..0661763 100644 --- a/app/src/main/java/de/readeckapp/ui/settings/ThemeDialog.kt +++ b/app/src/main/java/de/readeckapp/ui/settings/SingleChoiceDialog.kt @@ -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 @@ -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( + val value: T, + @StringRes + val label: Int, + val selected: Boolean +) + @Composable -fun ThemeDialog( - themeOptions: List, +fun SingleChoiceDialog( + @StringRes supportText: Int, + options: List>, onDismissRequest: () -> Unit, - onElementSelected: (selected: Theme) -> Unit, + onElementSelected: (selected: T) -> Unit, ) { Dialog( onDismissRequest = onDismissRequest @@ -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 @@ -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 = {} diff --git a/app/src/main/java/de/readeckapp/ui/settings/UiSettingsScreen.kt b/app/src/main/java/de/readeckapp/ui/settings/UiSettingsScreen.kt index 95830f2..bed5bf3 100644 --- a/app/src/main/java/de/readeckapp/ui/settings/UiSettingsScreen.kt +++ b/app/src/main/java/de/readeckapp/ui/settings/UiSettingsScreen.kt @@ -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 @@ -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() } @@ -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 ) @@ -85,6 +98,7 @@ fun UiSettingsView( snackbarHostState: SnackbarHostState, settingsUiState: UiSettingsUiState, onClickTheme: () -> Unit, + onClickDefaultFilter: () -> Unit, onScrollToProgressToggle: (Boolean) -> Unit, onClickBack: () -> Unit, ) { @@ -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, @@ -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 = {}, ) diff --git a/app/src/main/java/de/readeckapp/ui/settings/UiSettingsViewModel.kt b/app/src/main/java/de/readeckapp/ui/settings/UiSettingsViewModel.kt index 1fe619b..63e7672 100644 --- a/app/src/main/java/de/readeckapp/ui/settings/UiSettingsViewModel.kt +++ b/app/src/main/java/de/readeckapp/ui/settings/UiSettingsViewModel.kt @@ -9,6 +9,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import de.readeckapp.R +import de.readeckapp.domain.model.DefaultFilter import de.readeckapp.domain.model.Theme import de.readeckapp.io.prefs.SettingsDataStore import kotlinx.coroutines.flow.MutableStateFlow @@ -33,22 +34,29 @@ class UiSettingsViewModel @Inject constructor( private val theme = MutableStateFlow(Theme.SYSTEM) private val scrollToProgressEnabled = MutableStateFlow(true) private val showDialog = MutableStateFlow(false) + private val defaultFilter = MutableStateFlow(DefaultFilter.ALL) + private val showDefaultFilterDialog = MutableStateFlow(false) init { viewModelScope.launch { theme.value = settingsDataStore.getTheme() scrollToProgressEnabled.value = settingsDataStore.isScrollToProgressEnabled() + defaultFilter.value = settingsDataStore.getDefaultFilter() } } - val uiState = combine(theme, scrollToProgressEnabled, showDialog) { theme, scrollToProgressEnabled, showDialog -> + val uiState = combine(theme, scrollToProgressEnabled, showDialog, defaultFilter, showDefaultFilterDialog) { theme, scrollToProgressEnabled, showDialog, defaultFilter, showDefaultFilterDialog -> UiSettingsUiState( theme = theme, scrollToProgressEnabled = scrollToProgressEnabled, themeOptions = getThemeOptionList(theme), showDialog = showDialog, themeLabel = theme.toLabelResource(), + defaultFilter = defaultFilter, + defaultFilterLabel = defaultFilter.toLabelResource(), + defaultFilterOptions = getDefaultFilterOptionList(defaultFilter), + showDefaultFilterDialog = showDefaultFilterDialog, ) } .stateIn( @@ -61,6 +69,10 @@ class UiSettingsViewModel @Inject constructor( themeOptions = getThemeOptionList(Theme.SYSTEM), showDialog = false, themeLabel = Theme.SYSTEM.toLabelResource(), + defaultFilter = DefaultFilter.ALL, + defaultFilterLabel = DefaultFilter.ALL.toLabelResource(), + defaultFilterOptions = getDefaultFilterOptionList(DefaultFilter.ALL), + showDefaultFilterDialog = false, ) ) @@ -92,14 +104,37 @@ class UiSettingsViewModel @Inject constructor( _navigationEvent.update { NavigationEvent.NavigateBack } } + fun onClickDefaultFilter() { + showDefaultFilterDialog.value = true + } + + fun onDismissDefaultFilterDialog() { + showDefaultFilterDialog.value = false + } + + fun onDefaultFilterSelected(selected: DefaultFilter) { + Timber.d("onDefaultFilterSelected [selected=$selected]") + updateDefaultFilter(selected) + } + sealed class NavigationEvent { data object NavigateBack : NavigationEvent() } - private fun getThemeOptionList(selected: Theme): List { + private fun getThemeOptionList(selected: Theme): List> { return Theme.entries.map { - ThemeOption( - theme = it, + SelectableOption( + value = it, + label = it.toLabelResource(), + selected = it == selected + ) + } + } + + private fun getDefaultFilterOptionList(selected: DefaultFilter): List> { + return DefaultFilter.entries.map { + SelectableOption( + value = it, label = it.toLabelResource(), selected = it == selected ) @@ -112,23 +147,28 @@ class UiSettingsViewModel @Inject constructor( theme.value = settingsDataStore.getTheme() } } + + private fun updateDefaultFilter(value: DefaultFilter) { + viewModelScope.launch { + settingsDataStore.saveDefaultFilter(value) + defaultFilter.value = settingsDataStore.getDefaultFilter() + } + } } @Immutable data class UiSettingsUiState( val theme: Theme, val scrollToProgressEnabled: Boolean, - val themeOptions: List, + val themeOptions: List>, val showDialog: Boolean, @StringRes val themeLabel: Int, -) - -data class ThemeOption( - val theme: Theme, + val defaultFilter: DefaultFilter, @StringRes - val label: Int, - val selected: Boolean + val defaultFilterLabel: Int, + val defaultFilterOptions: List>, + val showDefaultFilterDialog: Boolean, ) @StringRes @@ -140,3 +180,13 @@ fun Theme.toLabelResource(): Int { Theme.SYSTEM -> R.string.theme_system } } + +@StringRes +fun DefaultFilter.toLabelResource(): Int { + return when (this) { + DefaultFilter.ALL -> R.string.default_filter_all + DefaultFilter.UNREAD -> R.string.default_filter_unread + DefaultFilter.ARCHIVED -> R.string.default_filter_archived + DefaultFilter.FAVORITES -> R.string.default_filter_favorites + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9a24da8..a438e9c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -67,6 +67,12 @@ other{} Which theme should be used? Scroll to reading progress When opening a bookmark, automatically scroll to your last reading position. + Default view + Which view should be shown on startup? + All + Unread + Archive + Favorites Sync reading progress When leaving a bookmark, sync your reading progress to the server. diff --git a/app/src/test/java/de/readeckapp/ui/list/BookmarkListViewModelTest.kt b/app/src/test/java/de/readeckapp/ui/list/BookmarkListViewModelTest.kt index 60734fd..22e093e 100644 --- a/app/src/test/java/de/readeckapp/ui/list/BookmarkListViewModelTest.kt +++ b/app/src/test/java/de/readeckapp/ui/list/BookmarkListViewModelTest.kt @@ -9,6 +9,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 io.mockk.coEvery @@ -64,6 +65,7 @@ class BookmarkListViewModelTest { // Default Mocking Behavior coEvery { settingsDataStore.isInitialSyncPerformed() } returns true // Assume sync is done + coEvery { settingsDataStore.getDefaultFilter() } returns DefaultFilter.ALL every { bookmarkRepository.observeBookmarkListItems(any(), any(), any(), any(), any()) } returns flowOf( emptyList() ) // No bookmarks initially @@ -323,19 +325,15 @@ class BookmarkListViewModelTest { settingsDataStore, savedStateHandle ) + advanceUntilIdle() // let init apply default filter before user actions viewModel.onClickArticles() viewModel.onClickUnread() + advanceUntilIdle() - val uiStates = viewModel.uiState.take(2).toList() - val empty = uiStates[0] - val success = uiStates[1] - // Assert initial state - assert(empty is BookmarkListViewModel.UiState.Empty) - // Assert success state assertEquals( BookmarkListViewModel.UiState.Success(expectedBookmarks, null), - success + viewModel.uiState.value ) } @@ -578,18 +576,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleFavoriteBookmark(bookmarkId, isFavorite) @@ -643,18 +636,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleFavoriteBookmark(bookmarkId, isFavorite) @@ -708,18 +696,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleFavoriteBookmark(bookmarkId, isFavorite) @@ -772,18 +755,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleArchiveBookmark(bookmarkId, isArchived) @@ -837,18 +815,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleArchiveBookmark(bookmarkId, isArchived) @@ -902,18 +875,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleArchiveBookmark(bookmarkId, isArchived) @@ -967,18 +935,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleMarkReadBookmark(bookmarkId, isRead) @@ -1032,18 +995,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleMarkReadBookmark(bookmarkId, isRead) @@ -1097,18 +1055,13 @@ class BookmarkListViewModelTest { savedStateHandle ) - val uiStates = viewModel.uiState.take(2).toList() - val emptyState = uiStates[0] - val successState = uiStates[1] - // Assert initial state - assert(emptyState is BookmarkListViewModel.UiState.Empty) - // Assert success state + advanceUntilIdle() assertEquals( BookmarkListViewModel.UiState.Success( bookmarks, null ), - successState + viewModel.uiState.value ) viewModel.onToggleMarkReadBookmark(bookmarkId, isRead)