mirror of
https://github.com/bitwarden/android.git
synced 2026-05-11 02:15:43 -05:00
[PM-6702] Add Check your email screen used for cloud registration flow.
This commit is contained in:
@@ -6,6 +6,8 @@ import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navOptions
|
||||
import androidx.navigation.navigation
|
||||
import com.x8bit.bitwarden.ui.auth.feature.checkemail.checkEmailDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.checkemail.navigateToCheckEmail
|
||||
import com.x8bit.bitwarden.ui.auth.feature.completeregistration.completeRegistrationDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.completeregistration.navigateToCompleteRegistration
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.createAccountDestination
|
||||
@@ -59,8 +61,16 @@ fun NavGraphBuilder.authGraph(navController: NavHostController) {
|
||||
onNavigateToCompleteRegistration = { emailAddress, verificationToken, captchaToken ->
|
||||
navController.navigateToCompleteRegistration()
|
||||
},
|
||||
onNavigateToCheckEmail = {emailAddress ->
|
||||
navController.navigateToCheckEmail(emailAddress)
|
||||
},
|
||||
onNavigateToEnvironment = { navController.navigateToEnvironment() }
|
||||
)
|
||||
checkEmailDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateBackToLanding = {
|
||||
navController.popBackStack(route = LANDING_ROUTE, inclusive = false)
|
||||
})
|
||||
completeRegistrationDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToLogin = { emailAddress, captchaToken ->
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.checkemail
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.navArgument
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
|
||||
private const val EMAIL_ADDRESS: String = "email"
|
||||
private const val CHECK_EMAIL_ROUTE: String = "check_email/{$EMAIL_ADDRESS}"
|
||||
|
||||
/**
|
||||
* Navigate to the check email screen.
|
||||
*/
|
||||
fun NavController.navigateToCheckEmail(emailAddress: String, navOptions: NavOptions? = null) {
|
||||
this.navigate("check_email/$emailAddress", navOptions)
|
||||
}
|
||||
/**
|
||||
* Class to retrieve check email arguments from the [SavedStateHandle].
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
data class CheckEmailArgs(
|
||||
val emailAddress: String
|
||||
) {
|
||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||
emailAddress = checkNotNull(savedStateHandle.get<String>(EMAIL_ADDRESS)),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the check email screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.checkEmailDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateBackToLanding: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = CHECK_EMAIL_ROUTE,
|
||||
arguments = listOf(
|
||||
navArgument(EMAIL_ADDRESS) { type = NavType.StringType },
|
||||
)
|
||||
) {
|
||||
CheckEmailScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateBackToLanding = onNavigateBackToLanding
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.checkemail
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
|
||||
/**
|
||||
* Top level composable for the check email screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun CheckEmailScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateBackToLanding: () -> Unit,
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
viewModel: CheckEmailViewModel = hiltViewModel(),
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
EventsEffect(viewModel) { event ->
|
||||
when (event) {
|
||||
is CheckEmailEvent.NavigateBack -> {
|
||||
onNavigateBack.invoke()
|
||||
}
|
||||
|
||||
is CheckEmailEvent.NavigateToEmailApp -> {
|
||||
val intent = Intent(Intent.ACTION_SENDTO)
|
||||
intent.setData(Uri.parse("mailto:"))
|
||||
intentManager.startActivity(intent)
|
||||
}
|
||||
|
||||
is CheckEmailEvent.NavigateBackToLanding -> {
|
||||
onNavigateBackToLanding.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
BitwardenScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.create_account),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = rememberVectorPainter(id = R.drawable.ic_close),
|
||||
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(CheckEmailAction.CloseTap) }
|
||||
}
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.imePadding()
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Image(
|
||||
painter = rememberVectorPainter(id = R.drawable.email_check),
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillHeight,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.height(112.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.check_your_email),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
val descriptionAnnotatedString = CreateAnnotatedString(
|
||||
mainText = stringResource(id = R.string.follow_the_instructions_in_the_email_sent_to_x_to_continue_creating_your_account, state.email),
|
||||
highlightText = state.email,
|
||||
highlightSpanStyle = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = descriptionAnnotatedString,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = R.string.open_email_app),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(CheckEmailAction.OpenEmailTap) }
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("OpenEmailApp")
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
val goBackAnnotatedString = CreateAnnotatedString(
|
||||
mainText = stringResource(id = R.string.no_email_go_back_to_edit_your_email_address),
|
||||
highlightText = stringResource(id = R.string.go_back)
|
||||
)
|
||||
ClickableText(
|
||||
text = goBackAnnotatedString,
|
||||
onClick = {
|
||||
goBackAnnotatedString
|
||||
.getStringAnnotations("URL", it, it)
|
||||
.firstOrNull()?.let {
|
||||
viewModel.trySendAction(CheckEmailAction.CloseTap)
|
||||
}
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
val logInAnnotatedString = CreateAnnotatedString(
|
||||
mainText = stringResource(id = R.string.or_log_in_you_may_already_have_an_account),
|
||||
highlightText = stringResource(id = R.string.log_in)
|
||||
)
|
||||
ClickableText(
|
||||
text = logInAnnotatedString,
|
||||
onClick = {
|
||||
logInAnnotatedString
|
||||
.getStringAnnotations("URL", it, it)
|
||||
.firstOrNull()?.let {
|
||||
viewModel.trySendAction(CheckEmailAction.LoginTap)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateAnnotatedString(
|
||||
mainText: String,
|
||||
highlightText: String,
|
||||
mainSpanStyle: SpanStyle = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
),
|
||||
highlightSpanStyle: SpanStyle = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
): AnnotatedString {
|
||||
return buildAnnotatedString {
|
||||
val startIndex = mainText.indexOf(highlightText, ignoreCase = true)
|
||||
val endIndex = startIndex + highlightText.length
|
||||
append(mainText)
|
||||
addStyle(
|
||||
style = mainSpanStyle,
|
||||
start = 0,
|
||||
end = mainText.length
|
||||
)
|
||||
addStyle(
|
||||
style = highlightSpanStyle,
|
||||
start = startIndex,
|
||||
end = endIndex
|
||||
)
|
||||
addStringAnnotation(
|
||||
tag = "URL",
|
||||
annotation = highlightText,
|
||||
start = startIndex,
|
||||
end = endIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.checkemail
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_STATE = "state"
|
||||
|
||||
/**
|
||||
* Models logic for the check email screen.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
@HiltViewModel
|
||||
class CheckEmailViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : BaseViewModel<CheckEmailState, CheckEmailEvent, CheckEmailAction>(
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
?: CheckEmailState(
|
||||
email = CheckEmailArgs(savedStateHandle).emailAddress
|
||||
),
|
||||
) {
|
||||
init {
|
||||
// As state updates, write to saved state handle:
|
||||
stateFlow
|
||||
.onEach { savedStateHandle[KEY_STATE] = it }
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
override fun handleAction(action: CheckEmailAction) {
|
||||
when (action) {
|
||||
CheckEmailAction.CloseTap -> sendEvent(CheckEmailEvent.NavigateBack)
|
||||
CheckEmailAction.LoginTap -> sendEvent(CheckEmailEvent.NavigateBackToLanding)
|
||||
CheckEmailAction.OpenEmailTap -> sendEvent(CheckEmailEvent.NavigateToEmailApp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UI state for the check email screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class CheckEmailState(
|
||||
val email: String
|
||||
) : Parcelable {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the check email screen.
|
||||
*/
|
||||
sealed class CheckEmailEvent {
|
||||
|
||||
/**
|
||||
* Navigate back to previous screen.
|
||||
*/
|
||||
data object NavigateBack : CheckEmailEvent()
|
||||
|
||||
/**
|
||||
* Navigate to email app.
|
||||
*/
|
||||
data object NavigateToEmailApp : CheckEmailEvent()
|
||||
|
||||
/**
|
||||
* Navigate to email app.
|
||||
*/
|
||||
data object NavigateBackToLanding : CheckEmailEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the check email screen.
|
||||
*/
|
||||
sealed class CheckEmailAction {
|
||||
/**
|
||||
* User tapped close.
|
||||
*/
|
||||
data object CloseTap : CheckEmailAction()
|
||||
|
||||
/**
|
||||
* User tapped log in.
|
||||
*/
|
||||
data object LoginTap : CheckEmailAction()
|
||||
|
||||
/**
|
||||
* User tapped open email.
|
||||
*/
|
||||
data object OpenEmailTap : CheckEmailAction()
|
||||
}
|
||||
@@ -57,7 +57,7 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
|
||||
/**
|
||||
* Top level composable for the create account screen.
|
||||
* Top level composable for the complete registration screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("LongMethod")
|
||||
|
||||
@@ -350,7 +350,7 @@ class CompleteRegistrationViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* UI state for the create account screen.
|
||||
* UI state for the complete registration screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class CompleteRegistrationState(
|
||||
@@ -394,7 +394,7 @@ data class CompleteRegistrationState(
|
||||
}
|
||||
|
||||
/**
|
||||
* Models dialogs that can be displayed on the create account screen.
|
||||
* Models dialogs that can be displayed on the complete registration screen.
|
||||
*/
|
||||
sealed class CompleteRegistrationDialog : Parcelable {
|
||||
/**
|
||||
@@ -423,7 +423,7 @@ sealed class CompleteRegistrationDialog : Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the create account screen.
|
||||
* Models events for the complete registration screen.
|
||||
*/
|
||||
sealed class CompleteRegistrationEvent {
|
||||
|
||||
@@ -452,7 +452,7 @@ sealed class CompleteRegistrationEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the create account screen.
|
||||
* Models actions for the complete registration screen.
|
||||
*/
|
||||
sealed class CompleteRegistrationAction {
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.createaccount
|
||||
package com.x8bit.bitwarden.ui.auth.feature.completeregistration
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
@@ -49,6 +49,7 @@ import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.auth.feature.completeregistration.PasswordStrengthIndicator
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.AcceptPoliciesToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.CheckDataBreachesToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.CloseClick
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.PasswordStrengthResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||
import com.x8bit.bitwarden.ui.auth.feature.completeregistration.PasswordStrengthState
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.AcceptPoliciesToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.CheckDataBreachesToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.ConfirmPasswordInputChange
|
||||
|
||||
@@ -24,6 +24,7 @@ fun NavGraphBuilder.startRegistrationDestination(
|
||||
verificationToken: String,
|
||||
captchaToken: String
|
||||
) -> Unit,
|
||||
onNavigateToCheckEmail: (email: String) -> Unit,
|
||||
onNavigateToEnvironment: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
@@ -32,6 +33,7 @@ fun NavGraphBuilder.startRegistrationDestination(
|
||||
StartRegistrationScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToCompleteRegistration = onNavigateToCompleteRegistration,
|
||||
onNavigateToCheckEmail = onNavigateToCheckEmail,
|
||||
onNavigateToEnvironment = onNavigateToEnvironment,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
|
||||
/**
|
||||
* Top level composable for the create account screen.
|
||||
* Top level composable for the start registration screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("LongMethod")
|
||||
@@ -80,6 +80,7 @@ fun StartRegistrationScreen(
|
||||
emailAddress: String,
|
||||
verificationToken: String,
|
||||
captchaToken: String) -> Unit,
|
||||
onNavigateToCheckEmail: (email: String) -> Unit,
|
||||
onNavigateToEnvironment: () -> Unit,
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
viewModel: StartRegistrationViewModel = hiltViewModel(),
|
||||
@@ -117,6 +118,12 @@ fun StartRegistrationScreen(
|
||||
)
|
||||
}
|
||||
|
||||
is StartRegistrationEvent.NavigateToCheckEmail -> {
|
||||
onNavigateToCheckEmail(
|
||||
event.email
|
||||
)
|
||||
}
|
||||
|
||||
StartRegistrationEvent.NavigateToEnvironment -> onNavigateToEnvironment()
|
||||
}
|
||||
}
|
||||
@@ -278,8 +285,6 @@ private fun TermsAndPrivacyText(
|
||||
start = startIndexPrivacy,
|
||||
end = endIndexPrivacy
|
||||
)
|
||||
|
||||
// attach a string annotation that stores a URL to the text "link"
|
||||
addStringAnnotation(
|
||||
tag = "URL",
|
||||
annotation = strTerms,
|
||||
@@ -292,7 +297,6 @@ private fun TermsAndPrivacyText(
|
||||
start = startIndexPrivacy,
|
||||
end = endIndexPrivacy
|
||||
)
|
||||
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
@@ -323,7 +327,6 @@ private fun TermsAndPrivacyText(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ReceiveMarketingEmailsSwitch(
|
||||
@@ -334,7 +337,7 @@ private fun ReceiveMarketingEmailsSwitch(
|
||||
val annotatedLinkString: AnnotatedString = buildAnnotatedString {
|
||||
val strMarketingEmail = stringResource(id = R.string.get_emails_from_bitwarden_for_announcements_advices_and_research_opportunities_unsubscribe_any_time)
|
||||
val strUnsubscribe = stringResource(id = R.string.unsubscribe)
|
||||
val startIndexUnsubscribe = strMarketingEmail.indexOf(strUnsubscribe)
|
||||
val startIndexUnsubscribe = strMarketingEmail.indexOf(strUnsubscribe, ignoreCase = true)
|
||||
val endIndexUnsubscribe = startIndexUnsubscribe + strUnsubscribe.length
|
||||
append(strMarketingEmail)
|
||||
addStyle(
|
||||
|
||||
@@ -31,7 +31,7 @@ import javax.inject.Inject
|
||||
private const val KEY_STATE = "state"
|
||||
|
||||
/**
|
||||
* Models logic for the create account screen.
|
||||
* Models logic for the start registration screen.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
@HiltViewModel
|
||||
@@ -98,8 +98,8 @@ class StartRegistrationViewModel @Inject constructor(
|
||||
|
||||
is StartRegistrationAction.EnvironmentTypeSelect -> handleEnvironmentTypeSelect(action)
|
||||
is StartRegistrationAction.Internal.UpdatedEnvironmentReceive -> {
|
||||
handleUpdatedEnvironmentReceive(action)
|
||||
}
|
||||
handleUpdatedEnvironmentReceive(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,11 +234,16 @@ class StartRegistrationViewModel @Inject constructor(
|
||||
*/
|
||||
|
||||
viewModelScope.launch {
|
||||
sendEvent(StartRegistrationEvent.NavigateToCompleteRegistration(
|
||||
email = state.emailInput,
|
||||
verificationToken = "",
|
||||
captchaToken = ""
|
||||
))
|
||||
if (environmentRepository.environment.type == Environment.Type.US || environmentRepository.environment.type == Environment.Type.EU)
|
||||
sendEvent(StartRegistrationEvent.NavigateToCheckEmail(
|
||||
email = state.emailInput
|
||||
))
|
||||
else
|
||||
sendEvent(StartRegistrationEvent.NavigateToCompleteRegistration(
|
||||
email = state.emailInput,
|
||||
verificationToken = "",
|
||||
captchaToken = ""
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,7 +273,7 @@ class StartRegistrationViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* UI state for the create account screen.
|
||||
* UI state for the start registration screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class StartRegistrationState(
|
||||
@@ -283,7 +288,7 @@ data class StartRegistrationState(
|
||||
}
|
||||
|
||||
/**
|
||||
* Models dialogs that can be displayed on the create account screen.
|
||||
* Models dialogs that can be displayed on the start registration screen.
|
||||
*/
|
||||
sealed class StartRegistrationDialog : Parcelable {
|
||||
/**
|
||||
@@ -300,7 +305,7 @@ sealed class StartRegistrationDialog : Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the create account screen.
|
||||
* Models events for the start registration screen.
|
||||
*/
|
||||
sealed class StartRegistrationEvent {
|
||||
|
||||
@@ -328,6 +333,13 @@ sealed class StartRegistrationEvent {
|
||||
val captchaToken: String,
|
||||
) : StartRegistrationEvent()
|
||||
|
||||
/**
|
||||
* Navigates to the complete registration screen.
|
||||
*/
|
||||
data class NavigateToCheckEmail(
|
||||
val email: String
|
||||
) : StartRegistrationEvent()
|
||||
|
||||
/**
|
||||
* Navigate to terms and conditions.
|
||||
*/
|
||||
@@ -350,7 +362,7 @@ sealed class StartRegistrationEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the create account screen.
|
||||
* Models actions for the start registration screen.
|
||||
*/
|
||||
sealed class StartRegistrationAction {
|
||||
/**
|
||||
|
||||
84
app/src/main/res/drawable/email_check.xml
Normal file
84
app/src/main/res/drawable/email_check.xml
Normal file
@@ -0,0 +1,84 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="413dp"
|
||||
android:height="114dp"
|
||||
android:viewportWidth="413"
|
||||
android:viewportHeight="114">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M134.84,0.57h143.82v112.71h-143.82z"/>
|
||||
<path
|
||||
android:pathData="M260.14,44.62V59.04M192.86,14.74L201.04,8.64C204.02,6.42 208.12,6.45 211.07,8.73L212.84,10.09M164.61,35.82L156.15,42.14C154.05,43.7 152.82,46.17 152.82,48.79V100.48C152.82,105.07 156.53,108.78 161.11,108.78H251.85C256.43,108.78 260.14,105.07 260.14,100.48V71.05"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M165.38,54.52V17.83C165.38,16.3 166.61,15.07 168.14,15.07H206.8"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M175.32,27.21H194.11"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M175.32,38.39H193.56"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M175.32,49.57H196.57"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M175.32,60.75H203.13"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M257.86,106.5L223.49,74.77C220.93,72.41 217.59,71.1 214.11,71.1H197.24C193.65,71.1 190.2,72.5 187.62,75L155.1,106.5"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"/>
|
||||
<path
|
||||
android:pathData="M220.36,71.58L231.04,65.46"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M153.86,48.45L192.59,71.58"
|
||||
android:strokeWidth="2.77"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M260.35,35.09C260.35,51.78 246.82,65.3 230.13,65.3C213.45,65.3 199.92,51.78 199.92,35.09C199.92,18.4 213.45,4.88 230.13,4.88C246.82,4.88 260.35,18.4 260.35,35.09Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M256.3,35.09C256.3,49.44 244.53,61.07 230.02,61.07M230.02,9.11C215.5,9.11 203.73,20.74 203.73,35.09"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.38289"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M254.25,53.55L258.87,58.17L276.21,75.51C277.56,76.86 277.56,79.05 276.21,80.4L275.49,81.12C274.14,82.47 271.95,82.47 270.6,81.12L253.26,63.78L248.64,59.16"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.76577"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#175DDC"
|
||||
android:strokeLineCap="round"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -918,5 +918,10 @@ Do you want to switch to this account?</string>
|
||||
<string name="by_continuing_you_agree_to_the_terms_of_service_and_privacy_policy">By continuing, you agree to the Terms of Service and Privacy Policy</string>
|
||||
<string name="set_password">Set password</string>
|
||||
<string name="unsubscribe">Unsubscribe</string>
|
||||
<string name="check_your_email">Check your email</string>
|
||||
<string name="open_email_app">Open email app</string>
|
||||
<string name="go_back">Go back</string>
|
||||
<string name="no_email_go_back_to_edit_your_email_address">No email? Go back to edit your email address.</string>
|
||||
<string name="or_log_in_you_may_already_have_an_account">Or log in, you may already have an account.</string>
|
||||
<string name="get_emails_from_bitwarden_for_announcements_advices_and_research_opportunities_unsubscribe_any_time">Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time.</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user