Repository: element-hq/element-x-android (language: kotlin)

Issue:
Incoming verification iteration
- [ ] Verification cancelled: show the error screen.
- [x] Add new intermediate screen:
<img width="352" alt="image" src="https://github.com/user-attachments/assets/3d546b25-2e19-4065-a3df-d6890578259f">

- [ ] Check this screen copy:
<img width="374" alt="image" src="https://github.com/user-attachments/assets/87de5e2f-f4bb-4b60-8307-a154b671609b">



Discussion hints:
nan

Pull request: Verification UI / UX iteration
<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
 
## Content

<!-- Describe shortly what has been changed -->
Iterate on a few screens of the verification flows

## Motivation and context

<!-- Provide link to the corresponding issue if applicable or explain the context -->
Closes #3824

## Screenshots / GIFs

<!--
We have screenshot tests in the project, so attaching screenshots to a PR is not mandatory, as far as there
is a Composable Preview covering the changes. In this case, the change will appear in the file diff.
Note that all the UI composables should be covered by a Composable Preview.

Providing a video of the change is still very useful for the reviewer and for the history of the project.

You can use a table like this to show screenshots comparison.
Uncomment this markdown table below and edit the last line `|||`:
|copy screenshot of before here|copy screenshot of after here|

|Before|After|
|-|-|
|||
 -->
See the recorded screenshots

## Tests

<!-- Explain how you tested your development -->

- Perform a login followed by a session verification using another device
- From an other device, perform a session verification flow.

## Tested devices

- [ ] Physical
- [x] Emulator
- OS version(s):

## Checklist

<!-- Depending on the Pull Request content, it can be acceptable if some of the following checkboxes stay unchecked. -->

- [ ] Changes have been tested on an Android device or Android emulator with API 23
- [ ] UI change has been tested on both light and dark themes
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Pull request is based on the develop branch
- [ ] Pull request title will be used in the release note, it clearly define what will change for the user
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off)
- [ ] You've made a self review of your PR


Test command: ./gradlew :features:verifysession:impl:testDebugUnitTest --tests "io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionPresenterTest"

