Update "Learn more" link (#4686)

* Update target link for "Learn more".

* Rename VerifySelfSession* to OutgoingVerification*

* Optimize import

* Refactor to avoid long line

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Benoit Marty 2025-05-09 11:10:30 +02:00 committed by GitHub
parent 8f484c322f
commit 3391e7cc55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 194 additions and 196 deletions

View file

@ -9,6 +9,7 @@ package io.element.android.appconfig
object LearnMoreConfig { object LearnMoreConfig {
const val ENCRYPTION_URL: String = "https://element.io/help#encryption" const val ENCRYPTION_URL: String = "https://element.io/help#encryption"
const val DEVICE_VERIFICATION_URL: String = "https://element.io/help#encryption-device-verification"
const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5" const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5"
const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18" const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18"
} }

View file

@ -1,5 +1,4 @@
import extension.setupAnvil import extension.setupAnvil
import org.gradle.kotlin.dsl.test
/* /*
* Copyright 2023, 2024 New Vector Ltd. * Copyright 2023, 2024 New Vector Ltd.

View file

@ -26,7 +26,7 @@ import io.element.android.anvilannotations.ContributesNode
import io.element.android.appconfig.LearnMoreConfig import io.element.android.appconfig.LearnMoreConfig
import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeNode import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeNode
import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.securebackup.api.SecureBackupEntryPoint
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
@ -40,7 +40,7 @@ import kotlinx.parcelize.Parcelize
class FtueSessionVerificationFlowNode @AssistedInject constructor( class FtueSessionVerificationFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val verifySessionEntryPoint: VerifySessionEntryPoint, private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint,
private val secureBackupEntryPoint: SecureBackupEntryPoint, private val secureBackupEntryPoint: SecureBackupEntryPoint,
) : BaseFlowNode<FtueSessionVerificationFlowNode.NavTarget>( ) : BaseFlowNode<FtueSessionVerificationFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
@ -94,19 +94,19 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
} }
override fun onLearnMoreAboutEncryption() { override fun onLearnMoreAboutEncryption() {
learnMoreUrl.value = LearnMoreConfig.ENCRYPTION_URL learnMoreUrl.value = LearnMoreConfig.DEVICE_VERIFICATION_URL
} }
} }
createNode<ChooseSelfVerificationModeNode>(buildContext, plugins = listOf(callback)) createNode<ChooseSelfVerificationModeNode>(buildContext, plugins = listOf(callback))
} }
is NavTarget.UseAnotherDevice -> { is NavTarget.UseAnotherDevice -> {
verifySessionEntryPoint.nodeBuilder(this, buildContext) outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(VerifySessionEntryPoint.Params( .params(OutgoingVerificationEntryPoint.Params(
showDeviceVerifiedScreen = true, showDeviceVerifiedScreen = true,
verificationRequest = VerificationRequest.Outgoing.CurrentSession, verificationRequest = VerificationRequest.Outgoing.CurrentSession,
)) ))
.callback(object : VerifySessionEntryPoint.Callback { .callback(object : OutgoingVerificationEntryPoint.Callback {
override fun onDone() { override fun onDone() {
plugins<Callback>().forEach { it.onDone() } plugins<Callback>().forEach { it.onDone() }
} }
@ -116,7 +116,8 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
} }
override fun onLearnMoreAboutEncryption() { override fun onLearnMoreAboutEncryption() {
learnMoreUrl.value = LearnMoreConfig.ENCRYPTION_URL // Note that this callback is never called. The "Learn more" link is not displayed
// for the self session interactive verification.
} }
}) })
.build() .build()

View file

@ -25,7 +25,6 @@ import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.test import io.element.android.tests.testutils.test
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test

View file

@ -38,7 +38,7 @@ import io.element.android.features.roomdetails.impl.notificationsettings.RoomNot
import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode
import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyFlowNode import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyFlowNode
import io.element.android.features.userprofile.shared.UserProfileNodeHelper import io.element.android.features.userprofile.shared.UserProfileNodeHelper
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BackstackWithOverlayBox
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
@ -71,7 +71,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint, private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint,
private val mediaViewerEntryPoint: MediaViewerEntryPoint, private val mediaViewerEntryPoint: MediaViewerEntryPoint,
private val mediaGalleryEntryPoint: MediaGalleryEntryPoint, private val mediaGalleryEntryPoint: MediaGalleryEntryPoint,
private val verifySessionEntryPoint: VerifySessionEntryPoint, private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint,
private val reportRoomEntryPoint: ReportRoomEntryPoint, private val reportRoomEntryPoint: ReportRoomEntryPoint,
) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>( ) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
@ -328,13 +328,13 @@ class RoomDetailsFlowNode @AssistedInject constructor(
createNode<SecurityAndPrivacyFlowNode>(buildContext) createNode<SecurityAndPrivacyFlowNode>(buildContext)
} }
is NavTarget.VerifyUser -> { is NavTarget.VerifyUser -> {
val params = VerifySessionEntryPoint.Params( val params = OutgoingVerificationEntryPoint.Params(
showDeviceVerifiedScreen = true, showDeviceVerifiedScreen = true,
verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId,) verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId,)
) )
verifySessionEntryPoint.nodeBuilder(this, buildContext) outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(params) .params(params)
.callback(object : VerifySessionEntryPoint.Callback { .callback(object : OutgoingVerificationEntryPoint.Callback {
override fun onDone() { override fun onDone() {
backstack.pop() backstack.pop()
} }

View file

@ -25,7 +25,7 @@ import io.element.android.features.call.api.ElementCallEntryPoint
import io.element.android.features.userprofile.api.UserProfileEntryPoint import io.element.android.features.userprofile.api.UserProfileEntryPoint
import io.element.android.features.userprofile.impl.root.UserProfileNode import io.element.android.features.userprofile.impl.root.UserProfileNode
import io.element.android.features.userprofile.shared.UserProfileNodeHelper import io.element.android.features.userprofile.shared.UserProfileNodeHelper
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
@ -46,7 +46,7 @@ class UserProfileFlowNode @AssistedInject constructor(
private val elementCallEntryPoint: ElementCallEntryPoint, private val elementCallEntryPoint: ElementCallEntryPoint,
private val sessionIdHolder: CurrentSessionIdHolder, private val sessionIdHolder: CurrentSessionIdHolder,
private val mediaViewerEntryPoint: MediaViewerEntryPoint, private val mediaViewerEntryPoint: MediaViewerEntryPoint,
private val verifySessionEntryPoint: VerifySessionEntryPoint, private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint,
) : BaseFlowNode<UserProfileFlowNode.NavTarget>( ) : BaseFlowNode<UserProfileFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Root, initialElement = NavTarget.Root,
@ -110,11 +110,11 @@ class UserProfileFlowNode @AssistedInject constructor(
.build() .build()
} }
is NavTarget.VerifyUser -> { is NavTarget.VerifyUser -> {
val params = VerifySessionEntryPoint.Params( val params = OutgoingVerificationEntryPoint.Params(
showDeviceVerifiedScreen = false, showDeviceVerifiedScreen = false,
verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId) verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId)
) )
verifySessionEntryPoint.nodeBuilder(this, buildContext) outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(params) .params(params)
.build() .build()
} }

View file

@ -14,7 +14,7 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.matrix.api.verification.VerificationRequest
interface VerifySessionEntryPoint : FeatureEntryPoint { interface OutgoingVerificationEntryPoint : FeatureEntryPoint {
data class Params( data class Params(
val showDeviceVerifiedScreen: Boolean, val showDeviceVerifiedScreen: Boolean,
val verificationRequest: VerificationRequest.Outgoing, val verificationRequest: VerificationRequest.Outgoing,

View file

@ -11,29 +11,29 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import javax.inject.Inject import javax.inject.Inject
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultVerifySessionEntryPoint @Inject constructor() : VerifySessionEntryPoint { class DefaultOutgoingVerificationEntryPoint @Inject constructor() : OutgoingVerificationEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): VerifySessionEntryPoint.NodeBuilder { override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): OutgoingVerificationEntryPoint.NodeBuilder {
val plugins = ArrayList<Plugin>() val plugins = ArrayList<Plugin>()
return object : VerifySessionEntryPoint.NodeBuilder { return object : OutgoingVerificationEntryPoint.NodeBuilder {
override fun callback(callback: VerifySessionEntryPoint.Callback): VerifySessionEntryPoint.NodeBuilder { override fun callback(callback: OutgoingVerificationEntryPoint.Callback): OutgoingVerificationEntryPoint.NodeBuilder {
plugins += callback plugins += callback
return this return this
} }
override fun params(params: VerifySessionEntryPoint.Params): VerifySessionEntryPoint.NodeBuilder { override fun params(params: OutgoingVerificationEntryPoint.Params): OutgoingVerificationEntryPoint.NodeBuilder {
plugins += params plugins += params
return this return this
} }
override fun build(): Node { override fun build(): Node {
return parentNode.createNode<VerifySelfSessionNode>(buildContext, plugins) return parentNode.createNode<OutgoingVerificationNode>(buildContext, plugins)
} }
} }
} }

View file

@ -16,19 +16,19 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
class VerifySelfSessionNode @AssistedInject constructor( class OutgoingVerificationNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
presenterFactory: VerifySelfSessionPresenter.Factory, presenterFactory: OutgoingVerificationPresenter.Factory,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
private val callback = plugins<VerifySessionEntryPoint.Callback>().first() private val callback = plugins<OutgoingVerificationEntryPoint.Callback>().first()
private val inputs = inputs<VerifySessionEntryPoint.Params>() private val inputs = inputs<OutgoingVerificationEntryPoint.Params>()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
showDeviceVerifiedScreen = inputs.showDeviceVerifiedScreen, showDeviceVerifiedScreen = inputs.showDeviceVerifiedScreen,
@ -38,7 +38,7 @@ class VerifySelfSessionNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
VerifySelfSessionView( OutgoingVerificationView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onLearnMoreClick = callback::onLearnMoreAboutEncryption, onLearnMoreClick = callback::onLearnMoreAboutEncryption,

View file

@ -31,30 +31,30 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import timber.log.Timber import timber.log.Timber
import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionStateMachine.Event as StateMachineEvent import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationStateMachine.Event as StateMachineEvent
import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionStateMachine.State as StateMachineState import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationStateMachine.State as StateMachineState
class VerifySelfSessionPresenter @AssistedInject constructor( class OutgoingVerificationPresenter @AssistedInject constructor(
@Assisted private val showDeviceVerifiedScreen: Boolean, @Assisted private val showDeviceVerifiedScreen: Boolean,
@Assisted private val verificationRequest: VerificationRequest.Outgoing, @Assisted private val verificationRequest: VerificationRequest.Outgoing,
private val sessionVerificationService: SessionVerificationService, private val sessionVerificationService: SessionVerificationService,
private val encryptionService: EncryptionService, private val encryptionService: EncryptionService,
) : Presenter<VerifySelfSessionState> { ) : Presenter<OutgoingVerificationState> {
@AssistedFactory @AssistedFactory
interface Factory { interface Factory {
fun create( fun create(
verificationRequest: VerificationRequest.Outgoing, verificationRequest: VerificationRequest.Outgoing,
showDeviceVerifiedScreen: Boolean, showDeviceVerifiedScreen: Boolean,
): VerifySelfSessionPresenter ): OutgoingVerificationPresenter
} }
private val stateMachine = VerifySelfSessionStateMachine( private val stateMachine = OutgoingVerificationStateMachine(
sessionVerificationService = sessionVerificationService, sessionVerificationService = sessionVerificationService,
encryptionService = encryptionService, encryptionService = encryptionService,
) )
@Composable @Composable
override fun present(): VerifySelfSessionState { override fun present(): OutgoingVerificationState {
val stateAndDispatch = stateMachine.rememberStateAndDispatch() val stateAndDispatch = stateMachine.rememberStateAndDispatch()
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState() val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
@ -63,17 +63,17 @@ class VerifySelfSessionPresenter @AssistedInject constructor(
when (verificationRequest) { when (verificationRequest) {
is VerificationRequest.Outgoing.CurrentSession -> { is VerificationRequest.Outgoing.CurrentSession -> {
when (sessionVerifiedStatus) { when (sessionVerifiedStatus) {
SessionVerifiedStatus.Unknown -> VerifySelfSessionState.Step.Loading SessionVerifiedStatus.Unknown -> OutgoingVerificationState.Step.Loading
SessionVerifiedStatus.NotVerified -> { SessionVerifiedStatus.NotVerified -> {
stateAndDispatch.state.value.toVerificationStep() stateAndDispatch.state.value.toVerificationStep()
} }
SessionVerifiedStatus.Verified -> { SessionVerifiedStatus.Verified -> {
if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) { if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) {
// The user has verified the session, we need to show the success screen // The user has verified the session, we need to show the success screen
VerifySelfSessionState.Step.Completed OutgoingVerificationState.Step.Completed
} else { } else {
// Automatic verification, which can happen on freshly created account, in this case, skip the screen // Automatic verification, which can happen on freshly created account, in this case, skip the screen
VerifySelfSessionState.Step.Exit OutgoingVerificationState.Step.Exit
} }
} }
} }
@ -91,42 +91,44 @@ class VerifySelfSessionPresenter @AssistedInject constructor(
observeVerificationService() observeVerificationService()
} }
fun handleEvents(event: VerifySelfSessionViewEvents) { fun handleEvents(event: OutgoingVerificationViewEvents) {
Timber.d("Verification user action: ${event::class.simpleName}") Timber.d("Verification user action: ${event::class.simpleName}")
when (event) { when (event) {
// Just relay the event to the state machine // Just relay the event to the state machine
VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification(verificationRequest)) OutgoingVerificationViewEvents.RequestVerification -> StateMachineEvent.RequestVerification(verificationRequest)
VerifySelfSessionViewEvents.StartSasVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.StartSasVerification) OutgoingVerificationViewEvents.StartSasVerification -> StateMachineEvent.StartSasVerification
VerifySelfSessionViewEvents.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge) OutgoingVerificationViewEvents.ConfirmVerification -> StateMachineEvent.AcceptChallenge
VerifySelfSessionViewEvents.DeclineVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge) OutgoingVerificationViewEvents.DeclineVerification -> StateMachineEvent.DeclineChallenge
VerifySelfSessionViewEvents.Cancel -> stateAndDispatch.dispatchAction(StateMachineEvent.Cancel) OutgoingVerificationViewEvents.Cancel -> StateMachineEvent.Cancel
VerifySelfSessionViewEvents.Reset -> stateAndDispatch.dispatchAction(StateMachineEvent.Reset) OutgoingVerificationViewEvents.Reset -> StateMachineEvent.Reset
}.let { stateMachineEvent ->
stateAndDispatch.dispatchAction(stateMachineEvent)
} }
} }
return VerifySelfSessionState( return OutgoingVerificationState(
step = step, step = step,
request = verificationRequest, request = verificationRequest,
eventSink = ::handleEvents, eventSink = ::handleEvents,
) )
} }
private fun StateMachineState?.toVerificationStep(): VerifySelfSessionState.Step = private fun StateMachineState?.toVerificationStep(): OutgoingVerificationState.Step =
when (val machineState = this) { when (val machineState = this) {
StateMachineState.Initial, null -> { StateMachineState.Initial, null -> {
VerifySelfSessionState.Step.Initial OutgoingVerificationState.Step.Initial
} }
is StateMachineState.RequestingVerification, is StateMachineState.RequestingVerification,
is StateMachineState.StartingSasVerification, is StateMachineState.StartingSasVerification,
StateMachineState.SasVerificationStarted -> { StateMachineState.SasVerificationStarted -> {
VerifySelfSessionState.Step.AwaitingOtherDeviceResponse OutgoingVerificationState.Step.AwaitingOtherDeviceResponse
} }
StateMachineState.VerificationRequestAccepted -> { StateMachineState.VerificationRequestAccepted -> {
VerifySelfSessionState.Step.Ready OutgoingVerificationState.Step.Ready
} }
is StateMachineState.Canceled -> { is StateMachineState.Canceled -> {
VerifySelfSessionState.Step.Canceled OutgoingVerificationState.Step.Canceled
} }
is StateMachineState.Verifying -> { is StateMachineState.Verifying -> {
@ -134,15 +136,15 @@ class VerifySelfSessionPresenter @AssistedInject constructor(
is StateMachineState.Verifying.Replying -> AsyncData.Loading() is StateMachineState.Verifying.Replying -> AsyncData.Loading()
else -> AsyncData.Uninitialized else -> AsyncData.Uninitialized
} }
VerifySelfSessionState.Step.Verifying(machineState.data, async) OutgoingVerificationState.Step.Verifying(machineState.data, async)
} }
StateMachineState.Completed -> { StateMachineState.Completed -> {
VerifySelfSessionState.Step.Completed OutgoingVerificationState.Step.Completed
} }
StateMachineState.Exit -> { StateMachineState.Exit -> {
VerifySelfSessionState.Step.Exit OutgoingVerificationState.Step.Exit
} }
} }

View file

@ -14,10 +14,10 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationD
import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.matrix.api.verification.VerificationRequest
@Immutable @Immutable
data class VerifySelfSessionState( data class OutgoingVerificationState(
val step: Step, val step: Step,
val request: VerificationRequest.Outgoing, val request: VerificationRequest.Outgoing,
val eventSink: (VerifySelfSessionViewEvents) -> Unit, val eventSink: (OutgoingVerificationViewEvents) -> Unit,
) { ) {
@Stable @Stable
sealed interface Step { sealed interface Step {

View file

@ -29,10 +29,10 @@ import kotlin.time.Duration.Companion.seconds
import com.freeletics.flowredux.dsl.State as MachineState import com.freeletics.flowredux.dsl.State as MachineState
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
class VerifySelfSessionStateMachine( class OutgoingVerificationStateMachine(
private val sessionVerificationService: SessionVerificationService, private val sessionVerificationService: SessionVerificationService,
private val encryptionService: EncryptionService, private val encryptionService: EncryptionService,
) : FlowReduxStateMachine<VerifySelfSessionStateMachine.State, VerifySelfSessionStateMachine.Event>( ) : FlowReduxStateMachine<OutgoingVerificationStateMachine.State, OutgoingVerificationStateMachine.Event>(
initialState = State.Initial, initialState = State.Initial,
) { ) {
init { init {

View file

@ -8,64 +8,64 @@
package io.element.android.features.verifysession.impl.outgoing package io.element.android.features.verifysession.impl.outgoing
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationState.Step
import io.element.android.features.verifysession.impl.ui.aDecimalsSessionVerificationData import io.element.android.features.verifysession.impl.ui.aDecimalsSessionVerificationData
import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.matrix.api.verification.VerificationRequest
open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfSessionState> { open class OutgoingVerificationStateProvider : PreviewParameterProvider<OutgoingVerificationState> {
override val values: Sequence<VerifySelfSessionState> override val values: Sequence<OutgoingVerificationState>
get() = sequenceOf( get() = sequenceOf(
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Initial, step = Step.Initial,
request = anOutgoingSessionVerificationRequest(), request = anOutgoingSessionVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Initial, step = Step.Initial,
request = anOutgoingUserVerificationRequest(), request = anOutgoingUserVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.AwaitingOtherDeviceResponse, step = Step.AwaitingOtherDeviceResponse,
request = anOutgoingSessionVerificationRequest(), request = anOutgoingSessionVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.AwaitingOtherDeviceResponse, step = Step.AwaitingOtherDeviceResponse,
request = anOutgoingUserVerificationRequest(), request = anOutgoingUserVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized), step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized),
request = anOutgoingSessionVerificationRequest(), request = anOutgoingSessionVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized), step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized),
request = anOutgoingUserVerificationRequest(), request = anOutgoingUserVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading()) step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading())
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Canceled step = Step.Canceled
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Ready step = Step.Ready
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized) step = Step.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized)
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Completed, step = Step.Completed,
request = anOutgoingSessionVerificationRequest(), request = anOutgoingSessionVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Completed, step = Step.Completed,
request = anOutgoingUserVerificationRequest(), request = anOutgoingUserVerificationRequest(),
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Loading step = Step.Loading
), ),
aVerifySelfSessionState( anOutgoingVerificationState(
step = Step.Exit step = Step.Exit
), ),
// Add other state here // Add other state here
@ -75,11 +75,11 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS
internal fun anOutgoingUserVerificationRequest() = VerificationRequest.Outgoing.User(userId = UserId("@alice:example.com")) internal fun anOutgoingUserVerificationRequest() = VerificationRequest.Outgoing.User(userId = UserId("@alice:example.com"))
internal fun anOutgoingSessionVerificationRequest() = VerificationRequest.Outgoing.CurrentSession internal fun anOutgoingSessionVerificationRequest() = VerificationRequest.Outgoing.CurrentSession
internal fun aVerifySelfSessionState( internal fun anOutgoingVerificationState(
step: Step = Step.Initial, step: Step = Step.Initial,
request: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(), request: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(),
eventSink: (VerifySelfSessionViewEvents) -> Unit = {}, eventSink: (OutgoingVerificationViewEvents) -> Unit = {},
) = VerifySelfSessionState( ) = OutgoingVerificationState(
step = step, step = step,
request = request, request = request,
eventSink = eventSink, eventSink = eventSink,

View file

@ -27,7 +27,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.verifysession.impl.R import io.element.android.features.verifysession.impl.R
import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationState.Step
import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu
import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
@ -49,8 +49,8 @@ import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun VerifySelfSessionView( fun OutgoingVerificationView(
state: VerifySelfSessionState, state: OutgoingVerificationState,
onLearnMoreClick: () -> Unit, onLearnMoreClick: () -> Unit,
onFinish: () -> Unit, onFinish: () -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
@ -59,12 +59,12 @@ fun VerifySelfSessionView(
val step = state.step val step = state.step
fun cancelOrResetFlow() { fun cancelOrResetFlow() {
when (step) { when (step) {
is Step.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset) is Step.Canceled -> state.eventSink(OutgoingVerificationViewEvents.Reset)
Step.Initial, Step.Completed -> onBack() Step.Initial, Step.Completed -> onBack()
Step.Ready, is Step.AwaitingOtherDeviceResponse -> state.eventSink(VerifySelfSessionViewEvents.Cancel) Step.Ready, is Step.AwaitingOtherDeviceResponse -> state.eventSink(OutgoingVerificationViewEvents.Cancel)
is Step.Verifying -> { is Step.Verifying -> {
if (!step.state.isLoading()) { if (!step.state.isLoading()) {
state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) state.eventSink(OutgoingVerificationViewEvents.DeclineVerification)
} }
} }
else -> Unit else -> Unit
@ -98,10 +98,10 @@ fun VerifySelfSessionView(
) )
}, },
header = { header = {
VerifySelfSessionHeader(step = step, request = state.request) OutgoingVerificationHeader(step = step, request = state.request)
}, },
footer = { footer = {
VerifySelfSessionBottomMenu( OutgoingVerificationViewBottomMenu(
screenState = state, screenState = state,
onCancelClick = ::cancelOrResetFlow, onCancelClick = ::cancelOrResetFlow,
onContinueClick = onFinish, onContinueClick = onFinish,
@ -109,7 +109,7 @@ fun VerifySelfSessionView(
}, },
isScrollable = true, isScrollable = true,
) { ) {
VerifySelfSessionContent( OutgoingVerificationContent(
flowState = step, flowState = step,
request = state.request, request = state.request,
onLearnMoreClick = onLearnMoreClick, onLearnMoreClick = onLearnMoreClick,
@ -119,7 +119,7 @@ fun VerifySelfSessionView(
} }
@Composable @Composable
private fun VerifySelfSessionHeader(step: Step, request: VerificationRequest.Outgoing) { private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.Outgoing) {
val iconStyle = when (step) { val iconStyle = when (step) {
Step.Loading -> error("Should not happen") Step.Loading -> error("Should not happen")
Step.Initial -> when (request) { Step.Initial -> when (request) {
@ -189,7 +189,7 @@ private fun VerifySelfSessionHeader(step: Step, request: VerificationRequest.Out
} }
@Composable @Composable
private fun VerifySelfSessionContent( private fun OutgoingVerificationContent(
flowState: Step, flowState: Step,
request: VerificationRequest.Outgoing, request: VerificationRequest.Outgoing,
onLearnMoreClick: () -> Unit, onLearnMoreClick: () -> Unit,
@ -227,8 +227,8 @@ private fun ContentInitial(
} }
@Composable @Composable
private fun VerifySelfSessionBottomMenu( private fun OutgoingVerificationViewBottomMenu(
screenState: VerifySelfSessionState, screenState: OutgoingVerificationState,
onCancelClick: () -> Unit, onCancelClick: () -> Unit,
onContinueClick: () -> Unit, onContinueClick: () -> Unit,
) { ) {
@ -244,7 +244,7 @@ private fun VerifySelfSessionBottomMenu(
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_start_verification), text = stringResource(CommonStrings.action_start_verification),
onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, onClick = { eventSink(OutgoingVerificationViewEvents.RequestVerification) },
) )
InvisibleButton() InvisibleButton()
} }
@ -264,7 +264,7 @@ private fun VerifySelfSessionBottomMenu(
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_start), text = stringResource(CommonStrings.action_start),
onClick = { eventSink(VerifySelfSessionViewEvents.StartSasVerification) }, onClick = { eventSink(OutgoingVerificationViewEvents.StartSasVerification) },
) )
TextButton( TextButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -287,14 +287,14 @@ private fun VerifySelfSessionBottomMenu(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.screen_session_verification_they_match), text = stringResource(R.string.screen_session_verification_they_match),
onClick = { onClick = {
eventSink(VerifySelfSessionViewEvents.ConfirmVerification) eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
}, },
) )
TextButton( TextButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.screen_session_verification_they_dont_match), text = stringResource(R.string.screen_session_verification_they_dont_match),
onClick = { eventSink(VerifySelfSessionViewEvents.DeclineVerification) }, onClick = { eventSink(OutgoingVerificationViewEvents.DeclineVerification) },
) )
} }
} }
@ -315,8 +315,8 @@ private fun VerifySelfSessionBottomMenu(
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreview { internal fun OutgoingVerificationViewPreview(@PreviewParameter(OutgoingVerificationStateProvider::class) state: OutgoingVerificationState) = ElementPreview {
VerifySelfSessionView( OutgoingVerificationView(
state = state, state = state,
onLearnMoreClick = {}, onLearnMoreClick = {},
onFinish = {}, onFinish = {},

View file

@ -0,0 +1,17 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.verifysession.impl.outgoing
sealed interface OutgoingVerificationViewEvents {
data object RequestVerification : OutgoingVerificationViewEvents
data object StartSasVerification : OutgoingVerificationViewEvents
data object ConfirmVerification : OutgoingVerificationViewEvents
data object DeclineVerification : OutgoingVerificationViewEvents
data object Cancel : OutgoingVerificationViewEvents
data object Reset : OutgoingVerificationViewEvents
}

View file

@ -1,17 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.verifysession.impl.outgoing
sealed interface VerifySelfSessionViewEvents {
data object RequestVerification : VerifySelfSessionViewEvents
data object StartSasVerification : VerifySelfSessionViewEvents
data object ConfirmVerification : VerifySelfSessionViewEvents
data object DeclineVerification : VerifySelfSessionViewEvents
data object Cancel : VerifySelfSessionViewEvents
data object Reset : VerifySelfSessionViewEvents
}

View file

@ -9,7 +9,7 @@ package io.element.android.features.verifysession.impl.outgoing
import app.cash.turbine.ReceiveTurbine import app.cash.turbine.ReceiveTurbine
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationState.Step
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.EncryptionService
@ -31,13 +31,13 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
class VerifySelfSessionPresenterTest { class OutgoingVerificationPresenterTest {
@get:Rule @get:Rule
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()
@Test @Test
fun `present - Initial state is received`() = runTest { fun `present - Initial state is received`() = runTest {
val presenter = createVerifySelfSessionPresenter( val presenter = createOutgoingVerificationPresenter(
service = unverifiedSessionService(), service = unverifiedSessionService(),
) )
presenter.test { presenter.test {
@ -55,7 +55,7 @@ class VerifySelfSessionPresenterTest {
requestSessionVerificationLambda = requestSessionVerificationRecorder, requestSessionVerificationLambda = requestSessionVerificationRecorder,
startVerificationLambda = startVerificationRecorder, startVerificationLambda = startVerificationRecorder,
) )
val presenter = createVerifySelfSessionPresenter( val presenter = createOutgoingVerificationPresenter(
service = service, service = service,
verificationRequest = anOutgoingSessionVerificationRequest(), verificationRequest = anOutgoingSessionVerificationRequest(),
) )
@ -75,7 +75,7 @@ class VerifySelfSessionPresenterTest {
requestUserVerificationLambda = requestUserVerificationRecorder, requestUserVerificationLambda = requestUserVerificationRecorder,
startVerificationLambda = startVerificationRecorder, startVerificationLambda = startVerificationRecorder,
) )
val presenter = createVerifySelfSessionPresenter( val presenter = createOutgoingVerificationPresenter(
service = service, service = service,
verificationRequest = anOutgoingUserVerificationRequest(), verificationRequest = anOutgoingUserVerificationRequest(),
) )
@ -89,14 +89,14 @@ class VerifySelfSessionPresenterTest {
@Test @Test
fun `present - Cancellation on initial state moves to Exit state`() = runTest { fun `present - Cancellation on initial state moves to Exit state`() = runTest {
val presenter = createVerifySelfSessionPresenter( val presenter = createOutgoingVerificationPresenter(
service = unverifiedSessionService(), service = unverifiedSessionService(),
) )
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.step).isEqualTo(Step.Initial) assertThat(initialState.step).isEqualTo(Step.Initial)
val eventSink = initialState.eventSink val eventSink = initialState.eventSink
eventSink(VerifySelfSessionViewEvents.Cancel) eventSink(OutgoingVerificationViewEvents.Cancel)
assertThat(awaitItem().step).isEqualTo(Step.Exit) assertThat(awaitItem().step).isEqualTo(Step.Exit)
} }
@ -109,10 +109,10 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { }, startVerificationLambda = { },
approveVerificationLambda = { }, approveVerificationLambda = { },
) )
val presenter = createVerifySelfSessionPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification) state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
// Cancelling // Cancelling
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFail) service.emitVerificationFlowState(VerificationFlowState.DidFail)
@ -126,9 +126,9 @@ class VerifySelfSessionPresenterTest {
val service = unverifiedSessionService( val service = unverifiedSessionService(
requestSessionVerificationLambda = { }, requestSessionVerificationLambda = { },
) )
val presenter = createVerifySelfSessionPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification) awaitItem().eventSink(OutgoingVerificationViewEvents.RequestVerification)
service.emitVerificationFlowState(VerificationFlowState.DidFail) service.emitVerificationFlowState(VerificationFlowState.DidFail)
assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java) assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java)
assertThat(awaitItem().step).isEqualTo(Step.Canceled) assertThat(awaitItem().step).isEqualTo(Step.Canceled)
@ -142,10 +142,10 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { }, startVerificationLambda = { },
cancelVerificationLambda = { }, cancelVerificationLambda = { },
) )
val presenter = createVerifySelfSessionPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(VerifySelfSessionViewEvents.Cancel) state.eventSink(OutgoingVerificationViewEvents.Cancel)
assertThat(awaitItem().step).isEqualTo(Step.Canceled) assertThat(awaitItem().step).isEqualTo(Step.Canceled)
} }
} }
@ -156,7 +156,7 @@ class VerifySelfSessionPresenterTest {
requestSessionVerificationLambda = { }, requestSessionVerificationLambda = { },
startVerificationLambda = { }, startVerificationLambda = { },
) )
val presenter = createVerifySelfSessionPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
requestVerificationAndAwaitVerifyingState(service) requestVerificationAndAwaitVerifyingState(service)
service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList()))) service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList())))
@ -170,12 +170,12 @@ class VerifySelfSessionPresenterTest {
requestSessionVerificationLambda = { }, requestSessionVerificationLambda = { },
startVerificationLambda = { }, startVerificationLambda = { },
) )
val presenter = createVerifySelfSessionPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
service.emitVerificationFlowState(VerificationFlowState.DidCancel) service.emitVerificationFlowState(VerificationFlowState.DidCancel)
assertThat(awaitItem().step).isEqualTo(Step.Canceled) assertThat(awaitItem().step).isEqualTo(Step.Canceled)
state.eventSink(VerifySelfSessionViewEvents.Reset) state.eventSink(OutgoingVerificationViewEvents.Reset)
// Went back to initial state // Went back to initial state
assertThat(awaitItem().step).isEqualTo(Step.Initial) assertThat(awaitItem().step).isEqualTo(Step.Initial)
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
@ -192,13 +192,13 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { }, startVerificationLambda = { },
approveVerificationLambda = { }, approveVerificationLambda = { },
) )
val presenter = createVerifySelfSessionPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState( val state = requestVerificationAndAwaitVerifyingState(
service, service,
SessionVerificationData.Emojis(emojis) SessionVerificationData.Emojis(emojis)
) )
state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification) state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
assertThat(awaitItem().step).isEqualTo( assertThat(awaitItem().step).isEqualTo(
Step.Verifying( Step.Verifying(
SessionVerificationData.Emojis(emojis), SessionVerificationData.Emojis(emojis),
@ -217,10 +217,10 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { }, startVerificationLambda = { },
declineVerificationLambda = { }, declineVerificationLambda = { },
) )
val presenter = createVerifySelfSessionPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) state.eventSink(OutgoingVerificationViewEvents.DeclineVerification)
assertThat(awaitItem().step).isEqualTo( assertThat(awaitItem().step).isEqualTo(
Step.Verifying( Step.Verifying(
SessionVerificationData.Emojis(emptyList()), SessionVerificationData.Emojis(emptyList()),
@ -241,7 +241,7 @@ class VerifySelfSessionPresenterTest {
emitVerifiedStatus(SessionVerifiedStatus.Verified) emitVerifiedStatus(SessionVerifiedStatus.Verified)
emitVerificationFlowState(VerificationFlowState.DidFinish) emitVerificationFlowState(VerificationFlowState.DidFinish)
} }
val presenter = createVerifySelfSessionPresenter( val presenter = createOutgoingVerificationPresenter(
service = service, service = service,
showDeviceVerifiedScreen = true, showDeviceVerifiedScreen = true,
) )
@ -259,7 +259,7 @@ class VerifySelfSessionPresenterTest {
emitVerifiedStatus(SessionVerifiedStatus.Verified) emitVerifiedStatus(SessionVerifiedStatus.Verified)
emitVerificationFlowState(VerificationFlowState.DidFinish) emitVerificationFlowState(VerificationFlowState.DidFinish)
} }
val presenter = createVerifySelfSessionPresenter( val presenter = createOutgoingVerificationPresenter(
service = service, service = service,
showDeviceVerifiedScreen = false, showDeviceVerifiedScreen = false,
) )
@ -269,13 +269,13 @@ class VerifySelfSessionPresenterTest {
} }
} }
private suspend fun ReceiveTurbine<VerifySelfSessionState>.requestVerificationAndAwaitVerifyingState( private suspend fun ReceiveTurbine<OutgoingVerificationState>.requestVerificationAndAwaitVerifyingState(
fakeService: FakeSessionVerificationService, fakeService: FakeSessionVerificationService,
sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()), sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()),
): VerifySelfSessionState { ): OutgoingVerificationState {
var state = awaitItem() var state = awaitItem()
assertThat(state.step).isEqualTo(Step.Initial) assertThat(state.step).isEqualTo(Step.Initial)
state.eventSink(VerifySelfSessionViewEvents.RequestVerification) state.eventSink(OutgoingVerificationViewEvents.RequestVerification)
// Await for other device response: // Await for other device response:
fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
state = awaitItem() state = awaitItem()
@ -283,7 +283,7 @@ class VerifySelfSessionPresenterTest {
// Await for the state to be Ready // Await for the state to be Ready
state = awaitItem() state = awaitItem()
assertThat(state.step).isEqualTo(Step.Ready) assertThat(state.step).isEqualTo(Step.Ready)
state.eventSink(VerifySelfSessionViewEvents.StartSasVerification) state.eventSink(OutgoingVerificationViewEvents.StartSasVerification)
// Await for other device response (again): // Await for other device response (again):
fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification)
state = awaitItem() state = awaitItem()
@ -321,13 +321,13 @@ class VerifySelfSessionPresenterTest {
} }
} }
private fun createVerifySelfSessionPresenter( private fun createOutgoingVerificationPresenter(
service: SessionVerificationService, service: SessionVerificationService,
verificationRequest: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(), verificationRequest: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(),
encryptionService: EncryptionService = FakeEncryptionService(), encryptionService: EncryptionService = FakeEncryptionService(),
showDeviceVerifiedScreen: Boolean = false, showDeviceVerifiedScreen: Boolean = false,
): VerifySelfSessionPresenter { ): OutgoingVerificationPresenter {
return VerifySelfSessionPresenter( return OutgoingVerificationPresenter(
showDeviceVerifiedScreen = showDeviceVerifiedScreen, showDeviceVerifiedScreen = showDeviceVerifiedScreen,
verificationRequest = verificationRequest, verificationRequest = verificationRequest,
sessionVerificationService = service, sessionVerificationService = service,

View file

@ -26,54 +26,54 @@ import org.junit.rules.TestRule
import org.junit.runner.RunWith import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class VerifySelfSessionViewTest { class OutgoingVerificationViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>() @get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test @Test
fun `back key pressed - when canceled resets the flow`() { fun `back key pressed - when canceled resets the flow`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>()
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Canceled, step = OutgoingVerificationState.Step.Canceled,
eventSink = eventsRecorder eventSink = eventsRecorder
), ),
) )
rule.pressBackKey() rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.Reset) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Reset)
} }
@Test @Test
fun `back key pressed - when awaiting response cancels the verification`() { fun `back key pressed - when awaiting response cancels the verification`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>()
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.AwaitingOtherDeviceResponse, step = OutgoingVerificationState.Step.AwaitingOtherDeviceResponse,
eventSink = eventsRecorder eventSink = eventsRecorder
), ),
) )
rule.pressBackKey() rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.Cancel) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel)
} }
@Test @Test
fun `back key pressed - when ready to verify cancels the verification`() { fun `back key pressed - when ready to verify cancels the verification`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>()
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Ready, step = OutgoingVerificationState.Step.Ready,
eventSink = eventsRecorder eventSink = eventsRecorder
), ),
) )
rule.pressBackKey() rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.Cancel) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel)
} }
@Test @Test
fun `back key pressed - when verifying and not loading declines the verification`() { fun `back key pressed - when verifying and not loading declines the verification`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>()
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
data = aEmojisSessionVerificationData(), data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized, state = AsyncData.Uninitialized,
), ),
@ -81,15 +81,15 @@ class VerifySelfSessionViewTest {
), ),
) )
rule.pressBackKey() rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.DeclineVerification) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification)
} }
@Test @Test
fun `back key pressed - when verifying and loading does nothing`() { fun `back key pressed - when verifying and loading does nothing`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>()
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
data = aEmojisSessionVerificationData(), data = aEmojisSessionVerificationData(),
state = AsyncData.Loading(), state = AsyncData.Loading(),
), ),
@ -103,10 +103,10 @@ class VerifySelfSessionViewTest {
@Test @Test
fun `back key pressed - on Completed exits the flow`() { fun `back key pressed - on Completed exits the flow`() {
ensureCalledOnce { callback -> ensureCalledOnce { callback ->
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
onBack = callback, onBack = callback,
state = aVerifySelfSessionState( state = anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Completed, step = OutgoingVerificationState.Step.Completed,
), ),
) )
rule.pressBackKey() rule.pressBackKey()
@ -115,11 +115,11 @@ class VerifySelfSessionViewTest {
@Test @Test
fun `when flow is completed and the user clicks on the continue button, the expected callback is invoked`() { fun `when flow is completed and the user clicks on the continue button, the expected callback is invoked`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>(expectEvents = false) val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>(expectEvents = false)
ensureCalledOnce { callback -> ensureCalledOnce { callback ->
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Completed, step = OutgoingVerificationState.Step.Completed,
eventSink = eventsRecorder eventSink = eventsRecorder
), ),
onFinished = callback, onFinished = callback,
@ -130,10 +130,10 @@ class VerifySelfSessionViewTest {
@Test @Test
fun `clicking on they match emits the expected event`() { fun `clicking on they match emits the expected event`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>()
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
data = aEmojisSessionVerificationData(), data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized, state = AsyncData.Uninitialized,
), ),
@ -141,15 +141,15 @@ class VerifySelfSessionViewTest {
), ),
) )
rule.clickOn(R.string.screen_session_verification_they_match) rule.clickOn(R.string.screen_session_verification_they_match)
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.ConfirmVerification) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.ConfirmVerification)
} }
@Test @Test
fun `clicking on they do not match emits the expected event`() { fun `clicking on they do not match emits the expected event`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>()
rule.setVerifySelfSessionView( rule.setOutgoingVerificationView(
aVerifySelfSessionState( anOutgoingVerificationState(
step = VerifySelfSessionState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
data = aEmojisSessionVerificationData(), data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized, state = AsyncData.Uninitialized,
), ),
@ -157,17 +157,17 @@ class VerifySelfSessionViewTest {
), ),
) )
rule.clickOn(R.string.screen_session_verification_they_dont_match) rule.clickOn(R.string.screen_session_verification_they_dont_match)
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.DeclineVerification) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification)
} }
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setVerifySelfSessionView( private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setOutgoingVerificationView(
state: VerifySelfSessionState, state: OutgoingVerificationState,
onLearnMoreClick: () -> Unit = EnsureNeverCalled(), onLearnMoreClick: () -> Unit = EnsureNeverCalled(),
onFinished: () -> Unit = EnsureNeverCalled(), onFinished: () -> Unit = EnsureNeverCalled(),
onBack: () -> Unit = EnsureNeverCalled(), onBack: () -> Unit = EnsureNeverCalled(),
) { ) {
setContent { setContent {
VerifySelfSessionView( OutgoingVerificationView(
state = state, state = state,
onLearnMoreClick = onLearnMoreClick, onLearnMoreClick = onLearnMoreClick,
onFinish = onFinished, onFinish = onFinished,

View file

@ -16,8 +16,6 @@ import androidx.core.text.util.LinkifyCompat
import timber.log.Timber import timber.log.Timber
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
import kotlin.collections.isNotEmpty
import kotlin.collections.iterator
/** /**
* Helper class to linkify text while preserving existing URL spans. * Helper class to linkify text while preserving existing URL spans.

View file

@ -27,8 +27,6 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
import kotlin.collections.iterator
import kotlin.collections.orEmpty
@ContributesMultibinding(AppScope::class) @ContributesMultibinding(AppScope::class)
class SentryAnalyticsProvider @Inject constructor( class SentryAnalyticsProvider @Inject constructor(