From 053dfc1647de420cbc63310dd4ac59893fdfeaeb Mon Sep 17 00:00:00 2001 From: David Perez Date: Fri, 13 Jun 2025 11:25:39 -0500 Subject: [PATCH] PM-22643: Do not clear error dialogs when updating TOTP data (#5361) --- .../VerificationCodeViewModel.kt | 25 ++----- .../VerificationCodeViewModelTest.kt | 69 ++++++++++++++++++- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt index f37cb5b991..01d974ab29 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt @@ -282,10 +282,7 @@ class VerificationCodeViewModel @Inject constructor( ) { val data = verificationCodeData.data if (data != null) { - updateStateWithVerificationCodeData( - verificationCodeData = data, - clearDialogState = true, - ) + updateStateWithVerificationCodeData(verificationCodeData = data) } else { mutableStateFlow.update { currentState -> currentState.copy( @@ -307,20 +304,14 @@ class VerificationCodeViewModel @Inject constructor( private fun vaultPendingReceive( verificationCodeData: DataState.Pending>, ) { - updateStateWithVerificationCodeData( - verificationCodeData = verificationCodeData.data, - clearDialogState = false, - ) + updateStateWithVerificationCodeData(verificationCodeData = verificationCodeData.data) } private fun vaultLoadedReceive( verificationCodeData: DataState.Loaded>, ) { - updateStateWithVerificationCodeData( - verificationCodeData = verificationCodeData.data, - clearDialogState = true, - ) + updateStateWithVerificationCodeData(verificationCodeData = verificationCodeData.data) mutableStateFlow.update { it.copy(isRefreshing = false) } } @@ -331,10 +322,7 @@ class VerificationCodeViewModel @Inject constructor( private fun vaultErrorReceive(vaultData: DataState.Error>) { val data = vaultData.data if (data != null) { - updateStateWithVerificationCodeData( - verificationCodeData = data, - clearDialogState = true, - ) + updateStateWithVerificationCodeData(verificationCodeData = data) } else { mutableStateFlow.update { it.copy( @@ -350,7 +338,6 @@ class VerificationCodeViewModel @Inject constructor( private fun updateStateWithVerificationCodeData( verificationCodeData: List, - clearDialogState: Boolean, ) { if (verificationCodeData.isEmpty()) { sendEvent(VerificationCodeEvent.NavigateBack) @@ -378,7 +365,9 @@ class VerificationCodeViewModel @Inject constructor( ) }, ), - dialogState = state.dialogState.takeUnless { clearDialogState }, + dialogState = state.dialogState.takeUnless { + it is VerificationCodeState.DialogState.Loading + }, ) } } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt index 5541250377..0783762ab2 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt @@ -474,6 +474,70 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { ) } + @Suppress("MaxLineLength") + @Test + fun `AuthCodeFlow Loaded with valid items should update ViewState to content but leave the error dialog state alone`() = + runTest { + setupMockUri() + val state = createVerificationCodeState( + dialogState = VerificationCodeState.DialogState.Error( + title = null, + message = "Test".asText(), + ), + ) + val viewModel = createViewModel(state = state) + + mutableAuthCodeFlow.tryEmit( + value = DataState.Loaded( + data = listOf( + createVerificationCodeItem(number = 1), + createVerificationCodeItem(number = 2).copy(hasPasswordReprompt = true), + ), + ), + ) + + assertEquals( + state.copy( + viewState = VerificationCodeState.ViewState.Content( + createDisplayItemList(), + ), + ), + viewModel.stateFlow.value, + ) + } + + @Suppress("MaxLineLength") + @Test + fun `AuthCodeFlow Loaded with valid items should update ViewState to content and clear the loading dialog state`() = + runTest { + setupMockUri() + val state = createVerificationCodeState( + dialogState = VerificationCodeState.DialogState.Loading( + message = "Test".asText(), + ), + ) + val viewModel = createViewModel(state = state) + + mutableAuthCodeFlow.tryEmit( + value = DataState.Loaded( + data = listOf( + createVerificationCodeItem(number = 1), + createVerificationCodeItem(number = 2).copy(hasPasswordReprompt = true), + ), + ), + ) + + assertEquals( + state.copy( + viewState = VerificationCodeState.ViewState.Content( + createDisplayItemList(), + ), + dialogState = null, + ), + viewModel.stateFlow.value, + ) + } + @Suppress("MaxLineLength") @Test fun `AuthCodeState Loaded with non premium user and no org TOTP enabled should cause navigate back`() = @@ -605,12 +669,13 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { private fun createVerificationCodeState( viewState: VerificationCodeState.ViewState = VerificationCodeState.ViewState.Loading, - ) = VerificationCodeState( + dialogState: VerificationCodeState.DialogState? = null, + ): VerificationCodeState = VerificationCodeState( viewState = viewState, vaultFilterType = vaultRepository.vaultFilterType, isIconLoadingDisabled = settingsRepository.isIconLoadingDisabled, baseIconUrl = environmentRepository.environment.environmentUrlData.baseIconUrl, - dialogState = null, + dialogState = dialogState, isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value, isRefreshing = false, hasMasterPassword = true,