Session verification: add new screen to get ready on the other session.

This commit is contained in:
Benoit Marty 2024-11-07 12:16:05 +01:00
parent 4952c2f89f
commit b8d6c47532
7 changed files with 41 additions and 5 deletions

View file

@ -104,6 +104,7 @@ class VerifySelfSessionPresenter @AssistedInject constructor(
fun handleEvents(event: VerifySelfSessionViewEvents) { fun handleEvents(event: VerifySelfSessionViewEvents) {
Timber.d("Verification user action: ${event::class.simpleName}") Timber.d("Verification user action: ${event::class.simpleName}")
when (event) { when (event) {
VerifySelfSessionViewEvents.UseAnotherDevice -> stateAndDispatch.dispatchAction(StateMachineEvent.UseAnotherDevice)
VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification) VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification)
VerifySelfSessionViewEvents.StartSasVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.StartSasVerification) VerifySelfSessionViewEvents.StartSasVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.StartSasVerification)
VerifySelfSessionViewEvents.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge) VerifySelfSessionViewEvents.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge)
@ -134,6 +135,9 @@ class VerifySelfSessionPresenter @AssistedInject constructor(
isLastDevice = encryptionService.isLastDevice.value isLastDevice = encryptionService.isLastDevice.value
) )
} }
VerifySelfSessionStateMachine.State.UseAnotherDevice -> {
VerifySelfSessionState.Step.UseAnotherDevice
}
StateMachineState.RequestingVerification, StateMachineState.RequestingVerification,
StateMachineState.StartingSasVerification, StateMachineState.StartingSasVerification,
StateMachineState.SasVerificationStarted, StateMachineState.SasVerificationStarted,

View file

