Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.data.platform.manager.util.toImportCredentialsRequestDataOrNull
import com.x8bit.bitwarden.ui.vault.feature.exportitems.model.AccountSelectionListItem
import com.x8bit.bitwarden.ui.vault.feature.vault.util.initials
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -37,11 +37,13 @@ class SelectAccountViewModel @Inject constructor(
specialCircumstanceManager: SpecialCircumstanceManager,
) : BaseViewModel<SelectAccountState, SelectAccountEvent, SelectAccountAction>(
initialState = run {
val importRequest = specialCircumstanceManager.specialCircumstance
as SpecialCircumstance.CredentialExchangeExport

val importRequest = requireNotNull(
specialCircumstanceManager
.specialCircumstance
?.toImportCredentialsRequestDataOrNull(),
)
SelectAccountState(
importRequest = importRequest.data,
importRequest = importRequest,
viewState = SelectAccountState.ViewState.Loading,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitwarden.cxf.manager.CredentialExchangeCompletionManager
import com.bitwarden.cxf.manager.model.ExportCredentialsResult
import com.bitwarden.cxf.ui.composition.LocalCredentialExchangeCompletionManager
import com.bitwarden.cxf.ui.composition.LocalCredentialExchangeRequestValidator
import com.bitwarden.cxf.validator.CredentialExchangeRequestValidator
import com.bitwarden.ui.platform.base.util.EventsEffect
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.bitwarden.ui.platform.components.button.BitwardenFilledButton
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
import com.bitwarden.ui.platform.components.content.BitwardenErrorContent
import com.bitwarden.ui.platform.components.content.BitwardenLoadingContent
import com.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
import com.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
import com.bitwarden.ui.platform.components.field.BitwardenPasswordField
Expand All @@ -47,19 +51,23 @@ import com.bitwarden.ui.util.asText
import com.x8bit.bitwarden.ui.vault.feature.exportitems.component.AccountSummaryListItem
import com.x8bit.bitwarden.ui.vault.feature.exportitems.component.ExportItemsScaffold
import com.x8bit.bitwarden.ui.vault.feature.exportitems.model.AccountSelectionListItem
import com.x8bit.bitwarden.ui.vault.feature.exportitems.verifypassword.handlers.VerifyPasswordHandlers
import com.x8bit.bitwarden.ui.vault.feature.exportitems.verifypassword.handlers.rememberVerifyPasswordHandler

/**
* Top level composable for the Verify Password screen.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("LongMethod")
@Composable
fun VerifyPasswordScreen(
onNavigateBack: () -> Unit,
onPasswordVerified: (userId: String) -> Unit,
viewModel: VerifyPasswordViewModel = hiltViewModel(),
credentialExchangeCompletionManager: CredentialExchangeCompletionManager =
LocalCredentialExchangeCompletionManager.current,
credentialExchangeRequestValidator: CredentialExchangeRequestValidator =
LocalCredentialExchangeRequestValidator.current,
snackbarHostState: BitwardenSnackbarHostState = rememberBitwardenSnackbarHostState(),
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
Expand All @@ -80,6 +88,16 @@ fun VerifyPasswordScreen(
)
}

is VerifyPasswordEvent.ValidateImportRequest -> {
viewModel.trySendAction(
VerifyPasswordAction.ValidateImportRequestResultReceive(
isValid = credentialExchangeRequestValidator.validate(
importCredentialsRequestData = event.importCredentialsRequestData,
),
),
)
}

is VerifyPasswordEvent.PasswordVerified -> {
onPasswordVerified(event.userId)
}
Expand Down Expand Up @@ -108,13 +126,30 @@ fun VerifyPasswordScreen(
scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxSize(),
) {
VerifyPasswordContent(
state = state,
onInputChanged = handler.onInputChanged,
onContinueClick = handler.onContinueClick,
onResendCodeClick = handler.onSendCodeClick,
modifier = Modifier.fillMaxSize(),
)
when (val viewState = state.viewState) {
is VerifyPasswordState.ViewState.Content -> {
VerifyPasswordContent(
viewState = viewState,
accountSummaryListItem = state.accountSummaryListItem,
handler = handler,
modifier = Modifier.fillMaxSize(),
)
}

is VerifyPasswordState.ViewState.Error -> {
BitwardenErrorContent(
message = viewState.message(),
modifier = Modifier.fillMaxSize(),
)
}

VerifyPasswordState.ViewState.Loading -> {
BitwardenLoadingContent(
text = stringResource(id = BitwardenString.loading),
modifier = Modifier.fillMaxSize(),
)
}
}
}
}

Expand Down Expand Up @@ -144,10 +179,9 @@ private fun VerifyPasswordDialogs(
@Suppress("LongMethod")
@Composable
private fun VerifyPasswordContent(
state: VerifyPasswordState,
onInputChanged: (String) -> Unit,
onContinueClick: () -> Unit,
onResendCodeClick: () -> Unit,
viewState: VerifyPasswordState.ViewState.Content,
accountSummaryListItem: AccountSelectionListItem,
handler: VerifyPasswordHandlers,
modifier: Modifier = Modifier,
) {
Column(
Expand All @@ -158,15 +192,15 @@ private fun VerifyPasswordContent(
Spacer(Modifier.height(24.dp))

Text(
text = state.title(),
text = viewState.title(),
textAlign = TextAlign.Center,
style = BitwardenTheme.typography.titleMedium,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)

state.subtext?.let { subtext ->
viewState.subtext?.let { subtext ->
Spacer(Modifier.height(8.dp))
Text(
text = subtext(),
Expand All @@ -181,7 +215,7 @@ private fun VerifyPasswordContent(
Spacer(Modifier.height(16.dp))

AccountSummaryListItem(
item = state.accountSummaryListItem,
item = accountSummaryListItem,
cardStyle = CardStyle.Full,
clickable = false,
modifier = Modifier
Expand All @@ -191,17 +225,17 @@ private fun VerifyPasswordContent(

Spacer(Modifier.height(16.dp))

if (state.showResendCodeButton) {
if (viewState.showResendCodeButton) {
BitwardenPasswordField(
label = stringResource(id = BitwardenString.verification_code),
value = state.input,
onValueChange = onInputChanged,
value = viewState.input,
onValueChange = handler.onInputChanged,
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done,
keyboardActions = KeyboardActions(
onDone = {
if (state.isContinueButtonEnabled) {
onContinueClick()
if (viewState.isContinueButtonEnabled) {
handler.onContinueClick()
} else {
defaultKeyboardAction(ImeAction.Done)
}
Expand All @@ -218,14 +252,14 @@ private fun VerifyPasswordContent(
} else {
BitwardenPasswordField(
label = stringResource(BitwardenString.master_password),
value = state.input,
onValueChange = onInputChanged,
value = viewState.input,
onValueChange = handler.onInputChanged,
showPasswordTestTag = "PasswordVisibilityToggle",
imeAction = ImeAction.Done,
keyboardActions = KeyboardActions(
onDone = {
if (state.isContinueButtonEnabled) {
onContinueClick()
if (viewState.isContinueButtonEnabled) {
handler.onContinueClick()
} else {
defaultKeyboardAction(ImeAction.Done)
}
Expand All @@ -245,18 +279,18 @@ private fun VerifyPasswordContent(

BitwardenFilledButton(
label = stringResource(BitwardenString.continue_text),
onClick = onContinueClick,
isEnabled = state.isContinueButtonEnabled,
onClick = handler.onContinueClick,
isEnabled = viewState.isContinueButtonEnabled,
modifier = Modifier
.testTag("ContinueImportButton")
.fillMaxWidth()
.standardHorizontalMargin(),
)

if (state.showResendCodeButton) {
if (viewState.showResendCodeButton) {
BitwardenOutlinedButton(
label = stringResource(BitwardenString.resend_code),
onClick = onResendCodeClick,
onClick = handler.onSendCodeClick,
modifier = Modifier
.testTag("ResendTOTPCodeButton")
.fillMaxWidth()
Expand All @@ -273,35 +307,28 @@ private fun VerifyPasswordContent(
@Preview(showBackground = true)
@Composable
private fun VerifyPasswordContent_MasterPassword_preview() {
val accountSummaryListItem = AccountSelectionListItem(
userId = "userId",
isItemRestricted = false,
avatarColorHex = "#FF0000",
initials = "JD",
email = "john.doe@example.com",
)
val state = VerifyPasswordState(
title = BitwardenString.verify_your_master_password.asText(),
subtext = null,
hasOtherAccounts = true,
accountSummaryListItem = accountSummaryListItem,
)
ExportItemsScaffold(
navIcon = rememberVectorPainter(
BitwardenDrawable.ic_back,
),
navIcon = rememberVectorPainter(id = BitwardenDrawable.ic_back),
onNavigationIconClick = {},
navigationIconContentDescription = stringResource(BitwardenString.back),
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
modifier = Modifier.fillMaxSize(),
) {
VerifyPasswordContent(
state = state,
onInputChanged = {},
onContinueClick = {},
onResendCodeClick = {},
modifier = Modifier
.fillMaxSize(),
viewState = VerifyPasswordState.ViewState.Content(
title = BitwardenString.verify_your_master_password.asText(),
subtext = null,
showResendCodeButton = false,
),
accountSummaryListItem = AccountSelectionListItem(
userId = "userId",
isItemRestricted = false,
avatarColorHex = "#FF0000",
initials = "JD",
email = "john.doe@example.com",
),
handler = VerifyPasswordHandlers.createEmpty(),
modifier = Modifier.fillMaxSize(),
)
}
}
Expand All @@ -310,38 +337,30 @@ private fun VerifyPasswordContent_MasterPassword_preview() {
@Preview(showBackground = true)
@Composable
private fun VerifyPasswordContent_Otp_preview() {
val accountSummaryListItem = AccountSelectionListItem(
userId = "userId",
isItemRestricted = false,
avatarColorHex = "#FF0000",
initials = "JD",
email = "john.doe@example.com",
)
val state = VerifyPasswordState(
title = BitwardenString.verify_your_account_email_address.asText(),
subtext = BitwardenString
.enter_the_6_digit_code_that_was_emailed_to_the_address_below
.asText(),
accountSummaryListItem = accountSummaryListItem,
showResendCodeButton = true,
hasOtherAccounts = true,
)
ExportItemsScaffold(
navIcon = rememberVectorPainter(
BitwardenDrawable.ic_back,
),
navIcon = rememberVectorPainter(id = BitwardenDrawable.ic_back),
onNavigationIconClick = {},
navigationIconContentDescription = stringResource(BitwardenString.back),
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
modifier = Modifier.fillMaxSize(),
) {
VerifyPasswordContent(
state = state,
onInputChanged = {},
onContinueClick = {},
onResendCodeClick = {},
modifier = Modifier
.fillMaxSize(),
viewState = VerifyPasswordState.ViewState.Content(
title = BitwardenString.verify_your_account_email_address.asText(),
subtext = BitwardenString
.enter_the_6_digit_code_that_was_emailed_to_the_address_below
.asText(),
showResendCodeButton = true,
),
accountSummaryListItem = AccountSelectionListItem(
userId = "userId",
isItemRestricted = false,
avatarColorHex = "#FF0000",
initials = "JD",
email = "john.doe@example.com",
),
handler = VerifyPasswordHandlers.createEmpty(),
modifier = Modifier.fillMaxSize(),
)
}
}
Loading
Loading