This commit is contained in:
Matt Gibson 2024-10-22 14:33:42 -07:00
parent 563427c99f
commit 53c0c7d2ad
No known key found for this signature in database
GPG key ID: 7CBCA182C13B0912
2 changed files with 47 additions and 135 deletions

View file

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.vault.feature.unsyncedvaultitem package com.x8bit.bitwarden.ui.vault.feature.unsyncedvaultitem
import android.util.Range
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
@ -55,6 +56,7 @@ import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
import com.x8bit.bitwarden.ui.platform.components.scrim.BitwardenAnimatedScrim import com.x8bit.bitwarden.ui.platform.components.scrim.BitwardenAnimatedScrim
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.vault.feature.vault.model.NotificationSummary
import com.x8bit.bitwarden.ui.vault.feature.vault.util.iconRes import com.x8bit.bitwarden.ui.vault.feature.vault.util.iconRes
import com.x8bit.bitwarden.ui.vault.feature.vault.util.iconTestTag import com.x8bit.bitwarden.ui.vault.feature.vault.util.iconTestTag
import com.x8bit.bitwarden.ui.vault.feature.vault.util.initials import com.x8bit.bitwarden.ui.vault.feature.vault.util.initials
@ -99,13 +101,10 @@ private const val MAXIMUM_ACCOUNT_LIMIT = 5
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Suppress("LongMethod") @Suppress("LongMethod")
@Composable @Composable
fun BitwardenAccountSwitcher( fun NotificationCenter(
isVisible: Boolean, isVisible: Boolean,
accountSummaries: ImmutableList<AccountSummary>, notificationSummaries: ImmutableList<NotificationSummary>,
onSwitchAccountClick: (AccountSummary) -> Unit, onNotificationClick: (NotificationSummary) -> Unit,
onLockAccountClick: (AccountSummary) -> Unit,
onLogoutAccountClick: (AccountSummary) -> Unit,
onAddAccountClick: () -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isAddAccountAvailable: Boolean = true, isAddAccountAvailable: Boolean = true,
@ -118,52 +117,6 @@ fun BitwardenAccountSwitcher(
var lockOrLogoutAccount by remember { mutableStateOf<AccountSummary?>(null) } var lockOrLogoutAccount by remember { mutableStateOf<AccountSummary?>(null) }
var logoutConfirmationAccount by remember { mutableStateOf<AccountSummary?>(null) } var logoutConfirmationAccount by remember { mutableStateOf<AccountSummary?>(null) }
var removeConfirmationAccount by remember { mutableStateOf<AccountSummary?>(null) } var removeConfirmationAccount by remember { mutableStateOf<AccountSummary?>(null) }
when {
isVisibleActual -> {
// Can not show dialogs when the switcher itself is visible
}
lockOrLogoutAccount != null -> {
LockOrLogoutDialog(
accountSummary = requireNotNull(lockOrLogoutAccount),
onDismissRequest = { lockOrLogoutAccount = null },
onLockAccountClick = {
onLockAccountClick(it)
lockOrLogoutAccount = null
},
onLogoutAccountClick = {
lockOrLogoutAccount = null
logoutConfirmationAccount = it
},
onRemoveAccountClick = {
lockOrLogoutAccount = null
removeConfirmationAccount = it
},
)
}
logoutConfirmationAccount != null -> {
BitwardenLogoutConfirmationDialog(
accountSummary = requireNotNull(logoutConfirmationAccount),
onDismissRequest = { logoutConfirmationAccount = null },
onConfirmClick = {
onLogoutAccountClick(requireNotNull(logoutConfirmationAccount))
logoutConfirmationAccount = null
},
)
}
removeConfirmationAccount != null -> {
BitwardenRemovalConfirmationDialog(
accountSummary = requireNotNull(removeConfirmationAccount),
onDismissRequest = { removeConfirmationAccount = null },
onConfirmClick = {
onLogoutAccountClick(requireNotNull(removeConfirmationAccount))
removeConfirmationAccount = null
},
)
}
}
Box(modifier = modifier) { Box(modifier = modifier) {
BitwardenAnimatedScrim( BitwardenAnimatedScrim(
@ -172,22 +125,13 @@ fun BitwardenAccountSwitcher(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
) )
AnimatedAccountSwitcher( AnimatedNotificationCenter(
isVisible = isVisible, isVisible = isVisible,
accountSummaries = accountSummaries, notificationSummaries = notificationSummaries,
onSwitchAccountClick = { onNotificationClick = {
onDismissRequest() onDismissRequest()
onSwitchAccountClick(it) onNotificationClick(it)
}, },
onSwitchAccountLongClick = {
onDismissRequest()
lockOrLogoutAccount = it
},
onAddAccountClick = {
onDismissRequest()
onAddAccountClick()
},
isAddAccountAvailable = isAddAccountAvailable,
topAppBarScrollBehavior = topAppBarScrollBehavior, topAppBarScrollBehavior = topAppBarScrollBehavior,
currentAnimationState = { isVisibleActual = it }, currentAnimationState = { isVisibleActual = it },
modifier = Modifier modifier = Modifier
@ -201,13 +145,10 @@ fun BitwardenAccountSwitcher(
ExperimentalAnimationApi::class, ExperimentalAnimationApi::class,
) )
@Composable @Composable
private fun AnimatedAccountSwitcher( private fun AnimatedNotificationCenter(
isVisible: Boolean, isVisible: Boolean,
accountSummaries: ImmutableList<AccountSummary>, notificationSummaries: ImmutableList<NotificationSummary>,
onSwitchAccountClick: (AccountSummary) -> Unit, onNotificationClick: (NotificationSummary) -> Unit,
onSwitchAccountLongClick: (AccountSummary) -> Unit,
onAddAccountClick: () -> Unit,
isAddAccountAvailable: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
topAppBarScrollBehavior: TopAppBarScrollBehavior, topAppBarScrollBehavior: TopAppBarScrollBehavior,
currentAnimationState: (isVisible: Boolean) -> Unit, currentAnimationState: (isVisible: Boolean) -> Unit,
@ -231,28 +172,16 @@ private fun AnimatedAccountSwitcher(
// Match the color of the switcher the different states of the app bar. // Match the color of the switcher the different states of the app bar.
.scrolledContainerBackground(topAppBarScrollBehavior), .scrolledContainerBackground(topAppBarScrollBehavior),
) { ) {
items(accountSummaries) { accountSummary -> items(notificationSummaries) { notificationSummary ->
AccountSummaryItem( NotificationSummaryItem(
accountSummary = accountSummary, notificationSummary = notificationSummary,
onSwitchAccountClick = onSwitchAccountClick, onNotificationClick = onNotificationClick,
onSwitchAccountLongClick = onSwitchAccountLongClick,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp),
) )
BitwardenHorizontalDivider() BitwardenHorizontalDivider()
} }
if (accountSummaries.size < MAXIMUM_ACCOUNT_LIMIT && isAddAccountAvailable) {
item {
AddAccountItem(
onClick = onAddAccountClick,
modifier = Modifier
.fillMaxWidth()
.testTag("AddAccountButton")
.padding(horizontal = 16.dp),
)
}
}
} }
} }
} }
@ -260,10 +189,9 @@ private fun AnimatedAccountSwitcher(
@Suppress("LongMethod") @Suppress("LongMethod")
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun AccountSummaryItem( private fun NotificationSummaryItem(
accountSummary: AccountSummary, notificationSummary: NotificationSummary,
onSwitchAccountClick: (AccountSummary) -> Unit, onNotificationClick: (NotificationSummary) -> Unit,
onSwitchAccountLongClick: (AccountSummary) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Row( Row(
@ -276,8 +204,7 @@ private fun AccountSummaryItem(
indication = ripple( indication = ripple(
color = BitwardenTheme.colorScheme.background.pressed, color = BitwardenTheme.colorScheme.background.pressed,
), ),
onClick = { onSwitchAccountClick(accountSummary) }, onClick = { onNotificationClick(notificationSummary) },
onLongClick = { onSwitchAccountLongClick(accountSummary) },
) )
.padding(vertical = 8.dp) .padding(vertical = 8.dp)
.then(modifier), .then(modifier),
@ -288,16 +215,14 @@ private fun AccountSummaryItem(
Icon( Icon(
painter = rememberVectorPainter(id = R.drawable.ic_account_initials_container), painter = rememberVectorPainter(id = R.drawable.ic_account_initials_container),
contentDescription = null, contentDescription = null,
tint = accountSummary.avatarColor,
modifier = Modifier.size(40.dp), modifier = Modifier.size(40.dp),
) )
Text( Text(
text = accountSummary.initials, text = notificationSummary.title.substring(0,2),
style = BitwardenTheme.typography.titleMedium style = BitwardenTheme.typography.titleMedium
// Do not allow scaling // Do not allow scaling
.copy(fontSize = 16.dp.toUnscaledTextUnit()), .copy(fontSize = 16.dp.toUnscaledTextUnit()),
color = accountSummary.avatarColor.toSafeOverlayColor(),
modifier = Modifier.clearAndSetSemantics { }, modifier = Modifier.clearAndSetSemantics { },
) )
} }
@ -308,7 +233,7 @@ private fun AccountSummaryItem(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
) { ) {
Text( Text(
text = accountSummary.email, text = notificationSummary.title,
style = BitwardenTheme.typography.bodyLarge, style = BitwardenTheme.typography.bodyLarge,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
@ -316,36 +241,15 @@ private fun AccountSummaryItem(
) )
Text( Text(
text = accountSummary.environmentLabel, text = notificationSummary.subtitle,
style = BitwardenTheme.typography.bodyMedium, style = BitwardenTheme.typography.bodyMedium,
color = BitwardenTheme.colorScheme.text.secondary, color = BitwardenTheme.colorScheme.text.secondary,
modifier = Modifier.testTag("AccountEnvironmentLabel"), modifier = Modifier.testTag("AccountEnvironmentLabel"),
) )
accountSummary.supportingTextResOrNull?.let { supportingTextResId ->
Text(
text = stringResource(id = supportingTextResId).lowercaseWithCurrentLocal(),
style = BitwardenTheme.typography.bodyMedium,
color = BitwardenTheme.colorScheme.text.secondary,
modifier = Modifier.testTag("AccountStatusLabel"),
)
}
} }
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
Icon(
painter = rememberVectorPainter(id = accountSummary.iconRes),
contentDescription = null,
tint = when (accountSummary.status) {
AccountSummary.Status.ACTIVE -> BitwardenTheme.colorScheme.icon.secondary
else -> BitwardenTheme.colorScheme.icon.primary
},
modifier = Modifier
.testTag(accountSummary.iconTestTag)
.size(24.dp),
)
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
} }
} }
@ -430,26 +334,17 @@ private fun AddAccountItem(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun BitwardenAccountSwitcher_preview() { private fun NotificationCenter_preview() {
BitwardenAccountSwitcher( NotificationCenter(
isVisible = true, isVisible = true,
accountSummaries = listOf( notificationSummaries = listOf(
AccountSummary( NotificationSummary(
userId = "123", title = "The title",
name = "Cool Guy", subtitle = "The subtitle"
email = "coolestguyeverthatlikestosurfandbeachvibes@gmail.com",
avatarColorHex = "#EEEEEE",
environmentLabel = "label",
isActive = true,
isLoggedIn = true,
isVaultUnlocked = true,
), ),
) )
.toImmutableList(), .toImmutableList(),
onSwitchAccountClick = {}, onNotificationClick = {},
onLockAccountClick = {},
onLogoutAccountClick = {},
onAddAccountClick = {},
onDismissRequest = {}, onDismissRequest = {},
topAppBarScrollBehavior = topAppBarScrollBehavior =
TopAppBarDefaults.exitUntilCollapsedScrollBehavior( TopAppBarDefaults.exitUntilCollapsedScrollBehavior(

View file

@ -0,0 +1,17 @@
package com.x8bit.bitwarden.ui.vault.feature.vault.model
import android.os.Parcelable
import androidx.compose.ui.graphics.Color
import com.x8bit.bitwarden.ui.platform.base.util.hexToColor
import kotlinx.parcelize.Parcelize
/**
* Summary information about a user's account.
*
* @property
*/
@Parcelize
data class NotificationSummary(
val title: String,
val subtitle: String,
) : Parcelable