mirror of
https://github.com/bitwarden/android.git
synced 2026-08-01 18:53:39 -05:00
BIT-1111: Add delete account logic (#252)
This commit is contained in:
committed by
Álison Fernandes
parent
dd6e7639b5
commit
db5c57664f
+17
@@ -0,0 +1,17 @@
|
||||
package com.x8bit.bitwarden.data.auth.datasource.network.api
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.DeleteAccountRequestJson
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.HTTP
|
||||
|
||||
/**
|
||||
* Defines raw calls under the /accounts API with authentication applied.
|
||||
*/
|
||||
interface AuthenticatedAccountsApi {
|
||||
|
||||
/**
|
||||
* Deletes the current account.
|
||||
*/
|
||||
@HTTP(method = "DELETE", path = "/accounts", hasBody = true)
|
||||
suspend fun deleteAccount(@Body body: DeleteAccountRequestJson): Result<Unit>
|
||||
}
|
||||
+1
@@ -29,6 +29,7 @@ object AuthNetworkModule {
|
||||
json: Json,
|
||||
): AccountsService = AccountsServiceImpl(
|
||||
accountsApi = retrofits.unauthenticatedApiRetrofit.create(),
|
||||
authenticatedAccountsApi = retrofits.authenticatedApiRetrofit.create(),
|
||||
json = json,
|
||||
)
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Request body for deleting an account.
|
||||
*
|
||||
* @param masterPasswordHash the master password (encrypted).
|
||||
*/
|
||||
@Serializable
|
||||
data class DeleteAccountRequestJson(
|
||||
@SerialName("MasterPasswordHash")
|
||||
val masterPasswordHash: String,
|
||||
)
|
||||
+5
@@ -9,6 +9,11 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJs
|
||||
*/
|
||||
interface AccountsService {
|
||||
|
||||
/**
|
||||
* Make delete account request.
|
||||
*/
|
||||
suspend fun deleteAccount(masterPasswordHash: String): Result<Unit>
|
||||
|
||||
/**
|
||||
* Make pre login request to get KDF params.
|
||||
*/
|
||||
|
||||
+6
@@ -1,6 +1,8 @@
|
||||
package com.x8bit.bitwarden.data.auth.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.api.AccountsApi
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.api.AuthenticatedAccountsApi
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.DeleteAccountRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.PreLoginRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.PreLoginResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson
|
||||
@@ -11,9 +13,13 @@ import kotlinx.serialization.json.Json
|
||||
|
||||
class AccountsServiceImpl constructor(
|
||||
private val accountsApi: AccountsApi,
|
||||
private val authenticatedAccountsApi: AuthenticatedAccountsApi,
|
||||
private val json: Json,
|
||||
) : AccountsService {
|
||||
|
||||
override suspend fun deleteAccount(masterPasswordHash: String): Result<Unit> =
|
||||
authenticatedAccountsApi.deleteAccount(DeleteAccountRequestJson(masterPasswordHash))
|
||||
|
||||
override suspend fun preLogin(email: String): Result<PreLoginResponseJson> =
|
||||
accountsApi.preLogin(PreLoginRequestJson(email = email))
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ interface AuthRepository {
|
||||
*/
|
||||
var rememberedEmailAddress: String?
|
||||
|
||||
/**
|
||||
* Attempt to delete the current account and logout them out upon success.
|
||||
*/
|
||||
suspend fun deleteAccount(password: String): Result<Unit>
|
||||
|
||||
/**
|
||||
* Attempt to login with the given email and password. Updated access token will be reflected
|
||||
* in [authStateFlow].
|
||||
|
||||
@@ -17,10 +17,12 @@ import com.x8bit.bitwarden.data.auth.repository.model.AuthState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.toUserState
|
||||
import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS
|
||||
import com.x8bit.bitwarden.data.auth.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.platform.util.flatMap
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
@@ -82,6 +84,19 @@ class AuthRepositoryImpl constructor(
|
||||
authDiskSource.rememberedEmailAddress = value
|
||||
}
|
||||
|
||||
override suspend fun deleteAccount(password: String): Result<Unit> {
|
||||
val profile = authDiskSource.userState?.activeAccount?.profile
|
||||
?: return IllegalStateException("Not logged in.").asFailure()
|
||||
return authSdkSource
|
||||
.hashPassword(
|
||||
email = profile.email,
|
||||
password = password,
|
||||
kdf = profile.toSdkParams(),
|
||||
)
|
||||
.flatMap { hashedPassword -> accountsService.deleteAccount(hashedPassword) }
|
||||
.onSuccess { logout() }
|
||||
}
|
||||
|
||||
override suspend fun login(
|
||||
email: String,
|
||||
password: String,
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
|
||||
/**
|
||||
* Represents a Bitwarden-styled dialog for entering your master password.
|
||||
*
|
||||
* @param onConfirmClick called when the confirm button is clicked and emits the entered password.
|
||||
* @param onDismissRequest called when the user attempts to dismiss the dialog (for example by
|
||||
* tapping outside of it).
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenMasterPasswordDialog(
|
||||
onConfirmClick: (masterPassword: String) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
var masterPassword by remember { mutableStateOf("") }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
dismissButton = {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = R.string.cancel),
|
||||
onClick = onDismissRequest,
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = R.string.submit),
|
||||
isEnabled = masterPassword.isNotEmpty(),
|
||||
onClick = { onConfirmClick(masterPassword) },
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.password_confirmation),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(id = R.string.password_confirmation_desc),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.master_password),
|
||||
value = masterPassword,
|
||||
onValueChange = { masterPassword = it },
|
||||
modifier = Modifier.imePadding(),
|
||||
autoFocus = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
)
|
||||
}
|
||||
+20
-1
@@ -14,11 +14,15 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
@@ -42,6 +46,9 @@ import com.x8bit.bitwarden.R
|
||||
* @param onValueChange Callback that is triggered when the password changes.
|
||||
* @param modifier Modifier for the composable.
|
||||
* @param hint optional hint text that will appear below the text input.
|
||||
* @param showPasswordTestTag The test tag to be used on the show password button (testing tool).
|
||||
* @param autoFocus When set to true, the view will request focus after the first recomposition.
|
||||
* Setting this to true on multiple fields at once may have unexpected consequences.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenPasswordField(
|
||||
@@ -53,12 +60,16 @@ fun BitwardenPasswordField(
|
||||
modifier: Modifier = Modifier,
|
||||
hint: String? = null,
|
||||
showPasswordTestTag: String? = null,
|
||||
autoFocus: Boolean = false,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(focusRequester),
|
||||
textStyle = MaterialTheme.typography.bodyLarge,
|
||||
label = { Text(text = label) },
|
||||
value = value,
|
||||
@@ -107,6 +118,9 @@ fun BitwardenPasswordField(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (autoFocus) {
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,6 +134,9 @@ fun BitwardenPasswordField(
|
||||
* @param hint optional hint text that will appear below the text input.
|
||||
* @param initialShowPassword The initial state of the show/hide password control. A value of
|
||||
* `false` (the default) indicates that that password should begin in the hidden state.
|
||||
* @param showPasswordTestTag The test tag to be used on the show password button (testing tool).
|
||||
* @param autoFocus When set to true, the view will request focus after the first recomposition.
|
||||
* Setting this to true on multiple fields at once may have unexpected consequences.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenPasswordField(
|
||||
@@ -130,6 +147,7 @@ fun BitwardenPasswordField(
|
||||
hint: String? = null,
|
||||
initialShowPassword: Boolean = false,
|
||||
showPasswordTestTag: String? = null,
|
||||
autoFocus: Boolean = false,
|
||||
) {
|
||||
var showPassword by rememberSaveable { mutableStateOf(initialShowPassword) }
|
||||
BitwardenPasswordField(
|
||||
@@ -141,6 +159,7 @@ fun BitwardenPasswordField(
|
||||
onValueChange = onValueChange,
|
||||
hint = hint,
|
||||
showPasswordTestTag = showPasswordTestTag,
|
||||
autoFocus = autoFocus,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+58
-7
@@ -18,7 +18,11 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -28,10 +32,16 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenErrorButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenMasterPasswordDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||
|
||||
/**
|
||||
* Displays the delete account screen.
|
||||
@@ -43,6 +53,7 @@ fun DeleteAccountScreen(
|
||||
viewModel: DeleteAccountViewModel = hiltViewModel(),
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsState()
|
||||
val context = LocalContext.current
|
||||
val resources = context.resources
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
@@ -55,6 +66,26 @@ fun DeleteAccountScreen(
|
||||
}
|
||||
}
|
||||
|
||||
when (val dialog = state.dialog) {
|
||||
is DeleteAccountState.DeleteAccountDialog.Error -> BitwardenBasicDialog(
|
||||
visibilityState = BasicDialogState.Shown(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = dialog.message,
|
||||
),
|
||||
onDismissRequest = remember(viewModel) {
|
||||
{ viewModel.trySendAction(DeleteAccountAction.DismissDialog) }
|
||||
},
|
||||
)
|
||||
|
||||
DeleteAccountState.DeleteAccountDialog.Loading,
|
||||
|
||||
-> BitwardenLoadingDialog(
|
||||
visibilityState = LoadingDialogState.Shown(R.string.loading.asText()),
|
||||
)
|
||||
|
||||
null -> Unit
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
BitwardenScaffold(
|
||||
modifier = Modifier
|
||||
@@ -105,14 +136,10 @@ fun DeleteAccountScreen(
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
BitwardenErrorButton(
|
||||
label = stringResource(id = R.string.delete_account),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(DeleteAccountAction.DeleteAccountClick) }
|
||||
DeleteAccountButton(
|
||||
onConfirmationClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(DeleteAccountAction.DeleteAccountClick(it)) }
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
BitwardenOutlinedButton(
|
||||
@@ -128,3 +155,27 @@ fun DeleteAccountScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeleteAccountButton(
|
||||
onConfirmationClick: (masterPassword: String) -> Unit,
|
||||
) {
|
||||
var showPasswordDialog by remember { mutableStateOf(false) }
|
||||
if (showPasswordDialog) {
|
||||
BitwardenMasterPasswordDialog(
|
||||
onConfirmClick = {
|
||||
showPasswordDialog = false
|
||||
onConfirmationClick(it)
|
||||
},
|
||||
onDismissRequest = { showPasswordDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
BitwardenErrorButton(
|
||||
label = stringResource(id = R.string.delete_account),
|
||||
onClick = { showPasswordDialog = true },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
+111
-9
@@ -1,25 +1,51 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_STATE = "state"
|
||||
|
||||
/**
|
||||
* View model for the account security screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class DeleteAccountViewModel @Inject constructor() :
|
||||
BaseViewModel<Unit, DeleteAccountEvent, DeleteAccountAction>(
|
||||
initialState = Unit,
|
||||
) {
|
||||
class DeleteAccountViewModel @Inject constructor(
|
||||
private val authRepository: AuthRepository,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : BaseViewModel<DeleteAccountState, DeleteAccountEvent, DeleteAccountAction>(
|
||||
initialState = savedStateHandle[KEY_STATE] ?: DeleteAccountState(
|
||||
dialog = null,
|
||||
),
|
||||
) {
|
||||
|
||||
init {
|
||||
stateFlow
|
||||
.onEach { savedStateHandle[KEY_STATE] = it }
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
override fun handleAction(action: DeleteAccountAction) {
|
||||
when (action) {
|
||||
DeleteAccountAction.CancelClick -> handleCancelClick()
|
||||
DeleteAccountAction.CloseClick -> handleCloseClick()
|
||||
DeleteAccountAction.DeleteAccountClick -> handleDeleteAccountClick()
|
||||
is DeleteAccountAction.DeleteAccountClick -> handleDeleteAccountClick(action)
|
||||
DeleteAccountAction.DismissDialog -> handleDismissDialog()
|
||||
is DeleteAccountAction.Internal.DeleteAccountComplete -> {
|
||||
handleDeleteAccountComplete(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +57,66 @@ class DeleteAccountViewModel @Inject constructor() :
|
||||
sendEvent(DeleteAccountEvent.NavigateBack)
|
||||
}
|
||||
|
||||
private fun handleDeleteAccountClick() {
|
||||
// TODO: Delete the users account (BIT-1111)
|
||||
sendEvent(DeleteAccountEvent.ShowToast("Not yet implemented.".asText()))
|
||||
private fun handleDeleteAccountClick(action: DeleteAccountAction.DeleteAccountClick) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialog = DeleteAccountState.DeleteAccountDialog.Loading)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
val result = authRepository.deleteAccount(action.masterPassword)
|
||||
sendAction(DeleteAccountAction.Internal.DeleteAccountComplete(result))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDismissDialog() {
|
||||
mutableStateFlow.update { it.copy(dialog = null) }
|
||||
}
|
||||
|
||||
private fun handleDeleteAccountComplete(
|
||||
action: DeleteAccountAction.Internal.DeleteAccountComplete,
|
||||
) {
|
||||
action.result.fold(
|
||||
onSuccess = {
|
||||
mutableStateFlow.update { it.copy(dialog = null) }
|
||||
// TODO: Display a dialog confirming account deletion (BIT-1184)
|
||||
},
|
||||
onFailure = {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = DeleteAccountState.DeleteAccountDialog.Error(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models state for the Delete Account screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class DeleteAccountState(
|
||||
val dialog: DeleteAccountDialog?,
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
* Displays a dialog.
|
||||
*/
|
||||
sealed class DeleteAccountDialog : Parcelable {
|
||||
/**
|
||||
* Displays the error dialog when deleting an account fails.
|
||||
*/
|
||||
@Parcelize
|
||||
data class Error(
|
||||
val message: Text,
|
||||
) : DeleteAccountDialog()
|
||||
|
||||
/**
|
||||
* Displays the loading dialog when deleting an account.
|
||||
*/
|
||||
@Parcelize
|
||||
data object Loading : DeleteAccountDialog()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,5 +154,24 @@ sealed class DeleteAccountAction {
|
||||
/**
|
||||
* The user has clicked the delete account button.
|
||||
*/
|
||||
data object DeleteAccountClick : DeleteAccountAction()
|
||||
data class DeleteAccountClick(
|
||||
val masterPassword: String,
|
||||
) : DeleteAccountAction()
|
||||
|
||||
/**
|
||||
* The user has clicked to dismiss the dialog.
|
||||
*/
|
||||
data object DismissDialog : DeleteAccountAction()
|
||||
|
||||
/**
|
||||
* Models actions that the [DeleteAccountViewModel] itself might send.
|
||||
*/
|
||||
sealed class Internal : DeleteAccountAction() {
|
||||
/**
|
||||
* Indicates that the delete account request has completed.
|
||||
*/
|
||||
data class DeleteAccountComplete(
|
||||
val result: Result<Unit>,
|
||||
) : Internal()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user