Bit 1207 add lock and logout to switcher (#353)

This commit is contained in:
Brian Yencho
2023-12-08 10:22:42 -06:00
committed by Álison Fernandes
parent d0205b4b59
commit 266db5cc04
7 changed files with 192 additions and 16 deletions

View File

@@ -34,6 +34,7 @@ import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
@@ -48,6 +49,7 @@ 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
@@ -178,12 +180,21 @@ fun LandingScreen(
.fillMaxSize(),
)
val context = LocalContext.current
BitwardenAccountSwitcher(
isVisible = isAccountMenuVisible,
accountSummaries = state.accountSummaries.toImmutableList(),
onAccountSummaryClick = remember(viewModel) {
onSwitchAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LandingAction.SwitchAccountClick(it)) }
},
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onAddAccountClick = {
// Not available
},

View File

@@ -39,6 +39,7 @@ 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
@@ -143,9 +144,17 @@ fun LoginScreen(
BitwardenAccountSwitcher(
isVisible = isAccountMenuVisible,
accountSummaries = state.accountSummaries.toImmutableList(),
onAccountSummaryClick = remember(viewModel) {
onSwitchAccountClick = remember(viewModel) {
{ viewModel.trySendAction(LoginAction.SwitchAccountClick(it)) }
},
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onAddAccountClick = {
// Not available
},

View File

@@ -32,6 +32,7 @@ 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
@@ -193,9 +194,17 @@ fun VaultUnlockScreen(
BitwardenAccountSwitcher(
isVisible = accountMenuVisible,
accountSummaries = state.accountSummaries.toImmutableList(),
onAccountSummaryClick = remember(viewModel) {
onSwitchAccountClick = remember(viewModel) {
{ viewModel.trySendAction(VaultUnlockAction.SwitchAccountClick(it)) }
},
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onAddAccountClick = remember(viewModel) {
{ viewModel.trySendAction(VaultUnlockAction.AddAccountClick) }
},

View File

@@ -0,0 +1,19 @@
package com.x8bit.bitwarden.ui.platform.base.util
import android.content.Context
import android.widget.Toast
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
/**
* Shows a [Toast] with a message indicating something is not yet implemented.
*/
@OmitFromCoverage
fun showNotYetImplementedToast(context: Context) {
Toast
.makeText(
context,
"Not yet implemented",
Toast.LENGTH_SHORT,
)
.show()
}

View File

@@ -1,9 +1,13 @@
package com.x8bit.bitwarden.ui.platform.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -25,7 +29,10 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
@@ -62,7 +69,12 @@ private const val MAXIMUM_ACCOUNT_LIMIT = 5
* @param isVisible Whether or not this component is visible. Changing this value will animate the
* component in or out of view.
* @param accountSummaries The accounts to display in the switcher.
* @param onAccountSummaryClick A callback when an account is clicked.
* @param onSwitchAccountClick A callback when an account is clicked indicating that the account
* should be switched to.
* @param onLockAccountClick A callback when an account is clicked indicating that the account
* should be locked.
* @param onLogoutAccountClick A callback when an account is clicked indicating that the account
* should be logged out.
* @param onAddAccountClick A callback when the Add Account row is clicked.
* @param onDismissRequest A callback when the component requests to be dismissed. This is triggered
* whenever the user clicks on the scrim or any of the switcher items.
@@ -78,13 +90,29 @@ private const val MAXIMUM_ACCOUNT_LIMIT = 5
fun BitwardenAccountSwitcher(
isVisible: Boolean,
accountSummaries: ImmutableList<AccountSummary>,
onAccountSummaryClick: (AccountSummary) -> Unit,
onSwitchAccountClick: (AccountSummary) -> Unit,
onLockAccountClick: (AccountSummary) -> Unit,
onLogoutAccountClick: (AccountSummary) -> Unit,
onAddAccountClick: () -> Unit,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
isAddAccountAvailable: Boolean = true,
topAppBarScrollBehavior: TopAppBarScrollBehavior,
) {
// Track the actual visibility (according to the internal transitions) so that we know when we
// can safely show dialogs.
var isVisibleActual by remember { mutableStateOf(isVisible) }
var lockOrLogoutAccount by remember { mutableStateOf<AccountSummary?>(null) }
if (lockOrLogoutAccount != null && !isVisibleActual) {
LockOrLogoutDialog(
accountSummary = requireNotNull(lockOrLogoutAccount),
onDismissRequest = { lockOrLogoutAccount = null },
onLockAccountClick = onLockAccountClick,
onLogoutAccountClick = onLogoutAccountClick,
)
}
Box(modifier = modifier) {
BitwardenAnimatedScrim(
isVisible = isVisible,
@@ -95,9 +123,13 @@ fun BitwardenAccountSwitcher(
AnimatedAccountSwitcher(
isVisible = isVisible,
accountSummaries = accountSummaries,
onAccountSummaryClick = {
onSwitchAccountClick = {
onDismissRequest()
onAccountSummaryClick(it)
onSwitchAccountClick(it)
},
onSwitchAccountLongClick = {
onDismissRequest()
lockOrLogoutAccount = it
},
onAddAccountClick = {
onDismissRequest()
@@ -105,27 +137,38 @@ fun BitwardenAccountSwitcher(
},
isAddAccountAvailable = isAddAccountAvailable,
topAppBarScrollBehavior = topAppBarScrollBehavior,
currentAnimationState = { isVisibleActual = it },
modifier = Modifier
.fillMaxWidth(),
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(
ExperimentalMaterial3Api::class,
ExperimentalAnimationApi::class,
)
@Composable
private fun AnimatedAccountSwitcher(
isVisible: Boolean,
accountSummaries: ImmutableList<AccountSummary>,
onAccountSummaryClick: (AccountSummary) -> Unit,
onSwitchAccountClick: (AccountSummary) -> Unit,
onSwitchAccountLongClick: (AccountSummary) -> Unit,
onAddAccountClick: () -> Unit,
isAddAccountAvailable: Boolean,
modifier: Modifier = Modifier,
topAppBarScrollBehavior: TopAppBarScrollBehavior,
currentAnimationState: (isVisible: Boolean) -> Unit,
) {
val expandedColor = MaterialTheme.colorScheme.surface
val collapsedColor = MaterialTheme.colorScheme.surfaceContainer
AnimatedVisibility(
visible = isVisible,
val transition = updateTransition(
targetState = isVisible,
label = "AnimatedAccountSwitcher",
)
.also { currentAnimationState(it.currentState) }
transition.AnimatedVisibility(
visible = { it },
enter = slideInVertically { -it },
exit = slideOutVertically { -it },
) {
@@ -153,7 +196,8 @@ private fun AnimatedAccountSwitcher(
items(accountSummaries) { accountSummary ->
AccountSummaryItem(
accountSummary = accountSummary,
onClick = onAccountSummaryClick,
onSwitchAccountClick = onSwitchAccountClick,
onSwitchAccountLongClick = onSwitchAccountLongClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
@@ -177,20 +221,23 @@ private fun AnimatedAccountSwitcher(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun AccountSummaryItem(
accountSummary: AccountSummary,
onClick: (AccountSummary) -> Unit,
onSwitchAccountClick: (AccountSummary) -> Unit,
onSwitchAccountLongClick: (AccountSummary) -> Unit,
modifier: Modifier = Modifier,
) {
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable(
.combinedClickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
onClick = { onClick(accountSummary) },
onClick = { onSwitchAccountClick(accountSummary) },
onLongClick = { onSwitchAccountLongClick(accountSummary) },
)
.padding(vertical = 8.dp)
.then(modifier),
@@ -248,6 +295,33 @@ private fun AccountSummaryItem(
}
}
@Composable
private fun LockOrLogoutDialog(
accountSummary: AccountSummary,
onDismissRequest: () -> Unit,
onLockAccountClick: (AccountSummary) -> Unit,
onLogoutAccountClick: (AccountSummary) -> Unit,
) {
BitwardenSelectionDialog(
title = "${accountSummary.email}\n${accountSummary.environmentLabel}",
onDismissRequest = onDismissRequest,
selectionItems = {
BitwardenBasicDialogRow(
text = stringResource(id = R.string.lock),
onClick = {
onLockAccountClick(accountSummary)
},
)
BitwardenBasicDialogRow(
text = stringResource(id = R.string.log_out),
onClick = {
onLogoutAccountClick(accountSummary)
},
)
},
)
}
@Composable
private fun AddAccountItem(
onClick: () -> Unit,

View File

@@ -0,0 +1,44 @@
package com.x8bit.bitwarden.ui.platform.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
/**
* A simple clickable row for use within a [BitwardenSelectionDialog] as an alternative to a
* [BitwardenSelectionRow].
*
* @param text The text to display in the row.
* @param onClick A callback to be invoked when the row is clicked.
* @param modifier A [Modifier] for the composable.
*/
@Composable
fun BitwardenBasicDialogRow(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Text(
text = text,
style = MaterialTheme.typography.bodyLarge,
modifier = modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.padding(
vertical = 16.dp,
horizontal = 24.dp,
)
.fillMaxWidth(),
)
}

View File

@@ -28,6 +28,7 @@ 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
@@ -213,10 +214,19 @@ private fun VaultScreenScaffold(
)
}
val context = LocalContext.current
BitwardenAccountSwitcher(
isVisible = accountMenuVisible,
accountSummaries = state.accountSummaries.toImmutableList(),
onAccountSummaryClick = accountSwitchClickAction,
onSwitchAccountClick = accountSwitchClickAction,
onLockAccountClick = {
// TODO: Implement lock functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onLogoutAccountClick = {
// TODO: Implement logout functionality (BIT-1207)
showNotYetImplementedToast(context)
},
onAddAccountClick = addAccountClickAction,
onDismissRequest = { updateAccountMenuVisibility(false) },
topAppBarScrollBehavior = scrollBehavior,