Fix patch:
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt
index cc2c9fbd2d0..e8f098f848e 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt
@@ -80,14 +80,14 @@ fun IncomingVerificationView(
 @Composable
 private fun IncomingVerificationHeader(step: Step) {
     val iconStyle = when (step) {
-        Step.Canceled,
+        Step.Canceled -> BigIcon.Style.AlertSolid
         is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid())
         is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction())
         Step.Completed -> BigIcon.Style.SuccessSolid
         Step.Failure -> BigIcon.Style.AlertSolid
     }
     val titleTextId = when (step) {
-        Step.Canceled -> CommonStrings.common_verification_cancelled
+        Step.Canceled -> R.string.screen_session_verification_request_failure_title
         is Step.Initial -> R.string.screen_session_verification_request_title
         is Step.Verifying -> when (step.data) {
             is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_title
@@ -97,7 +97,7 @@ private fun IncomingVerificationHeader(step: Step) {
         Step.Failure -> R.string.screen_session_verification_request_failure_title
     }
     val subtitleTextId = when (step) {
-        Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle
+        Step.Canceled -> R.string.screen_session_verification_request_failure_subtitle
         is Step.Initial -> R.string.screen_session_verification_request_subtitle
         is Step.Verifying -> when (step.data) {
             is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_subtitle
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt
index 360bf648750..97778b322b1 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt
@@ -104,6 +104,7 @@ class VerifySelfSessionPresenter @AssistedInject constructor(
         fun handleEvents(event: VerifySelfSessionViewEvents) {
             Timber.d("Verification user action: ${event::class.simpleName}")
             when (event) {
+                VerifySelfSessionViewEvents.UseAnotherDevice -> stateAndDispatch.dispatchAction(StateMachineEvent.UseAnotherDevice)
                 VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification)
                 VerifySelfSessionViewEvents.StartSasVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.StartSasVerification)
                 VerifySelfSessionViewEvents.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge)
@@ -134,6 +135,9 @@ class VerifySelfSessionPresenter @AssistedInject constructor(
                     isLastDevice = encryptionService.isLastDevice.value
                 )
             }
+            VerifySelfSessionStateMachine.State.UseAnotherDevice -> {
+                VerifySelfSessionState.Step.UseAnotherDevice
+            }
             StateMachineState.RequestingVerification,
             StateMachineState.StartingSasVerification,
             StateMachineState.SasVerificationStarted,
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt
index e763305caa2..32664b347f0 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt
@@ -26,6 +26,7 @@ data class VerifySelfSessionState(
 
         // FIXME canEnterRecoveryKey value is never read.
         data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : Step
+        data object UseAnotherDevice : Step
         data object Canceled : Step
         data object AwaitingOtherDeviceResponse : Step
         data object Ready : Step
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt
index f423b14aae3..a67cc4d3318 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt
@@ -38,12 +38,14 @@ class VerifySelfSessionStateMachine @Inject constructor(
     init {
         spec {
             inState<State.Initial> {
+                on { _: Event.UseAnotherDevice, state ->
+                    state.override { State.UseAnotherDevice.andLogStateChange() }
+                }
+            }
+            inState<State.UseAnotherDevice> {
                 on { _: Event.RequestVerification, state ->
                     state.override { State.RequestingVerification.andLogStateChange() }
                 }
-                on { _: Event.StartSasVerification, state ->
-                    state.override { State.StartingSasVerification.andLogStateChange() }
-                }
             }
             inState<State.RequestingVerification> {
                 onEnterEffect {
@@ -64,9 +66,6 @@ class VerifySelfSessionStateMachine @Inject constructor(
                 }
             }
             inState<State.Canceled> {
-                on { _: Event.RequestVerification, state ->
-                    state.override { State.RequestingVerification.andLogStateChange() }
-                }
                 on { _: Event.Reset, state ->
                     state.override { State.Initial.andLogStateChange() }
                 }
@@ -119,6 +118,7 @@ class VerifySelfSessionStateMachine @Inject constructor(
                 on { _: Event.Cancel, state: MachineState<State> ->
                     when (state.snapshot) {
                         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
                         // `Canceling` state to `Canceled` automatically anymore
                         else -> {
@@ -144,6 +144,9 @@ class VerifySelfSessionStateMachine @Inject constructor(
         /** The initial state, before verification started. */
         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. */
         data object RequestingVerification : State
 
@@ -175,6 +178,9 @@ class VerifySelfSessionStateMachine @Inject constructor(
     }
 
     sealed interface Event {
+        /** User wants to use another session. */
+        data object UseAnotherDevice : Event
+
         /** Request verification. */
         data object RequestVerification : Event
 
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt
index 8cb60a822f7..b9de96aae48 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt
@@ -56,6 +56,9 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS
             aVerifySelfSessionState(
                 step = Step.Skipped
             ),
+            aVerifySelfSessionState(
+                step = Step.UseAnotherDevice
+            ),
             // Add other state here
         )
 }
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt
index 9bb59daa6f0..afebffea471 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt
@@ -66,7 +66,9 @@ fun VerifySelfSessionView(
     fun cancelOrResetFlow() {
         when (step) {
             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 -> {
                 if (!step.state.isLoading()) {
                     state.eventSink(VerifySelfSessionViewEvents.DeclineVerification)
@@ -159,7 +161,9 @@ fun VerifySelfSessionView(
 private fun VerifySelfSessionHeader(step: Step) {
     val iconStyle = when (step) {
         Step.Loading -> error("Should not happen")
-        is Step.Initial, Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.LockSolid())
+        is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid())
+        Step.UseAnotherDevice -> BigIcon.Style.Default(CompoundIcons.Devices())
+        Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.Devices())
         Step.Canceled -> BigIcon.Style.AlertSolid
         Step.Ready, is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction())
         Step.Completed -> BigIcon.Style.SuccessSolid
@@ -167,8 +171,10 @@ private fun VerifySelfSessionHeader(step: Step) {
     }
     val titleTextId = when (step) {
         Step.Loading -> error("Should not happen")
-        is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_title
-        Step.Canceled -> CommonStrings.common_verification_cancelled
+        is Step.Initial -> R.string.screen_identity_confirmation_title
+        Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_title
+        Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_title
+        Step.Canceled -> CommonStrings.common_verification_failed
         Step.Ready -> R.string.screen_session_verification_compare_emojis_title
         Step.Completed -> R.string.screen_identity_confirmed_title
         is Step.Verifying -> when (step.data) {
@@ -179,8 +185,10 @@ private fun VerifySelfSessionHeader(step: Step) {
     }
     val subtitleTextId = when (step) {
         Step.Loading -> error("Should not happen")
-        is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_subtitle
-        Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle
+        is Step.Initial -> R.string.screen_identity_confirmation_subtitle
+        Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_subtitle
+        Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_subtitle
+        Step.Canceled -> R.string.screen_session_verification_failed_subtitle
         Step.Ready -> R.string.screen_session_verification_ready_subtitle
         Step.Completed -> R.string.screen_identity_confirmed_subtitle
         is Step.Verifying -> when (step.data) {
@@ -252,7 +260,7 @@ private fun VerifySelfSessionBottomMenu(
                     Button(
                         modifier = Modifier.fillMaxWidth(),
                         text = stringResource(R.string.screen_identity_use_another_device),
-                        onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) },
+                        onClick = { eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) },
                     )
                 }
                 Button(
@@ -267,18 +275,26 @@ private fun VerifySelfSessionBottomMenu(
                 )
             }
         }
-        is Step.Canceled -> {
+        is Step.UseAnotherDevice -> {
             VerificationBottomMenu {
                 Button(
                     modifier = Modifier.fillMaxWidth(),
-                    text = stringResource(R.string.screen_session_verification_positive_button_canceled),
+                    text = stringResource(CommonStrings.action_start_verification),
                     onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) },
                 )
-                TextButton(
+                // Placeholder so the 1st button keeps its vertical position
+                Spacer(modifier = Modifier.height(40.dp))
+            }
+        }
+        is Step.Canceled -> {
+            VerificationBottomMenu {
+                Button(
                     modifier = Modifier.fillMaxWidth(),
-                    text = stringResource(CommonStrings.action_cancel),
+                    text = stringResource(CommonStrings.action_done),
                     onClick = onCancelClick,
                 )
+                // Placeholder so the 1st button keeps its vertical position
+                Spacer(modifier = Modifier.height(40.dp))
             }
         }
         is Step.Ready -> {
@@ -302,6 +318,7 @@ private fun VerifySelfSessionBottomMenu(
                     text = stringResource(R.string.screen_identity_waiting_on_other_device),
                     onClick = {},
                     showProgress = true,
+                    enabled = false,
                 )
                 // Placeholder so the 1st button keeps its vertical position
                 Spacer(modifier = Modifier.height(40.dp))
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt
index 869bdc7051b..b4af38f7801 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt
@@ -8,6 +8,7 @@
 package io.element.android.features.verifysession.impl.outgoing
 
 sealed interface VerifySelfSessionViewEvents {
+    data object UseAnotherDevice : VerifySelfSessionViewEvents
     data object RequestVerification : VerifySelfSessionViewEvents
     data object StartSasVerification : VerifySelfSessionViewEvents
     data object ConfirmVerification : VerifySelfSessionViewEvents
diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml
index f67a2024b92..db18590cbe2 100644
--- a/features/verifysession/impl/src/main/res/values/localazy.xml
+++ b/features/verifysession/impl/src/main/res/values/localazy.xml
@@ -35,6 +35,10 @@
   <string name="screen_session_verification_request_title">"Verification requested"</string>
   <string name="screen_session_verification_they_dont_match">"They don’t 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_another_device_subtitle">"You should see a popup on the other device. Start the verification from there now."</string>
+  <string name="screen_session_verification_waiting_another_device_title">"Start verification on the other 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_title">"Waiting to accept request"</string>
   <string name="screen_signout_in_progress_dialog_content">"Signing out…"</string>


Test patch:
diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt
index bfdf27ee5d2..d8fe2f671c5 100644
--- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt
+++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt
@@ -7,10 +7,7 @@
 
 package io.element.android.features.verifysession.impl.outgoing
 
-import app.cash.molecule.RecompositionMode
-import app.cash.molecule.moleculeFlow
 import app.cash.turbine.ReceiveTurbine
-import app.cash.turbine.test
 import com.google.common.truth.Truth.assertThat
 import io.element.android.features.logout.api.LogoutUseCase
 import io.element.android.features.logout.test.FakeLogoutUseCase
@@ -33,6 +30,7 @@ import io.element.android.tests.testutils.WarmUpRule
 import io.element.android.tests.testutils.lambda.lambdaError
 import io.element.android.tests.testutils.lambda.lambdaRecorder
 import io.element.android.tests.testutils.lambda.value
+import io.element.android.tests.testutils.test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.Rule
@@ -48,9 +46,7 @@ class VerifySelfSessionPresenterTest {
         val presenter = createVerifySelfSessionPresenter(
             service = unverifiedSessionService(),
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             awaitItem().run {
                 assertThat(step).isEqualTo(Step.Initial(false))
                 assertThat(displaySkipButton).isTrue()
@@ -65,9 +61,7 @@ class VerifySelfSessionPresenterTest {
             service = unverifiedSessionService(),
             buildMeta = buildMeta,
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             assertThat(awaitItem().displaySkipButton).isFalse()
         }
     }
@@ -83,9 +77,7 @@ class VerifySelfSessionPresenterTest {
                 emitRecoveryState(RecoveryState.INCOMPLETE)
             }
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             assertThat(awaitItem().step).isEqualTo(Step.Initial(true))
             resetLambda.assertions().isCalledOnce().with(value(true))
         }
@@ -100,9 +92,7 @@ class VerifySelfSessionPresenterTest {
                 emitRecoveryState(RecoveryState.INCOMPLETE)
             }
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             assertThat(awaitItem().step).isEqualTo(Step.Initial(canEnterRecoveryKey = true, isLastDevice = true))
         }
     }
@@ -114,43 +104,17 @@ class VerifySelfSessionPresenterTest {
             startVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             requestVerificationAndAwaitVerifyingState(service)
         }
     }
 
-    @Test
-    fun `present - Handles startSasVerification`() = runTest {
-        val service = unverifiedSessionService(
-            startVerificationLambda = { },
-        )
-        val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
-            val initialState = awaitItem()
-            assertThat(initialState.step).isEqualTo(Step.Initial(false))
-            initialState.eventSink(VerifySelfSessionViewEvents.StartSasVerification)
-            // Await for other device response:
-            assertThat(awaitItem().step).isEqualTo(Step.AwaitingOtherDeviceResponse)
-            service.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification)
-            // ChallengeReceived:
-            service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList())))
-            val verifyingState = awaitItem()
-            assertThat(verifyingState.step).isInstanceOf(Step.Verifying::class.java)
-        }
-    }
-
     @Test
     fun `present - Cancellation on initial state does nothing`() = runTest {
         val presenter = createVerifySelfSessionPresenter(
             service = unverifiedSessionService(),
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             val initialState = awaitItem()
             assertThat(initialState.step).isEqualTo(Step.Initial(false))
             val eventSink = initialState.eventSink
@@ -167,9 +131,7 @@ class VerifySelfSessionPresenterTest {
             approveVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             val state = requestVerificationAndAwaitVerifyingState(service)
             state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification)
             // Cancelling
@@ -186,9 +148,8 @@ class VerifySelfSessionPresenterTest {
             requestVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
+            awaitItem().eventSink(VerifySelfSessionViewEvents.UseAnotherDevice)
             awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification)
             service.emitVerificationFlowState(VerificationFlowState.DidFail)
             assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java)
@@ -204,9 +165,7 @@ class VerifySelfSessionPresenterTest {
             cancelVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             val state = requestVerificationAndAwaitVerifyingState(service)
             state.eventSink(VerifySelfSessionViewEvents.Cancel)
             assertThat(awaitItem().step).isEqualTo(Step.Canceled)
@@ -220,35 +179,13 @@ class VerifySelfSessionPresenterTest {
             startVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             requestVerificationAndAwaitVerifyingState(service)
             service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList())))
             ensureAllEventsConsumed()
         }
     }
 
-    @Test
-    fun `present - Restart after cancellation returns to requesting verification`() = runTest {
-        val service = unverifiedSessionService(
-            requestVerificationLambda = { },
-            startVerificationLambda = { },
-        )
-        val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
-            val state = requestVerificationAndAwaitVerifyingState(service)
-            service.emitVerificationFlowState(VerificationFlowState.DidCancel)
-            assertThat(awaitItem().step).isEqualTo(Step.Canceled)
-            state.eventSink(VerifySelfSessionViewEvents.RequestVerification)
-            // Went back to requesting verification
-            assertThat(awaitItem().step).isEqualTo(Step.AwaitingOtherDeviceResponse)
-            cancelAndIgnoreRemainingEvents()
-        }
-    }
-
     @Test
     fun `present - Go back after cancellation returns to initial state`() = runTest {
         val service = unverifiedSessionService(
@@ -256,9 +193,7 @@ class VerifySelfSessionPresenterTest {
             startVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             val state = requestVerificationAndAwaitVerifyingState(service)
             service.emitVerificationFlowState(VerificationFlowState.DidCancel)
             assertThat(awaitItem().step).isEqualTo(Step.Canceled)
@@ -280,9 +215,7 @@ class VerifySelfSessionPresenterTest {
             approveVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             val state = requestVerificationAndAwaitVerifyingState(
                 service,
                 SessionVerificationData.Emojis(emojis)
@@ -307,9 +240,7 @@ class VerifySelfSessionPresenterTest {
             declineVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             val state = requestVerificationAndAwaitVerifyingState(service)
             state.eventSink(VerifySelfSessionViewEvents.DeclineVerification)
             assertThat(awaitItem().step).isEqualTo(
@@ -330,9 +261,7 @@ class VerifySelfSessionPresenterTest {
             startVerificationLambda = { },
         )
         val presenter = createVerifySelfSessionPresenter(service)
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             val state = requestVerificationAndAwaitVerifyingState(service)
             state.eventSink(VerifySelfSessionViewEvents.SkipVerification)
             assertThat(awaitItem().step).isEqualTo(Step.Skipped)
@@ -352,9 +281,7 @@ class VerifySelfSessionPresenterTest {
             service = service,
             showDeviceVerifiedScreen = true,
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             assertThat(awaitItem().step).isEqualTo(Step.Completed)
         }
     }
@@ -372,9 +299,7 @@ class VerifySelfSessionPresenterTest {
             service = service,
             showDeviceVerifiedScreen = false,
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             skipItems(1)
             assertThat(awaitItem().step).isEqualTo(Step.Skipped)
         }
@@ -394,9 +319,7 @@ class VerifySelfSessionPresenterTest {
             service,
             logoutUseCase = FakeLogoutUseCase(signOutLambda)
         )
-        moleculeFlow(RecompositionMode.Immediate) {
-            presenter.present()
-        }.test {
+        presenter.test {
             skipItems(1)
             val initialItem = awaitItem()
             initialItem.eventSink(VerifySelfSessionViewEvents.SignOut)
@@ -414,6 +337,9 @@ class VerifySelfSessionPresenterTest {
     ): VerifySelfSessionState {
         var state = awaitItem()
         assertThat(state.step).isEqualTo(Step.Initial(false))
+        state.eventSink(VerifySelfSessionViewEvents.UseAnotherDevice)
+        state = awaitItem()
+        assertThat(state.step).isEqualTo(Step.UseAnotherDevice)
         state.eventSink(VerifySelfSessionViewEvents.RequestVerification)
         // Await for other device response:
         fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png
index 0aa47d9aa96..1c316959ed7 100644
--- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:bee1454db757b0897ae2113d3a11ed9adef0eb6bc52f3775edac56ed8533a88f
-size 24076
+oid sha256:d3340a2d29e6c1d86f5ec5664179cae9501fcc95381311174d7d6b45b15af326
+size 24123
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png
index 32fa8a3383f..1c46f8eb533 100644
--- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:59a23ec5e3086349d3382f006cf1bcb4121183c83ea8c749aa95e3160561aef1
-size 23524
+oid sha256:c6c7e8cdf40bdf018931635565ac649fed518140a047a71cf70cf63e9edf54d3
+size 23932
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png
new file mode 100644
index 00000000000..567462d90f9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:18dadaebe7a32aacde31afa0352a343913955b099ca4a07851e3ffe75e88b4d6
+size 31012
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png
index 130cc1d84a0..29667b296d6 100644
--- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:2cc647747c41f4c2cf96a3425e8b279b39fd4e09e1441e5b5afcd260f79afaa1
-size 22714
+oid sha256:71b9f32b26b391ff3bf231ca9f364a157f535c1e6ff52ee3e3ead3630bb1b239
+size 30007
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png
index 4e84bcb0c25..1faa04af50c 100644
--- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:a7fe45575eb8423161d355d9b99ecff4acc0f708e5b106d9d38aa2657942d736
-size 28219
+oid sha256:000374157cb5fbf6670b4af041fc385538df78bf4aecaf83ea24d39dfec84f23
+size 24278
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png
new file mode 100644
index 00000000000..bebfa94f6d2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4dd274f8c2ade6213a13a47400ec3a571844eafcc82b292b1c813bd9aa098236
+size 30241
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png
index 7e3a1136101..af8d7eadbee 100644
--- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:786a63d0d824657270b44428a1a251d0d521e6a97a4239516ae5f2eff06fdc3b
-size 22125
+oid sha256:994b39ba25e011cce97504a1f3d4ed0c420b7bce87daafae77ba481cc629cdae
+size 29051
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png
index 76173e1b2d5..d9b8a3a0ded 100644
--- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c29d3f5bbff739d23800345feb42e73fe9374c5cb5db27690278fb22c5546fe3
-size 27754
+oid sha256:3a3f08002e805fe5f7a4c96aa4b73c2fcd6e8b79e6a9e82bcc7bd3df50d8c22d
+size 24015