Always display 'lost recovery key?' option (#2745)
* Always display 'lost recovery key?' option * Use `isLastDevice` it to display only 'enter recovery key' option for verification. * Update strings. This should fix the wrong term 'passcode' being used in the recovery key screen title. * Disable 'lost your recovery key?' button while the screen is in a loading state * Update screenshots --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
6219190ef1
commit
7397df806b
30 changed files with 181 additions and 83 deletions
|
|
@ -23,6 +23,12 @@ plugins {
|
|||
|
||||
android {
|
||||
namespace = "io.element.android.features.securebackup.impl"
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anvil {
|
||||
|
|
@ -51,8 +57,11 @@ dependencies {
|
|||
testImplementation(libs.molecule.runtime)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(libs.test.robolectric)
|
||||
testImplementation(libs.androidx.compose.ui.test.junit)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
||||
ksp(libs.showkase.processor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ class SecureBackupFlowNode @AssistedInject constructor(
|
|||
backstack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateNewRecoveryKey() {
|
||||
backstack.push(NavTarget.CreateNewRecoveryKey)
|
||||
}
|
||||
}
|
||||
createNode<SecureBackupEnterRecoveryKeyNode>(buildContext, plugins = listOf(callback))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class SecureBackupEnterRecoveryKeyNode @AssistedInject constructor(
|
|||
) : Node(buildContext, plugins = plugins) {
|
||||
interface Callback : Plugin {
|
||||
fun onEnterRecoveryKeySuccess()
|
||||
fun onCreateNewRecoveryKey()
|
||||
}
|
||||
|
||||
private val callback = plugins<Callback>().first()
|
||||
|
|
@ -47,6 +48,7 @@ class SecureBackupEnterRecoveryKeyNode @AssistedInject constructor(
|
|||
modifier = modifier,
|
||||
onDone = callback::onEnterRecoveryKeySuccess,
|
||||
onBackClicked = ::navigateUp,
|
||||
onCreateNewRecoveryKey = callback::onCreateNewRecoveryKey
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ fun aSecureBackupEnterRecoveryKeyState(
|
|||
recoveryKey: String = aFormattedRecoveryKey(),
|
||||
isSubmitEnabled: Boolean = recoveryKey.isNotEmpty(),
|
||||
submitAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (SecureBackupEnterRecoveryKeyEvents) -> Unit = {},
|
||||
) = SecureBackupEnterRecoveryKeyState(
|
||||
recoveryKeyViewState = RecoveryKeyViewState(
|
||||
recoveryKeyUserStory = RecoveryKeyUserStory.Enter,
|
||||
|
|
@ -44,5 +45,5 @@ fun aSecureBackupEnterRecoveryKeyState(
|
|||
),
|
||||
isSubmitEnabled = isSubmitEnabled,
|
||||
submitAction = submitAction,
|
||||
eventSink = {}
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncActionVie
|
|||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
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.TextButton
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
|
|
@ -39,6 +40,7 @@ fun SecureBackupEnterRecoveryKeyView(
|
|||
state: SecureBackupEnterRecoveryKeyState,
|
||||
onDone: () -> Unit,
|
||||
onBackClicked: () -> Unit,
|
||||
onCreateNewRecoveryKey: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AsyncActionView(
|
||||
|
|
@ -57,7 +59,7 @@ fun SecureBackupEnterRecoveryKeyView(
|
|||
title = stringResource(id = R.string.screen_recovery_key_confirm_title),
|
||||
subTitle = stringResource(id = R.string.screen_recovery_key_confirm_description),
|
||||
content = { Content(state = state) },
|
||||
buttons = { Buttons(state = state) }
|
||||
buttons = { Buttons(state = state, onCreateRecoveryKey = onCreateNewRecoveryKey) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +83,7 @@ private fun Content(
|
|||
@Composable
|
||||
private fun ColumnScope.Buttons(
|
||||
state: SecureBackupEnterRecoveryKeyState,
|
||||
onCreateRecoveryKey: () -> Unit,
|
||||
) {
|
||||
Button(
|
||||
text = stringResource(id = CommonStrings.action_continue),
|
||||
|
|
@ -91,6 +94,12 @@ private fun ColumnScope.Buttons(
|
|||
state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit)
|
||||
}
|
||||
)
|
||||
TextButton(
|
||||
text = stringResource(id = R.string.screen_recovery_key_confirm_lost_recovery_key),
|
||||
enabled = !state.submitAction.isLoading(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onCreateRecoveryKey,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
|
|
@ -102,5 +111,6 @@ internal fun SecureBackupEnterRecoveryKeyViewPreview(
|
|||
state = state,
|
||||
onDone = {},
|
||||
onBackClicked = {},
|
||||
onCreateNewRecoveryKey = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@
|
|||
<string name="screen_recovery_key_confirm_key_description">"If you have a security key or security phrase, this will work too."</string>
|
||||
<string name="screen_recovery_key_confirm_key_label">"Recovery key or passcode"</string>
|
||||
<string name="screen_recovery_key_confirm_key_placeholder">"Enter…"</string>
|
||||
<string name="screen_recovery_key_confirm_lost_recovery_key">"Lost your recovery key?"</string>
|
||||
<string name="screen_recovery_key_confirm_success">"Recovery key confirmed"</string>
|
||||
<string name="screen_recovery_key_confirm_title">"Enter your recovery key or passcode"</string>
|
||||
<string name="screen_recovery_key_confirm_title">"Enter your recovery key"</string>
|
||||
<string name="screen_recovery_key_copied_to_clipboard">"Copied recovery key"</string>
|
||||
<string name="screen_recovery_key_generating_key">"Generating…"</string>
|
||||
<string name="screen_recovery_key_save_action">"Save recovery key"</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.securebackup.impl.enter
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.securebackup.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import io.element.android.tests.testutils.pressBackKey
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SecureBackupEnterRecoveryKeyViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `back key pressed - calls onBackClicked`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
aSecureBackupEnterRecoveryKeyState(),
|
||||
onBackClicked = callback,
|
||||
)
|
||||
rule.pressBackKey()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `back button clicked - calls onBackClicked`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
aSecureBackupEnterRecoveryKeyState(),
|
||||
onBackClicked = callback,
|
||||
)
|
||||
rule.pressBack()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tapping on Continue when key is valid - calls expected action`() {
|
||||
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>()
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_continue)
|
||||
|
||||
recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tapping on Lost your recovery key - calls onCreateNewRecoveryKey`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
aSecureBackupEnterRecoveryKeyState(),
|
||||
onCreateNewRecoveryKey = callback,
|
||||
)
|
||||
rule.clickOn(R.string.screen_recovery_key_confirm_lost_recovery_key)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when submit action succeeds - calls onDone`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
aSecureBackupEnterRecoveryKeyState(submitAction = AsyncAction.Success(Unit)),
|
||||
onDone = callback,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setSecureBackupEnterRecoveryKeyView(
|
||||
state: SecureBackupEnterRecoveryKeyState,
|
||||
onDone: () -> Unit = EnsureNeverCalled(),
|
||||
onBackClicked: () -> Unit = EnsureNeverCalled(),
|
||||
onCreateNewRecoveryKey: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
rule.setContent {
|
||||
SecureBackupEnterRecoveryKeyView(
|
||||
state = state,
|
||||
onDone = onDone,
|
||||
onBackClicked = onBackClicked,
|
||||
onCreateNewRecoveryKey = onCreateNewRecoveryKey
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue