SecureBackupEnterRecoveryKeyEvents -> SecureBackupEnterRecoveryKeyEvent

This commit is contained in:
Benoit Marty
2026-08-25 10:20:48 +02:00
parent c8c579ed7e
commit 2d611475da
7 changed files with 29 additions and 29 deletions
@@ -8,9 +8,9 @@
package io.element.android.features.securebackup.impl.enter package io.element.android.features.securebackup.impl.enter
sealed interface SecureBackupEnterRecoveryKeyEvents { sealed interface SecureBackupEnterRecoveryKeyEvent {
data class OnRecoveryKeyChange(val recoveryKey: String) : SecureBackupEnterRecoveryKeyEvents data class OnRecoveryKeyChange(val recoveryKey: String) : SecureBackupEnterRecoveryKeyEvent
data class ChangeRecoveryKeyFieldContentsVisibility(val visible: Boolean) : SecureBackupEnterRecoveryKeyEvents data class ChangeRecoveryKeyFieldContentsVisibility(val visible: Boolean) : SecureBackupEnterRecoveryKeyEvent
data object Submit : SecureBackupEnterRecoveryKeyEvents data object Submit : SecureBackupEnterRecoveryKeyEvent
data object ClearDialog : SecureBackupEnterRecoveryKeyEvents data object ClearDialog : SecureBackupEnterRecoveryKeyEvent
} }
@@ -45,12 +45,12 @@ class SecureBackupEnterRecoveryKeyPresenter(
mutableStateOf(AsyncAction.Uninitialized) mutableStateOf(AsyncAction.Uninitialized)
} }
fun handleEvent(event: SecureBackupEnterRecoveryKeyEvents) { fun handleEvent(event: SecureBackupEnterRecoveryKeyEvent) {
when (event) { when (event) {
SecureBackupEnterRecoveryKeyEvents.ClearDialog -> { SecureBackupEnterRecoveryKeyEvent.ClearDialog -> {
submitAction.value = AsyncAction.Uninitialized submitAction.value = AsyncAction.Uninitialized
} }
is SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange -> { is SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange -> {
val previousRecoveryKey = recoveryKey val previousRecoveryKey = recoveryKey
recoveryKey = if (previousRecoveryKey.isEmpty() && recoveryKeyTools.isRecoveryKeyFormatValid(event.recoveryKey)) { recoveryKey = if (previousRecoveryKey.isEmpty() && recoveryKeyTools.isRecoveryKeyFormatValid(event.recoveryKey)) {
// A Recovery key has been entered, remove the spaces for a better rendering // A Recovery key has been entered, remove the spaces for a better rendering
@@ -60,11 +60,11 @@ class SecureBackupEnterRecoveryKeyPresenter(
event.recoveryKey event.recoveryKey
} }
} }
SecureBackupEnterRecoveryKeyEvents.Submit -> { SecureBackupEnterRecoveryKeyEvent.Submit -> {
// No need to remove the spaces, the SDK will do it. // No need to remove the spaces, the SDK will do it.
coroutineScope.submitRecoveryKey(recoveryKey, submitAction) coroutineScope.submitRecoveryKey(recoveryKey, submitAction)
} }
is SecureBackupEnterRecoveryKeyEvents.ChangeRecoveryKeyFieldContentsVisibility -> { is SecureBackupEnterRecoveryKeyEvent.ChangeRecoveryKeyFieldContentsVisibility -> {
displayRecoveryKeyFieldContents = event.visible displayRecoveryKeyFieldContents = event.visible
} }
} }
@@ -15,5 +15,5 @@ data class SecureBackupEnterRecoveryKeyState(
val recoveryKeyViewState: RecoveryKeyViewState, val recoveryKeyViewState: RecoveryKeyViewState,
val isSubmitEnabled: Boolean, val isSubmitEnabled: Boolean,
val submitAction: AsyncAction<Unit>, val submitAction: AsyncAction<Unit>,
val eventSink: (SecureBackupEnterRecoveryKeyEvents) -> Unit val eventSink: (SecureBackupEnterRecoveryKeyEvent) -> Unit
) )
@@ -30,7 +30,7 @@ fun aSecureBackupEnterRecoveryKeyState(
isSubmitEnabled: Boolean = recoveryKey.isNotEmpty(), isSubmitEnabled: Boolean = recoveryKey.isNotEmpty(),
displayTextFieldContents: Boolean = true, displayTextFieldContents: Boolean = true,
submitAction: AsyncAction<Unit> = AsyncAction.Uninitialized, submitAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (SecureBackupEnterRecoveryKeyEvents) -> Unit = {}, eventSink: (SecureBackupEnterRecoveryKeyEvent) -> Unit = {},
) = SecureBackupEnterRecoveryKeyState( ) = SecureBackupEnterRecoveryKeyState(
recoveryKeyViewState = RecoveryKeyViewState( recoveryKeyViewState = RecoveryKeyViewState(
recoveryKeyUserStory = RecoveryKeyUserStory.Enter, recoveryKeyUserStory = RecoveryKeyUserStory.Enter,
@@ -41,7 +41,7 @@ fun SecureBackupEnterRecoveryKeyView(
progressDialog = { }, progressDialog = { },
errorTitle = { stringResource(id = R.string.screen_recovery_key_confirm_error_title) }, errorTitle = { stringResource(id = R.string.screen_recovery_key_confirm_error_title) },
errorMessage = { stringResource(id = R.string.screen_recovery_key_confirm_error_content) }, errorMessage = { stringResource(id = R.string.screen_recovery_key_confirm_error_content) },
onErrorDismiss = { state.eventSink(SecureBackupEnterRecoveryKeyEvents.ClearDialog) }, onErrorDismiss = { state.eventSink(SecureBackupEnterRecoveryKeyEvent.ClearDialog) },
) )
FlowStepPage( FlowStepPage(
@@ -68,13 +68,13 @@ private fun Content(
state = state.recoveryKeyViewState, state = state.recoveryKeyViewState,
onClick = null, onClick = null,
onChange = { onChange = {
state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange(it)) state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange(it))
}, },
onSubmit = { onSubmit = {
state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvent.Submit)
}, },
toggleRecoveryKeyVisibility = { toggleRecoveryKeyVisibility = {
state.eventSink(SecureBackupEnterRecoveryKeyEvents.ChangeRecoveryKeyFieldContentsVisibility(it)) state.eventSink(SecureBackupEnterRecoveryKeyEvent.ChangeRecoveryKeyFieldContentsVisibility(it))
} }
) )
} }
@@ -89,7 +89,7 @@ private fun ColumnScope.Buttons(
showProgress = state.submitAction.isLoading(), showProgress = state.submitAction.isLoading(),
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { onClick = {
state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvent.Submit)
} }
) )
} }
@@ -56,7 +56,7 @@ class SecureBackupEnterRecoveryKeyPresenterTest {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange("1234")) initialState.eventSink(SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange("1234"))
val withRecoveryKeyState = awaitItem() val withRecoveryKeyState = awaitItem()
assertThat(withRecoveryKeyState.isSubmitEnabled).isTrue() assertThat(withRecoveryKeyState.isSubmitEnabled).isTrue()
assertThat(withRecoveryKeyState.recoveryKeyViewState).isEqualTo( assertThat(withRecoveryKeyState.recoveryKeyViewState).isEqualTo(
@@ -68,19 +68,19 @@ class SecureBackupEnterRecoveryKeyPresenterTest {
) )
) )
encryptionService.givenRecoverFailure(AN_EXCEPTION) encryptionService.givenRecoverFailure(AN_EXCEPTION)
withRecoveryKeyState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit) withRecoveryKeyState.eventSink(SecureBackupEnterRecoveryKeyEvent.Submit)
val loadingState = awaitItem() val loadingState = awaitItem()
assertThat(loadingState.submitAction).isEqualTo(AsyncAction.Loading) assertThat(loadingState.submitAction).isEqualTo(AsyncAction.Loading)
assertThat(loadingState.isSubmitEnabled).isFalse() assertThat(loadingState.isSubmitEnabled).isFalse()
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.submitAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) assertThat(errorState.submitAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
assertThat(errorState.isSubmitEnabled).isFalse() assertThat(errorState.isSubmitEnabled).isFalse()
errorState.eventSink(SecureBackupEnterRecoveryKeyEvents.ClearDialog) errorState.eventSink(SecureBackupEnterRecoveryKeyEvent.ClearDialog)
val clearedState = awaitItem() val clearedState = awaitItem()
assertThat(clearedState.submitAction).isEqualTo(AsyncAction.Uninitialized) assertThat(clearedState.submitAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(clearedState.isSubmitEnabled).isTrue() assertThat(clearedState.isSubmitEnabled).isTrue()
encryptionService.givenRecoverFailure(null) encryptionService.givenRecoverFailure(null)
clearedState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit) clearedState.eventSink(SecureBackupEnterRecoveryKeyEvent.Submit)
val loadingState2 = awaitItem() val loadingState2 = awaitItem()
assertThat(loadingState2.submitAction).isEqualTo(AsyncAction.Loading) assertThat(loadingState2.submitAction).isEqualTo(AsyncAction.Loading)
assertThat(loadingState2.isSubmitEnabled).isFalse() assertThat(loadingState2.isSubmitEnabled).isFalse()
@@ -58,32 +58,32 @@ class SecureBackupEnterRecoveryKeyViewTest : RobolectricTest() {
@Test @Test
@Config(qualifiers = "h1024dp") @Config(qualifiers = "h1024dp")
fun `tapping on Continue when key is valid - calls expected action`() = runAndroidComposeUiTest { fun `tapping on Continue when key is valid - calls expected action`() = runAndroidComposeUiTest {
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>() val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvent>()
setSecureBackupEnterRecoveryKeyView( setSecureBackupEnterRecoveryKeyView(
aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder),
) )
clickOn(CommonStrings.action_continue) clickOn(CommonStrings.action_continue)
recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit) recorder.assertSingle(SecureBackupEnterRecoveryKeyEvent.Submit)
} }
@Test @Test
fun `entering a char emits the expected event`() = runAndroidComposeUiTest { fun `entering a char emits the expected event`() = runAndroidComposeUiTest {
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>() val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvent>()
val keyValue = aFormattedRecoveryKey() val keyValue = aFormattedRecoveryKey()
setSecureBackupEnterRecoveryKeyView( setSecureBackupEnterRecoveryKeyView(
aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder),
) )
onNodeWithText(keyValue).performTextInput("X") onNodeWithText(keyValue).performTextInput("X")
recorder.assertSingle( recorder.assertSingle(
SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange("X$keyValue") SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange("X$keyValue")
) )
} }
@Test @Test
@Config(qualifiers = "h1024dp") @Config(qualifiers = "h1024dp")
fun `toggling the visibility of the textfield changes it`() = runAndroidComposeUiTest { fun `toggling the visibility of the textfield changes it`() = runAndroidComposeUiTest {
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>() val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvent>()
val keyValue = aFormattedRecoveryKey() val keyValue = aFormattedRecoveryKey()
setSecureBackupEnterRecoveryKeyView(aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder)) setSecureBackupEnterRecoveryKeyView(aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder))
@@ -94,18 +94,18 @@ class SecureBackupEnterRecoveryKeyViewTest : RobolectricTest() {
waitForIdle() waitForIdle()
recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.ChangeRecoveryKeyFieldContentsVisibility(false)) recorder.assertSingle(SecureBackupEnterRecoveryKeyEvent.ChangeRecoveryKeyFieldContentsVisibility(false))
} }
@Test @Test
fun `validating from keyboard emits the expected event`() = runAndroidComposeUiTest { fun `validating from keyboard emits the expected event`() = runAndroidComposeUiTest {
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>() val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvent>()
val keyValue = aFormattedRecoveryKey() val keyValue = aFormattedRecoveryKey()
setSecureBackupEnterRecoveryKeyView( setSecureBackupEnterRecoveryKeyView(
aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder),
) )
onNodeWithText(keyValue).performImeAction() onNodeWithText(keyValue).performImeAction()
recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit) recorder.assertSingle(SecureBackupEnterRecoveryKeyEvent.Submit)
} }
@Test @Test