mirror of
https://github.com/bitwarden/android.git
synced 2026-06-06 14:28:45 -05:00
BIT-1184: Add pending deletion state to display deletion dialog (#793)
This commit is contained in:
committed by
Álison Fernandes
parent
317cc7396e
commit
26335bf217
@@ -69,6 +69,11 @@ interface AuthRepository : AuthenticatorProvider {
|
||||
*/
|
||||
var hasPendingAccountAddition: Boolean
|
||||
|
||||
/**
|
||||
* Clears the pending deletion state that occurs when the an account is successfully deleted.
|
||||
*/
|
||||
fun clearPendingAccountDeletion()
|
||||
|
||||
/**
|
||||
* Attempt to delete the current account and logout them out upon success.
|
||||
*/
|
||||
|
||||
@@ -6,8 +6,8 @@ import com.bitwarden.crypto.Kdf
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson.CaptchaRequired
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson.TwoFactorRequired
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson.Success
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson.TwoFactorRequired
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.IdentityTokenAuthModel
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.PasswordHintResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RefreshTokenResponseJson
|
||||
@@ -62,6 +62,7 @@ import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import javax.inject.Singleton
|
||||
@@ -86,7 +87,8 @@ class AuthRepositoryImpl(
|
||||
dispatcherManager: DispatcherManager,
|
||||
private val elapsedRealtimeMillisProvider: () -> Long = { SystemClock.elapsedRealtime() },
|
||||
) : AuthRepository {
|
||||
private val mutableHasPendingAccountAdditionStateFlow = MutableStateFlow<Boolean>(false)
|
||||
private val mutableHasPendingAccountAdditionStateFlow = MutableStateFlow(false)
|
||||
private val mutableHasPendingAccountDeletionStateFlow = MutableStateFlow(false)
|
||||
|
||||
/**
|
||||
* The auth information to make the identity token request will need to be
|
||||
@@ -128,11 +130,13 @@ class AuthRepositoryImpl(
|
||||
authDiskSource.userOrganizationsListFlow,
|
||||
vaultRepository.vaultStateFlow,
|
||||
mutableHasPendingAccountAdditionStateFlow,
|
||||
mutableHasPendingAccountDeletionStateFlow,
|
||||
) {
|
||||
userStateJson,
|
||||
userOrganizationsList,
|
||||
vaultState,
|
||||
hasPendingAccountAddition,
|
||||
_,
|
||||
->
|
||||
userStateJson
|
||||
?.toUserState(
|
||||
@@ -142,6 +146,11 @@ class AuthRepositoryImpl(
|
||||
vaultUnlockTypeProvider = ::getVaultUnlockType,
|
||||
)
|
||||
}
|
||||
.filter {
|
||||
// If there is a pending account deletion, continue showing
|
||||
// the original UserState until it is confirmed.
|
||||
!mutableHasPendingAccountDeletionStateFlow.value
|
||||
}
|
||||
.stateIn(
|
||||
scope = collectionScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
@@ -170,9 +179,14 @@ class AuthRepositoryImpl(
|
||||
override var hasPendingAccountAddition: Boolean
|
||||
by mutableHasPendingAccountAdditionStateFlow::value
|
||||
|
||||
override fun clearPendingAccountDeletion() {
|
||||
mutableHasPendingAccountDeletionStateFlow.value = false
|
||||
}
|
||||
|
||||
override suspend fun deleteAccount(password: String): DeleteAccountResult {
|
||||
val profile = authDiskSource.userState?.activeAccount?.profile
|
||||
?: return DeleteAccountResult.Error
|
||||
mutableHasPendingAccountDeletionStateFlow.value = true
|
||||
return authSdkSource
|
||||
.hashPassword(
|
||||
email = profile.email,
|
||||
@@ -182,6 +196,7 @@ class AuthRepositoryImpl(
|
||||
)
|
||||
.flatMap { hashedPassword -> accountsService.deleteAccount(hashedPassword) }
|
||||
.onSuccess { logout() }
|
||||
.onFailure { clearPendingAccountDeletion() }
|
||||
.fold(
|
||||
onFailure = { DeleteAccountResult.Error },
|
||||
onSuccess = { DeleteAccountResult.Success },
|
||||
|
||||
@@ -67,6 +67,16 @@ fun DeleteAccountScreen(
|
||||
}
|
||||
|
||||
when (val dialog = state.dialog) {
|
||||
DeleteAccountState.DeleteAccountDialog.DeleteSuccess -> BitwardenBasicDialog(
|
||||
visibilityState = BasicDialogState.Shown(
|
||||
title = null,
|
||||
message = R.string.your_account_has_been_permanently_deleted.asText(),
|
||||
),
|
||||
onDismissRequest = remember(viewModel) {
|
||||
{ viewModel.trySendAction(DeleteAccountAction.AccountDeletionConfirm) }
|
||||
},
|
||||
)
|
||||
|
||||
is DeleteAccountState.DeleteAccountDialog.Error -> BitwardenBasicDialog(
|
||||
visibilityState = BasicDialogState.Shown(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
|
||||
@@ -43,6 +43,7 @@ class DeleteAccountViewModel @Inject constructor(
|
||||
DeleteAccountAction.CancelClick -> handleCancelClick()
|
||||
DeleteAccountAction.CloseClick -> handleCloseClick()
|
||||
is DeleteAccountAction.DeleteAccountClick -> handleDeleteAccountClick(action)
|
||||
DeleteAccountAction.AccountDeletionConfirm -> handleAccountDeletionConfirm()
|
||||
DeleteAccountAction.DismissDialog -> handleDismissDialog()
|
||||
is DeleteAccountAction.Internal.DeleteAccountComplete -> {
|
||||
handleDeleteAccountComplete(action)
|
||||
@@ -68,6 +69,11 @@ class DeleteAccountViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAccountDeletionConfirm() {
|
||||
authRepository.clearPendingAccountDeletion()
|
||||
mutableStateFlow.update { it.copy(dialog = null) }
|
||||
}
|
||||
|
||||
private fun handleDismissDialog() {
|
||||
mutableStateFlow.update { it.copy(dialog = null) }
|
||||
}
|
||||
@@ -77,8 +83,9 @@ class DeleteAccountViewModel @Inject constructor(
|
||||
) {
|
||||
when (action.result) {
|
||||
DeleteAccountResult.Success -> {
|
||||
mutableStateFlow.update { it.copy(dialog = null) }
|
||||
// TODO: Display a dialog confirming account deletion (BIT-1184)
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialog = DeleteAccountState.DeleteAccountDialog.DeleteSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
DeleteAccountResult.Error -> {
|
||||
@@ -106,6 +113,12 @@ data class DeleteAccountState(
|
||||
* Displays a dialog.
|
||||
*/
|
||||
sealed class DeleteAccountDialog : Parcelable {
|
||||
/**
|
||||
* Dialog to confirm to the user that the account has been deleted.
|
||||
*/
|
||||
@Parcelize
|
||||
data object DeleteSuccess : DeleteAccountDialog()
|
||||
|
||||
/**
|
||||
* Displays the error dialog when deleting an account fails.
|
||||
*/
|
||||
@@ -160,6 +173,11 @@ sealed class DeleteAccountAction {
|
||||
val masterPassword: String,
|
||||
) : DeleteAccountAction()
|
||||
|
||||
/**
|
||||
* The user has confirmed that their account has been deleted.
|
||||
*/
|
||||
data object AccountDeletionConfirm : DeleteAccountAction()
|
||||
|
||||
/**
|
||||
* The user has clicked to dismiss the dialog.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user