From e012dbf45da77a336f5dc0b47e9b6983f75f2796 Mon Sep 17 00:00:00 2001 From: David Perez Date: Mon, 18 Mar 2024 12:54:26 -0500 Subject: [PATCH] Add UI support for hiding all button on TrustedDeviceScreen (#1152) --- .../trusteddevice/TrustedDeviceScreen.kt | 75 ++++++++++++------- .../trusteddevice/TrustedDeviceViewModel.kt | 30 ++++++-- .../handlers/TrustedDeviceHandlers.kt | 2 + .../trusteddevice/TrustedDeviceScreenTest.kt | 71 ++++++++++++++++++ .../TrustedDeviceViewModelTest.kt | 14 ++++ 5 files changed, 161 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreen.kt index 1e7e04429f..c08ed15314 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreen.kt @@ -110,35 +110,53 @@ private fun TrustedDeviceScaffold( .padding(horizontal = 16.dp) .fillMaxWidth(), ) - Spacer(modifier = Modifier.height(24.dp)) - BitwardenFilledButton( - label = stringResource(id = R.string.approve_with_my_other_device), - onClick = handlers.onApproveWithDeviceClick, - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxWidth(), - ) + + if (state.showContinueButton) { + BitwardenFilledButton( + label = stringResource(id = R.string.continue_text), + onClick = handlers.onContinueClick, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Spacer(modifier = Modifier.height(12.dp)) + } + + if (state.showOtherDeviceButton) { + BitwardenFilledButton( + label = stringResource(id = R.string.approve_with_my_other_device), + onClick = handlers.onApproveWithDeviceClick, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Spacer(modifier = Modifier.height(12.dp)) + } + + if (state.showRequestAdminButton) { + BitwardenOutlinedButton( + label = stringResource(id = R.string.request_admin_approval), + onClick = handlers.onApproveWithAdminClick, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Spacer(modifier = Modifier.height(12.dp)) + } + + if (state.showMasterPasswordButton) { + BitwardenOutlinedButton( + label = stringResource(id = R.string.approve_with_master_password), + onClick = handlers.onApproveWithPasswordClick, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Spacer(modifier = Modifier.height(12.dp)) + } Spacer(modifier = Modifier.height(12.dp)) - BitwardenOutlinedButton( - label = stringResource(id = R.string.request_admin_approval), - onClick = handlers.onApproveWithAdminClick, - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxWidth(), - ) - - Spacer(modifier = Modifier.height(12.dp)) - BitwardenOutlinedButton( - label = stringResource(id = R.string.approve_with_master_password), - onClick = handlers.onApproveWithPasswordClick, - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxWidth(), - ) - - Spacer(modifier = Modifier.height(24.dp)) Text( text = stringResource( id = R.string.logging_in_as_x_on_y, @@ -174,10 +192,15 @@ private fun TrustedDeviceScaffold_preview() { isRemembered = false, emailAddress = "email@bitwarden.com", environmentLabel = "vault.bitwarden.pw", + showContinueButton = false, + showOtherDeviceButton = true, + showRequestAdminButton = true, + showMasterPasswordButton = true, ), handlers = TrustedDeviceHandlers( onBackClick = {}, onRememberToggle = {}, + onContinueClick = {}, onApproveWithAdminClick = {}, onApproveWithDeviceClick = {}, onApproveWithPasswordClick = {}, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModel.kt index b5c1cc3ade..0e2b7fa239 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModel.kt @@ -22,16 +22,23 @@ class TrustedDeviceViewModel @Inject constructor( environmentRepository: EnvironmentRepository, ) : BaseViewModel( initialState = savedStateHandle[KEY_STATE] - ?: TrustedDeviceState( - emailAddress = TrustedDeviceArgs(savedStateHandle).emailAddress, - environmentLabel = environmentRepository.environment.label, - isRemembered = false, - ), + ?: run { + TrustedDeviceState( + emailAddress = TrustedDeviceArgs(savedStateHandle).emailAddress, + environmentLabel = environmentRepository.environment.label, + isRemembered = false, + showContinueButton = false, + showOtherDeviceButton = false, + showRequestAdminButton = false, + showMasterPasswordButton = false, + ) + }, ) { override fun handleAction(action: TrustedDeviceAction) { when (action) { TrustedDeviceAction.BackClick -> handleBackClick() is TrustedDeviceAction.RememberToggle -> handleRememberToggle(action) + TrustedDeviceAction.ContinueClick -> handleContinueClick() TrustedDeviceAction.ApproveWithAdminClick -> handleApproveWithAdminClick() TrustedDeviceAction.ApproveWithDeviceClick -> handleApproveWithDeviceClick() TrustedDeviceAction.ApproveWithPasswordClick -> handleApproveWithPasswordClick() @@ -47,6 +54,10 @@ class TrustedDeviceViewModel @Inject constructor( mutableStateFlow.update { it.copy(isRemembered = action.isRemembered) } } + private fun handleContinueClick() { + sendEvent(TrustedDeviceEvent.ShowToast("Not yet implemented".asText())) + } + private fun handleApproveWithAdminClick() { sendEvent(TrustedDeviceEvent.ShowToast("Not yet implemented".asText())) } @@ -72,6 +83,10 @@ data class TrustedDeviceState( val emailAddress: String, val environmentLabel: String, val isRemembered: Boolean, + val showContinueButton: Boolean, + val showOtherDeviceButton: Boolean, + val showRequestAdminButton: Boolean, + val showMasterPasswordButton: Boolean, ) : Parcelable /** @@ -105,6 +120,11 @@ sealed class TrustedDeviceAction { val isRemembered: Boolean, ) : TrustedDeviceAction() + /** + * User clicked the "Continue" button. + */ + data object ContinueClick : TrustedDeviceAction() + /** * User clicked the "Approve with my other device" button. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/handlers/TrustedDeviceHandlers.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/handlers/TrustedDeviceHandlers.kt index 3c93ac4a47..89c2b84ded 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/handlers/TrustedDeviceHandlers.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/handlers/TrustedDeviceHandlers.kt @@ -10,6 +10,7 @@ import com.x8bit.bitwarden.ui.auth.feature.trusteddevice.TrustedDeviceViewModel data class TrustedDeviceHandlers( val onBackClick: () -> Unit, val onRememberToggle: (Boolean) -> Unit, + val onContinueClick: () -> Unit, val onApproveWithDeviceClick: () -> Unit, val onApproveWithAdminClick: () -> Unit, val onApproveWithPasswordClick: () -> Unit, @@ -26,6 +27,7 @@ data class TrustedDeviceHandlers( onRememberToggle = { viewModel.trySendAction(TrustedDeviceAction.RememberToggle(it)) }, + onContinueClick = { viewModel.trySendAction(TrustedDeviceAction.ContinueClick) }, onApproveWithDeviceClick = { viewModel.trySendAction(TrustedDeviceAction.ApproveWithDeviceClick) }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt index fe3ca99c6f..fa3acc9cc8 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt @@ -64,6 +64,45 @@ class TrustedDeviceScreenTest : BaseComposeTest() { } } + @Test + fun `continue button should be displayed according to state`() { + composeTestRule + .onNodeWithText("Continue") + .performScrollTo() + .assertIsDisplayed() + + mutableStateFlow.update { it.copy(showContinueButton = false) } + + composeTestRule + .onNodeWithText("Continue") + .assertDoesNotExist() + } + + @Test + fun `on continue clicked should send ContinueClick`() { + composeTestRule + .onNodeWithText("Continue") + .performScrollTo() + .performClick() + verify(exactly = 1) { + viewModel.trySendAction(TrustedDeviceAction.ContinueClick) + } + } + + @Test + fun `other device button should be displayed according to state`() { + composeTestRule + .onNodeWithText("Approve with my other device") + .performScrollTo() + .assertIsDisplayed() + + mutableStateFlow.update { it.copy(showOtherDeviceButton = false) } + + composeTestRule + .onNodeWithText("Approve with my other device") + .assertDoesNotExist() + } + @Test fun `on approve with device clicked should send ApproveWithDeviceClick`() { composeTestRule @@ -75,6 +114,20 @@ class TrustedDeviceScreenTest : BaseComposeTest() { } } + @Test + fun `admin approval button should be displayed according to state`() { + composeTestRule + .onNodeWithText("Request admin approval") + .performScrollTo() + .assertIsDisplayed() + + mutableStateFlow.update { it.copy(showRequestAdminButton = false) } + + composeTestRule + .onNodeWithText("Request admin approval") + .assertDoesNotExist() + } + @Test fun `on approve with admin clicked should send ApproveWithAdminClick`() { composeTestRule @@ -86,6 +139,20 @@ class TrustedDeviceScreenTest : BaseComposeTest() { } } + @Test + fun `master password button should be displayed according to state`() { + composeTestRule + .onNodeWithText("Approve with master password") + .performScrollTo() + .assertIsDisplayed() + + mutableStateFlow.update { it.copy(showMasterPasswordButton = false) } + + composeTestRule + .onNodeWithText("Approve with master password") + .assertDoesNotExist() + } + @Test fun `on approve with master password clicked should send ApproveWithPasswordClick`() { composeTestRule @@ -158,4 +225,8 @@ private val DEFAULT_STATE: TrustedDeviceState = TrustedDeviceState( emailAddress = "email@bitwarden.com", environmentLabel = "vault.bitwarden.pw", isRemembered = false, + showContinueButton = true, + showOtherDeviceButton = true, + showRequestAdminButton = true, + showMasterPasswordButton = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt index 67be5fc0c9..f55425d42a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt @@ -37,6 +37,16 @@ class TrustedDeviceViewModelTest : BaseViewModelTest() { } } + @Test + fun `on ContinueClick emits ShowToast`() = runTest { + val viewModel = createViewModel() + + viewModel.eventFlow.test { + viewModel.trySendAction(TrustedDeviceAction.ContinueClick) + assertEquals(TrustedDeviceEvent.ShowToast("Not yet implemented".asText()), awaitItem()) + } + } + @Test fun `on ApproveWithAdminClick emits ShowToast`() = runTest { val viewModel = createViewModel() @@ -94,4 +104,8 @@ private val DEFAULT_STATE: TrustedDeviceState = TrustedDeviceState( emailAddress = "email@bitwarden.com", environmentLabel = "bitwarden.com", isRemembered = false, + showContinueButton = false, + showOtherDeviceButton = false, + showRequestAdminButton = false, + showMasterPasswordButton = false, )