Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crypto copy adjustment #3649

Merged
merged 6 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.persistentListOf

@Composable
Expand All @@ -50,7 +49,7 @@ fun ResetIdentityRootView(
buttons = {
Button(
modifier = Modifier.fillMaxWidth(),
text = stringResource(id = CommonStrings.action_continue),
text = stringResource(id = R.string.screen_encryption_reset_action_continue_reset),
onClick = { state.eventSink(ResetIdentityRootEvent.Continue) },
destructive = true,
)
Expand Down Expand Up @@ -98,9 +97,9 @@ private fun Content() {
iconComposable = {
Icon(
modifier = Modifier.size(20.dp),
imageVector = CompoundIcons.Close(),
imageVector = CompoundIcons.Info(),
contentDescription = null,
tint = ElementTheme.colors.iconCriticalPrimary,
tint = ElementTheme.colors.iconSecondary,
)
},
),
Expand All @@ -109,9 +108,9 @@ private fun Content() {
iconComposable = {
Icon(
modifier = Modifier.size(20.dp),
imageVector = CompoundIcons.Close(),
imageVector = CompoundIcons.Info(),
contentDescription = null,
tint = ElementTheme.colors.iconCriticalPrimary,
tint = ElementTheme.colors.iconSecondary,
)
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<string name="screen_create_new_recovery_key_title">"Reset the encryption for your account using another device"</string>
<string name="screen_encryption_reset_action_continue_reset">"Continue reset"</string>
<string name="screen_encryption_reset_bullet_1">"Your account details, contacts, preferences, and chat list will be kept"</string>
<string name="screen_encryption_reset_bullet_2">"You will lose your existing message history unless it is stored on another device"</string>
<string name="screen_encryption_reset_bullet_2">"You will lose any message history that’s stored only on the server"</string>
<string name="screen_encryption_reset_bullet_3">"You will need to verify all your existing devices and contacts again"</string>
<string name="screen_encryption_reset_footer">"Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."</string>
<string name="screen_encryption_reset_title">"Can\'t confirm? You’ll need to reset your identity."</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class ResetIdentityRootViewTest {
ResetIdentityRootState(displayConfirmationDialog = false, eventSink = eventsRecorder),
)

rule.clickOn(CommonStrings.action_continue)
rule.clickOn(R.string.screen_encryption_reset_action_continue_reset)

eventsRecorder.assertSingle(ResetIdentityRootEvent.Continue)
}
Expand Down
2 changes: 2 additions & 0 deletions features/verifysession/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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()
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -140,7 +141,10 @@ fun VerifySelfSessionView(
)
}
) {
Content(flowState = verificationFlowStep)
Content(
flowState = verificationFlowStep,
onLearnMoreClick = onLearnMoreClick,
)
}
}

Expand Down Expand Up @@ -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
private 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))
}
}
}
}
Expand Down Expand Up @@ -292,14 +326,14 @@ private fun BottomMenu(
text = stringResource(R.string.screen_identity_use_another_device),
onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) },
)
OutlinedButton(
Button(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.screen_session_verification_enter_recovery_key),
onClick = onEnterRecoveryKey,
)
}
// This option should always be displayed
TextButton(
OutlinedButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.screen_identity_confirmation_cannot_confirm),
onClick = onResetKey,
Expand Down Expand Up @@ -402,6 +436,7 @@ private fun BottomMenu(
internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreview {
VerifySelfSessionView(
state = state,
onLearnMoreClick = {},
onEnterRecoveryKey = {},
onResetKey = {},
onFinish = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>()
Expand Down Expand Up @@ -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(),
Expand All @@ -230,6 +247,7 @@ class VerifySelfSessionViewTest {
setContent {
VerifySelfSessionView(
state = state,
onLearnMoreClick = onLearnMoreClick,
onEnterRecoveryKey = onEnterRecoveryKey,
onFinish = onFinished,
onResetKey = onResetKey,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading