Add "Learn more" on identity verification first screen.

This commit is contained in:
Benoit Marty
2024-10-10 17:01:28 +02:00
parent afba74cb63
commit 89e9baa9d5
6 changed files with 93 additions and 27 deletions

View File

@@ -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"
}

View File

@@ -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)

View File

@@ -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<VerifySessionEntryPoint.Params>().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,

View File

@@ -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

View File

@@ -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 = {},

View File

@@ -146,6 +146,22 @@ class VerifySelfSessionViewTest {
}
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on learn more invokes the expected callback`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>(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<VerifySelfSessionViewEvents>()
@@ -222,6 +238,7 @@ class VerifySelfSessionViewTest {
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.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,