mirror of
https://github.com/bitwarden/android.git
synced 2026-06-06 14:28:45 -05:00
BIT-1648: Add the copy totp code overflow option (#850)
This commit is contained in:
committed by
Álison Fernandes
parent
0c6ea8d18d
commit
20dd839923
@@ -59,6 +59,7 @@ fun SearchContent(
|
||||
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,
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
@@ -190,6 +191,10 @@ class SearchViewModel @Inject constructor(
|
||||
is ListingItemOverflowAction.VaultAction.ViewClick -> {
|
||||
handleViewCipherClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick -> {
|
||||
handleCopyTotpClick(overflowAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +216,15 @@ class SearchViewModel @Inject constructor(
|
||||
sendEvent(SearchEvent.NavigateToEditSend(action.sendId))
|
||||
}
|
||||
|
||||
private fun handleCopyTotpClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepo.generateTotp(action.totpCode, clock.instant())
|
||||
sendAction(SearchAction.Internal.GenerateTotpResultReceive(result))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRemovePasswordClick(
|
||||
action: ListingItemOverflowAction.SendAction.RemovePasswordClick,
|
||||
) {
|
||||
@@ -283,6 +297,10 @@ class SearchViewModel @Inject constructor(
|
||||
handleDeleteSendResultReceive(action)
|
||||
}
|
||||
|
||||
is SearchAction.Internal.GenerateTotpResultReceive -> {
|
||||
handleGenerateTotpResultReceive(action)
|
||||
}
|
||||
|
||||
is SearchAction.Internal.RemovePasswordSendResultReceive -> {
|
||||
handleRemovePasswordSendResultReceive(action)
|
||||
}
|
||||
@@ -320,6 +338,17 @@ class SearchViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGenerateTotpResultReceive(
|
||||
action: SearchAction.Internal.GenerateTotpResultReceive,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is GenerateTotpResult.Error -> Unit
|
||||
is GenerateTotpResult.Success -> {
|
||||
clipboardManager.setText(result.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRemovePasswordSendResultReceive(
|
||||
action: SearchAction.Internal.RemovePasswordSendResultReceive,
|
||||
) {
|
||||
@@ -758,6 +787,13 @@ sealed class SearchAction {
|
||||
val result: DeleteSendResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for generating a verification code has been received.
|
||||
*/
|
||||
data class GenerateTotpResultReceive(
|
||||
val result: GenerateTotpResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for removing the password protection from a send has been received.
|
||||
*/
|
||||
|
||||
@@ -66,6 +66,7 @@ fun VaultItemListingContent(
|
||||
is ListingItemOverflowAction.VaultAction.EditClick,
|
||||
is ListingItemOverflowAction.VaultAction.LaunchClick,
|
||||
is ListingItemOverflowAction.VaultAction.ViewClick,
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
null,
|
||||
-> Unit
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.map
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
@@ -256,6 +257,15 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
clipboardManager.setText(action.securityCode)
|
||||
}
|
||||
|
||||
private fun handleCopyTotpClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepository.generateTotp(action.totpCode, clock.instant())
|
||||
sendAction(VaultItemListingsAction.Internal.GenerateTotpResultReceive(result))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyUsernameClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyUsernameClick,
|
||||
) {
|
||||
@@ -347,6 +357,10 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
handleCopySecurityCodeClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick -> {
|
||||
handleCopyTotpClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyUsernameClick -> {
|
||||
handleCopyUsernameClick(overflowAction)
|
||||
}
|
||||
@@ -383,6 +397,10 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
is VaultItemListingsAction.Internal.IconLoadingSettingReceive -> {
|
||||
handleIconsSettingReceived(action)
|
||||
}
|
||||
|
||||
is VaultItemListingsAction.Internal.GenerateTotpResultReceive -> {
|
||||
handleGenerateTotpResultReceive(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +463,17 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGenerateTotpResultReceive(
|
||||
action: VaultItemListingsAction.Internal.GenerateTotpResultReceive,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is GenerateTotpResult.Error -> Unit
|
||||
is GenerateTotpResult.Success -> {
|
||||
clipboardManager.setText(result.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleVaultDataReceive(
|
||||
action: VaultItemListingsAction.Internal.VaultDataReceive,
|
||||
) {
|
||||
@@ -1033,6 +1062,13 @@ sealed class VaultItemListingsAction {
|
||||
*/
|
||||
data class DeleteSendResultReceive(val result: DeleteSendResult) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for generating a verification code has been received.
|
||||
*/
|
||||
data class GenerateTotpResultReceive(
|
||||
val result: GenerateTotpResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for removing the password protection from a send has been received.
|
||||
*/
|
||||
|
||||
@@ -97,6 +97,14 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val title: Text get() = R.string.copy_password.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the copy TOTP code overflow option.
|
||||
*/
|
||||
@Parcelize
|
||||
data class CopyTotpClick(val totpCode: String) : VaultAction() {
|
||||
override val title: Text get() = R.string.copy_totp.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the copy number overflow option.
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,9 @@ fun CipherView.toOverflowActions(): List<ListingItemOverflowAction.VaultAction>
|
||||
this.login?.password?.let {
|
||||
ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = it)
|
||||
},
|
||||
this.login?.totp
|
||||
?.let { ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode = it) }
|
||||
.takeIf { this.type == CipherType.LOGIN },
|
||||
this.card?.number?.let {
|
||||
ListingItemOverflowAction.VaultAction.CopyNumberClick(number = it)
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ 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
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
@@ -37,7 +38,9 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.time.Clock
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -46,10 +49,11 @@ 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 clipboardManager: BitwardenClipboardManager,
|
||||
private val clock: Clock,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val vaultRepository: VaultRepository,
|
||||
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
|
||||
initialState = run {
|
||||
val userState = requireNotNull(authRepository.userStateFlow.value)
|
||||
@@ -293,6 +297,10 @@ class VaultViewModel @Inject constructor(
|
||||
handleCopySecurityCodeClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick -> {
|
||||
handleCopyTotpClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyUsernameClick -> {
|
||||
handleCopyUsernameClick(overflowAction)
|
||||
}
|
||||
@@ -333,6 +341,15 @@ class VaultViewModel @Inject constructor(
|
||||
clipboardManager.setText(action.securityCode)
|
||||
}
|
||||
|
||||
private fun handleCopyTotpClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepository.generateTotp(action.totpCode, clock.instant())
|
||||
sendAction(VaultAction.Internal.GenerateTotpResultReceive(result))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyUsernameClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyUsernameClick,
|
||||
) {
|
||||
@@ -353,6 +370,10 @@ class VaultViewModel @Inject constructor(
|
||||
|
||||
private fun handleInternalAction(action: VaultAction.Internal) {
|
||||
when (action) {
|
||||
is VaultAction.Internal.GenerateTotpResultReceive -> {
|
||||
handleGenerateTotpResultReceive(action)
|
||||
}
|
||||
|
||||
is VaultAction.Internal.PullToRefreshEnableReceive -> {
|
||||
handlePullToRefreshEnableReceive(action)
|
||||
}
|
||||
@@ -365,6 +386,17 @@ class VaultViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGenerateTotpResultReceive(
|
||||
action: VaultAction.Internal.GenerateTotpResultReceive,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is GenerateTotpResult.Error -> Unit
|
||||
is GenerateTotpResult.Success -> {
|
||||
clipboardManager.setText(result.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePullToRefreshEnableReceive(
|
||||
action: VaultAction.Internal.PullToRefreshEnableReceive,
|
||||
) {
|
||||
@@ -1002,6 +1034,13 @@ sealed class VaultAction {
|
||||
val isIconLoadingDisabled: Boolean,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for generating a verification code has been received.
|
||||
*/
|
||||
data class GenerateTotpResultReceive(
|
||||
val result: GenerateTotpResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates that the pull to refresh feature toggle has changed.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user