mirror of
https://github.com/bitwarden/android.git
synced 2026-06-06 14:28:45 -05:00
Vault screen overflow option actions (#739)
This commit is contained in:
committed by
Álison Fernandes
parent
9a371843ee
commit
376278e97a
@@ -79,6 +79,8 @@ fun VaultContent(
|
||||
label = favoriteItem.name(),
|
||||
supportingLabel = favoriteItem.supportingLabel?.invoke(),
|
||||
onClick = { vaultHandlers.vaultItemClick(favoriteItem) },
|
||||
overflowOptions = favoriteItem.overflowOptions,
|
||||
onOverflowOptionClick = vaultHandlers.overflowOptionClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
@@ -227,12 +229,13 @@ fun VaultContent(
|
||||
)
|
||||
}
|
||||
items(state.noFolderItems) { noFolderItem ->
|
||||
|
||||
VaultEntryListItem(
|
||||
startIcon = noFolderItem.startIcon,
|
||||
label = noFolderItem.name(),
|
||||
supportingLabel = noFolderItem.supportingLabel?.invoke(),
|
||||
onClick = { vaultHandlers.vaultItemClick(noFolderItem) },
|
||||
overflowOptions = noFolderItem.overflowOptions,
|
||||
onOverflowOptionClick = vaultHandlers.overflowOptionClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenListItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.SelectionItemData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* A Composable function that displays a row item for different types of vault entries.
|
||||
@@ -18,6 +17,8 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
* @param label The primary text label to display for the item.
|
||||
* @param supportingLabel An optional secondary text label to display beneath the primary label.
|
||||
* @param onClick The lambda to be invoked when the item is clicked.
|
||||
* @param overflowOptions List of options to display for the item.
|
||||
* @param onOverflowOptionClick The lambda to be invoked when an overflow option is clicked.
|
||||
* @param modifier An optional [Modifier] for this Composable, defaulting to an empty Modifier.
|
||||
* This allows the caller to specify things like padding, size, etc.
|
||||
*/
|
||||
@@ -26,25 +27,25 @@ fun VaultEntryListItem(
|
||||
startIcon: IconData,
|
||||
label: String,
|
||||
onClick: () -> Unit,
|
||||
overflowOptions: List<ListingItemOverflowAction.VaultAction>,
|
||||
onOverflowOptionClick: (ListingItemOverflowAction.VaultAction) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingLabel: String? = null,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
BitwardenListItem(
|
||||
modifier = modifier,
|
||||
label = label,
|
||||
supportingLabel = supportingLabel,
|
||||
startIcon = startIcon,
|
||||
onClick = onClick,
|
||||
selectionDataList = persistentListOf(
|
||||
SelectionItemData(
|
||||
text = "Not yet implemented",
|
||||
onClick = {
|
||||
// TODO: Provide dialog-based implementation (BIT-1353 - BIT-1356)
|
||||
Toast.makeText(context, "Not yet implemented.", Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
),
|
||||
),
|
||||
selectionDataList = overflowOptions
|
||||
.map { option ->
|
||||
SelectionItemData(
|
||||
text = option.title(),
|
||||
onClick = { onOverflowOptionClick(option) },
|
||||
)
|
||||
}
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,6 +58,8 @@ private fun VaultEntryListItem_preview() {
|
||||
label = "Example Login",
|
||||
supportingLabel = "Username",
|
||||
onClick = {},
|
||||
overflowOptions = emptyList(),
|
||||
onOverflowOptionClick = {},
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
@@ -51,7 +52,9 @@ import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||
import com.x8bit.bitwarden.ui.platform.components.OverflowMenuItemData
|
||||
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
|
||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.handlers.VaultHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultItemListingType
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -73,6 +76,7 @@ fun VaultScreen(
|
||||
onNavigateToSearchVault: (searchType: SearchType.Vault) -> Unit,
|
||||
onDimBottomNavBarRequest: (shouldDim: Boolean) -> Unit,
|
||||
exitManager: ExitManager = LocalExitManager.current,
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsState()
|
||||
val context = LocalContext.current
|
||||
@@ -102,6 +106,8 @@ fun VaultScreen(
|
||||
onNavigateToVaultItemListingScreen(event.itemListingType)
|
||||
}
|
||||
|
||||
is VaultEvent.NavigateToUrl -> intentManager.launchUri(event.url.toUri())
|
||||
|
||||
VaultEvent.NavigateOutOfApp -> exitManager.exitApplication()
|
||||
is VaultEvent.ShowToast -> {
|
||||
Toast
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||
@@ -19,6 +20,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.hexToColor
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterData
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.initials
|
||||
@@ -43,6 +45,7 @@ import javax.inject.Inject
|
||||
@Suppress("TooManyFunctions")
|
||||
@HiltViewModel
|
||||
class VaultViewModel @Inject constructor(
|
||||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val authRepository: AuthRepository,
|
||||
private val vaultRepository: VaultRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
@@ -128,6 +131,7 @@ class VaultViewModel @Inject constructor(
|
||||
is VaultAction.TryAgainClick -> handleTryAgainClick()
|
||||
is VaultAction.DialogDismiss -> handleDialogDismiss()
|
||||
is VaultAction.RefreshPull -> handleRefreshPull()
|
||||
is VaultAction.OverflowOptionClick -> handleOverflowOptionClick(action)
|
||||
is VaultAction.Internal -> handleInternalAction(action)
|
||||
}
|
||||
}
|
||||
@@ -270,6 +274,82 @@ class VaultViewModel @Inject constructor(
|
||||
vaultRepository.sync()
|
||||
}
|
||||
|
||||
private fun handleOverflowOptionClick(action: VaultAction.OverflowOptionClick) {
|
||||
when (val overflowAction = action.overflowAction) {
|
||||
is ListingItemOverflowAction.VaultAction.CopyNoteClick -> {
|
||||
handleCopyNoteClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyNumberClick -> {
|
||||
handleCopyNumberClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyPasswordClick -> {
|
||||
handleCopyPasswordClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopySecurityCodeClick -> {
|
||||
handleCopySecurityCodeClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyUsernameClick -> {
|
||||
handleCopyUsernameClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.EditClick -> {
|
||||
handleEditClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.LaunchClick -> {
|
||||
handleLaunchClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.ViewClick -> {
|
||||
handleViewClick(overflowAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyNoteClick(action: ListingItemOverflowAction.VaultAction.CopyNoteClick) {
|
||||
clipboardManager.setText(action.notes)
|
||||
}
|
||||
|
||||
private fun handleCopyNumberClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyNumberClick,
|
||||
) {
|
||||
clipboardManager.setText(action.number)
|
||||
}
|
||||
|
||||
private fun handleCopyPasswordClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyPasswordClick,
|
||||
) {
|
||||
clipboardManager.setText(action.password)
|
||||
}
|
||||
|
||||
private fun handleCopySecurityCodeClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopySecurityCodeClick,
|
||||
) {
|
||||
clipboardManager.setText(action.securityCode)
|
||||
}
|
||||
|
||||
private fun handleCopyUsernameClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyUsernameClick,
|
||||
) {
|
||||
clipboardManager.setText(action.username)
|
||||
}
|
||||
|
||||
private fun handleEditClick(action: ListingItemOverflowAction.VaultAction.EditClick) {
|
||||
sendEvent(VaultEvent.NavigateToEditVaultItem(action.cipherId))
|
||||
}
|
||||
|
||||
private fun handleLaunchClick(action: ListingItemOverflowAction.VaultAction.LaunchClick) {
|
||||
sendEvent(VaultEvent.NavigateToUrl(action.url))
|
||||
}
|
||||
|
||||
private fun handleViewClick(action: ListingItemOverflowAction.VaultAction.ViewClick) {
|
||||
sendEvent(VaultEvent.NavigateToVaultItem(action.cipherId))
|
||||
}
|
||||
|
||||
private fun handleInternalAction(action: VaultAction.Internal) {
|
||||
when (action) {
|
||||
is VaultAction.Internal.PullToRefreshEnableReceive -> {
|
||||
@@ -584,6 +664,11 @@ data class VaultState(
|
||||
*/
|
||||
abstract val supportingLabel: Text?
|
||||
|
||||
/**
|
||||
* The overflow options to be displayed for the vault item.
|
||||
*/
|
||||
abstract val overflowOptions: List<ListingItemOverflowAction.VaultAction>
|
||||
|
||||
/**
|
||||
* Represents a login item within the vault.
|
||||
*
|
||||
@@ -594,6 +679,7 @@ data class VaultState(
|
||||
override val id: String,
|
||||
override val name: Text,
|
||||
override val startIcon: IconData = IconData.Local(R.drawable.ic_login_item),
|
||||
override val overflowOptions: List<ListingItemOverflowAction.VaultAction>,
|
||||
val username: Text?,
|
||||
) : VaultItem() {
|
||||
override val supportingLabel: Text? get() = username
|
||||
@@ -610,6 +696,7 @@ data class VaultState(
|
||||
override val id: String,
|
||||
override val name: Text,
|
||||
override val startIcon: IconData = IconData.Local(R.drawable.ic_card_item),
|
||||
override val overflowOptions: List<ListingItemOverflowAction.VaultAction>,
|
||||
val brand: Text? = null,
|
||||
val lastFourDigits: Text? = null,
|
||||
) : VaultItem() {
|
||||
@@ -636,6 +723,7 @@ data class VaultState(
|
||||
override val id: String,
|
||||
override val name: Text,
|
||||
override val startIcon: IconData = IconData.Local(R.drawable.ic_identity_item),
|
||||
override val overflowOptions: List<ListingItemOverflowAction.VaultAction>,
|
||||
val firstName: Text?,
|
||||
) : VaultItem() {
|
||||
override val supportingLabel: Text? get() = firstName
|
||||
@@ -650,6 +738,7 @@ data class VaultState(
|
||||
override val id: String,
|
||||
override val name: Text,
|
||||
override val startIcon: IconData = IconData.Local(R.drawable.ic_secure_note_item),
|
||||
override val overflowOptions: List<ListingItemOverflowAction.VaultAction>,
|
||||
) : VaultItem() {
|
||||
override val supportingLabel: Text? get() = null
|
||||
}
|
||||
@@ -726,6 +815,13 @@ sealed class VaultEvent {
|
||||
val itemListingType: VaultItemListingType,
|
||||
) : VaultEvent()
|
||||
|
||||
/**
|
||||
* Navigates to the given [url].
|
||||
*/
|
||||
data class NavigateToUrl(
|
||||
val url: String,
|
||||
) : VaultEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the verification code screen.
|
||||
*/
|
||||
@@ -874,6 +970,13 @@ sealed class VaultAction {
|
||||
*/
|
||||
data object TryAgainClick : VaultAction()
|
||||
|
||||
/**
|
||||
* User clicked an overflow action.
|
||||
*/
|
||||
data class OverflowOptionClick(
|
||||
val overflowAction: ListingItemOverflowAction.VaultAction,
|
||||
) : VaultAction()
|
||||
|
||||
/**
|
||||
* Models actions that the [VaultViewModel] itself might send.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault.handlers
|
||||
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.VaultAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.VaultState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.VaultViewModel
|
||||
@@ -31,6 +32,7 @@ data class VaultHandlers(
|
||||
val trashClick: () -> Unit,
|
||||
val tryAgainClick: () -> Unit,
|
||||
val dialogDismiss: () -> Unit,
|
||||
val overflowOptionClick: (ListingItemOverflowAction.VaultAction) -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -74,6 +76,9 @@ data class VaultHandlers(
|
||||
trashClick = { viewModel.trySendAction(VaultAction.TrashClick) },
|
||||
tryAgainClick = { viewModel.trySendAction(VaultAction.TryAgainClick) },
|
||||
dialogDismiss = { viewModel.trySendAction(VaultAction.DialogDismiss) },
|
||||
overflowOptionClick = {
|
||||
viewModel.trySendAction(VaultAction.OverflowOptionClick(it))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.vault.feature.util.toOverflowActions
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.VaultState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
||||
|
||||
@@ -156,11 +157,13 @@ private fun CipherView.toVaultItemOrNull(
|
||||
isIconLoadingDisabled = isIconLoadingDisabled,
|
||||
baseIconUrl = baseIconUrl,
|
||||
),
|
||||
overflowOptions = toOverflowActions(),
|
||||
)
|
||||
|
||||
CipherType.SECURE_NOTE -> VaultState.ViewState.VaultItem.SecureNote(
|
||||
id = id,
|
||||
name = name.asText(),
|
||||
overflowOptions = toOverflowActions(),
|
||||
)
|
||||
|
||||
CipherType.CARD -> VaultState.ViewState.VaultItem.Card(
|
||||
@@ -170,12 +173,14 @@ private fun CipherView.toVaultItemOrNull(
|
||||
lastFourDigits = card?.number
|
||||
?.takeLast(4)
|
||||
?.asText(),
|
||||
overflowOptions = toOverflowActions(),
|
||||
)
|
||||
|
||||
CipherType.IDENTITY -> VaultState.ViewState.VaultItem.Identity(
|
||||
id = id,
|
||||
name = name.asText(),
|
||||
firstName = identity?.firstName?.asText(),
|
||||
overflowOptions = toOverflowActions(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user