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

Issue:
 Device verification success screen appears after a fresh new registration #3126 
### Steps to reproduce

1. Select a HS with a MAS accepting registration (ask if you do not know one)
2. Create an account

<details>
<summary>The device verification success screen unexpectly appears</summary>
<img src="https://github.com/user-attachments/assets/0787482b-6a15-4237-a901-a4a93b27f717"/>
</details>




### Outcome

#### What did you expect?
Enteting within the app and seeing an empty room list.

#### What happened instead?
I had to hit to Continue instead


### Your phone model

_No response_

### Operating system version

_No response_

### Application version and app store

_No response_

### Homeserver

_No response_

### Will you send logs?

Yes

### Are you willing to provide a PR?

Yes


Discussion hints:
It is very similar as https://github.com/element-hq/element-x-ios/issues/3126 but less impactful. It is still a bad FTUE and we need to fix it.
I cannot enter the app because of this "Device verified" screen! When is this going to be fixed?

Pull request: Skip device verification screen when creating a new account using OIDC
<!-- 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 -->
It can happen that the session is automatically verified and in this case the FTUE verification step has to be skipped.

This PR handle this.

Note that some shortcut had been taken to show the confirmation screen when the user enter a recovery key, so I had to add a parameter `showDeviceVerifiedScreen` to handle this case properly.

## Motivation and context

<!-- Provide link to the corresponding issue if applicable or explain the context -->

Closes #3279

## 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|
|-|-|
|||
 -->

## Tests

<!-- Explain how you tested your development -->

- Login using OIDC, creating a new account
- The device verification screen must be skipped. A loader can be displayed for a short moment.

Note that I have seen the "Confirm your identity" for a few millis, it seems that the SDK emit a `SessionVerifiedStatus.NotVerified` between the `SessionVerifiedStatus.Unknown` and `SessionVerifiedStatus.Verified`, but I need to check again befor reporting to the SDK.

## 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.VerifySelfSessionPresenterTest"