@ -26,6 +26,7 @@ data class VerifySelfSessionState(
// FIXME canEnterRecoveryKey value is never read. // FIXME canEnterRecoveryKey value is never read.
data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : Step data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : Step
data object UseAnotherDevice : Step
data object Canceled : Step data object Canceled : Step
data object AwaitingOtherDeviceResponse : Step data object AwaitingOtherDeviceResponse : Step
data object Ready : Step data object Ready : Step

View file

@ -38,12 +38,14 @@ class VerifySelfSessionStateMachine @Inject constructor(
init { init {
spec { spec {
inState<State.Initial> { inState<State.Initial> {
on { _: Event.UseAnotherDevice, state ->
state.override { State.UseAnotherDevice.andLogStateChange() }
}
}
inState<State.UseAnotherDevice> {
on { _: Event.RequestVerification, state -> on { _: Event.RequestVerification, state ->
state.override { State.RequestingVerification.andLogStateChange() } state.override { State.RequestingVerification.andLogStateChange() }
} }
on { _: Event.StartSasVerification, state ->
state.override { State.StartingSasVerification.andLogStateChange() }
}
} }
inState<State.RequestingVerification> { inState<State.RequestingVerification> {
onEnterEffect { onEnterEffect {
@ -119,6 +121,7 @@ class VerifySelfSessionStateMachine @Inject constructor(
on { _: Event.Cancel, state: MachineState<State> -> on { _: Event.Cancel, state: MachineState<State> ->
when (state.snapshot) { when (state.snapshot) {
State.Initial, State.Completed, State.Canceled -> state.noChange() State.Initial, State.Completed, State.Canceled -> state.noChange()
State.UseAnotherDevice -> state.override { State.Initial.andLogStateChange() }
// For some reason `cancelVerification` is not calling its delegate `didCancel` method so we don't pass from // For some reason `cancelVerification` is not calling its delegate `didCancel` method so we don't pass from
// `Canceling` state to `Canceled` automatically anymore // `Canceling` state to `Canceled` automatically anymore
else -> { else -> {
@ -144,6 +147,9 @@ class VerifySelfSessionStateMachine @Inject constructor(
/** The initial state, before verification started. */ /** The initial state, before verification started. */
data object Initial : State data object Initial : State
/** Let the user know that they need to get ready on their other session. */
data object UseAnotherDevice : State
/** Waiting for verification acceptance. */ /** Waiting for verification acceptance. */
data object RequestingVerification : State data object RequestingVerification : State
@ -175,6 +181,9 @@ class VerifySelfSessionStateMachine @Inject constructor(
} }
sealed interface Event { sealed interface Event {
/** User wants to use another session. */
data object UseAnotherDevice : Event
/** Request verification. */ /** Request verification. */
data object RequestVerification : Event data object RequestVerification : Event

View file

@ -56,6 +56,9 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS
aVerifySelfSessionState( aVerifySelfSessionState(
step = Step.Skipped step = Step.Skipped
), ),
aVerifySelfSessionState(
step = Step.UseAnotherDevice
),
// Add other state here // Add other state here
) )
} }

View file

@ -66,7 +66,9 @@ fun VerifySelfSessionView(
fun cancelOrResetFlow() { fun cancelOrResetFlow() {
when (step) { when (step) {
is Step.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset) is Step.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset)
is Step.AwaitingOtherDeviceResponse, Step.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel) is Step.AwaitingOtherDeviceResponse,
Step.UseAnotherDevice,
Step.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel)
is Step.Verifying -> { is Step.Verifying -> {
if (!step.state.isLoading()) { if (!step.state.isLoading()) {
state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) state.eventSink(VerifySelfSessionViewEvents.DeclineVerification)
@ -159,6 +161,7 @@ fun VerifySelfSessionView(
private fun VerifySelfSessionHeader(step: Step) { private fun VerifySelfSessionHeader(step: Step) {
val iconStyle = when (step) { val iconStyle = when (step) {
Step.Loading -> error("Should not happen") Step.Loading -> error("Should not happen")
Step.UseAnotherDevice -> BigIcon.Style.Default(CompoundIcons.Devices())
is Step.Initial, Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.LockSolid()) is Step.Initial, Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.LockSolid())
Step.Canceled -> BigIcon.Style.AlertSolid Step.Canceled -> BigIcon.Style.AlertSolid
Step.Ready, is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) Step.Ready, is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction())
@ -167,6 +170,7 @@ private fun VerifySelfSessionHeader(step: Step) {
} }
val titleTextId = when (step) { val titleTextId = when (step) {
Step.Loading -> error("Should not happen") Step.Loading -> error("Should not happen")
Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_title
is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_title is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_title
Step.Canceled -> CommonStrings.common_verification_cancelled Step.Canceled -> CommonStrings.common_verification_cancelled
Step.Ready -> R.string.screen_session_verification_compare_emojis_title Step.Ready -> R.string.screen_session_verification_compare_emojis_title
@ -179,6 +183,7 @@ private fun VerifySelfSessionHeader(step: Step) {
} }
val subtitleTextId = when (step) { val subtitleTextId = when (step) {
Step.Loading -> error("Should not happen") Step.Loading -> error("Should not happen")
Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_subtitle
is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_subtitle is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_subtitle
Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle
Step.Ready -> R.string.screen_session_verification_ready_subtitle Step.Ready -> R.string.screen_session_verification_ready_subtitle
@ -252,7 +257,7 @@ private fun VerifySelfSessionBottomMenu(
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.screen_identity_use_another_device), text = stringResource(R.string.screen_identity_use_another_device),
onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, onClick = { eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) },
) )
} }
Button( Button(
@ -267,6 +272,17 @@ private fun VerifySelfSessionBottomMenu(
) )
} }
} }
is Step.UseAnotherDevice -> {
VerificationBottomMenu {
Button(
modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_start_verification),
onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) },
)
// Placeholder so the 1st button keeps its vertical position
Spacer(modifier = Modifier.height(40.dp))
}
}
is Step.Canceled -> { is Step.Canceled -> {
VerificationBottomMenu { VerificationBottomMenu {
Button( Button(

View file

@ -8,6 +8,7 @@
package io.element.android.features.verifysession.impl.outgoing package io.element.android.features.verifysession.impl.outgoing
sealed interface VerifySelfSessionViewEvents { sealed interface VerifySelfSessionViewEvents {
data object UseAnotherDevice : VerifySelfSessionViewEvents
data object RequestVerification : VerifySelfSessionViewEvents data object RequestVerification : VerifySelfSessionViewEvents
data object StartSasVerification : VerifySelfSessionViewEvents data object StartSasVerification : VerifySelfSessionViewEvents
data object ConfirmVerification : VerifySelfSessionViewEvents data object ConfirmVerification : VerifySelfSessionViewEvents

View file

@ -35,6 +35,8 @@
<string name="screen_session_verification_request_title">"Verification requested"</string> <string name="screen_session_verification_request_title">"Verification requested"</string>
<string name="screen_session_verification_they_dont_match">"They dont match"</string> <string name="screen_session_verification_they_dont_match">"They dont match"</string>
<string name="screen_session_verification_they_match">"They match"</string> <string name="screen_session_verification_they_match">"They match"</string>
<string name="screen_session_verification_use_another_device_subtitle">"Make sure you have the app open in the other device before starting verification from here."</string>
<string name="screen_session_verification_use_another_device_title">"Open the app on another verified device"</string>
<string name="screen_session_verification_waiting_to_accept_subtitle">"Accept the request to start the verification process in your other session to continue."</string> <string name="screen_session_verification_waiting_to_accept_subtitle">"Accept the request to start the verification process in your other session to continue."</string>
<string name="screen_session_verification_waiting_to_accept_title">"Waiting to accept request"</string> <string name="screen_session_verification_waiting_to_accept_title">"Waiting to accept request"</string>
<string name="screen_signout_in_progress_dialog_content">"Signing out…"</string> <string name="screen_signout_in_progress_dialog_content">"Signing out…"</string>