mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Ask for session verification before sharing with an unverified account
The incoming share is now displayed on top of the root, so the FTUE, and the session verification in particular, could be bypassed. - When the current account is not verified, wait for the FTUE to be complete before displaying the incoming share, as before. - When the user selects another account which is not verified in the room select screen, display the session verification flow of the FTUE for this account on top of the share. As in the FTUE, the user has to verify the session or to sign out. - Expose the FTUE session verification flow with a new SessionVerificationEntryPoint, displayed by OtherSessionVerificationNode which sets up the session graph of the account to verify. - Introduce RoomSelectNavigator so that RoomSelectPresenter can request the verification of the selected session. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4
@@ -72,6 +72,10 @@ class ForwardMessagesNode(
|
||||
override fun onCancel() {
|
||||
callback.onDone(emptyList())
|
||||
}
|
||||
|
||||
override fun onSessionVerificationRequired(sessionId: SessionId) {
|
||||
// Only called in Share mode
|
||||
}
|
||||
}
|
||||
|
||||
return roomSelectEntryPoint.createNode(
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations 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.ftue.api
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* Entry point to the session verification flow displayed during the FTUE, to be used outside of the FTUE.
|
||||
* As in the FTUE, the user cannot go back from this flow: they have to verify the session or to sign out.
|
||||
*/
|
||||
interface SessionVerificationEntryPoint : FeatureEntryPoint {
|
||||
fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: Callback,
|
||||
): Node
|
||||
|
||||
interface Callback : Plugin {
|
||||
/**
|
||||
* Called when the session has been verified.
|
||||
*/
|
||||
fun onDone()
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations 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.ftue.impl
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.ftue.api.SessionVerificationEntryPoint
|
||||
import io.element.android.features.ftue.impl.sessionverification.FtueSessionVerificationFlowNode
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultSessionVerificationEntryPoint : SessionVerificationEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: SessionVerificationEntryPoint.Callback,
|
||||
): Node {
|
||||
val flowNodeCallback = object : FtueSessionVerificationFlowNode.Callback {
|
||||
override fun onDone() {
|
||||
callback.onDone()
|
||||
}
|
||||
}
|
||||
return parentNode.createNode<FtueSessionVerificationFlowNode>(buildContext, listOf(flowNodeCallback))
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations 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.ftue.impl
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.ftue.api.SessionVerificationEntryPoint
|
||||
import io.element.android.features.ftue.impl.sessionverification.FtueSessionVerificationFlowNode
|
||||
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
||||
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultSessionVerificationEntryPointTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
@Test
|
||||
fun `test node builder`() = runTest {
|
||||
val entryPoint = DefaultSessionVerificationEntryPoint()
|
||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||
FtueSessionVerificationFlowNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
outgoingVerificationEntryPoint = object : OutgoingVerificationEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
params: OutgoingVerificationEntryPoint.Params,
|
||||
callback: OutgoingVerificationEntryPoint.Callback,
|
||||
) = lambdaError()
|
||||
},
|
||||
secureBackupEntryPoint = object : SecureBackupEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
params: SecureBackupEntryPoint.Params,
|
||||
callback: SecureBackupEntryPoint.Callback,
|
||||
) = lambdaError()
|
||||
},
|
||||
)
|
||||
}
|
||||
val callback = object : SessionVerificationEntryPoint.Callback {
|
||||
override fun onDone() = lambdaError()
|
||||
}
|
||||
val result = entryPoint.createNode(parentNode, BuildContext.root(null), callback)
|
||||
assertThat(result).isInstanceOf(FtueSessionVerificationFlowNode::class.java)
|
||||
}
|
||||
}
|
||||
+7
@@ -44,5 +44,12 @@ interface ShareEntryPoint : FeatureEntryPoint {
|
||||
* Called when the user cancels the share.
|
||||
*/
|
||||
fun onCancel()
|
||||
|
||||
/**
|
||||
* Called when the user has selected an account whose session is not verified.
|
||||
* The session has to be verified before the user can share content with it.
|
||||
* @param sessionId the session to verify.
|
||||
*/
|
||||
fun onSessionVerificationRequired(sessionId: SessionId)
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -69,6 +69,10 @@ class ShareNode(
|
||||
override fun onCancel() {
|
||||
callback.onCancel()
|
||||
}
|
||||
|
||||
override fun onSessionVerificationRequired(sessionId: SessionId) {
|
||||
callback.onSessionVerificationRequired(sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
return roomSelectEntryPoint.createNode(
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ class DefaultShareEntryPointTest {
|
||||
val callback = object : ShareEntryPoint.Callback {
|
||||
override fun onDone(sessionId: SessionId, roomIds: List<RoomId>) = lambdaError()
|
||||
override fun onCancel() = lambdaError()
|
||||
override fun onSessionVerificationRequired(sessionId: SessionId) = lambdaError()
|
||||
}
|
||||
val params = ShareEntryPoint.Params(
|
||||
sessionId = A_SESSION_ID,
|
||||
|
||||
Reference in New Issue
Block a user