PM-33458: feat: Add speed bump when archiving item from a list (#6774)

This commit is contained in:
David Perez
2026-04-10 11:01:32 -05:00
committed by GitHub
parent 954ac5b7d7
commit 747d2d58e5
10 changed files with 341 additions and 104 deletions

View File

@@ -33,7 +33,7 @@ import kotlinx.collections.immutable.toPersistentList
/**
* The contents state for the search screen.
*/
@Suppress("LongMethod")
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
fun SearchContent(
viewState: SearchState.ViewState.Content,
@@ -41,43 +41,28 @@ fun SearchContent(
searchType: SearchTypeData,
modifier: Modifier = Modifier,
) {
var showConfirmationDialog: ListingItemOverflowAction? by rememberSaveable {
var overflowSpeedBumpAction: ListingItemOverflowAction? by rememberSaveable {
mutableStateOf(null)
}
when (val option = showConfirmationDialog) {
is ListingItemOverflowAction.SendAction.DeleteClick -> {
BitwardenTwoButtonDialog(
title = stringResource(id = BitwardenString.delete),
message = stringResource(id = BitwardenString.are_you_sure_delete_send),
confirmButtonText = stringResource(id = BitwardenString.yes),
dismissButtonText = stringResource(id = BitwardenString.cancel),
onConfirmClick = {
showConfirmationDialog = null
searchHandlers.onOverflowItemClick(option)
},
onDismissClick = { showConfirmationDialog = null },
onDismissRequest = { showConfirmationDialog = null },
)
}
is ListingItemOverflowAction.SendAction.CopyUrlClick,
is ListingItemOverflowAction.SendAction.EditClick,
is ListingItemOverflowAction.SendAction.RemovePasswordClick,
is ListingItemOverflowAction.SendAction.ShareUrlClick,
is ListingItemOverflowAction.SendAction.ViewClick,
is ListingItemOverflowAction.VaultAction.CopyNoteClick,
is ListingItemOverflowAction.VaultAction.CopyNumberClick,
is ListingItemOverflowAction.VaultAction.CopyPasswordClick,
is ListingItemOverflowAction.VaultAction.CopyTotpClick,
is ListingItemOverflowAction.VaultAction.CopySecurityCodeClick,
is ListingItemOverflowAction.VaultAction.CopyUsernameClick,
is ListingItemOverflowAction.VaultAction.EditClick,
is ListingItemOverflowAction.VaultAction.LaunchClick,
is ListingItemOverflowAction.VaultAction.ViewClick,
is ListingItemOverflowAction.VaultAction.ArchiveClick,
is ListingItemOverflowAction.VaultAction.UnarchiveClick,
null,
-> Unit
overflowSpeedBumpAction?.let { action ->
action
.speedBump
?.let { speedBump ->
BitwardenTwoButtonDialog(
twoButtonDialogData = speedBump,
onConfirmClick = {
overflowSpeedBumpAction = null
searchHandlers.onOverflowItemClick(action)
},
onDismissClick = { overflowSpeedBumpAction = null },
onDismissRequest = { overflowSpeedBumpAction = null },
)
}
?: run {
// If we somehow get here and there is no speed bump, then we should keep on going.
overflowSpeedBumpAction = null
searchHandlers.onOverflowItemClick(action)
}
}
var autofillSelectionOptionsItem by rememberSaveable {
@@ -143,8 +128,12 @@ fun SearchContent(
contentDescription = option.contentDescription(),
onClick = {
when (option) {
is ListingItemOverflowAction.SendAction.DeleteClick -> {
showConfirmationDialog = option
is ListingItemOverflowAction.SendAction -> {
if (option.speedBump != null) {
overflowSpeedBumpAction = option
} else {
searchHandlers.onOverflowItemClick(option)
}
}
is ListingItemOverflowAction.VaultAction -> {
@@ -155,12 +144,12 @@ fun SearchContent(
MasterPasswordRepromptData.OverflowItem(
action = option,
)
} else if (option.speedBump != null) {
overflowSpeedBumpAction = option
} else {
searchHandlers.onOverflowItemClick(option)
}
}
else -> searchHandlers.onOverflowItemClick(option)
}
},
)

View File

@@ -31,7 +31,7 @@ import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenListItem
import com.x8bit.bitwarden.ui.platform.components.listitem.SelectionItemData
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.handlers.VaultItemListingHandlers
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toImmutableList
/**
* Content view for the [VaultItemListingScreen].
@@ -46,43 +46,29 @@ fun VaultItemListingContent(
vaultItemListingHandlers: VaultItemListingHandlers,
modifier: Modifier = Modifier,
) {
var showConfirmationDialog: ListingItemOverflowAction? by rememberSaveable {
var overflowSpeedBumpAction: ListingItemOverflowAction? by rememberSaveable {
mutableStateOf(null)
}
when (val option = showConfirmationDialog) {
is ListingItemOverflowAction.SendAction.DeleteClick -> {
BitwardenTwoButtonDialog(
title = stringResource(id = BitwardenString.delete),
message = stringResource(id = BitwardenString.are_you_sure_delete_send),
confirmButtonText = stringResource(id = BitwardenString.yes),
dismissButtonText = stringResource(id = BitwardenString.cancel),
onConfirmClick = {
showConfirmationDialog = null
vaultItemListingHandlers.overflowItemClick(option)
},
onDismissClick = { showConfirmationDialog = null },
onDismissRequest = { showConfirmationDialog = null },
)
}
is ListingItemOverflowAction.SendAction.CopyUrlClick,
is ListingItemOverflowAction.SendAction.EditClick,
is ListingItemOverflowAction.SendAction.ViewClick,
is ListingItemOverflowAction.SendAction.RemovePasswordClick,
is ListingItemOverflowAction.SendAction.ShareUrlClick,
is ListingItemOverflowAction.VaultAction.CopyNoteClick,
is ListingItemOverflowAction.VaultAction.CopyNumberClick,
is ListingItemOverflowAction.VaultAction.CopyPasswordClick,
is ListingItemOverflowAction.VaultAction.CopySecurityCodeClick,
is ListingItemOverflowAction.VaultAction.CopyUsernameClick,
is ListingItemOverflowAction.VaultAction.EditClick,
is ListingItemOverflowAction.VaultAction.LaunchClick,
is ListingItemOverflowAction.VaultAction.ViewClick,
is ListingItemOverflowAction.VaultAction.CopyTotpClick,
is ListingItemOverflowAction.VaultAction.ArchiveClick,
is ListingItemOverflowAction.VaultAction.UnarchiveClick,
null,
-> Unit
overflowSpeedBumpAction?.let { action ->
action
.speedBump
?.let { speedBump ->
BitwardenTwoButtonDialog(
twoButtonDialogData = speedBump,
onConfirmClick = {
overflowSpeedBumpAction = null
vaultItemListingHandlers.overflowItemClick(action)
},
onDismissClick = { overflowSpeedBumpAction = null },
onDismissRequest = { overflowSpeedBumpAction = null },
)
}
?: run {
// If we somehow get here and there is no speed bump, then we should keep on going.
overflowSpeedBumpAction = null
vaultItemListingHandlers.overflowItemClick(action)
}
}
var masterPasswordRepromptData by remember { mutableStateOf<MasterPasswordRepromptData?>(null) }
@@ -264,8 +250,12 @@ fun VaultItemListingContent(
contentDescription = option.contentDescription(),
onClick = {
when (option) {
is ListingItemOverflowAction.SendAction.DeleteClick -> {
showConfirmationDialog = option
is ListingItemOverflowAction.SendAction -> {
if (option.speedBump != null) {
overflowSpeedBumpAction = option
} else {
vaultItemListingHandlers.overflowItemClick(option)
}
}
is ListingItemOverflowAction.VaultAction -> {
@@ -276,19 +266,19 @@ fun VaultItemListingContent(
MasterPasswordRepromptData.OverflowItem(
action = option,
)
} else if (option.speedBump != null) {
overflowSpeedBumpAction = option
} else {
vaultItemListingHandlers.overflowItemClick(option)
}
}
else -> vaultItemListingHandlers.overflowItemClick(option)
}
},
)
}
// Only show options if allowed
.filter { !policyDisablesSend }
.toPersistentList(),
.toImmutableList(),
cardStyle = state
.displayItemList
.toListItemCardStyle(index = index, dividerPadding = 56.dp),

View File

@@ -1,7 +1,11 @@
@file:OmitFromCoverage
package com.x8bit.bitwarden.ui.vault.feature.itemlisting.model
import android.os.Parcelable
import com.bitwarden.annotation.OmitFromCoverage
import com.bitwarden.send.SendType
import com.bitwarden.ui.platform.components.dialog.model.BitwardenTwoButtonDialogData
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.util.Text
import com.bitwarden.ui.util.asText
@@ -23,6 +27,11 @@ sealed class ListingItemOverflowAction : Parcelable {
*/
abstract val contentDescription: Text
/**
* The data to be displayed for an optional speed bump dialog.
*/
abstract val speedBump: BitwardenTwoButtonDialogData?
/**
* Represents the send actions.
*/
@@ -37,6 +46,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : SendAction() {
override val title: Text get() = BitwardenString.view.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -49,6 +59,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : SendAction() {
override val title: Text get() = BitwardenString.edit.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -58,6 +69,7 @@ sealed class ListingItemOverflowAction : Parcelable {
data class CopyUrlClick(val sendUrl: String) : SendAction() {
override val title: Text get() = BitwardenString.copy_link.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -68,6 +80,7 @@ sealed class ListingItemOverflowAction : Parcelable {
override val title: Text get() = BitwardenString.share_link.asText()
override val contentDescription: Text
get() = BitwardenString.external_link_format.asText(title)
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -77,6 +90,7 @@ sealed class ListingItemOverflowAction : Parcelable {
data class RemovePasswordClick(val sendId: String) : SendAction() {
override val title: Text get() = BitwardenString.remove_password.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -86,6 +100,13 @@ sealed class ListingItemOverflowAction : Parcelable {
data class DeleteClick(val sendId: String) : SendAction() {
override val title: Text get() = BitwardenString.delete.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData?
get() = BitwardenTwoButtonDialogData(
title = BitwardenString.delete.asText(),
message = BitwardenString.are_you_sure_delete_send.asText(),
confirmButtonText = BitwardenString.yes.asText(),
dismissButtonText = BitwardenString.cancel.asText(),
)
}
}
@@ -110,6 +131,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : VaultAction() {
override val title: Text get() = BitwardenString.view.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -123,6 +145,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : VaultAction() {
override val title: Text get() = BitwardenString.edit.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -133,6 +156,7 @@ sealed class ListingItemOverflowAction : Parcelable {
override val title: Text get() = BitwardenString.copy_username.asText()
override val requiresPasswordReprompt: Boolean get() = false
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -145,6 +169,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : VaultAction() {
override val title: Text get() = BitwardenString.copy_password.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -157,6 +182,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : VaultAction() {
override val title: Text get() = BitwardenString.copy_totp.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -169,6 +195,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : VaultAction() {
override val title: Text get() = BitwardenString.copy_number.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -181,6 +208,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : VaultAction() {
override val title: Text get() = BitwardenString.copy_security_code.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -193,6 +221,7 @@ sealed class ListingItemOverflowAction : Parcelable {
) : VaultAction() {
override val title: Text get() = BitwardenString.copy_notes.asText()
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -204,6 +233,7 @@ sealed class ListingItemOverflowAction : Parcelable {
override val requiresPasswordReprompt: Boolean get() = false
override val contentDescription: Text
get() = BitwardenString.external_link_format.asText(title)
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
/**
@@ -214,6 +244,13 @@ sealed class ListingItemOverflowAction : Parcelable {
override val title: Text get() = BitwardenString.archive_verb.asText()
override val requiresPasswordReprompt: Boolean get() = true
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData?
get() = BitwardenTwoButtonDialogData(
title = BitwardenString.archive_item.asText(),
message = BitwardenString.once_archived_this_item_will_be_excluded.asText(),
confirmButtonText = BitwardenString.archive_verb.asText(),
dismissButtonText = BitwardenString.cancel.asText(),
)
}
/**
@@ -224,6 +261,7 @@ sealed class ListingItemOverflowAction : Parcelable {
override val title: Text get() = BitwardenString.unarchive.asText()
override val requiresPasswordReprompt: Boolean get() = true
override val contentDescription: Text get() = title
override val speedBump: BitwardenTwoButtonDialogData? get() = null
}
}
}

View File

@@ -11,7 +11,7 @@ import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@@ -20,6 +20,7 @@ import androidx.compose.ui.unit.dp
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.bitwarden.ui.platform.base.util.toListItemCardStyle
import com.bitwarden.ui.platform.components.card.BitwardenActionCard
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
import com.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.platform.components.model.CardStyle
@@ -48,7 +49,7 @@ fun VaultContent(
modifier: Modifier = Modifier,
) {
// Handles the master password prompt for the row click
var masterPasswordRepromptItem by remember {
var masterPasswordRepromptItem by rememberSaveable {
mutableStateOf<VaultState.ViewState.VaultItem?>(value = null)
}
masterPasswordRepromptItem?.let { action ->
@@ -61,7 +62,7 @@ fun VaultContent(
)
}
// Handles the master password prompt for the overflow clicks
var overflowMasterPasswordRepromptAction by remember {
var overflowMasterPasswordRepromptAction by rememberSaveable {
mutableStateOf<ListingItemOverflowAction.VaultAction?>(value = null)
}
overflowMasterPasswordRepromptAction?.let { action ->
@@ -73,6 +74,31 @@ fun VaultContent(
onDismissRequest = { overflowMasterPasswordRepromptAction = null },
)
}
var overflowSpeedBumpAction: ListingItemOverflowAction.VaultAction? by rememberSaveable {
mutableStateOf(value = null)
}
overflowSpeedBumpAction?.let { action ->
action
.speedBump
?.let { speedBump ->
BitwardenTwoButtonDialog(
twoButtonDialogData = speedBump,
onConfirmClick = {
overflowSpeedBumpAction = null
vaultHandlers.overflowOptionClick(action)
},
onDismissClick = { overflowSpeedBumpAction = null },
onDismissRequest = { overflowSpeedBumpAction = null },
)
}
?: run {
// If we somehow get here and there is no speed bump, then we should keep on going.
overflowSpeedBumpAction = null
vaultHandlers.overflowOptionClick(action)
}
}
LazyColumn(
modifier = modifier,
) {
@@ -162,6 +188,8 @@ fun VaultContent(
action.requiresPasswordReprompt
) {
overflowMasterPasswordRepromptAction = action
} else if (action.speedBump != null) {
overflowSpeedBumpAction = action
} else {
vaultHandlers.overflowOptionClick(action)
}
@@ -364,6 +392,8 @@ fun VaultContent(
action.requiresPasswordReprompt
) {
overflowMasterPasswordRepromptAction = action
} else if (action.speedBump != null) {
overflowSpeedBumpAction = action
} else {
vaultHandlers.overflowOptionClick(action)
}

View File

@@ -728,26 +728,6 @@ class SearchScreenTest : BitwardenComposeTest() {
)
}
composeTestRule
.onNodeWithContentDescription("More options")
.assertIsDisplayed()
.performClick()
composeTestRule
.onNodeWithText("Archive")
.assert(hasAnyAncestor(isDialog()))
.performScrollTo()
.assertIsDisplayed()
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(
SearchAction.OverflowOptionClick(
overflowAction = ListingItemOverflowAction.VaultAction.ArchiveClick(
cipherId = "mockId-1",
),
),
)
}
composeTestRule.assertNoDialogExists()
}
@@ -959,7 +939,52 @@ class SearchScreenTest : BitwardenComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `on send item delete overflow option click should display delete confirmation dialog and emits DeleteSendConfirmClick on confirmation`() {
fun `on vault item archive overflow option click should display archive confirmation dialog and emits ArchiveClick on confirmation`() {
val itemId = "mockId-1"
val title = "Archive item"
mutableStateFlow.update {
it.copy(
viewState = SearchState.ViewState.Content(
displayItems = persistentListOf(createMockDisplayItemForCipher(number = 1)),
),
)
}
composeTestRule.onNode(isDialog()).assertDoesNotExist()
composeTestRule.onNodeWithText(text = title).assertDoesNotExist()
composeTestRule
.onNodeWithContentDescription(label = "More options")
.assertIsDisplayed()
.performClick()
composeTestRule
.onNodeWithText(text = "Archive")
.performScrollTo()
.assert(hasAnyAncestor(isDialog()))
.performClick()
composeTestRule
.onNodeWithText(title)
.assertIsDisplayed()
.assert(hasAnyAncestor(isDialog()))
composeTestRule
.onNodeWithText(text = "Archive")
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(
SearchAction.OverflowOptionClick(
overflowAction = ListingItemOverflowAction.VaultAction.ArchiveClick(
cipherId = itemId,
),
),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `on send item delete overflow option click should display delete confirmation dialog and emits DeleteClick on confirmation`() {
val sendId = "mockId-1"
val message = "Are you sure you want to delete this Send?"
mutableStateFlow.update {

View File

@@ -1450,6 +1450,55 @@ class VaultItemListingScreenTest : BitwardenComposeTest() {
composeTestRule.assertNoDialogExists()
}
@Suppress("MaxLineLength")
@Test
fun `on item archive overflow option click should display archive confirmation dialog and emits ArchiveClick on confirmation`() {
val cipherId = "mockId-1"
val archiveAction = ListingItemOverflowAction.VaultAction.ArchiveClick(cipherId = cipherId)
mutableStateFlow.update {
it.copy(
itemListingType = VaultItemListingState.ItemListingType.Vault.Login,
viewState = VaultItemListingState.ViewState.Content(
displayCollectionList = emptyList(),
displayItemList = listOf(
createCipherDisplayItem(number = 1).copy(
overflowOptions = listOf(archiveAction),
),
),
displayFolderList = emptyList(),
),
)
}
composeTestRule
.onNodeWithText(text = "mockTitle-1")
.onChildren()
.filterToOne(hasContentDescription(value = "More options"))
.assertIsDisplayed()
.performClick()
composeTestRule
.onNodeWithText(text = "Archive")
.assert(hasAnyAncestor(isDialog()))
.performClick()
composeTestRule
.onAllNodesWithText(text = "Archive item")
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
composeTestRule
.onAllNodesWithText(text = "Archive")
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(
action = VaultItemListingsAction.OverflowOptionClick(action = archiveAction),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `on cipher item overflow option click when reprompt is required should show the master password dialog`() {

View File

@@ -1353,6 +1353,57 @@ class VaultScreenTest : BitwardenComposeTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `on vault item archive overflow option click should display archive confirmation dialog and emits ArchiveClick on confirmation`() {
val itemText = "Test Item"
val userName = "Bitwarden"
val cipherId = "12345"
val archiveAction = ListingItemOverflowAction.VaultAction.ArchiveClick(cipherId = cipherId)
val vaultItem = VaultState.ViewState.VaultItem.Login(
id = cipherId,
name = itemText.asText(),
username = userName.asText(),
overflowOptions = persistentListOf(archiveAction),
shouldShowMasterPasswordReprompt = false,
hasDecryptionError = false,
)
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_CONTENT_VIEW_STATE.copy(
favoriteItems = listOf(vaultItem),
),
)
}
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(itemText))
composeTestRule
.onNodeWithText(text = itemText)
.onChildren()
.filterToOne(hasContentDescription(value = "More options"))
.assertIsDisplayed()
.performClick()
composeTestRule
.onNodeWithText(text = "Archive")
.assert(hasAnyAncestor(isDialog()))
.performClick()
composeTestRule
.onAllNodesWithText(text = "Archive item")
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
composeTestRule
.onAllNodesWithText(text = "Archive")
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(VaultAction.OverflowOptionClick(overflowAction = archiveAction))
}
}
@Suppress("MaxLineLength")
@Test
fun `clicking a favorite item overflow with password prompt should prompt for password before dismissing upon Cancel`() {

View File

@@ -28,11 +28,51 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.bitwarden.ui.platform.base.util.toAnnotatedString
import com.bitwarden.ui.platform.components.button.BitwardenTextButton
import com.bitwarden.ui.platform.components.dialog.model.BitwardenTwoButtonDialogData
import com.bitwarden.ui.platform.components.dialog.util.maxDialogHeight
import com.bitwarden.ui.platform.components.dialog.util.maxDialogWidth
import com.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
import com.bitwarden.ui.platform.theme.BitwardenTheme
/**
* Represents a Bitwarden-styled dialog with two buttons.
*
* @param twoButtonDialogData The data to be displayed.
* @param onConfirmClick Called when the confirm button is clicked.
* @param onDismissClick Called when the dismiss button is clicked.
* @param onDismissRequest Called when the user attempts to dismiss the dialog (for example by
* tapping outside of it).
* @param confirmTextColor The color of the confirm text.
* @param dismissTextColor The color of the dismiss text.
* @param dismissOnBackPress Indicates that the back button should dismiss the dialog.
* @param dismissOnClickOutside Indicates that tapping outside the dialog should dismiss the dialog.
*/
@Composable
fun BitwardenTwoButtonDialog(
twoButtonDialogData: BitwardenTwoButtonDialogData,
onConfirmClick: () -> Unit,
onDismissClick: () -> Unit,
onDismissRequest: () -> Unit,
confirmTextColor: Color = BitwardenTheme.colorScheme.outlineButton.foreground,
dismissTextColor: Color = BitwardenTheme.colorScheme.outlineButton.foreground,
dismissOnBackPress: Boolean = true,
dismissOnClickOutside: Boolean = true,
) {
BitwardenTwoButtonDialog(
title = twoButtonDialogData.title?.invoke(),
message = twoButtonDialogData.message(),
confirmButtonText = twoButtonDialogData.confirmButtonText(),
dismissButtonText = twoButtonDialogData.dismissButtonText(),
onConfirmClick = onConfirmClick,
onDismissClick = onDismissClick,
onDismissRequest = onDismissRequest,
confirmTextColor = confirmTextColor,
dismissTextColor = dismissTextColor,
dismissOnBackPress = dismissOnBackPress,
dismissOnClickOutside = dismissOnClickOutside,
)
}
/**
* Represents a Bitwarden-styled dialog with two buttons.
*
@@ -46,6 +86,8 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme
* tapping outside of it).
* @param confirmTextColor The color of the confirm text.
* @param dismissTextColor The color of the dismiss text.
* @param dismissOnBackPress Indicates that the back button should dismiss the dialog.
* @param dismissOnClickOutside Indicates that tapping outside the dialog should dismiss the dialog.
*/
@Composable
fun BitwardenTwoButtonDialog(
@@ -89,6 +131,8 @@ fun BitwardenTwoButtonDialog(
* tapping outside of it).
* @param confirmTextColor The color of the confirm text.
* @param dismissTextColor The color of the dismiss text.
* @param dismissOnBackPress Indicates that the back button should dismiss the dialog.
* @param dismissOnClickOutside Indicates that tapping outside the dialog should dismiss the dialog.
*/
@Composable
@Suppress("LongMethod")

View File

@@ -0,0 +1,19 @@
package com.bitwarden.ui.platform.components.dialog.model
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
import com.bitwarden.ui.util.Text
/**
* Contains the data for displaying a [BitwardenTwoButtonDialog].
*
* @property title The optional title to show.
* @property message The message to show.
* @property confirmButtonText The text to show on confirm button.
* @property dismissButtonText The text to show on dismiss button.
*/
data class BitwardenTwoButtonDialogData(
val title: Text?,
val message: Text,
val confirmButtonText: Text,
val dismissButtonText: Text,
)

View File

@@ -42,6 +42,8 @@
<string name="avoid_logout_on_kdf_change">Avoid logout on KDF change</string>
<string name="migrate_my_vault_to_my_items">Migrate My Vault to My Items</string>
<string name="archive_items">Archive Items</string>
<string name="archive_item">Archive item</string>
<string name="once_archived_this_item_will_be_excluded">Once archived, this item will be excluded from search results and autofill suggestions.</string>
<string name="send_email_verification">Send Email Verification</string>
<string name="trigger_cookie_acquisition">Trigger cookie acquisition</string>
<string name="clear_sso_cookies">Clear SSO cookies</string>