diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt index c88439566..7eae8de00 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt @@ -8,8 +8,11 @@ import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.pulltorefresh.PullToRefreshState +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -40,6 +43,7 @@ import kotlinx.collections.immutable.persistentListOf /** * Displays the vault item listing screen. */ +@OptIn(ExperimentalMaterial3Api::class) @Composable fun VaultItemListingScreen( onNavigateBack: () -> Unit, @@ -53,10 +57,19 @@ fun VaultItemListingScreen( val state by viewModel.stateFlow.collectAsState() val context = LocalContext.current val resources = context.resources + + val pullToRefreshState = rememberPullToRefreshState().takeIf { state.isPullToRefreshEnabled } + LaunchedEffect(key1 = pullToRefreshState?.isRefreshing) { + if (pullToRefreshState?.isRefreshing == true) { + viewModel.trySendAction(VaultItemListingsAction.RefreshPull) + } + } EventsEffect(viewModel = viewModel) { event -> when (event) { is VaultItemListingEvent.NavigateBack -> onNavigateBack() + is VaultItemListingEvent.DismissPullToRefresh -> pullToRefreshState?.endRefresh() + is VaultItemListingEvent.NavigateToVaultItem -> { onNavigateToVaultItem(event.id) } @@ -99,6 +112,7 @@ fun VaultItemListingScreen( VaultItemListingScaffold( state = state, + pullToRefreshState = pullToRefreshState, vaultItemListingHandlers = remember(viewModel) { VaultItemListingHandlers.create(viewModel) }, @@ -132,6 +146,7 @@ private fun VaultItemListingDialogs( @Composable private fun VaultItemListingScaffold( state: VaultItemListingState, + pullToRefreshState: PullToRefreshState?, vaultItemListingHandlers: VaultItemListingHandlers, ) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) @@ -179,6 +194,7 @@ private fun VaultItemListingScaffold( } } }, + pullToRefreshState = pullToRefreshState, ) { paddingValues -> val modifier = Modifier .fillMaxSize() diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt index f71c759a7..2e20ff3fe 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt @@ -29,6 +29,7 @@ import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType import com.x8bit.bitwarden.ui.vault.feature.vault.util.toFilteredList import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -59,11 +60,18 @@ class VaultItemListingViewModel @Inject constructor( baseWebSendUrl = environmentRepository.environment.environmentUrlData.baseWebSendUrl, baseIconUrl = environmentRepository.environment.environmentUrlData.baseIconUrl, isIconLoadingDisabled = settingsRepository.isIconLoadingDisabled, + isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value, dialogState = null, ), ) { init { + settingsRepository + .getPullToRefreshEnabledFlow() + .map { VaultItemListingsAction.Internal.PullToRefreshEnableReceive(it) } + .onEach(::sendAction) + .launchIn(viewModelScope) + settingsRepository .isIconLoadingDisabledFlow .onEach { sendAction(VaultItemListingsAction.Internal.IconLoadingSettingReceive(it)) } @@ -86,6 +94,7 @@ class VaultItemListingViewModel @Inject constructor( is VaultItemListingsAction.ItemClick -> handleItemClick(action) is VaultItemListingsAction.AddVaultItemClick -> handleAddVaultItemClick() is VaultItemListingsAction.RefreshClick -> handleRefreshClick() + is VaultItemListingsAction.RefreshPull -> handleRefreshPull() is VaultItemListingsAction.Internal -> handleInternalAction(action) } } @@ -95,6 +104,12 @@ class VaultItemListingViewModel @Inject constructor( vaultRepository.sync() } + private fun handleRefreshPull() { + // The Pull-To-Refresh composable is already in the refreshing state. + // We will reset that state when sendDataStateFlow emits later on. + vaultRepository.sync() + } + private fun handleCopySendUrlClick(action: ListingItemOverflowAction.SendAction.CopyUrlClick) { clipboardManager.setText(text = action.sendUrl) } @@ -222,6 +237,10 @@ class VaultItemListingViewModel @Inject constructor( private fun handleInternalAction(action: VaultItemListingsAction.Internal) { when (action) { + is VaultItemListingsAction.Internal.PullToRefreshEnableReceive -> { + handlePullToRefreshEnableReceive(action) + } + is VaultItemListingsAction.Internal.DeleteSendResultReceive -> { handleDeleteSendResultReceive(action) } @@ -237,6 +256,14 @@ class VaultItemListingViewModel @Inject constructor( } } + private fun handlePullToRefreshEnableReceive( + action: VaultItemListingsAction.Internal.PullToRefreshEnableReceive, + ) { + mutableStateFlow.update { + it.copy(isPullToRefreshSettingEnabled = action.isPullToRefreshEnabled) + } + } + private fun handleDeleteSendResultReceive( action: VaultItemListingsAction.Internal.DeleteSendResultReceive, ) { @@ -326,10 +353,12 @@ class VaultItemListingViewModel @Inject constructor( ) } } + sendEvent(VaultItemListingEvent.DismissPullToRefresh) } private fun vaultLoadedReceive(vaultData: DataState.Loaded) { updateStateWithVaultData(vaultData = vaultData.data, clearDialogState = true) + sendEvent(VaultItemListingEvent.DismissPullToRefresh) } private fun vaultLoadingReceive() { @@ -351,6 +380,7 @@ class VaultItemListingViewModel @Inject constructor( ) } } + sendEvent(VaultItemListingEvent.DismissPullToRefresh) } private fun vaultPendingReceive(vaultData: DataState.Pending) { @@ -413,8 +443,15 @@ data class VaultItemListingState( val baseIconUrl: String, val isIconLoadingDisabled: Boolean, val dialogState: DialogState?, + private val isPullToRefreshSettingEnabled: Boolean, ) { + /** + * Indicates that the pull-to-refresh should be enabled in the UI. + */ + val isPullToRefreshEnabled: Boolean + get() = isPullToRefreshSettingEnabled && viewState.isPullToRefreshEnabled + /** * Represents the current state of any dialogs on the screen. */ @@ -442,17 +479,25 @@ data class VaultItemListingState( * Represents the specific view states for the [VaultItemListingScreen]. */ sealed class ViewState { + /** + * Indicates the pull-to-refresh feature should be available during the current state. + */ + abstract val isPullToRefreshEnabled: Boolean /** * Loading state for the [VaultItemListingScreen], * signifying that the content is being processed. */ - data object Loading : ViewState() + data object Loading : ViewState() { + override val isPullToRefreshEnabled: Boolean get() = false + } /** * Represents a state where the [VaultItemListingScreen] has no items to display. */ - data object NoItems : ViewState() + data object NoItems : ViewState() { + override val isPullToRefreshEnabled: Boolean get() = true + } /** * Content state for the [VaultItemListingScreen] showing the actual content or items. @@ -461,7 +506,9 @@ data class VaultItemListingState( */ data class Content( val displayItemList: List, - ) : ViewState() + ) : ViewState() { + override val isPullToRefreshEnabled: Boolean get() = true + } /** * Represents an error state for the [VaultItemListingScreen]. @@ -470,7 +517,9 @@ data class VaultItemListingState( */ data class Error( val message: Text, - ) : ViewState() + ) : ViewState() { + override val isPullToRefreshEnabled: Boolean get() = true + } } /** @@ -609,6 +658,10 @@ data class VaultItemListingState( * Models events for the [VaultItemListingScreen]. */ sealed class VaultItemListingEvent { + /** + * Dismisses the pull-to-refresh indicator. + */ + data object DismissPullToRefresh : VaultItemListingEvent() /** * Navigates to the Create Account screen. @@ -711,10 +764,19 @@ sealed class VaultItemListingsAction { */ data class ItemClick(val id: String) : VaultItemListingsAction() + /** + * User has triggered a pull to refresh. + */ + data object RefreshPull : VaultItemListingsAction() + /** * Models actions that the [VaultItemListingViewModel] itself might send. */ sealed class Internal : VaultItemListingsAction() { + /** + * Indicates that the pull to refresh feature toggle has changed. + */ + data class PullToRefreshEnableReceive(val isPullToRefreshEnabled: Boolean) : Internal() /** * Indicates a result for deleting the send has been received. diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt index f3a32f06f..466093419 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt @@ -699,6 +699,7 @@ private val DEFAULT_STATE = VaultItemListingState( baseWebSendUrl = Environment.Us.environmentUrlData.baseWebSendUrl, isIconLoadingDisabled = false, baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, + isPullToRefreshSettingEnabled = false, dialogState = null, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index e025734ec..c4718dbe6 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -66,10 +66,12 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { every { environmentStateFlow } returns mockk() } + private val mutablePullToRefreshEnabledFlow = MutableStateFlow(false) private val mutableIsIconLoadingDisabledFlow = MutableStateFlow(false) private val settingsRepository: SettingsRepository = mockk { every { isIconLoadingDisabled } returns false every { isIconLoadingDisabledFlow } returns mutableIsIconLoadingDisabledFlow + every { getPullToRefreshEnabledFlow() } returns mutablePullToRefreshEnabledFlow } private val initialState = createVaultItemListingState() private val initialSavedStateHandle = createSavedStateHandleWithVaultItemListingType( @@ -350,25 +352,27 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `vaultDataStateFlow Loaded with items should update ViewState to Content`() = runTest { setupMockUri() - - mutableVaultDataStateFlow.tryEmit( - value = DataState.Loaded( - data = VaultData( - cipherViewList = listOf( - createMockCipherView( - number = 1, - isDeleted = false, - ), + val dataState = DataState.Loaded( + data = VaultData( + cipherViewList = listOf( + createMockCipherView( + number = 1, + isDeleted = false, ), - folderViewList = listOf(createMockFolderView(number = 1)), - collectionViewList = listOf(createMockCollectionView(number = 1)), - sendViewList = listOf(createMockSendView(number = 1)), ), + folderViewList = listOf(createMockFolderView(number = 1)), + collectionViewList = listOf(createMockCollectionView(number = 1)), + sendViewList = listOf(createMockSendView(number = 1)), ), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } + assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.Content( @@ -384,17 +388,19 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow Loaded with empty items should update ViewState to NoItems`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.Loaded( - data = VaultData( - cipherViewList = emptyList(), - folderViewList = emptyList(), - collectionViewList = emptyList(), - sendViewList = emptyList(), - ), + val dataState = DataState.Loaded( + data = VaultData( + cipherViewList = emptyList(), + folderViewList = emptyList(), + collectionViewList = emptyList(), + sendViewList = emptyList(), ), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState(viewState = VaultItemListingState.ViewState.NoItems), viewModel.stateFlow.value, @@ -404,17 +410,20 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow Loaded with trash items should update ViewState to NoItems`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.Loaded( - data = VaultData( - cipherViewList = listOf(createMockCipherView(number = 1)), - folderViewList = listOf(createMockFolderView(number = 1)), - collectionViewList = listOf(createMockCollectionView(number = 1)), - sendViewList = listOf(createMockSendView(number = 1)), - ), + val dataState = DataState.Loaded( + data = VaultData( + cipherViewList = listOf(createMockCipherView(number = 1)), + folderViewList = listOf(createMockFolderView(number = 1)), + collectionViewList = listOf(createMockCollectionView(number = 1)), + sendViewList = listOf(createMockSendView(number = 1)), ), ) val viewModel = createVaultItemListingViewModel() + + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.NoItems, @@ -508,14 +517,16 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow Error without data should update state to Error`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.Error( - error = IllegalStateException(), - ), + val dataState = DataState.Error( + error = IllegalStateException(), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.Error( @@ -530,20 +541,22 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `vaultDataStateFlow Error with data should update state to Content`() = runTest { setupMockUri() - mutableVaultDataStateFlow.tryEmit( - value = DataState.Error( - data = VaultData( - cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = false)), - folderViewList = listOf(createMockFolderView(number = 1)), - collectionViewList = listOf(createMockCollectionView(number = 1)), - sendViewList = listOf(createMockSendView(number = 1)), - ), - error = IllegalStateException(), + val dataState = DataState.Error( + data = VaultData( + cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = false)), + folderViewList = listOf(createMockFolderView(number = 1)), + collectionViewList = listOf(createMockCollectionView(number = 1)), + sendViewList = listOf(createMockSendView(number = 1)), ), + error = IllegalStateException(), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.Content( @@ -560,20 +573,22 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow Error with empty data should update state to NoItems`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.Error( - data = VaultData( - cipherViewList = emptyList(), - folderViewList = emptyList(), - collectionViewList = emptyList(), - sendViewList = emptyList(), - ), - error = IllegalStateException(), + val dataState = DataState.Error( + data = VaultData( + cipherViewList = emptyList(), + folderViewList = emptyList(), + collectionViewList = emptyList(), + sendViewList = emptyList(), ), + error = IllegalStateException(), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.NoItems, @@ -584,20 +599,22 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow Error with trash data should update state to NoItems`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.Error( - data = VaultData( - cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = true)), - folderViewList = listOf(createMockFolderView(number = 1)), - collectionViewList = listOf(createMockCollectionView(number = 1)), - sendViewList = listOf(createMockSendView(number = 1)), - ), - error = IllegalStateException(), + val dataState = DataState.Error( + data = VaultData( + cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = true)), + folderViewList = listOf(createMockFolderView(number = 1)), + collectionViewList = listOf(createMockCollectionView(number = 1)), + sendViewList = listOf(createMockSendView(number = 1)), ), + error = IllegalStateException(), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.NoItems, @@ -608,12 +625,14 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow NoNetwork without data should update state to Error`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.NoNetwork(), - ) + val dataState = DataState.NoNetwork() val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.Error( @@ -630,19 +649,21 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `vaultDataStateFlow NoNetwork with data should update state to Content`() = runTest { setupMockUri() - mutableVaultDataStateFlow.tryEmit( - value = DataState.NoNetwork( - data = VaultData( - cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = false)), - folderViewList = listOf(createMockFolderView(number = 1)), - collectionViewList = listOf(createMockCollectionView(number = 1)), - sendViewList = listOf(createMockSendView(number = 1)), - ), + val dataState = DataState.NoNetwork( + data = VaultData( + cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = false)), + folderViewList = listOf(createMockFolderView(number = 1)), + collectionViewList = listOf(createMockCollectionView(number = 1)), + sendViewList = listOf(createMockSendView(number = 1)), ), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.Content( @@ -659,19 +680,21 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow NoNetwork with empty data should update state to NoItems`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.NoNetwork( - data = VaultData( - cipherViewList = emptyList(), - folderViewList = emptyList(), - collectionViewList = emptyList(), - sendViewList = emptyList(), - ), + val dataState = DataState.NoNetwork( + data = VaultData( + cipherViewList = emptyList(), + folderViewList = emptyList(), + collectionViewList = emptyList(), + sendViewList = emptyList(), ), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.NoItems, @@ -682,19 +705,21 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow NoNetwork with trash data should update state to NoItems`() = runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.NoNetwork( - data = VaultData( - cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = true)), - folderViewList = listOf(createMockFolderView(number = 1)), - collectionViewList = listOf(createMockCollectionView(number = 1)), - sendViewList = listOf(createMockSendView(number = 1)), - ), + val dataState = DataState.NoNetwork( + data = VaultData( + cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = true)), + folderViewList = listOf(createMockFolderView(number = 1)), + collectionViewList = listOf(createMockCollectionView(number = 1)), + sendViewList = listOf(createMockSendView(number = 1)), ), ) val viewModel = createVaultItemListingViewModel() + viewModel.eventFlow.test { + mutableVaultDataStateFlow.tryEmit(value = dataState) + assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem()) + } assertEquals( createVaultItemListingState( viewState = VaultItemListingState.ViewState.NoItems, @@ -713,6 +738,33 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { assertTrue(viewModel.stateFlow.value.isIconLoadingDisabled) } + @Test + fun `RefreshPull should call vault repository sync`() { + val viewModel = createVaultItemListingViewModel() + + viewModel.trySendAction(VaultItemListingsAction.RefreshPull) + + verify(exactly = 1) { + vaultRepository.sync() + } + } + + @Test + fun `PullToRefreshEnableReceive should update isPullToRefreshEnabled`() = runTest { + val viewModel = createVaultItemListingViewModel() + + viewModel.trySendAction( + VaultItemListingsAction.Internal.PullToRefreshEnableReceive( + isPullToRefreshEnabled = true, + ), + ) + + assertEquals( + initialState.copy(isPullToRefreshSettingEnabled = true), + viewModel.stateFlow.value, + ) + } + @Suppress("CyclomaticComplexMethod") private fun createSavedStateHandleWithVaultItemListingType( vaultItemListingType: VaultItemListingType, @@ -779,6 +831,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { baseWebSendUrl = Environment.Us.environmentUrlData.baseWebSendUrl, baseIconUrl = environmentRepository.environment.environmentUrlData.baseIconUrl, isIconLoadingDisabled = settingsRepository.isIconLoadingDisabled, + isPullToRefreshSettingEnabled = false, dialogState = null, ) }