From bd45d5f56a46c9f41b683d943768ab75d435445f Mon Sep 17 00:00:00 2001 From: David Perez Date: Tue, 2 Jun 2026 15:02:04 -0500 Subject: [PATCH] PM-38479: Feat: Update the RemovePasswordScreen UI (#7010) --- .../removepassword/RemovePasswordScreen.kt | 96 +++++++++++++------ .../removepassword/RemovePasswordViewModel.kt | 6 +- .../RemovePasswordViewModelTest.kt | 5 +- ui/src/main/res/values/strings.xml | 5 +- 4 files changed, 74 insertions(+), 38 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreen.kt index b2c69bee3b..16f9af7754 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreen.kt @@ -1,11 +1,14 @@ package com.x8bit.bitwarden.ui.auth.feature.removepassword import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize 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.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api @@ -14,14 +17,17 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.bitwarden.ui.platform.base.util.cardStyle import com.bitwarden.ui.platform.base.util.standardHorizontalMargin import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar import com.bitwarden.ui.platform.components.button.BitwardenFilledButton @@ -30,8 +36,11 @@ import com.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog import com.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog import com.bitwarden.ui.platform.components.field.BitwardenPasswordField +import com.bitwarden.ui.platform.components.icon.BitwardenIcon +import com.bitwarden.ui.platform.components.icon.model.IconData import com.bitwarden.ui.platform.components.model.CardStyle import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold +import com.bitwarden.ui.platform.resource.BitwardenDrawable import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.platform.theme.BitwardenTheme import com.bitwarden.ui.util.asText @@ -91,53 +100,41 @@ private fun RemovePasswordScreenContent( modifier = modifier .verticalScroll(rememberScrollState()), ) { - Spacer(modifier = Modifier.height(height = 12.dp)) + Spacer(modifier = Modifier.height(height = 24.dp)) Text( text = state.description(), style = BitwardenTheme.typography.bodyMedium, color = BitwardenTheme.colorScheme.text.primary, + textAlign = TextAlign.Center, modifier = Modifier .standardHorizontalMargin() .fillMaxWidth(), ) - Spacer(modifier = Modifier.height(height = 12.dp)) - Text( - text = state.labelOrg(), - style = BitwardenTheme.typography.bodyMedium, - color = BitwardenTheme.colorScheme.text.primary, + Spacer(modifier = Modifier.height(height = 24.dp)) + RemovePasswordInfoColumn( + label = state.labelOrg(), + value = state.orgName?.invoke().orEmpty(), + icon = IconData.Local(iconRes = BitwardenDrawable.ic_organization), modifier = Modifier + .fillMaxWidth() .standardHorizontalMargin() - .fillMaxWidth(), + .cardStyle( + cardStyle = CardStyle.Top(dividerPadding = 56.dp), + paddingBottom = 4.dp, + ), ) - Text( - text = state.orgName?.invoke().orEmpty(), - style = BitwardenTheme.typography.bodyMedium, - color = BitwardenTheme.colorScheme.text.secondary, + RemovePasswordInfoColumn( + label = state.labelDomain(), + value = state.domainName?.invoke().orEmpty(), + icon = IconData.Local(iconRes = BitwardenDrawable.ic_globe), modifier = Modifier + .fillMaxWidth() .standardHorizontalMargin() - .fillMaxWidth(), + .cardStyle(cardStyle = CardStyle.Bottom), ) - Spacer(modifier = Modifier.height(height = 12.dp)) - Text( - text = state.labelDomain(), - style = BitwardenTheme.typography.bodyMedium, - color = BitwardenTheme.colorScheme.text.primary, - modifier = Modifier - .standardHorizontalMargin() - .fillMaxWidth(), - ) - - Text( - text = state.domainName?.invoke().orEmpty(), - style = BitwardenTheme.typography.bodyMedium, - color = BitwardenTheme.colorScheme.text.secondary, - modifier = Modifier - .standardHorizontalMargin() - .fillMaxWidth(), - ) - Spacer(modifier = Modifier.height(16.dp)) + Spacer(modifier = Modifier.height(height = 24.dp)) BitwardenPasswordField( label = stringResource(id = BitwardenString.master_password), @@ -179,6 +176,43 @@ private fun RemovePasswordScreenContent( } } +@Composable +private fun RemovePasswordInfoColumn( + label: String, + value: String, + icon: IconData, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier.defaultMinSize(minHeight = 60.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(modifier = Modifier.width(width = 12.dp)) + BitwardenIcon( + iconData = icon, + tint = BitwardenTheme.colorScheme.icon.secondary, + ) + Spacer(modifier = Modifier.width(width = 12.dp)) + Column(modifier = Modifier.fillMaxWidth()) { + Text( + text = label, + style = BitwardenTheme.typography.titleSmall, + color = BitwardenTheme.colorScheme.text.primary, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(height = 4.dp)) + + Text( + text = value, + style = BitwardenTheme.typography.bodyMedium, + color = BitwardenTheme.colorScheme.text.secondary, + modifier = Modifier.fillMaxWidth(), + ) + } + } +} + @Composable private fun RemovePasswordDialogs( dialogState: RemovePasswordState.DialogState?, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModel.kt index 1a994736ec..401766702e 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModel.kt @@ -36,8 +36,10 @@ class RemovePasswordViewModel @Inject constructor( RemovePasswordState( input = "", - description = BitwardenString.password_no_longer_required_confirm_domain.asText(), - labelOrg = BitwardenString.key_connector_organization.asText(), + description = BitwardenString + .your_organization_no_longer_requires_a_master_password + .asText(), + labelOrg = BitwardenString.organization.asText(), orgName = org?.name?.asText(), labelDomain = BitwardenString.key_connector_domain.asText(), domainName = org?.keyConnectorUrl?.asText(), diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt index ebf25588b2..ff0e271ceb 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt @@ -268,8 +268,9 @@ private val DEFAULT_STATE = RemovePasswordState( input = "", dialogState = null, description = BitwardenString - .password_no_longer_required_confirm_domain.asText(), - labelOrg = BitwardenString.key_connector_organization.asText(), + .your_organization_no_longer_requires_a_master_password + .asText(), + labelOrg = BitwardenString.organization.asText(), orgName = ORGANIZATION_NAME.asText(), labelDomain = BitwardenString.key_connector_domain.asText(), domainName = KEY_CONNECTOR_URL.asText(), diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 5d03ddcce6..b821d8d99f 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -991,9 +991,8 @@ Do you want to switch to this account? Delete logs Do you really want to delete all recorded logs? Confirm Key Connector domain - Key Connector domain: - Organization: - A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator. + Key Connector domain + Your organization no longer requires a master password for its members. Please verify the domain below with your admin and enter your master password to continue. Please confirm the domain below with your organization administrator.\n\nKey Connector domain:\n%1$s Flight recorder on Flight recorder will be active until %1$s at %2$s. Return to settings to deactivate now.