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 ee5abd3264
commit e8e75af506
48 changed files with 194 additions and 196 deletions

View File

@@ -9,6 +9,7 @@ package io.element.android.appconfig
object LearnMoreConfig {
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 IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18"
}

View File

@@ -1,5 +1,4 @@
import extension.setupAnvil
import org.gradle.kotlin.dsl.test
/*
* 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.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeNode
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.BaseFlowNode
import io.element.android.libraries.architecture.createNode
@@ -40,7 +40,7 @@ import kotlinx.parcelize.Parcelize
class FtueSessionVerificationFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val verifySessionEntryPoint: VerifySessionEntryPoint,
private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint,
private val secureBackupEntryPoint: SecureBackupEntryPoint,
) : BaseFlowNode<FtueSessionVerificationFlowNode.NavTarget>(
backstack = BackStack(
@@ -94,19 +94,19 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
}
override fun onLearnMoreAboutEncryption() {
learnMoreUrl.value = LearnMoreConfig.ENCRYPTION_URL
learnMoreUrl.value = LearnMoreConfig.DEVICE_VERIFICATION_URL
}
}
createNode<ChooseSelfVerificationModeNode>(buildContext, plugins = listOf(callback))
}
is NavTarget.UseAnotherDevice -> {
verifySessionEntryPoint.nodeBuilder(this, buildContext)
.params(VerifySessionEntryPoint.Params(
outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(OutgoingVerificationEntryPoint.Params(
showDeviceVerifiedScreen = true,
verificationRequest = VerificationRequest.Outgoing.CurrentSession,
))
.callback(object : VerifySessionEntryPoint.Callback {
.callback(object : OutgoingVerificationEntryPoint.Callback {
override fun onDone() {
plugins<Callback>().forEach { it.onDone() }
}
@@ -116,7 +116,8 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
}
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()

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.test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Rule
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.securityandprivacy.SecurityAndPrivacyFlowNode
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.BaseFlowNode
import io.element.android.libraries.architecture.createNode
@@ -71,7 +71,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint,
private val mediaViewerEntryPoint: MediaViewerEntryPoint,
private val mediaGalleryEntryPoint: MediaGalleryEntryPoint,
private val verifySessionEntryPoint: VerifySessionEntryPoint,
private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint,
private val reportRoomEntryPoint: ReportRoomEntryPoint,
) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
backstack = BackStack(
@@ -328,13 +328,13 @@ class RoomDetailsFlowNode @AssistedInject constructor(
createNode<SecurityAndPrivacyFlowNode>(buildContext)
}
is NavTarget.VerifyUser -> {
val params = VerifySessionEntryPoint.Params(
val params = OutgoingVerificationEntryPoint.Params(
showDeviceVerifiedScreen = true,
verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId,)
)
verifySessionEntryPoint.nodeBuilder(this, buildContext)
outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(params)
.callback(object : VerifySessionEntryPoint.Callback {
.callback(object : OutgoingVerificationEntryPoint.Callback {
override fun onDone() {
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.impl.root.UserProfileNode
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.BaseFlowNode
import io.element.android.libraries.architecture.createNode
@@ -46,7 +46,7 @@ class UserProfileFlowNode @AssistedInject constructor(
private val elementCallEntryPoint: ElementCallEntryPoint,
private val sessionIdHolder: CurrentSessionIdHolder,
private val mediaViewerEntryPoint: MediaViewerEntryPoint,
private val verifySessionEntryPoint: VerifySessionEntryPoint,
private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint,
) : BaseFlowNode<UserProfileFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.Root,
@@ -110,11 +110,11 @@ class UserProfileFlowNode @AssistedInject constructor(
.build()
}
is NavTarget.VerifyUser -> {
val params = VerifySessionEntryPoint.Params(
val params = OutgoingVerificationEntryPoint.Params(
showDeviceVerifiedScreen = false,
verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId)
)
verifySessionEntryPoint.nodeBuilder(this, buildContext)
outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(params)
.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.matrix.api.verification.VerificationRequest
interface VerifySessionEntryPoint : FeatureEntryPoint {
interface OutgoingVerificationEntryPoint : FeatureEntryPoint {
data class Params(
val showDeviceVerifiedScreen: Boolean,
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.plugin.Plugin
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.di.AppScope
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultVerifySessionEntryPoint @Inject constructor() : VerifySessionEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): VerifySessionEntryPoint.NodeBuilder {
class DefaultOutgoingVerificationEntryPoint @Inject constructor() : OutgoingVerificationEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): OutgoingVerificationEntryPoint.NodeBuilder {
val plugins = ArrayList<Plugin>()
return object : VerifySessionEntryPoint.NodeBuilder {
override fun callback(callback: VerifySessionEntryPoint.Callback): VerifySessionEntryPoint.NodeBuilder {
return object : OutgoingVerificationEntryPoint.NodeBuilder {
override fun callback(callback: OutgoingVerificationEntryPoint.Callback): OutgoingVerificationEntryPoint.NodeBuilder {
plugins += callback
return this
}
override fun params(params: VerifySessionEntryPoint.Params): VerifySessionEntryPoint.NodeBuilder {
override fun params(params: OutgoingVerificationEntryPoint.Params): OutgoingVerificationEntryPoint.NodeBuilder {
plugins += params
return this
}
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.AssistedInject
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.di.SessionScope
@ContributesNode(SessionScope::class)
class VerifySelfSessionNode @AssistedInject constructor(
class OutgoingVerificationNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: VerifySelfSessionPresenter.Factory,
presenterFactory: OutgoingVerificationPresenter.Factory,
) : 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(
showDeviceVerifiedScreen = inputs.showDeviceVerifiedScreen,
@@ -38,7 +38,7 @@ class VerifySelfSessionNode @AssistedInject constructor(
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
VerifySelfSessionView(
OutgoingVerificationView(
state = state,
modifier = modifier,
onLearnMoreClick = callback::onLearnMoreAboutEncryption,

View File

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

View File

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

View File

@@ -8,64 +8,64 @@
package io.element.android.features.verifysession.impl.outgoing
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.aEmojisSessionVerificationData
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.verification.VerificationRequest
open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfSessionState> {
override val values: Sequence<VerifySelfSessionState>
open class OutgoingVerificationStateProvider : PreviewParameterProvider<OutgoingVerificationState> {
override val values: Sequence<OutgoingVerificationState>
get() = sequenceOf(
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Initial,
request = anOutgoingSessionVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Initial,
request = anOutgoingUserVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.AwaitingOtherDeviceResponse,
request = anOutgoingSessionVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.AwaitingOtherDeviceResponse,
request = anOutgoingUserVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized),
request = anOutgoingSessionVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized),
request = anOutgoingUserVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading())
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Canceled
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Ready
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized)
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Completed,
request = anOutgoingSessionVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Completed,
request = anOutgoingUserVerificationRequest(),
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Loading
),
aVerifySelfSessionState(
anOutgoingVerificationState(
step = Step.Exit
),
// 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 anOutgoingSessionVerificationRequest() = VerificationRequest.Outgoing.CurrentSession
internal fun aVerifySelfSessionState(
internal fun anOutgoingVerificationState(
step: Step = Step.Initial,
request: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(),
eventSink: (VerifySelfSessionViewEvents) -> Unit = {},
) = VerifySelfSessionState(
eventSink: (OutgoingVerificationViewEvents) -> Unit = {},
) = OutgoingVerificationState(
step = step,
request = request,
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.tokens.generated.CompoundIcons
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.VerificationContentVerifying
import io.element.android.libraries.architecture.AsyncData
@@ -49,8 +49,8 @@ import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun VerifySelfSessionView(
state: VerifySelfSessionState,
fun OutgoingVerificationView(
state: OutgoingVerificationState,
onLearnMoreClick: () -> Unit,
onFinish: () -> Unit,
onBack: () -> Unit,
@@ -59,12 +59,12 @@ fun VerifySelfSessionView(
val step = state.step
fun cancelOrResetFlow() {
when (step) {
is Step.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset)
is Step.Canceled -> state.eventSink(OutgoingVerificationViewEvents.Reset)
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 -> {
if (!step.state.isLoading()) {
state.eventSink(VerifySelfSessionViewEvents.DeclineVerification)
state.eventSink(OutgoingVerificationViewEvents.DeclineVerification)
}
}
else -> Unit
@@ -98,10 +98,10 @@ fun VerifySelfSessionView(
)
},
header = {
VerifySelfSessionHeader(step = step, request = state.request)
OutgoingVerificationHeader(step = step, request = state.request)
},
footer = {
VerifySelfSessionBottomMenu(
OutgoingVerificationViewBottomMenu(
screenState = state,
onCancelClick = ::cancelOrResetFlow,
onContinueClick = onFinish,
@@ -109,7 +109,7 @@ fun VerifySelfSessionView(
},
isScrollable = true,
) {
VerifySelfSessionContent(
OutgoingVerificationContent(
flowState = step,
request = state.request,
onLearnMoreClick = onLearnMoreClick,
@@ -119,7 +119,7 @@ fun VerifySelfSessionView(
}
@Composable
private fun VerifySelfSessionHeader(step: Step, request: VerificationRequest.Outgoing) {
private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.Outgoing) {
val iconStyle = when (step) {
Step.Loading -> error("Should not happen")
Step.Initial -> when (request) {
@@ -189,7 +189,7 @@ private fun VerifySelfSessionHeader(step: Step, request: VerificationRequest.Out
}
@Composable
private fun VerifySelfSessionContent(
private fun OutgoingVerificationContent(
flowState: Step,
request: VerificationRequest.Outgoing,
onLearnMoreClick: () -> Unit,
@@ -227,8 +227,8 @@ private fun ContentInitial(
}
@Composable
private fun VerifySelfSessionBottomMenu(
screenState: VerifySelfSessionState,
private fun OutgoingVerificationViewBottomMenu(
screenState: OutgoingVerificationState,
onCancelClick: () -> Unit,
onContinueClick: () -> Unit,
) {
@@ -244,7 +244,7 @@ private fun VerifySelfSessionBottomMenu(
Button(
modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_start_verification),
onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) },
onClick = { eventSink(OutgoingVerificationViewEvents.RequestVerification) },
)
InvisibleButton()
}
@@ -264,7 +264,7 @@ private fun VerifySelfSessionBottomMenu(
Button(
modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_start),
onClick = { eventSink(VerifySelfSessionViewEvents.StartSasVerification) },
onClick = { eventSink(OutgoingVerificationViewEvents.StartSasVerification) },
)
TextButton(
modifier = Modifier.fillMaxWidth(),
@@ -287,14 +287,14 @@ private fun VerifySelfSessionBottomMenu(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.screen_session_verification_they_match),
onClick = {
eventSink(VerifySelfSessionViewEvents.ConfirmVerification)
eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
},
)
TextButton(
modifier = Modifier.fillMaxWidth(),
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
@Composable
internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreview {
VerifySelfSessionView(
internal fun OutgoingVerificationViewPreview(@PreviewParameter(OutgoingVerificationStateProvider::class) state: OutgoingVerificationState) = ElementPreview {
OutgoingVerificationView(
state = state,
onLearnMoreClick = {},
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 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.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.EncryptionService
@@ -31,13 +31,13 @@ import org.junit.Rule
import org.junit.Test
@ExperimentalCoroutinesApi
class VerifySelfSessionPresenterTest {
class OutgoingVerificationPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
@Test
fun `present - Initial state is received`() = runTest {
val presenter = createVerifySelfSessionPresenter(
val presenter = createOutgoingVerificationPresenter(
service = unverifiedSessionService(),
)
presenter.test {
@@ -55,7 +55,7 @@ class VerifySelfSessionPresenterTest {
requestSessionVerificationLambda = requestSessionVerificationRecorder,
startVerificationLambda = startVerificationRecorder,
)
val presenter = createVerifySelfSessionPresenter(
val presenter = createOutgoingVerificationPresenter(
service = service,
verificationRequest = anOutgoingSessionVerificationRequest(),
)
@@ -75,7 +75,7 @@ class VerifySelfSessionPresenterTest {
requestUserVerificationLambda = requestUserVerificationRecorder,
startVerificationLambda = startVerificationRecorder,
)
val presenter = createVerifySelfSessionPresenter(
val presenter = createOutgoingVerificationPresenter(
service = service,
verificationRequest = anOutgoingUserVerificationRequest(),
)
@@ -89,14 +89,14 @@ class VerifySelfSessionPresenterTest {
@Test
fun `present - Cancellation on initial state moves to Exit state`() = runTest {
val presenter = createVerifySelfSessionPresenter(
val presenter = createOutgoingVerificationPresenter(
service = unverifiedSessionService(),
)
presenter.test {
val initialState = awaitItem()
assertThat(initialState.step).isEqualTo(Step.Initial)
val eventSink = initialState.eventSink
eventSink(VerifySelfSessionViewEvents.Cancel)
eventSink(OutgoingVerificationViewEvents.Cancel)
assertThat(awaitItem().step).isEqualTo(Step.Exit)
}
@@ -109,10 +109,10 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { },
approveVerificationLambda = { },
)
val presenter = createVerifySelfSessionPresenter(service)
val presenter = createOutgoingVerificationPresenter(service)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
// Cancelling
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFail)
@@ -126,9 +126,9 @@ class VerifySelfSessionPresenterTest {
val service = unverifiedSessionService(
requestSessionVerificationLambda = { },
)
val presenter = createVerifySelfSessionPresenter(service)
val presenter = createOutgoingVerificationPresenter(service)
presenter.test {
awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification)
awaitItem().eventSink(OutgoingVerificationViewEvents.RequestVerification)
service.emitVerificationFlowState(VerificationFlowState.DidFail)
assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java)
assertThat(awaitItem().step).isEqualTo(Step.Canceled)
@@ -142,10 +142,10 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { },
cancelVerificationLambda = { },
)
val presenter = createVerifySelfSessionPresenter(service)
val presenter = createOutgoingVerificationPresenter(service)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(VerifySelfSessionViewEvents.Cancel)
state.eventSink(OutgoingVerificationViewEvents.Cancel)
assertThat(awaitItem().step).isEqualTo(Step.Canceled)
}
}
@@ -156,7 +156,7 @@ class VerifySelfSessionPresenterTest {
requestSessionVerificationLambda = { },
startVerificationLambda = { },
)
val presenter = createVerifySelfSessionPresenter(service)
val presenter = createOutgoingVerificationPresenter(service)
presenter.test {
requestVerificationAndAwaitVerifyingState(service)
service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList())))
@@ -170,12 +170,12 @@ class VerifySelfSessionPresenterTest {
requestSessionVerificationLambda = { },
startVerificationLambda = { },
)
val presenter = createVerifySelfSessionPresenter(service)
val presenter = createOutgoingVerificationPresenter(service)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service)
service.emitVerificationFlowState(VerificationFlowState.DidCancel)
assertThat(awaitItem().step).isEqualTo(Step.Canceled)
state.eventSink(VerifySelfSessionViewEvents.Reset)
state.eventSink(OutgoingVerificationViewEvents.Reset)
// Went back to initial state
assertThat(awaitItem().step).isEqualTo(Step.Initial)
cancelAndIgnoreRemainingEvents()
@@ -192,13 +192,13 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { },
approveVerificationLambda = { },
)
val presenter = createVerifySelfSessionPresenter(service)
val presenter = createOutgoingVerificationPresenter(service)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(
service,
SessionVerificationData.Emojis(emojis)
)
state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
assertThat(awaitItem().step).isEqualTo(
Step.Verifying(
SessionVerificationData.Emojis(emojis),
@@ -217,10 +217,10 @@ class VerifySelfSessionPresenterTest {
startVerificationLambda = { },
declineVerificationLambda = { },
)
val presenter = createVerifySelfSessionPresenter(service)
val presenter = createOutgoingVerificationPresenter(service)
presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(VerifySelfSessionViewEvents.DeclineVerification)
state.eventSink(OutgoingVerificationViewEvents.DeclineVerification)
assertThat(awaitItem().step).isEqualTo(
Step.Verifying(
SessionVerificationData.Emojis(emptyList()),
@@ -241,7 +241,7 @@ class VerifySelfSessionPresenterTest {
emitVerifiedStatus(SessionVerifiedStatus.Verified)
emitVerificationFlowState(VerificationFlowState.DidFinish)
}
val presenter = createVerifySelfSessionPresenter(
val presenter = createOutgoingVerificationPresenter(
service = service,
showDeviceVerifiedScreen = true,
)
@@ -259,7 +259,7 @@ class VerifySelfSessionPresenterTest {
emitVerifiedStatus(SessionVerifiedStatus.Verified)
emitVerificationFlowState(VerificationFlowState.DidFinish)
}
val presenter = createVerifySelfSessionPresenter(
val presenter = createOutgoingVerificationPresenter(
service = service,
showDeviceVerifiedScreen = false,
)
@@ -269,13 +269,13 @@ class VerifySelfSessionPresenterTest {
}
}
private suspend fun ReceiveTurbine<VerifySelfSessionState>.requestVerificationAndAwaitVerifyingState(
private suspend fun ReceiveTurbine<OutgoingVerificationState>.requestVerificationAndAwaitVerifyingState(
fakeService: FakeSessionVerificationService,
sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()),
): VerifySelfSessionState {
): OutgoingVerificationState {
var state = awaitItem()
assertThat(state.step).isEqualTo(Step.Initial)
state.eventSink(VerifySelfSessionViewEvents.RequestVerification)
state.eventSink(OutgoingVerificationViewEvents.RequestVerification)
// Await for other device response:
fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
state = awaitItem()
@@ -283,7 +283,7 @@ class VerifySelfSessionPresenterTest {
// Await for the state to be Ready
state = awaitItem()
assertThat(state.step).isEqualTo(Step.Ready)
state.eventSink(VerifySelfSessionViewEvents.StartSasVerification)
state.eventSink(OutgoingVerificationViewEvents.StartSasVerification)
// Await for other device response (again):
fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification)
state = awaitItem()
@@ -321,13 +321,13 @@ class VerifySelfSessionPresenterTest {
}
}
private fun createVerifySelfSessionPresenter(
private fun createOutgoingVerificationPresenter(
service: SessionVerificationService,
verificationRequest: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(),
encryptionService: EncryptionService = FakeEncryptionService(),
showDeviceVerifiedScreen: Boolean = false,
): VerifySelfSessionPresenter {
return VerifySelfSessionPresenter(
): OutgoingVerificationPresenter {
return OutgoingVerificationPresenter(
showDeviceVerifiedScreen = showDeviceVerifiedScreen,
verificationRequest = verificationRequest,
sessionVerificationService = service,

View File

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

View File

@@ -16,8 +16,6 @@ import androidx.core.text.util.LinkifyCompat
import timber.log.Timber
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.isNotEmpty
import kotlin.collections.iterator
/**
* 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 kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.iterator
import kotlin.collections.orEmpty
@ContributesMultibinding(AppScope::class)
class SentryAnalyticsProvider @Inject constructor(