diff --git a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt index 0e051cd090..2ca2bf1043 100644 --- a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt +++ b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt @@ -53,6 +53,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.bitwarden.authenticator.R import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.ItemListingExpandableFabAction import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.SharedCodesDisplayState +import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VaultDropdownMenuAction +import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VerificationCodeDisplayItem import com.bitwarden.authenticator.ui.platform.base.util.EventsEffect import com.bitwarden.authenticator.ui.platform.base.util.asText import com.bitwarden.authenticator.ui.platform.components.appbar.BitwardenMediumTopAppBar @@ -211,17 +213,13 @@ fun ItemListingScreen( ) } }, - onEditItemClick = remember(viewModel) { - { + onDropdownMenuClick = remember(viewModel) { + { action, item -> viewModel.trySendAction( - ItemListingAction.EditItemClick(it), - ) - } - }, - onDeleteItemClick = remember(viewModel) { - { - viewModel.trySendAction( - ItemListingAction.DeleteItemClick(it), + ItemListingAction.DropdownMenuClick( + menuAction = action, + item = item, + ), ) } }, @@ -245,11 +243,6 @@ fun ItemListingScreen( viewModel.trySendAction(ItemListingAction.SyncWithBitwardenDismiss) } }, - onMoveToBitwardenClick = remember(viewModel) { - { - viewModel.trySendAction(ItemListingAction.MoveToBitwardenClick(it)) - } - }, ) } @@ -354,9 +347,7 @@ private fun ItemListingContent( onScanQrCodeClick: () -> Unit, onEnterSetupKeyClick: () -> Unit, onItemClick: (String) -> Unit, - onEditItemClick: (String) -> Unit, - onDeleteItemClick: (String) -> Unit, - onMoveToBitwardenClick: (String) -> Unit, + onDropdownMenuClick: (VaultDropdownMenuAction, VerificationCodeDisplayItem) -> Unit, onDownloadBitwardenClick: () -> Unit, onDismissDownloadBitwardenClick: () -> Unit, onSyncWithBitwardenClick: () -> Unit, @@ -467,9 +458,9 @@ private fun ItemListingContent( alertThresholdSeconds = it.alertThresholdSeconds, startIcon = it.startIcon, onItemClick = { onItemClick(it.authCode) }, - onEditItemClick = { onEditItemClick(it.id) }, - onDeleteItemClick = { onDeleteItemClick(it.id) }, - onMoveToBitwardenClick = { onMoveToBitwardenClick(it.id) }, + onDropdownMenuClick = { action -> + onDropdownMenuClick(action, it) + }, showMoveToBitwarden = it.showMoveToBitwarden, allowLongPress = it.allowLongPressActions, modifier = Modifier.fillMaxWidth(), @@ -508,9 +499,9 @@ private fun ItemListingContent( alertThresholdSeconds = it.alertThresholdSeconds, startIcon = it.startIcon, onItemClick = { onItemClick(it.authCode) }, - onEditItemClick = { onEditItemClick(it.id) }, - onDeleteItemClick = { onDeleteItemClick(it.id) }, - onMoveToBitwardenClick = { onMoveToBitwardenClick(it.id) }, + onDropdownMenuClick = { action -> + onDropdownMenuClick(action, it) + }, showMoveToBitwarden = it.showMoveToBitwarden, allowLongPress = it.allowLongPressActions, modifier = Modifier.fillMaxWidth(), @@ -544,9 +535,9 @@ private fun ItemListingContent( alertThresholdSeconds = it.alertThresholdSeconds, startIcon = it.startIcon, onItemClick = { onItemClick(it.authCode) }, - onEditItemClick = { }, - onDeleteItemClick = { }, - onMoveToBitwardenClick = { }, + onDropdownMenuClick = { action -> + onDropdownMenuClick(action, it) + }, showMoveToBitwarden = it.showMoveToBitwarden, allowLongPress = it.allowLongPressActions, modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt index fa79362759..905abd5e8d 100644 --- a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt +++ b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt @@ -21,6 +21,7 @@ import com.bitwarden.authenticator.data.platform.manager.imports.model.GoogleAut import com.bitwarden.authenticator.data.platform.repository.SettingsRepository import com.bitwarden.authenticator.data.platform.repository.model.DataState import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.SharedCodesDisplayState +import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VaultDropdownMenuAction import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VerificationCodeDisplayItem import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.util.toDisplayItem import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.util.toSharedCodesDisplayState @@ -62,7 +63,6 @@ class ItemListingViewModel @Inject constructor( ) { init { - settingsRepository .authenticatorAlertThresholdSecondsFlow .map { ItemListingAction.Internal.AlertThresholdSecondsReceive(it) } @@ -110,10 +110,6 @@ class ItemListingViewModel @Inject constructor( sendEvent(ItemListingEvent.NavigateBack) } - is ItemListingAction.DeleteItemClick -> { - handleDeleteItemClick(action) - } - is ItemListingAction.ConfirmDeleteClick -> { handleConfirmDeleteClick(action) } @@ -123,11 +119,7 @@ class ItemListingViewModel @Inject constructor( } is ItemListingAction.ItemClick -> { - handleItemClick(action) - } - - is ItemListingAction.EditItemClick -> { - handleEditItemClick(action) + handleCopyItemClick(action.authCode) } is ItemListingAction.DialogDismiss -> { @@ -142,6 +134,10 @@ class ItemListingViewModel @Inject constructor( handleInternalAction(action) } + is ItemListingAction.DropdownMenuClick -> { + handleDropdownMenuClick(action) + } + ItemListingAction.DownloadBitwardenClick -> { handleDownloadBitwardenClick() } @@ -157,10 +153,6 @@ class ItemListingViewModel @Inject constructor( ItemListingAction.SyncWithBitwardenDismiss -> { handleSyncWithBitwardenDismiss() } - - is ItemListingAction.MoveToBitwardenClick -> { - handleMoveToBitwardenClick(action) - } } } @@ -168,27 +160,22 @@ class ItemListingViewModel @Inject constructor( sendEvent(ItemListingEvent.NavigateToAppSettings) } - private fun handleItemClick(action: ItemListingAction.ItemClick) { - clipboardManager.setText(action.authCode) - sendEvent( - ItemListingEvent.ShowToast( - message = R.string.value_has_been_copied.asText(action.authCode), - ), - ) + private fun handleCopyItemClick(authCode: String) { + clipboardManager.setText(authCode) } - private fun handleEditItemClick(action: ItemListingAction.EditItemClick) { - sendEvent(ItemListingEvent.NavigateToEditItem(action.itemId)) + private fun handleEditItemClick(itemId: String) { + sendEvent(ItemListingEvent.NavigateToEditItem(itemId)) } - private fun handleMoveToBitwardenClick(action: ItemListingAction.MoveToBitwardenClick) { + private fun handleMoveToBitwardenClick(itemId: String) { viewModelScope.launch { val item = authenticatorRepository - .getItemStateFlow(action.entityId) + .getItemStateFlow(itemId) .first { it.data != null } val didLaunchAddTotpFlow = authenticatorBridgeManager.startAddTotpLoginItemFlow( - totpUri = item.data!!.toOtpAuthUriString(), + totpUri = item.data?.toOtpAuthUriString().orEmpty(), ) if (!didLaunchAddTotpFlow) { mutableStateFlow.update { @@ -203,12 +190,12 @@ class ItemListingViewModel @Inject constructor( } } - private fun handleDeleteItemClick(action: ItemListingAction.DeleteItemClick) { + private fun handleDeleteItemClick(itemId: String) { mutableStateFlow.update { it.copy( dialog = ItemListingState.DialogState.DeleteConfirmationPrompt( message = R.string.do_you_really_want_to_permanently_delete_cipher.asText(), - itemId = action.itemId, + itemId = itemId, ), ) } @@ -528,6 +515,15 @@ class ItemListingViewModel @Inject constructor( sendEvent(ItemListingEvent.NavigateToBitwardenListing) } + private fun handleDropdownMenuClick(action: ItemListingAction.DropdownMenuClick) { + when (action.menuAction) { + VaultDropdownMenuAction.COPY -> handleCopyItemClick(action.item.authCode) + VaultDropdownMenuAction.EDIT -> handleEditItemClick(action.item.id) + VaultDropdownMenuAction.MOVE -> handleMoveToBitwardenClick(action.item.id) + VaultDropdownMenuAction.DELETE -> handleDeleteItemClick(action.item.id) + } + } + private fun handleDownloadBitwardenDismiss() { settingsRepository.hasUserDismissedDownloadBitwardenCard = true mutableStateFlow.update { @@ -663,7 +659,6 @@ data class ItemListingState( val viewState: ViewState, val dialog: DialogState?, ) : Parcelable { - /** * Represents the different view states of the [ItemListingScreen]. */ @@ -714,7 +709,6 @@ data class ItemListingState( * Display an action card on the item [ItemListingScreen]. */ sealed class ActionCardState : Parcelable { - /** * Display no action card. */ @@ -738,7 +732,6 @@ data class ItemListingState( * Display a dialog on the [ItemListingScreen]. */ sealed class DialogState : Parcelable { - /** * Displays the loading dialog to the user. */ @@ -769,7 +762,6 @@ data class ItemListingState( * Represents a set of events related to viewing the item listing. */ sealed class ItemListingEvent { - /** * Navigates to the Create Account screen. */ @@ -830,7 +822,6 @@ sealed class ItemListingEvent { * Each subclass of this sealed class denotes a distinct action that can be taken. */ sealed class ItemListingAction { - /** * The user clicked the back button. */ @@ -856,11 +847,6 @@ sealed class ItemListingAction { */ data class ItemClick(val authCode: String) : ItemListingAction() - /** - * The user clicked edit item. - */ - data class EditItemClick(val itemId: String) : ItemListingAction() - /** * The user dismissed the dialog. */ @@ -892,15 +878,25 @@ sealed class ItemListingAction { data object SyncWithBitwardenDismiss : ItemListingAction() /** - * The user clicked the "Move to Bitwarden" action on a local verification item. + * The user clicked confirm when prompted to delete an item. */ - data class MoveToBitwardenClick(val entityId: String) : ItemListingAction() + data class ConfirmDeleteClick(val itemId: String) : ItemListingAction() + + /** + * Represents an action triggered when the user clicks an item in the dropdown menu. + * + * @param menuAction The action selected from the dropdown menu. + * @param id The identifier of the item on which the action is being performed. + */ + data class DropdownMenuClick( + val menuAction: VaultDropdownMenuAction, + val item: VerificationCodeDisplayItem, + ) : ItemListingAction() /** * Models actions that [ItemListingScreen] itself may send. */ sealed class Internal : ItemListingAction() { - /** * Indicates verification items have been received. */ @@ -941,14 +937,4 @@ sealed class ItemListingAction { */ data object FirstTimeUserSyncReceive : Internal() } - - /** - * The user clicked Delete. - */ - data class DeleteItemClick(val itemId: String) : ItemListingAction() - - /** - * The user clicked confirm when prompted to delete an item. - */ - data class ConfirmDeleteClick(val itemId: String) : ItemListingAction() } diff --git a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/VaultVerificationCodeItem.kt b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/VaultVerificationCodeItem.kt index c322350e90..0ac68978ac 100644 --- a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/VaultVerificationCodeItem.kt +++ b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/VaultVerificationCodeItem.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.bitwarden.authenticator.R +import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VaultDropdownMenuAction import com.bitwarden.authenticator.ui.platform.components.icon.BitwardenIcon import com.bitwarden.authenticator.ui.platform.components.indicator.BitwardenCircularCountdownIndicator import com.bitwarden.authenticator.ui.platform.components.model.IconData @@ -46,8 +47,12 @@ import com.bitwarden.authenticator.ui.platform.theme.AuthenticatorTheme * @param secondaryLabel The supporting label for the item. Represents the OTP account name. * @param periodSeconds The times span where the code is valid. * @param timeLeftSeconds The seconds remaining until a new code is needed. + * @param alertThresholdSeconds The time threshold in seconds to display an expiration warning. * @param startIcon The leading icon for the item. * @param onItemClick The lambda function to be invoked when the item is clicked. + * @param onDropdownMenuClick A lambda function invoked when a dropdown menu action is clicked. + * @param allowLongPress Whether long-press interactions are enabled for the item. + * @param showMoveToBitwarden Whether the option to move the item to Bitwarden is displayed. * @param modifier The modifier for the item. */ @OptIn(ExperimentalFoundationApi::class) @@ -62,9 +67,7 @@ fun VaultVerificationCodeItem( alertThresholdSeconds: Int, startIcon: IconData, onItemClick: () -> Unit, - onEditItemClick: () -> Unit, - onDeleteItemClick: () -> Unit, - onMoveToBitwardenClick: () -> Unit, + onDropdownMenuClick: (VaultDropdownMenuAction) -> Unit, allowLongPress: Boolean, showMoveToBitwarden: Boolean, modifier: Modifier = Modifier, @@ -155,13 +158,29 @@ fun VaultVerificationCodeItem( expanded = shouldShowDropdownMenu, onDismissRequest = { shouldShowDropdownMenu = false }, ) { + DropdownMenuItem( + text = { + Text(text = stringResource(id = R.string.copy)) + }, + onClick = { + shouldShowDropdownMenu = false + onDropdownMenuClick(VaultDropdownMenuAction.COPY) + }, + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.ic_copy), + contentDescription = stringResource(id = R.string.copy), + ) + }, + ) + HorizontalDivider() DropdownMenuItem( text = { Text(text = stringResource(id = R.string.edit_item)) }, onClick = { shouldShowDropdownMenu = false - onEditItemClick() + onDropdownMenuClick(VaultDropdownMenuAction.EDIT) }, leadingIcon = { Icon( @@ -174,16 +193,16 @@ fun VaultVerificationCodeItem( HorizontalDivider() DropdownMenuItem( text = { - Text(text = stringResource(id = R.string.copy_to_bitwarden)) + Text(text = stringResource(id = R.string.move_to_bitwarden)) }, onClick = { shouldShowDropdownMenu = false - onMoveToBitwardenClick() + onDropdownMenuClick(VaultDropdownMenuAction.MOVE) }, leadingIcon = { Icon( painter = painterResource(id = R.drawable.ic_arrow_right), - contentDescription = stringResource(id = R.string.copy_to_bitwarden), + contentDescription = stringResource(id = R.string.move_to_bitwarden), ) }, ) @@ -195,7 +214,7 @@ fun VaultVerificationCodeItem( }, onClick = { shouldShowDropdownMenu = false - onDeleteItemClick() + onDropdownMenuClick(VaultDropdownMenuAction.DELETE) }, leadingIcon = { Icon( @@ -222,9 +241,7 @@ private fun VerificationCodeItem_preview() { alertThresholdSeconds = 7, startIcon = IconData.Local(R.drawable.ic_login_item), onItemClick = {}, - onEditItemClick = {}, - onDeleteItemClick = {}, - onMoveToBitwardenClick = {}, + onDropdownMenuClick = {}, allowLongPress = true, modifier = Modifier.padding(horizontal = 16.dp), showMoveToBitwarden = true, diff --git a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/model/VaultDropdownMenuAction.kt b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/model/VaultDropdownMenuAction.kt new file mode 100644 index 0000000000..0247844a10 --- /dev/null +++ b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/model/VaultDropdownMenuAction.kt @@ -0,0 +1,11 @@ +package com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model + +/** + * Enum representing the available actions in the Vault dropdown menu. + */ +enum class VaultDropdownMenuAction { + COPY, + EDIT, + MOVE, + DELETE, +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f39c0c64cd..2830d56f1c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -132,7 +132,7 @@ Allow Authenticator app syncing in settings to view all of your verification codes here. Something went wrong Please try again - Copy to Bitwarden + Move to Bitwarden Default save option Save to Bitwarden Save here diff --git a/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt b/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt index 412c6d5e1e..ced58fe8f4 100644 --- a/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt +++ b/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performTouchInput import com.bitwarden.authenticator.data.platform.repository.util.bufferedMutableSharedFlow import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.SharedCodesDisplayState +import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VaultDropdownMenuAction import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VerificationCodeDisplayItem import com.bitwarden.authenticator.ui.platform.base.BaseComposeTest import com.bitwarden.authenticator.ui.platform.base.util.asText @@ -202,7 +203,7 @@ class ItemListingScreenTest : BaseComposeTest() { } @Test - fun `clicking Copy to Bitwarden should send MoveToBitwardenClick`() { + fun `clicking Move to Bitwarden should send MoveToBitwardenClick`() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, @@ -216,10 +217,17 @@ class ItemListingScreenTest : BaseComposeTest() { .performTouchInput { longClick() } composeTestRule - .onNodeWithText("Copy to Bitwarden") + .onNodeWithText("Move to Bitwarden") .performClick() - verify { viewModel.trySendAction(ItemListingAction.MoveToBitwardenClick("1")) } + verify { + viewModel.trySendAction( + ItemListingAction.DropdownMenuClick( + menuAction = VaultDropdownMenuAction.MOVE, + item = LOCAL_CODE, + ), + ) + } } @Test diff --git a/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListViewModelTest.kt b/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModelTest.kt similarity index 89% rename from app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListViewModelTest.kt rename to app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModelTest.kt index b484502e5c..318224bad7 100644 --- a/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListViewModelTest.kt +++ b/app/src/test/java/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModelTest.kt @@ -12,6 +12,8 @@ import com.bitwarden.authenticator.data.platform.manager.clipboard.BitwardenClip import com.bitwarden.authenticator.data.platform.repository.SettingsRepository import com.bitwarden.authenticator.data.platform.repository.model.DataState import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.SharedCodesDisplayState +import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VaultDropdownMenuAction +import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VerificationCodeDisplayItem import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.util.toDisplayItem import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.util.toSharedCodesDisplayState import com.bitwarden.authenticator.ui.platform.base.BaseViewModelTest @@ -31,7 +33,7 @@ import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -class ItemListViewModelTest : BaseViewModelTest() { +class ItemListingViewModelTest : BaseViewModelTest() { private val mutableAuthenticatorAlertThresholdFlow = MutableStateFlow(AUTHENTICATOR_ALERT_SECONDS) @@ -381,7 +383,12 @@ class ItemListViewModelTest : BaseViewModelTest() { val viewModel = createViewModel() - viewModel.trySendAction(ItemListingAction.MoveToBitwardenClick(entityId = "1")) + viewModel.trySendAction( + ItemListingAction.DropdownMenuClick( + menuAction = VaultDropdownMenuAction.MOVE, + item = LOCAL_CODE, + ), + ) verify { authenticatorBridgeManager.startAddTotpLoginItemFlow(expectedUriString) } } @@ -406,7 +413,9 @@ class ItemListViewModelTest : BaseViewModelTest() { } returns false val viewModel = createViewModel() - viewModel.trySendAction(ItemListingAction.MoveToBitwardenClick(entityId = "1")) + viewModel.trySendAction( + ItemListingAction.DropdownMenuClick(VaultDropdownMenuAction.MOVE, LOCAL_CODE), + ) assertEquals( expectedState, viewModel.stateFlow.value, @@ -423,6 +432,66 @@ class ItemListViewModelTest : BaseViewModelTest() { } } + @Test + fun `should copy text to clipboard when DropdownMenuClick COPY is triggered`() = runTest { + val viewModel = createViewModel() + + every { clipboardManager.setText(text = LOCAL_CODE.authCode) } just runs + + viewModel.eventFlow.test { + viewModel.trySendAction( + ItemListingAction.DropdownMenuClick( + menuAction = VaultDropdownMenuAction.COPY, + item = LOCAL_CODE, + ), + ) + + verify(exactly = 1) { + clipboardManager.setText(text = LOCAL_CODE.authCode) + } + } + } + + @Test + fun `should trigger edit action when DropdownMenuClick EDIT is triggered`() = runTest { + val viewModel = createViewModel() + + viewModel.eventFlow.test { + viewModel.trySendAction( + ItemListingAction.DropdownMenuClick(VaultDropdownMenuAction.EDIT, LOCAL_CODE), + ) + + assertEquals( + ItemListingEvent.NavigateToEditItem(LOCAL_CODE.id), + awaitItem(), + ) + } + } + + @Test + fun `should trigger delete prompt when DropdownMenuClick DELETE is triggered`() = runTest { + val viewModel = createViewModel() + + val expectedState = DEFAULT_STATE.copy( + dialog = ItemListingState.DialogState.DeleteConfirmationPrompt( + message = R.string.do_you_really_want_to_permanently_delete_cipher.asText(), + itemId = LOCAL_CODE.id, + ), + ) + + viewModel.trySendAction( + ItemListingAction.DropdownMenuClick( + menuAction = VaultDropdownMenuAction.DELETE, + item = LOCAL_CODE, + ), + ) + + assertEquals( + expectedState, + viewModel.stateFlow.value, + ) + } + private fun createViewModel() = ItemListingViewModel( authenticatorRepository = authenticatorRepository, authenticatorBridgeManager = authenticatorBridgeManager, @@ -441,6 +510,19 @@ private val DEFAULT_STATE = ItemListingState( dialog = null, ) +private val LOCAL_CODE = VerificationCodeDisplayItem( + id = "1", + title = "issuer", + subtitle = null, + timeLeftSeconds = 10, + periodSeconds = 30, + alertThresholdSeconds = 7, + authCode = "123456", + favorite = false, + allowLongPressActions = true, + showMoveToBitwarden = true, +) + private val LOCAL_VERIFICATION_ITEMS = listOf( VerificationCodeItem( code = "123456",