-
Notifications
You must be signed in to change notification settings - Fork 849
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PM-15147 - Design Audit - Master Password Guidance Screen #4383
Changes from all commits
11c8711
8fb81e9
63ce644
47c5e86
79a9d56
c628d2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,46 @@ | ||
package com.x8bit.bitwarden.ui.auth.feature.masterpasswordguidance | ||
|
||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.navigationBarsPadding | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.foundation.rememberScrollState | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.Text | ||
import androidx.compose.material3.TopAppBarDefaults | ||
import androidx.compose.material3.rememberTopAppBarState | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.clip | ||
import androidx.compose.ui.input.nestedscroll.nestedScroll | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.semantics.clearAndSetSemantics | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.tooling.preview.Preview | ||
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.bitwardenBoldSpanStyle | ||
import com.x8bit.bitwarden.ui.platform.base.util.createAnnotatedString | ||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin | ||
import com.x8bit.bitwarden.ui.platform.base.util.toAnnotatedString | ||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar | ||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenActionCardSmall | ||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider | ||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenActionCard | ||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenContentCard | ||
import com.x8bit.bitwarden.ui.platform.components.model.ContentBlockData | ||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold | ||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter | ||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme | ||
|
||
private const val BULLET_TWO_TAB = "\u2022\t\t" | ||
import kotlinx.collections.immutable.persistentListOf | ||
|
||
/** | ||
* The top level composable for the Master Password Guidance screen. | ||
*/ | ||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Suppress("LongMethod") | ||
@Composable | ||
fun MasterPasswordGuidanceScreen( | ||
onNavigateBack: () -> Unit, | ||
|
@@ -81,128 +75,128 @@ fun MasterPasswordGuidanceScreen( | |
) | ||
}, | ||
) { | ||
Column( | ||
MasterPasswordGuidanceContent( | ||
modifier = Modifier | ||
.fillMaxSize() | ||
.verticalScroll(rememberScrollState()) | ||
.standardHorizontalMargin(), | ||
) { | ||
Column( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.clip(RoundedCornerShape(size = 4.dp)) | ||
.background(BitwardenTheme.colorScheme.background.tertiary), | ||
) { | ||
Column( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(all = 24.dp), | ||
) { | ||
|
||
Text( | ||
text = stringResource(R.string.what_makes_a_password_strong), | ||
style = BitwardenTheme.typography.titleMedium, | ||
color = BitwardenTheme.colorScheme.text.primary, | ||
) | ||
Spacer(modifier = Modifier.height(8.dp)) | ||
Text( | ||
style = BitwardenTheme.typography.bodyMedium, | ||
color = BitwardenTheme.colorScheme.text.primary, | ||
text = stringResource( | ||
R.string.the_longer_your_password_the_more_difficult_to_hack, | ||
), | ||
) | ||
} | ||
BitwardenHorizontalDivider() | ||
Column( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(horizontal = 24.dp, vertical = 16.dp), | ||
) { | ||
Text( | ||
text = stringResource(R.string.the_strongest_passwords_are_usually), | ||
style = BitwardenTheme.typography.titleSmall, | ||
color = BitwardenTheme.colorScheme.text.primary, | ||
) | ||
Spacer(modifier = Modifier.height(8.dp)) | ||
BulletTextRow(text = stringResource(R.string.twelve_or_more_characters)) | ||
BulletTextRow( | ||
text = stringResource( | ||
R.string.random_and_complex_using_numbers_and_special_characters, | ||
), | ||
) | ||
BulletTextRow( | ||
text = stringResource(R.string.totally_different_from_your_other_passwords), | ||
) | ||
} | ||
} | ||
Spacer(modifier = Modifier.height(16.dp)) | ||
TryGeneratorCard( | ||
onCardClicked = remember(viewModel) { | ||
{ | ||
viewModel.trySendAction( | ||
MasterPasswordGuidanceAction.TryPasswordGeneratorAction, | ||
) | ||
} | ||
}, | ||
) | ||
Spacer(modifier = Modifier.navigationBarsPadding()) | ||
} | ||
onTryPasswordGeneratorAction = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Be sure to |
||
viewModel.trySendAction( | ||
MasterPasswordGuidanceAction.TryPasswordGeneratorAction, | ||
) | ||
}, | ||
) | ||
} | ||
} | ||
|
||
@Composable | ||
private fun TryGeneratorCard( | ||
onCardClicked: () -> Unit, | ||
private fun MasterPasswordGuidanceContent( | ||
modifier: Modifier = Modifier, | ||
onTryPasswordGeneratorAction: () -> Unit, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be before the modifier. |
||
) { | ||
BitwardenActionCardSmall( | ||
actionIcon = rememberVectorPainter(id = R.drawable.ic_generate), | ||
actionText = stringResource( | ||
R.string.use_the_generator_to_create_a_strong_unique_password, | ||
), | ||
callToActionText = stringResource(R.string.try_it_out), | ||
onCardClicked = onCardClicked, | ||
modifier = modifier | ||
.fillMaxWidth(), | ||
trailingContent = { | ||
Icon( | ||
painter = rememberVectorPainter(id = R.drawable.ic_chevron_right), | ||
contentDescription = null, | ||
tint = BitwardenTheme.colorScheme.icon.primary, | ||
modifier = Modifier | ||
.align(Alignment.Center) | ||
.size(16.dp), | ||
) | ||
}, | ||
) | ||
} | ||
|
||
@Composable | ||
private fun BulletTextRow( | ||
text: String, | ||
modifier: Modifier = Modifier, | ||
) { | ||
Row( | ||
modifier = modifier | ||
.fillMaxWidth() | ||
.padding(horizontal = 8.dp), | ||
) { | ||
Column(modifier = modifier) { | ||
Spacer(modifier = Modifier.height(24.dp)) | ||
Text( | ||
text = BULLET_TWO_TAB, | ||
text = stringResource(R.string.a_secure_memorable_password), | ||
textAlign = TextAlign.Center, | ||
style = BitwardenTheme.typography.bodyMedium, | ||
style = BitwardenTheme.typography.titleMedium, | ||
color = BitwardenTheme.colorScheme.text.primary, | ||
modifier = Modifier.clearAndSetSemantics { }, | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(horizontal = 16.dp), | ||
) | ||
Spacer(modifier = Modifier.height(8.dp)) | ||
Text( | ||
text = text, | ||
text = stringResource( | ||
R.string.one_of_the_best_ways_to_create_a_secure_and_memorable_password, | ||
), | ||
textAlign = TextAlign.Center, | ||
style = BitwardenTheme.typography.bodyMedium, | ||
color = BitwardenTheme.colorScheme.text.primary, | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(horizontal = 16.dp), | ||
) | ||
Spacer(modifier = Modifier.height(24.dp)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. โ๏ธ maybe minor thing, curious what others thoughts are with this, maybe we can just remove the wrapping
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i like this idea better, ill make the change! |
||
MasterPasswordGuidanceContentBlocks() | ||
NeedSomeInspirationCard( | ||
onActionClicked = { | ||
onTryPasswordGeneratorAction() | ||
}, | ||
) | ||
Spacer(modifier = Modifier.navigationBarsPadding()) | ||
} | ||
} | ||
|
||
@Composable | ||
private fun MasterPasswordGuidanceContentBlocks(modifier: Modifier = Modifier) { | ||
Column(modifier = modifier) { | ||
BitwardenContentCard( | ||
contentItems = persistentListOf( | ||
ContentBlockData( | ||
headerText = stringResource(R.string.choose_three_or_four_random_words) | ||
.toAnnotatedString(), | ||
subtitleText = createAnnotatedString( | ||
mainString = stringResource( | ||
R.string.pick_three_or_four_random_unrelated_words, | ||
), | ||
highlights = listOf( | ||
stringResource( | ||
R.string.pick_three_or_four_random_unrelated_words_highlight, | ||
), | ||
), | ||
highlightStyle = bitwardenBoldSpanStyle, | ||
), | ||
iconVectorResource = R.drawable.ic_number1, | ||
), | ||
ContentBlockData( | ||
headerText = stringResource(R.string.combine_those_words_together) | ||
.toAnnotatedString(), | ||
subtitleText = createAnnotatedString( | ||
mainString = stringResource( | ||
R.string.put_the_words_together_in_any_order_to_form_your_passphrase, | ||
), | ||
highlights = listOf( | ||
stringResource( | ||
R.string.use_hyphens_spaces_or_leave_them_as_long_word_highlight, | ||
), | ||
), | ||
highlightStyle = bitwardenBoldSpanStyle, | ||
), | ||
iconVectorResource = R.drawable.ic_number2, | ||
), | ||
ContentBlockData( | ||
headerText = stringResource(R.string.make_it_yours).toAnnotatedString(), | ||
subtitleText = createAnnotatedString( | ||
mainString = stringResource( | ||
R.string.add_a_number_or_symbol_to_make_it_even_stronger, | ||
), | ||
highlights = listOf( | ||
stringResource(R.string.add_a_number_or_symbol_highlight), | ||
), | ||
highlightStyle = bitwardenBoldSpanStyle, | ||
), | ||
iconVectorResource = R.drawable.ic_number3, | ||
), | ||
), | ||
) | ||
} | ||
Spacer(modifier = Modifier.height(24.dp)) | ||
} | ||
|
||
@Composable | ||
private fun NeedSomeInspirationCard( | ||
onActionClicked: () -> Unit, | ||
modifier: Modifier = Modifier, | ||
) { | ||
BitwardenActionCard( | ||
cardTitle = stringResource(R.string.need_some_inspiration), | ||
actionText = stringResource(R.string.check_out_the_passphrase_generator), | ||
onActionClick = onActionClicked, | ||
modifier = modifier.fillMaxWidth(), | ||
) | ||
} | ||
|
||
@Preview | ||
@Composable | ||
private fun MasterPasswordGuidanceScreenPreview() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,7 +35,7 @@ import kotlin.let | |
* @param cardTitle The title of the card. | ||
* @param actionText The text content on the CTA button. | ||
* @param onActionClick The action to perform when the CTA button is clicked. | ||
* @param onDismissClick The action to perform when the dismiss button is clicked. | ||
* @param onDismissClick Optional action to perform when the dismiss button is clicked. | ||
* @param leadingContent Optional content to display on the leading side of the | ||
* [cardTitle] [Text]. | ||
*/ | ||
|
@@ -44,8 +44,8 @@ fun BitwardenActionCard( | |
cardTitle: String, | ||
actionText: String, | ||
onActionClick: () -> Unit, | ||
onDismissClick: () -> Unit, | ||
modifier: Modifier = Modifier, | ||
onDismissClick: (() -> Unit)? = null, | ||
cardSubtitle: String? = null, | ||
leadingContent: @Composable (() -> Unit)? = null, | ||
) { | ||
|
@@ -69,11 +69,13 @@ fun BitwardenActionCard( | |
) | ||
} | ||
Spacer(Modifier.weight(1f)) | ||
BitwardenStandardIconButton( | ||
painter = rememberVectorPainter(id = R.drawable.ic_close), | ||
contentDescription = stringResource(id = R.string.close), | ||
onClick = onDismissClick, | ||
) | ||
onDismissClick?.let { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ |
||
BitwardenStandardIconButton( | ||
painter = rememberVectorPainter(id = R.drawable.ic_close), | ||
contentDescription = stringResource(id = R.string.close), | ||
onClick = it, | ||
) | ||
} | ||
} | ||
cardSubtitle?.let { | ||
Spacer(Modifier.height(4.dp)) | ||
|
@@ -103,6 +105,19 @@ fun BitwardenActionCard( | |
*/ | ||
fun actionCardExitAnimation() = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Top) | ||
|
||
@Preview | ||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
@Composable | ||
private fun BitwardenActionCardWithSubtitleNoDismiss_preview() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ ๐ |
||
BitwardenTheme { | ||
BitwardenActionCard( | ||
cardTitle = "Title", | ||
actionText = "Action", | ||
onActionClick = {}, | ||
) | ||
} | ||
} | ||
|
||
@Preview | ||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
@Composable | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the
LongMethod
suppression above still needed?