Skip to content

Commit

Permalink
PM-13943 : PT1 Custom snackbar UI (#4135)
Browse files Browse the repository at this point in the history
  • Loading branch information
dseverns-livefront authored Oct 23, 2024
1 parent c5a266d commit a23fc31
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
Expand All @@ -22,7 +21,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand All @@ -36,7 +34,9 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButtonWi
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarHost
import com.x8bit.bitwarden.ui.platform.components.snackbar.rememberBitwardenSnackbarHostState
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenClickableText
import com.x8bit.bitwarden.ui.platform.components.util.nonLetterColorVisualTransformation
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
Expand All @@ -55,18 +55,14 @@ fun MasterPasswordGeneratorScreen(
viewModel: MasterPasswordGeneratorViewModel = hiltViewModel(),
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val context = LocalContext.current
val resources = context.resources
val snackbarHostState = remember {
SnackbarHostState()
}
val snackbarHostState = rememberBitwardenSnackbarHostState()
EventsEffect(viewModel = viewModel) { event ->
when (event) {
MasterPasswordGeneratorEvent.NavigateBack -> onNavigateBack()
MasterPasswordGeneratorEvent.NavigateToPreventLockout -> onNavigateToPreventLockout()
is MasterPasswordGeneratorEvent.ShowSnackbar -> {
snackbarHostState.showSnackbar(
message = event.text.toString(resources),
snackbarData = BitwardenSnackbarData(message = event.text),
duration = SnackbarDuration.Short,
)
}
Expand Down Expand Up @@ -100,7 +96,7 @@ fun MasterPasswordGeneratorScreen(
)
},
snackbarHost = {
BitwardenSnackbarHost(hostState = snackbarHostState)
BitwardenSnackbarHost(bitwardenHostState = snackbarHostState)
},
) { innerPadding ->
Column(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package com.x8bit.bitwarden.ui.platform.components.button

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand All @@ -26,6 +29,7 @@ fun BitwardenOutlinedButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
colors: BitwardenOutlinedButtonColors = bitwardenOutlinedButtonColors(),
) {
OutlinedButton(
onClick = onClick,
Expand All @@ -36,13 +40,13 @@ fun BitwardenOutlinedButton(
vertical = 10.dp,
horizontal = 24.dp,
),
colors = bitwardenOutlinedButtonColors(),
colors = colors.materialButtonColors,
border = BorderStroke(
width = 1.dp,
color = if (isEnabled) {
BitwardenTheme.colorScheme.outlineButton.border
colors.outlineBorderColor
} else {
BitwardenTheme.colorScheme.outlineButton.borderDisabled
colors.outlinedDisabledBorderColor
},
),
) {
Expand All @@ -53,6 +57,16 @@ fun BitwardenOutlinedButton(
}
}

/**
* Colors for a [BitwardenOutlinedButton].
*/
@Immutable
data class BitwardenOutlinedButtonColors(
val materialButtonColors: ButtonColors,
val outlineBorderColor: Color,
val outlinedDisabledBorderColor: Color,
)

@Preview
@Composable
private fun BitwardenOutlinedButton_preview_isEnabled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ fun BitwardenOutlinedButtonWithIcon(
onClick: () -> Unit,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
colors: BitwardenOutlinedButtonColors = bitwardenOutlinedButtonColors(),
) {
OutlinedButton(
onClick = onClick,
Expand All @@ -43,13 +44,13 @@ fun BitwardenOutlinedButtonWithIcon(
vertical = 10.dp,
horizontal = 24.dp,
),
colors = bitwardenOutlinedButtonColors(),
colors = colors.materialButtonColors,
border = BorderStroke(
width = 1.dp,
color = if (isEnabled) {
BitwardenTheme.colorScheme.outlineButton.border
colors.outlineBorderColor
} else {
BitwardenTheme.colorScheme.outlineButton.borderDisabled
colors.outlinedDisabledBorderColor
},
),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.platform.components.button.color
import androidx.compose.material3.ButtonColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButtonColors
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme

/**
Expand Down Expand Up @@ -42,12 +43,21 @@ fun bitwardenFilledTonalButtonColors(): ButtonColors = ButtonColors(
* Provides a default set of Bitwarden-styled colors for an outlined button.
*/
@Composable
fun bitwardenOutlinedButtonColors(): ButtonColors = ButtonColors(
containerColor = Color.Transparent,
contentColor = BitwardenTheme.colorScheme.outlineButton.foreground,
disabledContainerColor = Color.Transparent,
disabledContentColor = BitwardenTheme.colorScheme.outlineButton.foregroundDisabled,
)
fun bitwardenOutlinedButtonColors(
contentColor: Color = BitwardenTheme.colorScheme.outlineButton.foreground,
outlineColor: Color = BitwardenTheme.colorScheme.outlineButton.border,
outlineColorDisabled: Color = BitwardenTheme.colorScheme.outlineButton.borderDisabled,
): BitwardenOutlinedButtonColors =
BitwardenOutlinedButtonColors(
materialButtonColors = ButtonColors(
containerColor = Color.Transparent,
contentColor = contentColor,
disabledContainerColor = Color.Transparent,
disabledContentColor = BitwardenTheme.colorScheme.outlineButton.foregroundDisabled,
),
outlineBorderColor = outlineColor,
outlinedDisabledBorderColor = outlineColorDisabled,
)

/**
* Provides a default set of Bitwarden-styled colors for an outlined error button.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.x8bit.bitwarden.ui.platform.components.snackbar

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
import com.x8bit.bitwarden.ui.platform.components.button.color.bitwardenOutlinedButtonColors
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme

/**
* Custom snackbar for Bitwarden.
* Shows a message with an optional actions and title.
*/
@Composable
fun BitwardenSnackbar(
bitwardenSnackbarData: BitwardenSnackbarData,
modifier: Modifier = Modifier,
onDismiss: () -> Unit = {},
onActionClick: () -> Unit = {},
) {
Box(
modifier = modifier.padding(12.dp),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(
color = BitwardenTheme.colorScheme.background.alert,
shape = BitwardenTheme.shapes.snackbar,
)
.padding(16.dp),
) {
Column {
bitwardenSnackbarData.messageHeader?.let {
Text(
text = it(),
color = BitwardenTheme.colorScheme.text.reversed,
style = BitwardenTheme.typography.titleSmall,
)
Spacer(Modifier.height(4.dp))
}
Text(
text = bitwardenSnackbarData.message(),
color = BitwardenTheme.colorScheme.text.reversed,
style = BitwardenTheme.typography.bodyMedium,
)
bitwardenSnackbarData.actionLabel?.let {
Spacer(Modifier.height(12.dp))
BitwardenOutlinedButton(
label = it(),
onClick = onActionClick,
colors = bitwardenOutlinedButtonColors(
contentColor = BitwardenTheme.colorScheme.text.reversed,
outlineColor = BitwardenTheme
.colorScheme
.outlineButton
.borderReversed,
),
)
}
}
if (bitwardenSnackbarData.withDismissAction) {
Spacer(Modifier.weight(1f))
IconButton(
onClick = onDismiss,
content = {
Icon(
rememberVectorPainter(R.drawable.ic_close),
contentDescription = stringResource(R.string.close),
tint = BitwardenTheme.colorScheme.icon.reversed,
)
},
)
}
}
}
}

@Preview
@Composable
private fun BitwardenCustomSnackbar_preview() {
BitwardenTheme {
Surface {
BitwardenSnackbar(
BitwardenSnackbarData(
messageHeader = "Header".asText(),
message = "Message".asText(),
actionLabel = "Action".asText(),
withDismissAction = true,
),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
package com.x8bit.bitwarden.ui.platform.components.snackbar

import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme

/**
* A custom Bitwarden-themed snackbar.
*
* @param hostState The state of this snackbar.
* @param modifier The [Modifier] to be applied to this radio button.
* @param bitwardenHostState The state of this snackbar.
* @param modifier The [Modifier] to be applied to the [SnackbarHost].
*/
@Composable
fun BitwardenSnackbarHost(
hostState: SnackbarHostState,
bitwardenHostState: BitwardenSnackbarHostState,
modifier: Modifier = Modifier,
) {
SnackbarHost(
hostState = hostState,
hostState = bitwardenHostState.snackbarHostState,
modifier = modifier,
) {
Snackbar(
snackbarData = it,
shape = BitwardenTheme.shapes.snackbar,
containerColor = BitwardenTheme.colorScheme.background.alert,
contentColor = BitwardenTheme.colorScheme.text.reversed,
actionColor = BitwardenTheme.colorScheme.background.alert,
actionContentColor = BitwardenTheme.colorScheme.icon.reversed,
dismissActionContentColor = BitwardenTheme.colorScheme.icon.reversed,
)
) { snackbarData ->
val message = snackbarData.visuals.message
val currentCustomSnackbarData = bitwardenHostState.currentSnackbarData
if (currentCustomSnackbarData?.key == message) {
BitwardenSnackbar(
bitwardenSnackbarData = currentCustomSnackbarData,
onDismiss = snackbarData::dismiss,
onActionClick = snackbarData::performAction,
)
}
}
}
Loading

0 comments on commit a23fc31

Please sign in to comment.