BIT-1207: Fully implement account switcher lock and logout (#362)

This commit is contained in:
Brian Yencho
2023-12-11 13:56:02 -06:00
committed by Álison Fernandes
parent b7578b8f96
commit 1adf58aca8
19 changed files with 588 additions and 43 deletions

View File

@@ -49,7 +49,6 @@ import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.base.util.showNotYetImplementedToast
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
import com.x8bit.bitwarden.ui.platform.components.BitwardenAccountSwitcher
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
@@ -187,13 +186,11 @@ fun LandingScreen(
onSwitchAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LandingAction.SwitchAccountClick(it)) }
},
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
onLockAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LandingAction.LockAccountClick(it)) }
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
onLogoutAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LandingAction.LogoutAccountClick(it)) }
},
onAddAccountClick = {
// Not available

View File

@@ -7,6 +7,7 @@ import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
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
@@ -25,9 +26,11 @@ private const val KEY_STATE = "state"
/**
* Manages application state for the initial landing screen.
*/
@Suppress("TooManyFunctions")
@HiltViewModel
class LandingViewModel @Inject constructor(
private val authRepository: AuthRepository,
private val vaultRepository: VaultRepository,
private val environmentRepository: EnvironmentRepository,
savedStateHandle: SavedStateHandle,
) : BaseViewModel<LandingState, LandingEvent, LandingAction>(
@@ -75,6 +78,8 @@ class LandingViewModel @Inject constructor(
override fun handleAction(action: LandingAction) {
when (action) {
is LandingAction.LockAccountClick -> handleLockAccountClicked(action)
is LandingAction.LogoutAccountClick -> handleLogoutAccountClicked(action)
is LandingAction.SwitchAccountClick -> handleSwitchAccountClicked(action)
is LandingAction.ConfirmSwitchToMatchingAccountClick -> {
handleConfirmSwitchToMatchingAccountClicked(action)
@@ -92,6 +97,14 @@ class LandingViewModel @Inject constructor(
}
}
private fun handleLockAccountClicked(action: LandingAction.LockAccountClick) {
vaultRepository.lockVaultIfNecessary(userId = action.accountSummary.userId)
}
private fun handleLogoutAccountClicked(action: LandingAction.LogoutAccountClick) {
authRepository.logout(userId = action.accountSummary.userId)
}
private fun handleSwitchAccountClicked(action: LandingAction.SwitchAccountClick) {
authRepository.switchAccount(userId = action.accountSummary.userId)
}
@@ -247,6 +260,23 @@ sealed class LandingEvent {
* Models actions for the landing screen.
*/
sealed class LandingAction {
/**
* Indicates the user has clicked on the given [accountSummary] information in order to lock
* the associated account's vault.
*/
data class LockAccountClick(
val accountSummary: AccountSummary,
) : LandingAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to log out
* of that account.
*/
data class LogoutAccountClick(
val accountSummary: AccountSummary,
) : LandingAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to switch
* to it.

View File

@@ -39,7 +39,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
import com.x8bit.bitwarden.ui.platform.base.util.IntentHandler
import com.x8bit.bitwarden.ui.platform.base.util.showNotYetImplementedToast
import com.x8bit.bitwarden.ui.platform.components.BitwardenAccountSwitcher
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenFilledButton
@@ -147,13 +146,11 @@ fun LoginScreen(
onSwitchAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LoginAction.SwitchAccountClick(it)) }
},
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
onLockAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LoginAction.LockAccountClick(it)) }
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
onLogoutAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LoginAction.LogoutAccountClick(it)) }
},
onAddAccountClick = {
// Not available

View File

@@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
@@ -35,6 +36,7 @@ private const val KEY_STATE = "state"
class LoginViewModel @Inject constructor(
private val authRepository: AuthRepository,
private val environmentRepository: EnvironmentRepository,
private val vaultRepository: VaultRepository,
savedStateHandle: SavedStateHandle,
) : BaseViewModel<LoginState, LoginEvent, LoginAction>(
initialState = savedStateHandle[KEY_STATE]
@@ -68,6 +70,8 @@ class LoginViewModel @Inject constructor(
override fun handleAction(action: LoginAction) {
when (action) {
is LoginAction.LockAccountClick -> handleLockAccountClicked(action)
is LoginAction.LogoutAccountClick -> handleLogoutAccountClicked(action)
is LoginAction.SwitchAccountClick -> handleSwitchAccountClicked(action)
is LoginAction.CloseButtonClick -> handleCloseButtonClicked()
LoginAction.LoginButtonClick -> handleLoginButtonClicked()
@@ -86,6 +90,14 @@ class LoginViewModel @Inject constructor(
}
}
private fun handleLockAccountClicked(action: LoginAction.LockAccountClick) {
vaultRepository.lockVaultIfNecessary(userId = action.accountSummary.userId)
}
private fun handleLogoutAccountClicked(action: LoginAction.LogoutAccountClick) {
authRepository.logout(userId = action.accountSummary.userId)
}
private fun handleSwitchAccountClicked(action: LoginAction.SwitchAccountClick) {
authRepository.switchAccount(userId = action.accountSummary.userId)
}
@@ -234,6 +246,23 @@ sealed class LoginEvent {
* Models actions for the login screen.
*/
sealed class LoginAction {
/**
* Indicates the user has clicked on the given [accountSummary] information in order to lock
* the associated account's vault.
*/
data class LockAccountClick(
val accountSummary: AccountSummary,
) : LoginAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to log out
* of that account.
*/
data class LogoutAccountClick(
val accountSummary: AccountSummary,
) : LoginAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to switch
* to it.

View File

@@ -32,7 +32,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.base.util.showNotYetImplementedToast
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
import com.x8bit.bitwarden.ui.platform.components.BitwardenAccountActionItem
import com.x8bit.bitwarden.ui.platform.components.BitwardenAccountSwitcher
@@ -197,13 +196,11 @@ fun VaultUnlockScreen(
onSwitchAccountClick = remember(viewModel) {
{ viewModel.trySendAction(VaultUnlockAction.SwitchAccountClick(it)) }
},
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
onLockAccountClick = remember(viewModel) {
{ viewModel.trySendAction(VaultUnlockAction.LockAccountClick(it)) }
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
onLogoutAccountClick = remember(viewModel) {
{ viewModel.trySendAction(VaultUnlockAction.LogoutAccountClick(it)) }
},
onAddAccountClick = remember(viewModel) {
{ viewModel.trySendAction(VaultUnlockAction.AddAccountClick) }

View File

@@ -31,6 +31,7 @@ private const val KEY_STATE = "state"
/**
* Manages application state for the initial vault unlock screen.
*/
@Suppress("TooManyFunctions")
@HiltViewModel
class VaultUnlockViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
@@ -79,6 +80,8 @@ class VaultUnlockViewModel @Inject constructor(
VaultUnlockAction.DismissDialog -> handleDismissDialog()
VaultUnlockAction.ConfirmLogoutClick -> handleConfirmLogoutClick()
is VaultUnlockAction.PasswordInputChanged -> handlePasswordInputChanged(action)
is VaultUnlockAction.LockAccountClick -> handleLockAccountClick(action)
is VaultUnlockAction.LogoutAccountClick -> handleLogoutAccountClick(action)
is VaultUnlockAction.SwitchAccountClick -> handleSwitchAccountClick(action)
VaultUnlockAction.UnlockClick -> handleUnlockClick()
is VaultUnlockAction.Internal.ReceiveVaultUnlockResult -> {
@@ -109,6 +112,14 @@ class VaultUnlockViewModel @Inject constructor(
}
}
private fun handleLockAccountClick(action: VaultUnlockAction.LockAccountClick) {
vaultRepo.lockVaultIfNecessary(userId = action.accountSummary.userId)
}
private fun handleLogoutAccountClick(action: VaultUnlockAction.LogoutAccountClick) {
authRepository.logout(userId = action.accountSummary.userId)
}
private fun handleSwitchAccountClick(action: VaultUnlockAction.SwitchAccountClick) {
authRepository.switchAccount(userId = action.accountSummary.userId)
}
@@ -253,6 +264,22 @@ sealed class VaultUnlockAction {
val passwordInput: String,
) : VaultUnlockAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to lock
* the associated account's vault.
*/
data class LockAccountClick(
val accountSummary: AccountSummary,
) : VaultUnlockAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to log out
* of that account.
*/
data class LogoutAccountClick(
val accountSummary: AccountSummary,
) : VaultUnlockAction()
/**
* The user has clicked the an account to switch too.
*/

View File

@@ -108,8 +108,14 @@ fun BitwardenAccountSwitcher(
LockOrLogoutDialog(
accountSummary = requireNotNull(lockOrLogoutAccount),
onDismissRequest = { lockOrLogoutAccount = null },
onLockAccountClick = onLockAccountClick,
onLogoutAccountClick = onLogoutAccountClick,
onLockAccountClick = {
onLockAccountClick(it)
lockOrLogoutAccount = null
},
onLogoutAccountClick = {
onLogoutAccountClick(it)
lockOrLogoutAccount = null
},
)
}
@@ -306,12 +312,14 @@ private fun LockOrLogoutDialog(
title = "${accountSummary.email}\n${accountSummary.environmentLabel}",
onDismissRequest = onDismissRequest,
selectionItems = {
BitwardenBasicDialogRow(
text = stringResource(id = R.string.lock),
onClick = {
onLockAccountClick(accountSummary)
},
)
if (accountSummary.isVaultUnlocked) {
BitwardenBasicDialogRow(
text = stringResource(id = R.string.lock),
onClick = {
onLockAccountClick(accountSummary)
},
)
}
BitwardenBasicDialogRow(
text = stringResource(id = R.string.log_out),
onClick = {

View File

@@ -28,7 +28,6 @@ import androidx.compose.ui.res.stringResource
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.showNotYetImplementedToast
import com.x8bit.bitwarden.ui.platform.components.BitwardenAccountActionItem
import com.x8bit.bitwarden.ui.platform.components.BitwardenAccountSwitcher
import com.x8bit.bitwarden.ui.platform.components.BitwardenMediumTopAppBar
@@ -87,6 +86,12 @@ fun VaultScreen(
searchIconClickAction = remember(viewModel) {
{ viewModel.trySendAction(VaultAction.SearchIconClick) }
},
accountLockClickAction = remember(viewModel) {
{ viewModel.trySendAction(VaultAction.LockAccountClick(it)) }
},
accountLogoutClickAction = remember(viewModel) {
{ viewModel.trySendAction(VaultAction.LogoutAccountClick(it)) }
},
accountSwitchClickAction = remember(viewModel) {
{ viewModel.trySendAction(VaultAction.SwitchAccountClick(it)) }
},
@@ -128,6 +133,8 @@ private fun VaultScreenScaffold(
state: VaultState,
addItemClickAction: () -> Unit,
searchIconClickAction: () -> Unit,
accountLockClickAction: (AccountSummary) -> Unit,
accountLogoutClickAction: (AccountSummary) -> Unit,
accountSwitchClickAction: (AccountSummary) -> Unit,
addAccountClickAction: () -> Unit,
onDimBottomNavBarRequest: (shouldDim: Boolean) -> Unit,
@@ -219,14 +226,8 @@ private fun VaultScreenScaffold(
isVisible = accountMenuVisible,
accountSummaries = state.accountSummaries.toImmutableList(),
onSwitchAccountClick = accountSwitchClickAction,
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onLockAccountClick = accountLockClickAction,
onLogoutAccountClick = accountLogoutClickAction,
onAddAccountClick = addAccountClickAction,
onDismissRequest = { updateAccountMenuVisibility(false) },
topAppBarScrollBehavior = scrollBehavior,

View File

@@ -38,7 +38,7 @@ import javax.inject.Inject
@HiltViewModel
class VaultViewModel @Inject constructor(
private val authRepository: AuthRepository,
vaultRepository: VaultRepository,
private val vaultRepository: VaultRepository,
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
initialState = run {
val userState = requireNotNull(authRepository.userStateFlow.value)
@@ -87,6 +87,8 @@ class VaultViewModel @Inject constructor(
is VaultAction.IdentityGroupClick -> handleIdentityClick()
is VaultAction.LoginGroupClick -> handleLoginClick()
is VaultAction.SearchIconClick -> handleSearchIconClick()
is VaultAction.LockAccountClick -> handleLockAccountClick(action)
is VaultAction.LogoutAccountClick -> handleLogoutAccountClick(action)
is VaultAction.SwitchAccountClick -> handleSwitchAccountClick(action)
is VaultAction.AddAccountClick -> handleAddAccountClick()
is VaultAction.SecureNoteGroupClick -> handleSecureNoteClick()
@@ -128,6 +130,14 @@ class VaultViewModel @Inject constructor(
sendEvent(VaultEvent.NavigateToVaultSearchScreen)
}
private fun handleLockAccountClick(action: VaultAction.LockAccountClick) {
vaultRepository.lockVaultIfNecessary(userId = action.accountSummary.userId)
}
private fun handleLogoutAccountClick(action: VaultAction.LogoutAccountClick) {
authRepository.logout(userId = action.accountSummary.userId)
}
private fun handleSwitchAccountClick(action: VaultAction.SwitchAccountClick) {
val isSwitchingAccounts =
when (authRepository.switchAccount(userId = action.accountSummary.userId)) {
@@ -461,6 +471,22 @@ sealed class VaultAction {
*/
data object SearchIconClick : VaultAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to lock
* the associated account's vault.
*/
data class LockAccountClick(
val accountSummary: AccountSummary,
) : VaultAction()
/**
* Indicates the user has clicked on the given [accountSummary] information in order to log out
* of that account.
*/
data class LogoutAccountClick(
val accountSummary: AccountSummary,
) : VaultAction()
/**
* User clicked an account in the account switcher.
*/