From fd819c2381e08fa3d3f6d1dc2780f3ad0aa50a98 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 10 Oct 2024 17:01:28 +0200 Subject: [PATCH] Add "Learn more" on identity verification first screen. --- .../android/appconfig/LearnMoreConfig.kt | 1 + features/verifysession/impl/build.gradle.kts | 2 + .../impl/VerifySelfSessionNode.kt | 9 ++ .../impl/VerifySelfSessionState.kt | 1 + .../impl/VerifySelfSessionView.kt | 89 +++++++++++++------ .../impl/VerifySelfSessionViewTest.kt | 18 ++++ 6 files changed, 93 insertions(+), 27 deletions(-) diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt index 662c332582..d1db7e5da4 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt @@ -8,6 +8,7 @@ package io.element.android.appconfig object LearnMoreConfig { + const val ENCRYPTION_URL: String = "https://element.io/help#encryption" const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5" const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18" } diff --git a/features/verifysession/impl/build.gradle.kts b/features/verifysession/impl/build.gradle.kts index 27aeaa7bc3..8f5f6cae24 100644 --- a/features/verifysession/impl/build.gradle.kts +++ b/features/verifysession/impl/build.gradle.kts @@ -23,6 +23,8 @@ android { setupAnvil() dependencies { + implementation(projects.appconfig) + implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt index ec8745ddba..3eb33b0c8d 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt @@ -18,9 +18,11 @@ 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.appconfig.LearnMoreConfig import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.features.verifysession.api.VerifySessionEntryPoint +import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -36,6 +38,10 @@ class VerifySelfSessionNode @AssistedInject constructor( showDeviceVerifiedScreen = inputs().showDeviceVerifiedScreen, ) + private fun onLearnMoreClick(activity: Activity, dark: Boolean) { + activity.openUrlInChromeCustomTab(null, dark, LearnMoreConfig.ENCRYPTION_URL) + } + @Composable override fun View(modifier: Modifier) { val state = presenter.present() @@ -44,6 +50,9 @@ class VerifySelfSessionNode @AssistedInject constructor( VerifySelfSessionView( state = state, modifier = modifier, + onLearnMoreClick = { + onLearnMoreClick(activity, isDark) + }, onEnterRecoveryKey = callback::onEnterRecoveryKey, onResetKey = callback::onResetKey, onFinish = callback::onDone, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt index 8565d1a7d2..5aadd786a0 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt @@ -23,6 +23,7 @@ data class VerifySelfSessionState( @Stable sealed interface VerificationStep { data object Loading : VerificationStep + // FIXME canEnterRecoveryKey value is never read. data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : VerificationStep data object Canceled : VerificationStep data object AwaitingOtherDeviceResponse : VerificationStep diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index d03cca7399..1c6905c4da 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -9,13 +9,13 @@ package io.element.android.features.verifysession.impl import androidx.activity.compose.BackHandler import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -64,6 +64,7 @@ import io.element.android.features.verifysession.impl.VerifySelfSessionState.Ver @Composable fun VerifySelfSessionView( state: VerifySelfSessionState, + onLearnMoreClick: () -> Unit, onEnterRecoveryKey: () -> Unit, onResetKey: () -> Unit, onFinish: () -> Unit, @@ -140,7 +141,10 @@ fun VerifySelfSessionView( ) } ) { - Content(flowState = verificationFlowStep) + Content( + flowState = verificationFlowStep, + onLearnMoreClick = onLearnMoreClick, + ) } } @@ -203,38 +207,68 @@ private fun HeaderContent(verificationFlowStep: FlowStep) { } @Composable -private fun Content(flowState: FlowStep) { - Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) { - if (flowState is FlowStep.Verifying) { +private fun Content( + flowState: FlowStep, + onLearnMoreClick: () -> Unit, +) { + when (flowState) { + is VerifySelfSessionState.VerificationStep.Initial -> { + ContentInitial(onLearnMoreClick) + } + is FlowStep.Verifying -> { ContentVerifying(flowState) } + else -> Unit + } +} + +@Composable +fun ContentInitial( + onLearnMoreClick: () -> Unit, +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + Text( + modifier = Modifier + .clickable { onLearnMoreClick() } + .padding(vertical = 4.dp, horizontal = 16.dp), + text = stringResource(CommonStrings.action_learn_more), + style = ElementTheme.typography.fontBodyLgMedium + ) } } @Composable private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying) { - when (verificationFlowStep.data) { - is SessionVerificationData.Decimals -> { - val text = verificationFlowStep.data.decimals.joinToString(separator = " - ") { it.toString() } - Text( - modifier = Modifier.fillMaxWidth(), - text = text, - style = ElementTheme.typography.fontHeadingLgBold, - color = MaterialTheme.colorScheme.primary, - textAlign = TextAlign.Center, - ) - } - is SessionVerificationData.Emojis -> { - // We want each row to have up to 4 emojis - val rows = verificationFlowStep.data.emojis.chunked(4) - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(40.dp), - ) { - rows.forEach { emojis -> - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { - for (emoji in emojis) { - EmojiItemView(emoji = emoji, modifier = Modifier.widthIn(max = 60.dp)) + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + when (verificationFlowStep.data) { + is SessionVerificationData.Decimals -> { + val text = verificationFlowStep.data.decimals.joinToString(separator = " - ") { it.toString() } + Text( + modifier = Modifier.fillMaxWidth(), + text = text, + style = ElementTheme.typography.fontHeadingLgBold, + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.Center, + ) + } + is SessionVerificationData.Emojis -> { + // We want each row to have up to 4 emojis + val rows = verificationFlowStep.data.emojis.chunked(4) + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(40.dp), + ) { + rows.forEach { emojis -> + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { + for (emoji in emojis) { + EmojiItemView(emoji = emoji, modifier = Modifier.widthIn(max = 60.dp)) + } } } } @@ -402,6 +436,7 @@ private fun BottomMenu( internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreview { VerifySelfSessionView( state = state, + onLearnMoreClick = {}, onEnterRecoveryKey = {}, onResetKey = {}, onFinish = {}, diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt index 425ad79313..dfe8aaf85d 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt @@ -146,6 +146,22 @@ class VerifySelfSessionViewTest { } } + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on learn more invokes the expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { callback -> + rule.setVerifySelfSessionView( + aVerifySelfSessionState( + verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true), + eventSink = eventsRecorder + ), + onLearnMoreClick = callback, + ) + rule.clickOn(CommonStrings.action_learn_more) + } + } + @Test fun `clicking on they match emits the expected event`() { val eventsRecorder = EventsRecorder() @@ -222,6 +238,7 @@ class VerifySelfSessionViewTest { private fun AndroidComposeTestRule.setVerifySelfSessionView( state: VerifySelfSessionState, + onLearnMoreClick: () -> Unit = EnsureNeverCalled(), onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(), onFinished: () -> Unit = EnsureNeverCalled(), onResetKey: () -> Unit = EnsureNeverCalled(), @@ -230,6 +247,7 @@ class VerifySelfSessionViewTest { setContent { VerifySelfSessionView( state = state, + onLearnMoreClick = onLearnMoreClick, onEnterRecoveryKey = onEnterRecoveryKey, onFinish = onFinished, onResetKey = onResetKey,