BIT-1277 BIT-1279: Confirmation dialog (#805)

This commit is contained in:
Shannon Draeker 2024-01-27 07:06:10 -07:00 committed by Álison Fernandes
parent f023650730
commit b991acd0d0
4 changed files with 84 additions and 32 deletions

View file

@ -17,7 +17,9 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@ -40,6 +42,7 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenPasswordField
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
import com.x8bit.bitwarden.ui.platform.components.BitwardenTwoButtonDialog
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.feature.settings.exportvault.model.ExportVaultFormat
import com.x8bit.bitwarden.ui.platform.util.displayLabel
@ -67,6 +70,29 @@ fun ExportVaultScreen(
}
}
var shouldShowConfirmationDialog by remember { mutableStateOf(false) }
if (shouldShowConfirmationDialog) {
BitwardenTwoButtonDialog(
title = stringResource(id = R.string.export_vault_confirmation_title),
message = if (state.exportFormat == ExportVaultFormat.JSON_ENCRYPTED) {
stringResource(id = R.string.enc_export_key_warning)
.plus("\n\n")
.plus(stringResource(id = R.string.enc_export_account_warning))
} else {
stringResource(
id = R.string.export_vault_warning,
)
},
confirmButtonText = stringResource(id = R.string.export_vault),
dismissButtonText = stringResource(id = R.string.cancel),
onConfirmClick = remember(viewModel) {
{ viewModel.trySendAction(ExportVaultAction.ConfirmExportVaultClicked) }
},
onDismissClick = { shouldShowConfirmationDialog = false },
onDismissRequest = { shouldShowConfirmationDialog = false },
)
}
when (val dialog = state.dialogState) {
is ExportVaultState.DialogState.Error -> {
BitwardenBasicDialog(
@ -116,9 +142,7 @@ fun ExportVaultScreen(
onPasswordInputChanged = remember(viewModel) {
{ viewModel.trySendAction(ExportVaultAction.PasswordInputChanged(it)) }
},
onExportVaultClick = remember(viewModel) {
{ viewModel.trySendAction(ExportVaultAction.ExportVaultClick) }
},
onExportVaultClick = { shouldShowConfirmationDialog = true },
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),

View file

@ -40,9 +40,9 @@ class ExportVaultViewModel @Inject constructor(
override fun handleAction(action: ExportVaultAction) {
when (action) {
ExportVaultAction.CloseButtonClick -> handleCloseButtonClicked()
ExportVaultAction.ConfirmExportVaultClicked -> handleConfirmExportVaultClicked()
ExportVaultAction.DialogDismiss -> handleDialogDismiss()
is ExportVaultAction.ExportFormatOptionSelect -> handleExportFormatOptionSelect(action)
ExportVaultAction.ExportVaultClick -> handleExportVaultClick()
is ExportVaultAction.PasswordInputChanged -> handlePasswordInputChanged(action)
}
}
@ -54,6 +54,15 @@ class ExportVaultViewModel @Inject constructor(
sendEvent(ExportVaultEvent.NavigateBack)
}
/**
* Verify the master password after confirming exporting the vault.
*/
private fun handleConfirmExportVaultClicked() {
// TODO: BIT-1273
mutableStateFlow.update { it.copy(dialogState = null) }
sendEvent(ExportVaultEvent.ShowToast("Coming soon to a PR near you!".asText()))
}
/**
* Dismiss the dialog.
*/
@ -70,14 +79,6 @@ class ExportVaultViewModel @Inject constructor(
}
}
/**
* Show the confirmation dialog and export the vault.
*/
private fun handleExportVaultClick() {
// TODO: BIT-1273
sendEvent(ExportVaultEvent.ShowToast(message = "Coming soon to an app near you!".asText()))
}
/**
* Update the state with the new password input.
*/
@ -145,6 +146,11 @@ sealed class ExportVaultAction {
*/
data object CloseButtonClick : ExportVaultAction()
/**
* Indicates that the confirm export vault button was clicked.
*/
data object ConfirmExportVaultClicked : ExportVaultAction()
/**
* Indicates that the dialog has been dismissed.
*/
@ -155,11 +161,6 @@ sealed class ExportVaultAction {
*/
data class ExportFormatOptionSelect(val option: ExportVaultFormat) : ExportVaultAction()
/**
* Indicates that the export vault button was clicked.
*/
data object ExportVaultClick : ExportVaultAction()
/**
* Indicates that the password input has changed.
*/

View file

@ -1,6 +1,10 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.exportvault
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.isDisplayed
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst
@ -13,6 +17,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFl
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.feature.settings.exportvault.model.ExportVaultFormat
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@ -65,13 +70,48 @@ class ExportVaultScreenTest : BaseComposeTest() {
}
@Test
fun `export vault button click should emit ExportVaultClick action`() {
fun `export vault button click should display confirmation dialog`() {
composeTestRule.onNodeWithText("Confirm vault export").assertDoesNotExist()
// Click the export vault button shows the alert.
composeTestRule
.onAllNodesWithText("Export vault")
.onFirst()
.performClick()
composeTestRule
.onNodeWithText("Confirm vault export")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
// Clicking the cancel button dismisses the alert.
composeTestRule
.onNodeWithText("Cancel")
.assert(hasAnyAncestor(isDialog()))
.performClick()
composeTestRule.assertNoDialogExists()
}
@Test
fun `confirm export vault button click should send ConfirmExportClick action`() {
composeTestRule.onNodeWithText("Confirm vault export").assertDoesNotExist()
// Click the export vault button shows the alert.
composeTestRule
.onAllNodesWithText("Export vault")
.onFirst()
.performClick()
composeTestRule
.onNodeWithText("Confirm vault export")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
// Clicking the confirm button sends the confirm action.
composeTestRule
.onAllNodesWithText("Export vault")
.filterToOne(hasAnyAncestor(isDialog()))
.performClick()
verify {
viewModel.trySendAction(ExportVaultAction.ExportVaultClick)
viewModel.trySendAction(ExportVaultAction.ConfirmExportVaultClicked)
}
}

View file

@ -3,7 +3,6 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.exportvault
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.feature.settings.exportvault.model.ExportVaultFormat
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
@ -50,18 +49,6 @@ class ExportVaultViewModelTest : BaseViewModelTest() {
}
}
@Test
fun `ExportVaultClick should emit ShowToast`() = runTest {
val viewModel = createViewModel()
viewModel.eventFlow.test {
viewModel.actionChannel.trySend(ExportVaultAction.ExportVaultClick)
assertEquals(
ExportVaultEvent.ShowToast(message = "Coming soon to an app near you!".asText()),
awaitItem(),
)
}
}
@Test
fun `PasswordInputChanged should update the password input in the state`() = runTest {
val viewModel = createViewModel()