mirror of
https://github.com/bitwarden/android.git
synced 2026-03-21 22:00:42 -05:00
WIP
This commit is contained in:
@@ -50,6 +50,7 @@ private const val AUTH_CODE_SPACING_INTERVAL = 3
|
||||
fun VaultItemLoginContent(
|
||||
commonState: VaultItemState.ViewState.Content.Common,
|
||||
loginItemState: VaultItemState.ViewState.Content.ItemType.Login,
|
||||
totpCodeItemData: TotpCodeItemData?,
|
||||
vaultCommonItemTypeHandlers: VaultCommonItemTypeHandlers,
|
||||
vaultLoginItemTypeHandlers: VaultLoginItemTypeHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -136,7 +137,7 @@ fun VaultItemLoginContent(
|
||||
}
|
||||
}
|
||||
|
||||
loginItemState.totpCodeItemData?.let { totpCodeItemData ->
|
||||
totpCodeItemData?.let { totpCodeItemData ->
|
||||
item(key = "totpCode") {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
TotpField(
|
||||
@@ -422,7 +423,7 @@ private fun PasswordField(
|
||||
private fun TotpField(
|
||||
totpCodeItemData: TotpCodeItemData,
|
||||
enabled: Boolean,
|
||||
onCopyTotpClick: () -> Unit,
|
||||
onCopyTotpClick: (String) -> Unit,
|
||||
onAuthenticatorHelpToolTipClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -448,7 +449,7 @@ private fun TotpField(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_totp),
|
||||
onClick = onCopyTotpClick,
|
||||
onClick = { onCopyTotpClick(totpCodeItemData.totpCode) },
|
||||
modifier = Modifier.testTag(tag = "LoginCopyTotpButton"),
|
||||
)
|
||||
},
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHan
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultIdentityItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultLoginItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultSshKeyItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.model.TotpCodeItemData
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||
|
||||
/**
|
||||
@@ -260,6 +261,7 @@ fun VaultItemScreen(
|
||||
) {
|
||||
VaultItemContent(
|
||||
viewState = state.viewState,
|
||||
totpCodeItemData = state.totpCodeItemData,
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
vaultCommonItemTypeHandlers = remember(viewModel) {
|
||||
@@ -350,6 +352,7 @@ private fun VaultItemDialogs(
|
||||
@Composable
|
||||
private fun VaultItemContent(
|
||||
viewState: VaultItemState.ViewState,
|
||||
totpCodeItemData: TotpCodeItemData?,
|
||||
vaultCommonItemTypeHandlers: VaultCommonItemTypeHandlers,
|
||||
vaultLoginItemTypeHandlers: VaultLoginItemTypeHandlers,
|
||||
vaultCardItemTypeHandlers: VaultCardItemTypeHandlers,
|
||||
@@ -370,6 +373,7 @@ private fun VaultItemContent(
|
||||
VaultItemLoginContent(
|
||||
commonState = viewState.common,
|
||||
loginItemState = viewState.type,
|
||||
totpCodeItemData = totpCodeItemData,
|
||||
vaultCommonItemTypeHandlers = vaultCommonItemTypeHandlers,
|
||||
vaultLoginItemTypeHandlers = vaultLoginItemTypeHandlers,
|
||||
modifier = modifier,
|
||||
|
||||
@@ -104,23 +104,14 @@ class VaultItemViewModel @Inject constructor(
|
||||
vaultRepository.getAuthCodeFlow(state.vaultItemId),
|
||||
vaultRepository.collectionsStateFlow,
|
||||
vaultRepository.foldersStateFlow,
|
||||
) { cipherViewState, userState, authCodeState, collectionsState, folderState ->
|
||||
val totpCodeData = authCodeState.data?.let {
|
||||
TotpCodeItemData(
|
||||
periodSeconds = it.periodSeconds,
|
||||
timeLeftSeconds = it.timeLeftSeconds,
|
||||
totpCode = it.totpCode,
|
||||
verificationCode = it.code,
|
||||
)
|
||||
}
|
||||
) { cipherViewState, userState, collectionsState, folderState ->
|
||||
VaultItemAction.Internal.VaultDataReceive(
|
||||
userState = userState,
|
||||
vaultDataState = combineDataStates(
|
||||
cipherViewState,
|
||||
authCodeState,
|
||||
collectionsState,
|
||||
folderState,
|
||||
) { _, _, _, _ ->
|
||||
) { _, _, _ ->
|
||||
// We are only combining the DataStates to know the overall state,
|
||||
// we map it to the appropriate value below.
|
||||
}
|
||||
@@ -165,7 +156,6 @@ class VaultItemViewModel @Inject constructor(
|
||||
|
||||
VaultItemStateData(
|
||||
cipher = cipherView,
|
||||
totpCodeItemData = totpCodeData,
|
||||
canDelete = canDelete,
|
||||
canAssociateToCollections = canAssignToCollections,
|
||||
canEdit = canEdit,
|
||||
@@ -181,6 +171,21 @@ class VaultItemViewModel @Inject constructor(
|
||||
.map { VaultItemAction.Internal.IsIconLoadingDisabledUpdateReceive(it) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
vaultRepository.getAuthCodeFlow(state.vaultItemId)
|
||||
.map {
|
||||
VaultItemAction.Internal.TotpDataReceive(
|
||||
totpData = TotpCodeItemData(
|
||||
periodSeconds = it.data?.periodSeconds ?: 0,
|
||||
timeLeftSeconds = it.data?.timeLeftSeconds ?: 0,
|
||||
totpCode = it.data?.totpCode ?: "",
|
||||
verificationCode = it.data?.code ?: "",
|
||||
)
|
||||
)
|
||||
}
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
}
|
||||
|
||||
override fun handleAction(action: VaultItemAction) {
|
||||
@@ -602,7 +607,7 @@ class VaultItemViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
is VaultItemAction.ItemType.Login.CopyTotpClick -> {
|
||||
handleCopyTotpClick()
|
||||
handleCopyTotpClick(action)
|
||||
}
|
||||
|
||||
is VaultItemAction.ItemType.Login.CopyUriClick -> {
|
||||
@@ -663,14 +668,11 @@ class VaultItemViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyTotpClick() {
|
||||
onLoginContent { _, login ->
|
||||
val code = login.totpCodeItemData?.verificationCode ?: return@onLoginContent
|
||||
clipboardManager.setText(
|
||||
text = code,
|
||||
toastDescriptorOverride = R.string.totp.asText(),
|
||||
)
|
||||
}
|
||||
private fun handleCopyTotpClick(action: VaultItemAction.ItemType.Login.CopyTotpClick) {
|
||||
clipboardManager.setText(
|
||||
text = action.code,
|
||||
toastDescriptorOverride = R.string.totp.asText(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleCopyUriClick(action: VaultItemAction.ItemType.Login.CopyUriClick) {
|
||||
@@ -1093,6 +1095,10 @@ class VaultItemViewModel @Inject constructor(
|
||||
is VaultItemAction.Internal.IsIconLoadingDisabledUpdateReceive -> {
|
||||
handleIsIconLoadingDisabledUpdateReceive(action)
|
||||
}
|
||||
|
||||
is VaultItemAction.Internal.TotpDataReceive -> {
|
||||
handleTotpDataReceive(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1198,7 +1204,6 @@ class VaultItemViewModel @Inject constructor(
|
||||
previousState = state.viewState.asContentOrNull(),
|
||||
isPremiumUser = account.isPremium,
|
||||
hasMasterPassword = account.hasMasterPassword,
|
||||
totpCodeItemData = this.data?.totpCodeItemData,
|
||||
canDelete = this.data?.canDelete == true,
|
||||
canAssignToCollections = this.data?.canAssociateToCollections == true,
|
||||
canEdit = this.data?.canEdit == true,
|
||||
@@ -1353,6 +1358,12 @@ class VaultItemViewModel @Inject constructor(
|
||||
mutableStateFlow.update { it.copy(isIconLoadingDisabled = action.isDisabled) }
|
||||
}
|
||||
|
||||
private fun handleTotpDataReceive(
|
||||
action: VaultItemAction.Internal.TotpDataReceive,
|
||||
) {
|
||||
mutableStateFlow.update { it.copy(totpCodeItemData = action.totpData) }
|
||||
}
|
||||
|
||||
//endregion Internal Type Handlers
|
||||
|
||||
private fun updateDialogState(dialog: VaultItemState.DialogState?) {
|
||||
@@ -1440,6 +1451,7 @@ data class VaultItemState(
|
||||
val vaultItemId: String,
|
||||
val cipherType: VaultItemCipherType,
|
||||
val viewState: ViewState,
|
||||
val totpCodeItemData: TotpCodeItemData?,
|
||||
val dialog: DialogState?,
|
||||
val baseIconUrl: String,
|
||||
val isIconLoadingDisabled: Boolean,
|
||||
@@ -1653,7 +1665,6 @@ data class VaultItemState(
|
||||
* @property uris The URI associated with the login item.
|
||||
* @property passwordRevisionDate An optional string indicating the last time the
|
||||
* password was changed.
|
||||
* @property totpCodeItemData The optional data related the TOTP code.
|
||||
* @property isPremiumUser Indicates if the user has subscribed to a premium
|
||||
* account.
|
||||
* @property canViewTotpCode Indicates if the user can view an associated TOTP code.
|
||||
@@ -1662,7 +1673,7 @@ data class VaultItemState(
|
||||
*
|
||||
* **NOTE** [canViewTotpCode] currently supports a deprecated edge case where an
|
||||
* organization supports TOTP but not through the current premium model.
|
||||
* This additional field is added to allow for [isPremiumUser] to be an independent
|
||||
* This additional field is added to allow for [] to be an independent
|
||||
* value.
|
||||
* @see [CipherView.organizationUseTotp]
|
||||
*
|
||||
@@ -1673,7 +1684,6 @@ data class VaultItemState(
|
||||
val passwordData: PasswordData?,
|
||||
val uris: List<UriData>,
|
||||
val passwordRevisionDate: String?,
|
||||
val totpCodeItemData: TotpCodeItemData?,
|
||||
val isPremiumUser: Boolean,
|
||||
val canViewTotpCode: Boolean,
|
||||
val fido2CredentialCreationDateText: Text?,
|
||||
@@ -1685,8 +1695,7 @@ data class VaultItemState(
|
||||
val hasLoginCredentials: Boolean
|
||||
get() = username != null ||
|
||||
passwordData != null ||
|
||||
fido2CredentialCreationDateText != null ||
|
||||
totpCodeItemData != null
|
||||
fido2CredentialCreationDateText != null
|
||||
|
||||
/**
|
||||
* A wrapper for the password data.
|
||||
@@ -2127,7 +2136,9 @@ sealed class VaultItemAction {
|
||||
/**
|
||||
* The user has clicked the copy button for the TOTP code.
|
||||
*/
|
||||
data object CopyTotpClick : Login()
|
||||
data class CopyTotpClick(
|
||||
val code: String,
|
||||
) : Login()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for a URI.
|
||||
@@ -2285,6 +2296,10 @@ sealed class VaultItemAction {
|
||||
val vaultDataState: DataState<VaultItemStateData>,
|
||||
) : Internal()
|
||||
|
||||
data class TotpDataReceive(
|
||||
val totpData: TotpCodeItemData?,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates that the verify password result has been received.
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemViewModel
|
||||
data class VaultLoginItemTypeHandlers(
|
||||
val onCheckForBreachClick: () -> Unit,
|
||||
val onCopyPasswordClick: () -> Unit,
|
||||
val onCopyTotpCodeClick: () -> Unit,
|
||||
val onCopyTotpCodeClick: (String) -> Unit,
|
||||
val onAuthenticatorHelpToolTipClick: () -> Unit,
|
||||
val onCopyUriClick: (String) -> Unit,
|
||||
val onCopyUsernameClick: () -> Unit,
|
||||
@@ -35,7 +35,7 @@ data class VaultLoginItemTypeHandlers(
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Login.CopyPasswordClick)
|
||||
},
|
||||
onCopyTotpCodeClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Login.CopyTotpClick)
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Login.CopyTotpClick(it))
|
||||
},
|
||||
onAuthenticatorHelpToolTipClick = {
|
||||
viewModel.trySendAction(
|
||||
|
||||
@@ -7,7 +7,6 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
* The state containing totp code item information and the cipher for the item.
|
||||
*
|
||||
* @property cipher The cipher view for the item.
|
||||
* @property totpCodeItemData The data for the totp code.
|
||||
* @property canDelete Whether the item can be deleted.
|
||||
* @property canAssociateToCollections Whether the item can be associated to a collection.
|
||||
* @property canEdit Whether the item can be edited.
|
||||
@@ -15,7 +14,6 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
*/
|
||||
data class VaultItemStateData(
|
||||
val cipher: CipherView?,
|
||||
val totpCodeItemData: TotpCodeItemData?,
|
||||
val canDelete: Boolean,
|
||||
val canAssociateToCollections: Boolean,
|
||||
val canEdit: Boolean,
|
||||
|
||||
@@ -42,7 +42,6 @@ fun CipherView.toViewState(
|
||||
previousState: VaultItemState.ViewState.Content?,
|
||||
isPremiumUser: Boolean,
|
||||
hasMasterPassword: Boolean,
|
||||
totpCodeItemData: TotpCodeItemData?,
|
||||
clock: Clock = Clock.systemDefaultZone(),
|
||||
canDelete: Boolean,
|
||||
canAssignToCollections: Boolean,
|
||||
@@ -125,7 +124,6 @@ fun CipherView.toViewState(
|
||||
),
|
||||
isPremiumUser = isPremiumUser,
|
||||
canViewTotpCode = isPremiumUser || this.organizationUseTotp,
|
||||
totpCodeItemData = totpCodeItemData,
|
||||
fido2CredentialCreationDateText = loginValues
|
||||
.fido2Credentials
|
||||
?.firstOrNull()
|
||||
|
||||
Reference in New Issue
Block a user