From d4d64c2a6da51699c2cd21b6f3d0ecbdcf0f370f Mon Sep 17 00:00:00 2001 From: Brian Yencho Date: Wed, 29 Nov 2023 08:12:43 -0600 Subject: [PATCH] Listen to UserState changes on the Vault Unlock screen (#290) --- .../vaultunlock/VaultUnlockViewModel.kt | 36 +++++++++++ .../vaultunlock/VaultUnlockViewModelTest.kt | 62 ++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt index 488a24c241..6dfded32cd 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult @@ -65,6 +66,12 @@ class VaultUnlockViewModel @Inject constructor( } } .launchIn(viewModelScope) + authRepository + .userStateFlow + .onEach { + sendAction(VaultUnlockAction.Internal.UserStateUpdateReceive(userState = it)) + } + .launchIn(viewModelScope) } override fun handleAction(action: VaultUnlockAction) { @@ -78,6 +85,10 @@ class VaultUnlockViewModel @Inject constructor( is VaultUnlockAction.Internal.ReceiveVaultUnlockResult -> { handleReceiveVaultUnlockResult(action) } + + is VaultUnlockAction.Internal.UserStateUpdateReceive -> { + handleUserStateUpdateReceive(action) + } } } @@ -146,6 +157,24 @@ class VaultUnlockViewModel @Inject constructor( } } } + + private fun handleUserStateUpdateReceive( + action: VaultUnlockAction.Internal.UserStateUpdateReceive, + ) { + // Leave the current data alone if there is no UserState; we are in the process of logging + // out. + val userState = action.userState ?: return + + mutableStateFlow.update { + val accountSummaries = userState.toAccountSummaries() + val activeAccountSummary = userState.toActiveAccountSummary() + it.copy( + initials = activeAccountSummary.initials, + avatarColorString = activeAccountSummary.avatarColorHex, + accountSummaries = accountSummaries, + ) + } + } } /** @@ -252,5 +281,12 @@ sealed class VaultUnlockAction { data class ReceiveVaultUnlockResult( val vaultUnlockResult: VaultUnlockResult, ) : Internal() + + /** + * Indicates a change in user state has been received. + */ + data class UserStateUpdateReceive( + val userState: UserState?, + ) : Internal() } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt index 40fe6b5c26..527dc9dae8 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt @@ -28,9 +28,10 @@ import org.junit.jupiter.api.Test class VaultUnlockViewModelTest : BaseViewModelTest() { + private val mutableUserStateFlow = MutableStateFlow(DEFAULT_USER_STATE) private val environmentRepository = FakeEnvironmentRepository() private val authRepository = mockk() { - every { userStateFlow } returns MutableStateFlow(DEFAULT_USER_STATE) + every { userStateFlow } returns mutableUserStateFlow every { logout() } just runs } private val vaultRepository = mockk() @@ -44,8 +45,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { @Test fun `initial state should be correct when set`() { val state = DEFAULT_STATE.copy( - initials = "WB", - avatarColorString = "00FF00", + passwordInput = "pass", ) val viewModel = createViewModel(state = state) assertEquals(state, viewModel.stateFlow.value) @@ -64,6 +64,62 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { ) } + @Test + fun `UserState updates with a null value should do nothing`() { + val viewModel = createViewModel() + assertEquals( + DEFAULT_STATE, + viewModel.stateFlow.value, + ) + + mutableUserStateFlow.value = null + + assertEquals( + DEFAULT_STATE, + viewModel.stateFlow.value, + ) + } + + @Test + fun `UserState updates with a non-null value update the account information in the state`() { + val viewModel = createViewModel() + assertEquals( + DEFAULT_STATE, + viewModel.stateFlow.value, + ) + + mutableUserStateFlow.value = + DEFAULT_USER_STATE.copy( + accounts = listOf( + UserState.Account( + userId = "activeUserId", + name = "Other User", + email = "active@bitwarden.com", + avatarColorHex = "#00aaaa", + isPremium = true, + isVaultUnlocked = true, + ), + ), + ) + + assertEquals( + DEFAULT_STATE.copy( + avatarColorString = "#00aaaa", + initials = "OU", + accountSummaries = listOf( + AccountSummary( + userId = "activeUserId", + name = "Other User", + email = "active@bitwarden.com", + avatarColorHex = "#00aaaa", + status = AccountSummary.Status.ACTIVE, + ), + ), + ), + viewModel.stateFlow.value, + ) + } + @Test fun `on AddAccountClick should emit NavigateToLoginScreen`() = runTest { val viewModel = createViewModel()