Fix patch:
diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt
index 3e4ed218a5a..694819a2dc0 100644
--- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt
+++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt
@@ -46,7 +46,7 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
     private val secureBackupEntryPoint: SecureBackupEntryPoint,
 ) : BaseFlowNode<FtueSessionVerificationFlowNode.NavTarget>(
     backstack = BackStack(
-        initialElement = NavTarget.Root,
+        initialElement = NavTarget.Root(showDeviceVerifiedScreen = false),
         savedStateMap = buildContext.savedStateMap,
     ),
     buildContext = buildContext,
@@ -54,7 +54,7 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
 ) {
     sealed interface NavTarget : Parcelable {
         @Parcelize
-        data object Root : NavTarget
+        data class Root(val showDeviceVerifiedScreen: Boolean) : NavTarget
 
         @Parcelize
         data object EnterRecoveryKey : NavTarget
@@ -71,7 +71,7 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
         override fun onDone() {
             lifecycleScope.launch {
                 // Move to the completed state view in the verification flow
-                backstack.newRoot(NavTarget.Root)
+                backstack.newRoot(NavTarget.Root(showDeviceVerifiedScreen = true))
             }
         }
     }
@@ -80,6 +80,7 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
         return when (navTarget) {
             is NavTarget.Root -> {
                 verifySessionEntryPoint.nodeBuilder(this, buildContext)
+                    .params(VerifySessionEntryPoint.Params(navTarget.showDeviceVerifiedScreen))
                     .callback(object : VerifySessionEntryPoint.Callback {
                         override fun onEnterRecoveryKey() {
                             backstack.push(NavTarget.EnterRecoveryKey)
diff --git a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt
index deb5cdf2672..46ec8647b6c 100644
--- a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt
+++ b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt
@@ -20,12 +20,16 @@ import com.bumble.appyx.core.modality.BuildContext
 import com.bumble.appyx.core.node.Node
 import com.bumble.appyx.core.plugin.Plugin
 import io.element.android.libraries.architecture.FeatureEntryPoint
+import io.element.android.libraries.architecture.NodeInputs
 
 interface VerifySessionEntryPoint : FeatureEntryPoint {
+    data class Params(val showDeviceVerifiedScreen: Boolean) : NodeInputs
+
     fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
 
     interface NodeBuilder {
         fun callback(callback: Callback): NodeBuilder
+        fun params(params: Params): NodeBuilder
         fun build(): Node
     }
 
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/DefaultVerifySessionEntryPoint.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/DefaultVerifySessionEntryPoint.kt
index b514742a874..ce9136c7601 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/DefaultVerifySessionEntryPoint.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/DefaultVerifySessionEntryPoint.kt
@@ -36,6 +36,11 @@ class DefaultVerifySessionEntryPoint @Inject constructor() : VerifySessionEntryP
                 return this
             }
 
+            override fun params(params: VerifySessionEntryPoint.Params): VerifySessionEntryPoint.NodeBuilder {
+                plugins += params
+                return this
+            }
+
             override fun build(): Node {
                 return parentNode.createNode<VerifySelfSessionNode>(buildContext, plugins)
             }
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt
index 72640d3dbc4..6ca8ecf261c 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt
@@ -30,16 +30,21 @@ import io.element.android.anvilannotations.ContributesNode
 import io.element.android.compound.theme.ElementTheme
 import io.element.android.features.logout.api.util.onSuccessLogout
 import io.element.android.features.verifysession.api.VerifySessionEntryPoint
+import io.element.android.libraries.architecture.inputs
 import io.element.android.libraries.di.SessionScope
 
 @ContributesNode(SessionScope::class)
 class VerifySelfSessionNode @AssistedInject constructor(
     @Assisted buildContext: BuildContext,
     @Assisted plugins: List<Plugin>,
-    private val presenter: VerifySelfSessionPresenter,
+    presenterFactory: VerifySelfSessionPresenter.Factory,
 ) : Node(buildContext, plugins = plugins) {
     private val callback = plugins<VerifySessionEntryPoint.Callback>().first()
 
+    private val presenter = presenterFactory.create(
+        showDeviceVerifiedScreen = inputs<VerifySessionEntryPoint.Params>().showDeviceVerifiedScreen,
+    )
+
     @Composable
     override fun View(modifier: Modifier) {
         val state = presenter.present()
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt
index 03da02e4b33..54c21e67ee4 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt
@@ -28,6 +28,9 @@ import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import com.freeletics.flowredux.compose.rememberStateAndDispatch
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import io.element.android.features.logout.api.LogoutUseCase
 import io.element.android.libraries.architecture.AsyncAction
 import io.element.android.libraries.architecture.AsyncData
@@ -37,6 +40,7 @@ import io.element.android.libraries.core.meta.BuildMeta
 import io.element.android.libraries.matrix.api.encryption.EncryptionService
 import io.element.android.libraries.matrix.api.encryption.RecoveryState
 import io.element.android.libraries.matrix.api.verification.SessionVerificationService
+import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
 import io.element.android.libraries.matrix.api.verification.VerificationFlowState
 import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
 import kotlinx.coroutines.CoroutineScope
@@ -44,11 +48,11 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
-import javax.inject.Inject
 import io.element.android.features.verifysession.impl.VerifySelfSessionStateMachine.Event as StateMachineEvent
 import io.element.android.features.verifysession.impl.VerifySelfSessionStateMachine.State as StateMachineState
 
-class VerifySelfSessionPresenter @Inject constructor(
+class VerifySelfSessionPresenter @AssistedInject constructor(
+    @Assisted private val showDeviceVerifiedScreen: Boolean,
     private val sessionVerificationService: SessionVerificationService,
     private val encryptionService: EncryptionService,
     private val stateMachine: VerifySelfSessionStateMachine,
@@ -56,6 +60,11 @@ class VerifySelfSessionPresenter @Inject constructor(
     private val sessionPreferencesStore: SessionPreferencesStore,
     private val logoutUseCase: LogoutUseCase,
 ) : Presenter<VerifySelfSessionState> {
+    @AssistedFactory
+    interface Factory {
+        fun create(showDeviceVerifiedScreen: Boolean): VerifySelfSessionPresenter
+    }
+
     @Composable
     override fun present(): VerifySelfSessionState {
         val coroutineScope = rememberCoroutineScope()
@@ -66,18 +75,32 @@ class VerifySelfSessionPresenter @Inject constructor(
         val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
         val stateAndDispatch = stateMachine.rememberStateAndDispatch()
         val skipVerification by sessionPreferencesStore.isSessionVerificationSkipped().collectAsState(initial = false)
-        val needsVerification by sessionVerificationService.needsSessionVerification.collectAsState(initial = true)
+        val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
         val signOutAction = remember {
             mutableStateOf<AsyncAction<String?>>(AsyncAction.Uninitialized)
         }
         val verificationFlowStep by remember {
             derivedStateOf {
-                when {
-                    skipVerification -> VerifySelfSessionState.VerificationStep.Skipped
-                    needsVerification -> stateAndDispatch.state.value.toVerificationStep(
-                        canEnterRecoveryKey = recoveryState == RecoveryState.INCOMPLETE
-                    )
-                    else -> VerifySelfSessionState.VerificationStep.Completed
+                if (skipVerification) {
+                    VerifySelfSessionState.VerificationStep.Skipped
+                } else {
+                    when (sessionVerifiedStatus) {
+                        SessionVerifiedStatus.Unknown -> VerifySelfSessionState.VerificationStep.Loading
+                        SessionVerifiedStatus.NotVerified -> {
+                            stateAndDispatch.state.value.toVerificationStep(
+                                canEnterRecoveryKey = recoveryState == RecoveryState.INCOMPLETE
+                            )
+                        }
+                        SessionVerifiedStatus.Verified -> {
+                            if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) {
+                                // The user has verified the session, we need to show the success screen
+                                VerifySelfSessionState.VerificationStep.Completed
+                            } else {
+                                // Automatic verification, which can happen on freshly created account, in this case, skip the screen
+                                VerifySelfSessionState.VerificationStep.Skipped
+                            }
+                        }
+                    }
                 }
             }
         }
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt
index aca96eea1c4..2fbef1d8ef2 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt
@@ -31,6 +31,7 @@ data class VerifySelfSessionState(
 ) {
     @Stable
     sealed interface VerificationStep {
+        data object Loading : VerificationStep
         data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : VerificationStep
         data object Canceled : VerificationStep
         data object AwaitingOtherDeviceResponse : VerificationStep
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt
index 379d9b16866..8767fbfbc73 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt
@@ -59,6 +59,12 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS
                 signOutAction = AsyncAction.Loading,
                 displaySkipButton = true,
             ),
+            aVerifySelfSessionState(
+                verificationFlowStep = VerificationStep.Loading
+            ),
+            aVerifySelfSessionState(
+                verificationFlowStep = VerificationStep.Skipped
+            ),
             // Add other state here
         )
 }
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
index 7987272489d..bb5613f3872 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
@@ -19,11 +19,13 @@ package io.element.android.features.verifysession.impl
 import androidx.activity.compose.BackHandler
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
@@ -57,6 +59,7 @@ import io.element.android.libraries.designsystem.components.ProgressDialog
 import io.element.android.libraries.designsystem.preview.ElementPreview
 import io.element.android.libraries.designsystem.preview.PreviewsDayNight
 import io.element.android.libraries.designsystem.theme.components.Button
+import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
 import io.element.android.libraries.designsystem.theme.components.OutlinedButton
 import io.element.android.libraries.designsystem.theme.components.Text
 import io.element.android.libraries.designsystem.theme.components.TextButton
@@ -99,43 +102,55 @@ fun VerifySelfSessionView(
         }
     }
     val verificationFlowStep = state.verificationFlowStep
-    HeaderFooterPage(
-        modifier = modifier,
-        topBar = {
-            TopAppBar(
-                title = {},
-                actions = {
-                    if (state.verificationFlowStep != FlowStep.Completed &&
-                        state.displaySkipButton &&
-                        LocalInspectionMode.current.not()) {
-                        TextButton(
-                            text = stringResource(CommonStrings.action_skip),
-                            onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) }
-                        )
-                    }
-                    if (state.verificationFlowStep is FlowStep.Initial) {
-                        TextButton(
-                            text = stringResource(CommonStrings.action_signout),
-                            onClick = { state.eventSink(VerifySelfSessionViewEvents.SignOut) }
-                        )
+
+    if (state.verificationFlowStep is FlowStep.Loading ||
+        state.verificationFlowStep is FlowStep.Skipped) {
+        // Just display a loader in this case, to avoid UI glitch.
+        Box(
+            modifier = Modifier.fillMaxSize(),
+            contentAlignment = Alignment.Center,
+        ) {
+            CircularProgressIndicator()
+        }
+    } else {
+        HeaderFooterPage(
+            modifier = modifier,
+            topBar = {
+                TopAppBar(
+                    title = {},
+                    actions = {
+                        if (state.verificationFlowStep !is FlowStep.Completed &&
+                            state.displaySkipButton &&
+                            LocalInspectionMode.current.not()) {
+                            TextButton(
+                                text = stringResource(CommonStrings.action_skip),
+                                onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) }
+                            )
+                        }
+                        if (state.verificationFlowStep is FlowStep.Initial) {
+                            TextButton(
+                                text = stringResource(CommonStrings.action_signout),
+                                onClick = { state.eventSink(VerifySelfSessionViewEvents.SignOut) }
+                            )
+                        }
                     }
-                }
-            )
-        },
-        header = {
-            HeaderContent(verificationFlowStep = verificationFlowStep)
-        },
-        footer = {
-            BottomMenu(
-                screenState = state,
-                goBack = ::resetFlow,
-                onEnterRecoveryKey = onEnterRecoveryKey,
-                onFinish = onFinish,
-                onResetKey = onResetKey,
-            )
+                )
+            },
+            header = {
+                HeaderContent(verificationFlowStep = verificationFlowStep)
+            },
+            footer = {
+                BottomMenu(
+                    screenState = state,
+                    goBack = ::resetFlow,
+                    onEnterRecoveryKey = onEnterRecoveryKey,
+                    onFinish = onFinish,
+                    onResetKey = onResetKey,
+                )
+            }
+        ) {
+            Content(flowState = verificationFlowStep)
         }
-    ) {
-        Content(flowState = verificationFlowStep)
     }
 
     when (state.signOutAction) {
@@ -157,6 +172,7 @@ fun VerifySelfSessionView(
 @Composable
 private fun HeaderContent(verificationFlowStep: FlowStep) {
     val iconStyle = when (verificationFlowStep) {
+        VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen")
         is FlowStep.Initial, FlowStep.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.LockSolid())
         FlowStep.Canceled -> BigIcon.Style.AlertSolid
         FlowStep.Ready, is FlowStep.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction())
@@ -164,6 +180,7 @@ private fun HeaderContent(verificationFlowStep: FlowStep) {
         is FlowStep.Skipped -> return
     }
     val titleTextId = when (verificationFlowStep) {
+        VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen")
         is FlowStep.Initial, FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_title
         FlowStep.Canceled -> CommonStrings.common_verification_cancelled
         FlowStep.Ready -> R.string.screen_session_verification_compare_emojis_title
@@ -175,6 +192,7 @@ private fun HeaderContent(verificationFlowStep: FlowStep) {
         is FlowStep.Skipped -> return
     }
     val subtitleTextId = when (verificationFlowStep) {
+        VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen")
         is FlowStep.Initial, FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_subtitle
         FlowStep.Canceled -> R.string.screen_session_verification_cancelled_subtitle
         FlowStep.Ready -> R.string.screen_session_verification_ready_subtitle
@@ -268,6 +286,7 @@ private fun BottomMenu(
     val isVerifying = (verificationViewState as? FlowStep.Verifying)?.state is AsyncData.Loading<Unit>
 
     when (verificationViewState) {
+        VerifySelfSessionState.VerificationStep.Loading -> error("Should not happen")
         is FlowStep.Initial -> {
             BottomMenu {
                 if (verificationViewState.isLastDevice) {


Test patch:
diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt
index c258b9df03b..ca8836ac61b 100644
--- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt
+++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt
@@ -298,21 +298,42 @@ class VerifySelfSessionPresenterTest {
     }
 
     @Test
-    fun `present - When verification is not needed, the flow is completed`() = runTest {
+    fun `present - When verification is done using recovery key, the flow is completed`() = runTest {
         val service = FakeSessionVerificationService().apply {
             givenNeedsSessionVerification(false)
             givenVerifiedStatus(SessionVerifiedStatus.Verified)
             givenVerificationFlowState(VerificationFlowState.Finished)
         }
-        val presenter = createVerifySelfSessionPresenter(service)
+        val presenter = createVerifySelfSessionPresenter(
+            service = service,
+            showDeviceVerifiedScreen = true,
+        )
         moleculeFlow(RecompositionMode.Immediate) {
             presenter.present()
         }.test {
-            skipItems(1)
             assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Completed)
         }
     }
 
+    @Test
+    fun `present - When verification is not needed, the flow is skipped`() = runTest {
+        val service = FakeSessionVerificationService().apply {
+            givenNeedsSessionVerification(false)
+            givenVerifiedStatus(SessionVerifiedStatus.Verified)
+            givenVerificationFlowState(VerificationFlowState.Finished)
+        }
+        val presenter = createVerifySelfSessionPresenter(
+            service = service,
+            showDeviceVerifiedScreen = false,
+        )
+        moleculeFlow(RecompositionMode.Immediate) {
+            presenter.present()
+        }.test {
+            skipItems(1)
+            assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Skipped)
+        }
+    }
+
     @Test
     fun `present - When user request to sign out, the sign out use case is invoked`() = runTest {
         val service = FakeSessionVerificationService().apply {
@@ -374,8 +395,10 @@ class VerifySelfSessionPresenterTest {
         buildMeta: BuildMeta = aBuildMeta(),
         sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(),
         logoutUseCase: LogoutUseCase = FakeLogoutUseCase(),
+        showDeviceVerifiedScreen: Boolean = false,
     ): VerifySelfSessionPresenter {
         return VerifySelfSessionPresenter(
+            showDeviceVerifiedScreen = showDeviceVerifiedScreen,
             sessionVerificationService = service,
             encryptionService = encryptionService,
             stateMachine = VerifySelfSessionStateMachine(service, encryptionService),
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_11_en.png
new file mode 100644
index 00000000000..87845f860cb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_11_en.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5af3e9514bfa603491a2ba225260c8eceaa2156c255da83c547c59d50388953b
+size 5149
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_12_en.png
new file mode 100644
index 00000000000..87845f860cb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_12_en.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5af3e9514bfa603491a2ba225260c8eceaa2156c255da83c547c59d50388953b
+size 5149
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_11_en.png
new file mode 100644
index 00000000000..209ecadfe9f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_11_en.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fef553ca190fc051f6b03946b4de2f3066600da2b2b8e8443a9c83cd7fbf45a4
+size 5153
diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_12_en.png
new file mode 100644
index 00000000000..209ecadfe9f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_12_en.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fef553ca190fc051f6b03946b4de2f3066600da2b2b8e8443a9c83cd7fbf45a4
+size 5153