Add a way to enter recovery key to verify the session.
This commit is contained in:
parent
42e990e472
commit
4345f26d0b
14 changed files with 193 additions and 41 deletions
|
|
@ -216,7 +216,9 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
data object VerifySession : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object SecureBackup : NavTarget
|
||||
data class SecureBackup(
|
||||
val initialElement: SecureBackupEntryPoint.InitialTarget = SecureBackupEntryPoint.InitialTarget.Root
|
||||
) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object InviteList : NavTarget
|
||||
|
|
@ -298,7 +300,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun onSecureBackupClicked() {
|
||||
backstack.push(NavTarget.SecureBackup)
|
||||
backstack.push(NavTarget.SecureBackup())
|
||||
}
|
||||
|
||||
override fun onOpenRoomNotificationSettings(roomId: RoomId) {
|
||||
|
|
@ -324,10 +326,24 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
.build()
|
||||
}
|
||||
NavTarget.VerifySession -> {
|
||||
verifySessionEntryPoint.createNode(this, buildContext)
|
||||
val callback = object : VerifySessionEntryPoint.Callback {
|
||||
override fun onEnterRecoveryKey() {
|
||||
backstack.replace(
|
||||
NavTarget.SecureBackup(
|
||||
initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
verifySessionEntryPoint
|
||||
.nodeBuilder(this, buildContext)
|
||||
.callback(callback)
|
||||
.build()
|
||||
}
|
||||
NavTarget.SecureBackup -> {
|
||||
secureBackupEntryPoint.createNode(this, buildContext)
|
||||
is NavTarget.SecureBackup -> {
|
||||
secureBackupEntryPoint.nodeBuilder(this, buildContext)
|
||||
.params(SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement))
|
||||
.build()
|
||||
}
|
||||
NavTarget.InviteList -> {
|
||||
val callback = object : InviteListEntryPoint.Callback {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,28 @@
|
|||
|
||||
package io.element.android.features.securebackup.api
|
||||
|
||||
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
|
||||
import android.os.Parcelable
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||
import io.element.android.libraries.architecture.NodeInputs
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
interface SecureBackupEntryPoint : SimpleFeatureEntryPoint
|
||||
interface SecureBackupEntryPoint : FeatureEntryPoint {
|
||||
sealed interface InitialTarget : Parcelable {
|
||||
@Parcelize
|
||||
data object Root : InitialTarget
|
||||
|
||||
@Parcelize
|
||||
data object EnterRecoveryKey : InitialTarget
|
||||
}
|
||||
|
||||
data class Params(val initialElement: InitialTarget) : NodeInputs
|
||||
|
||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
||||
|
||||
interface NodeBuilder {
|
||||
fun params(params: Params): NodeBuilder
|
||||
fun build(): Node
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.securebackup.impl
|
|||
|
||||
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.securebackup.api.SecureBackupEntryPoint
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
|
|
@ -26,7 +27,18 @@ import javax.inject.Inject
|
|||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultSecureBackupEntryPoint @Inject constructor() : SecureBackupEntryPoint {
|
||||
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
|
||||
return parentNode.createNode<SecureBackupFlowNode>(buildContext)
|
||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): SecureBackupEntryPoint.NodeBuilder {
|
||||
val plugins = ArrayList<Plugin>()
|
||||
|
||||
return object : SecureBackupEntryPoint.NodeBuilder {
|
||||
override fun params(params: SecureBackupEntryPoint.Params): SecureBackupEntryPoint.NodeBuilder {
|
||||
plugins += params
|
||||
return this
|
||||
}
|
||||
|
||||
override fun build(): Node {
|
||||
return parentNode.createNode<SecureBackupFlowNode>(buildContext, plugins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
||||
import io.element.android.features.securebackup.impl.disable.SecureBackupDisableNode
|
||||
import io.element.android.features.securebackup.impl.enable.SecureBackupEnableNode
|
||||
import io.element.android.features.securebackup.impl.enter.SecureBackupEnterRecoveryKeyNode
|
||||
|
|
@ -44,7 +45,10 @@ class SecureBackupFlowNode @AssistedInject constructor(
|
|||
@Assisted plugins: List<Plugin>,
|
||||
) : BaseFlowNode<SecureBackupFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.Root,
|
||||
initialElement = when (plugins.filterIsInstance(SecureBackupEntryPoint.Params::class.java).first().initialElement) {
|
||||
SecureBackupEntryPoint.InitialTarget.Root -> NavTarget.Root
|
||||
SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey -> NavTarget.EnterRecoveryKey
|
||||
},
|
||||
savedStateMap = buildContext.savedStateMap,
|
||||
),
|
||||
buildContext = buildContext,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,20 @@
|
|||
|
||||
package io.element.android.features.verifysession.api
|
||||
|
||||
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||
|
||||
interface VerifySessionEntryPoint : SimpleFeatureEntryPoint
|
||||
interface VerifySessionEntryPoint : FeatureEntryPoint {
|
||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
||||
|
||||
interface NodeBuilder {
|
||||
fun callback(callback: Callback): NodeBuilder
|
||||
fun build(): Node
|
||||
}
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onEnterRecoveryKey()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.verifysession.impl
|
|||
|
||||
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.libraries.architecture.createNode
|
||||
|
|
@ -26,7 +27,18 @@ import javax.inject.Inject
|
|||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultVerifySessionEntryPoint @Inject constructor() : VerifySessionEntryPoint {
|
||||
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
|
||||
return parentNode.createNode<VerifySelfSessionNode>(buildContext)
|
||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): VerifySessionEntryPoint.NodeBuilder {
|
||||
val plugins = ArrayList<Plugin>()
|
||||
|
||||
return object : VerifySessionEntryPoint.NodeBuilder {
|
||||
override fun callback(callback: VerifySessionEntryPoint.Callback): VerifySessionEntryPoint.NodeBuilder {
|
||||
plugins += callback
|
||||
return this
|
||||
}
|
||||
|
||||
override fun build(): Node {
|
||||
return parentNode.createNode<VerifySelfSessionNode>(buildContext, plugins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ import androidx.compose.ui.Modifier
|
|||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
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.libraries.di.SessionScope
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
|
|
@ -32,12 +34,20 @@ class VerifySelfSessionNode @AssistedInject constructor(
|
|||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: VerifySelfSessionPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
|
||||
private fun onEnterRecoveryKey() {
|
||||
plugins<VerifySessionEntryPoint.Callback>().forEach {
|
||||
it.onEnterRecoveryKey()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
VerifySelfSessionView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onEnterRecoveryKey = { onEnterRecoveryKey() },
|
||||
goBack = { navigateUp() }
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,15 @@ package io.element.android.features.verifysession.impl
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import com.freeletics.flowredux.compose.rememberStateAndDispatch
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -38,6 +41,7 @@ import io.element.android.features.verifysession.impl.VerifySelfSessionStateMach
|
|||
|
||||
class VerifySelfSessionPresenter @Inject constructor(
|
||||
private val sessionVerificationService: SessionVerificationService,
|
||||
private val encryptionService: EncryptionService,
|
||||
private val stateMachine: VerifySelfSessionStateMachine,
|
||||
) : Presenter<VerifySelfSessionState> {
|
||||
@Composable
|
||||
|
|
@ -46,9 +50,14 @@ class VerifySelfSessionPresenter @Inject constructor(
|
|||
// Force reset, just in case the service was left in a broken state
|
||||
sessionVerificationService.reset()
|
||||
}
|
||||
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
|
||||
val stateAndDispatch = stateMachine.rememberStateAndDispatch()
|
||||
val verificationFlowStep by remember {
|
||||
derivedStateOf { stateAndDispatch.state.value.toVerificationStep() }
|
||||
derivedStateOf {
|
||||
stateAndDispatch.state.value.toVerificationStep(
|
||||
canEnterRecoveryKey = recoveryState == RecoveryState.INCOMPLETE
|
||||
)
|
||||
}
|
||||
}
|
||||
// Start this after observing state machine
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
@ -71,10 +80,12 @@ class VerifySelfSessionPresenter @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun StateMachineState?.toVerificationStep(): VerifySelfSessionState.VerificationStep =
|
||||
private fun StateMachineState?.toVerificationStep(
|
||||
canEnterRecoveryKey: Boolean
|
||||
): VerifySelfSessionState.VerificationStep =
|
||||
when (val machineState = this) {
|
||||
StateMachineState.Initial, null -> {
|
||||
VerifySelfSessionState.VerificationStep.Initial
|
||||
VerifySelfSessionState.VerificationStep.Initial(canEnterRecoveryKey = canEnterRecoveryKey)
|
||||
}
|
||||
StateMachineState.RequestingVerification,
|
||||
StateMachineState.StartingSasVerification,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ data class VerifySelfSessionState(
|
|||
) {
|
||||
@Stable
|
||||
sealed interface VerificationStep {
|
||||
data object Initial : VerificationStep
|
||||
data class Initial(val canEnterRecoveryKey: Boolean) : VerificationStep
|
||||
data object Canceled : VerificationStep
|
||||
data object AwaitingOtherDeviceResponse : VerificationStep
|
||||
data object Ready : VerificationStep
|
||||
|
|
|
|||
|
|
@ -25,24 +25,27 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS
|
|||
override val values: Sequence<VerifySelfSessionState>
|
||||
get() = sequenceOf(
|
||||
aVerifySelfSessionState(),
|
||||
aVerifySelfSessionState().copy(
|
||||
aVerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.AwaitingOtherDeviceResponse
|
||||
),
|
||||
aVerifySelfSessionState().copy(
|
||||
aVerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized)
|
||||
),
|
||||
aVerifySelfSessionState().copy(
|
||||
aVerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading())
|
||||
),
|
||||
aVerifySelfSessionState().copy(
|
||||
aVerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.Canceled
|
||||
),
|
||||
aVerifySelfSessionState().copy(
|
||||
aVerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.Ready
|
||||
),
|
||||
aVerifySelfSessionState().copy(
|
||||
aVerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized)
|
||||
),
|
||||
aVerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true)
|
||||
),
|
||||
// Add other state here
|
||||
)
|
||||
}
|
||||
|
|
@ -59,8 +62,10 @@ private fun aDecimalsSessionVerificationData(
|
|||
return SessionVerificationData.Decimals(decimals)
|
||||
}
|
||||
|
||||
private fun aVerifySelfSessionState() = VerifySelfSessionState(
|
||||
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial,
|
||||
private fun aVerifySelfSessionState(
|
||||
verificationFlowStep: VerifySelfSessionState.VerificationStep = VerifySelfSessionState.VerificationStep.Initial(false),
|
||||
) = VerifySelfSessionState(
|
||||
verificationFlowStep = verificationFlowStep,
|
||||
eventSink = {},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import io.element.android.features.verifysession.impl.VerifySelfSessionState.Ver
|
|||
fun VerifySelfSessionView(
|
||||
state: VerifySelfSessionState,
|
||||
modifier: Modifier = Modifier,
|
||||
onEnterRecoveryKey: () -> Unit,
|
||||
goBack: () -> Unit,
|
||||
) {
|
||||
fun goBackAndCancelIfNeeded() {
|
||||
|
|
@ -85,7 +86,11 @@ fun VerifySelfSessionView(
|
|||
},
|
||||
footer = {
|
||||
if (buttonsVisible) {
|
||||
BottomMenu(screenState = state, goBack = ::goBackAndCancelIfNeeded)
|
||||
BottomMenu(
|
||||
screenState = state,
|
||||
goBack = ::goBackAndCancelIfNeeded,
|
||||
onEnterRecoveryKey = onEnterRecoveryKey
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
|
@ -96,13 +101,13 @@ fun VerifySelfSessionView(
|
|||
@Composable
|
||||
private fun HeaderContent(verificationFlowStep: FlowStep) {
|
||||
val iconResourceId = when (verificationFlowStep) {
|
||||
FlowStep.Initial -> R.drawable.ic_verification_devices
|
||||
is FlowStep.Initial -> R.drawable.ic_verification_devices
|
||||
FlowStep.Canceled -> R.drawable.ic_verification_warning
|
||||
FlowStep.AwaitingOtherDeviceResponse -> R.drawable.ic_verification_waiting
|
||||
FlowStep.Ready, is FlowStep.Verifying, FlowStep.Completed -> R.drawable.ic_verification_emoji
|
||||
}
|
||||
val titleTextId = when (verificationFlowStep) {
|
||||
FlowStep.Initial -> R.string.screen_session_verification_open_existing_session_title
|
||||
is FlowStep.Initial -> R.string.screen_session_verification_open_existing_session_title
|
||||
FlowStep.Canceled -> CommonStrings.common_verification_cancelled
|
||||
FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_to_accept_title
|
||||
FlowStep.Ready,
|
||||
|
|
@ -113,7 +118,7 @@ private fun HeaderContent(verificationFlowStep: FlowStep) {
|
|||
}
|
||||
}
|
||||
val subtitleTextId = when (verificationFlowStep) {
|
||||
FlowStep.Initial -> R.string.screen_session_verification_open_existing_session_subtitle
|
||||
is FlowStep.Initial -> R.string.screen_session_verification_open_existing_session_subtitle
|
||||
FlowStep.Canceled -> R.string.screen_session_verification_cancelled_subtitle
|
||||
FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_to_accept_subtitle
|
||||
FlowStep.Ready -> R.string.screen_session_verification_ready_subtitle
|
||||
|
|
@ -136,7 +141,7 @@ private fun HeaderContent(verificationFlowStep: FlowStep) {
|
|||
private fun Content(flowState: FlowStep) {
|
||||
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) {
|
||||
when (flowState) {
|
||||
FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit
|
||||
is FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit
|
||||
FlowStep.AwaitingOtherDeviceResponse -> ContentWaiting()
|
||||
is FlowStep.Verifying -> ContentVerifying(flowState)
|
||||
}
|
||||
|
|
@ -203,13 +208,17 @@ private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifie
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) {
|
||||
private fun BottomMenu(
|
||||
screenState: VerifySelfSessionState,
|
||||
onEnterRecoveryKey: () -> Unit,
|
||||
goBack: () -> Unit,
|
||||
) {
|
||||
val verificationViewState = screenState.verificationFlowStep
|
||||
val eventSink = screenState.eventSink
|
||||
|
||||
val isVerifying = (verificationViewState as? FlowStep.Verifying)?.state is AsyncData.Loading<Unit>
|
||||
val positiveButtonTitle = when (verificationViewState) {
|
||||
FlowStep.Initial -> R.string.screen_session_verification_positive_button_initial
|
||||
is FlowStep.Initial -> R.string.screen_session_verification_positive_button_initial
|
||||
FlowStep.Canceled -> R.string.screen_session_verification_positive_button_canceled
|
||||
is FlowStep.Verifying -> {
|
||||
if (isVerifying) {
|
||||
|
|
@ -222,7 +231,7 @@ private fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
|
|||
else -> null
|
||||
}
|
||||
val negativeButtonTitle = when (verificationViewState) {
|
||||
FlowStep.Initial -> CommonStrings.action_cancel
|
||||
is FlowStep.Initial -> CommonStrings.action_cancel
|
||||
FlowStep.Canceled -> CommonStrings.action_cancel
|
||||
is FlowStep.Verifying -> R.string.screen_session_verification_they_dont_match
|
||||
else -> null
|
||||
|
|
@ -230,7 +239,7 @@ private fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
|
|||
val negativeButtonEnabled = !isVerifying
|
||||
|
||||
val positiveButtonEvent = when (verificationViewState) {
|
||||
FlowStep.Initial -> VerifySelfSessionViewEvents.RequestVerification
|
||||
is FlowStep.Initial -> VerifySelfSessionViewEvents.RequestVerification
|
||||
FlowStep.Ready -> VerifySelfSessionViewEvents.StartSasVerification
|
||||
is FlowStep.Verifying -> if (!isVerifying) VerifySelfSessionViewEvents.ConfirmVerification else null
|
||||
FlowStep.Canceled -> VerifySelfSessionViewEvents.Restart
|
||||
|
|
@ -263,6 +272,17 @@ private fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
|
|||
enabled = negativeButtonEnabled,
|
||||
)
|
||||
}
|
||||
if (verificationViewState is FlowStep.Initial && verificationViewState.canEnterRecoveryKey) {
|
||||
Text(
|
||||
text = stringResource(id = CommonStrings.common_or),
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
TextButton(
|
||||
text = stringResource(R.string.screen_session_verification_enter_recovery_key),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onEnterRecoveryKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -271,6 +291,7 @@ private fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
|
|||
internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreview {
|
||||
VerifySelfSessionView(
|
||||
state = state,
|
||||
onEnterRecoveryKey = {},
|
||||
goBack = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,13 @@ import app.cash.turbine.test
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
|
@ -44,7 +48,21 @@ class VerifySelfSessionPresenterTests {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial)
|
||||
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Initial state is received, can use recovery key`() = runTest {
|
||||
val presenter = createVerifySelfSessionPresenter(
|
||||
encryptionService = FakeEncryptionService().apply {
|
||||
emitRecoveryState(RecoveryState.INCOMPLETE)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(true))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +85,7 @@ class VerifySelfSessionPresenterTests {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial)
|
||||
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
|
||||
val eventSink = initialState.eventSink
|
||||
eventSink(VerifySelfSessionViewEvents.StartSasVerification)
|
||||
// Await for other device response:
|
||||
|
|
@ -86,7 +104,7 @@ class VerifySelfSessionPresenterTests {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial)
|
||||
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
|
||||
val eventSink = initialState.eventSink
|
||||
eventSink(VerifySelfSessionViewEvents.CancelAndClose)
|
||||
expectNoEvents()
|
||||
|
|
@ -203,7 +221,7 @@ class VerifySelfSessionPresenterTests {
|
|||
sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()),
|
||||
): VerifySelfSessionState {
|
||||
var state = awaitItem()
|
||||
assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.Initial)
|
||||
assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
|
||||
state.eventSink(VerifySelfSessionViewEvents.RequestVerification)
|
||||
// Await for other device response:
|
||||
state = awaitItem()
|
||||
|
|
@ -223,8 +241,13 @@ class VerifySelfSessionPresenterTests {
|
|||
}
|
||||
|
||||
private fun createVerifySelfSessionPresenter(
|
||||
service: FakeSessionVerificationService = FakeSessionVerificationService()
|
||||
service: SessionVerificationService = FakeSessionVerificationService(),
|
||||
encryptionService: EncryptionService = FakeEncryptionService(),
|
||||
): VerifySelfSessionPresenter {
|
||||
return VerifySelfSessionPresenter(service, VerifySelfSessionStateMachine(service))
|
||||
return VerifySelfSessionPresenter(
|
||||
sessionVerificationService = service,
|
||||
encryptionService = encryptionService,
|
||||
stateMachine = VerifySelfSessionStateMachine(service),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@
|
|||
<string name="common_mute">"Mute"</string>
|
||||
<string name="common_no_results">"No results"</string>
|
||||
<string name="common_offline">"Offline"</string>
|
||||
<string name="common_or">"or"</string>
|
||||
<string name="common_password">"Password"</string>
|
||||
<string name="common_people">"People"</string>
|
||||
<string name="common_permalink">"Permalink"</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue