diff --git a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreen.kt index a21ea5805b..98c22b7c76 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreen.kt @@ -104,6 +104,17 @@ fun AddUsernameScreen( ) } + if (state.dialogs.confirmSkip) { + Dialogs.SimpleAlertDialog( + title = stringResource(R.string.AddUsernameScreen__are_you_sure), + body = stringResource(R.string.AddUsernameScreen__without_a_username_people_wont_be_able_to_find_you), + confirm = stringResource(R.string.AddUsernameScreen__continue), + dismiss = stringResource(R.string.AddUsernameScreen__cancel), + onConfirm = { onEvent(AddUsernameScreenEvents.SkipConfirmed) }, + onDismiss = { onEvent(AddUsernameScreenEvents.SkipDialogDismissed) } + ) + } + if (state.dialogs.learnMore) { Dialogs.SimpleMessageDialog( title = stringResource(R.string.AddUsernameScreen__what_is_this_number), @@ -470,6 +481,17 @@ private fun AddUsernameScreenReservedPreview() { } } +@AllDevicePreviews +@Composable +private fun AddUsernameScreenConfirmSkipPreview() { + Previews.Preview { + AddUsernameScreen( + state = AddUsernameState(dialogs = AddUsernameState.Dialogs(confirmSkip = true)), + onEvent = {} + ) + } +} + @AllDevicePreviews @Composable private fun AddUsernameScreenErrorPreview() { diff --git a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreenEvents.kt b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreenEvents.kt index 90807110ef..74bfc00bfd 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreenEvents.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameScreenEvents.kt @@ -40,9 +40,15 @@ sealed class AddUsernameScreenEvents { /** The user dismissed the dialog explaining the digits after the username. */ data object LearnMoreDialogDismissed : AddUsernameScreenEvents() - /** The user opted out of choosing a username. */ + /** The user tapped the skip button, asking to opt out of choosing a username. */ data object SkipClicked : AddUsernameScreenEvents() + /** The user confirmed they want to skip choosing a username. */ + data object SkipConfirmed : AddUsernameScreenEvents() + + /** The user dismissed the dialog confirming they want to skip choosing a username. */ + data object SkipDialogDismissed : AddUsernameScreenEvents() + /** The user submitted the entered username. */ data object NextClicked : AddUsernameScreenEvents() diff --git a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameState.kt b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameState.kt index cb5604edcf..379aa0d3b0 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameState.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameState.kt @@ -61,6 +61,8 @@ data class AddUsernameState( data class Dialogs( /** Explains what the digits after the username are for. */ val learnMore: Boolean = false, + /** Confirms the user wants to skip choosing a username. */ + val confirmSkip: Boolean = false, val networkError: Boolean = false, val unknownError: Boolean = false, /** The reserved username was claimed by someone else before it could be confirmed. */ diff --git a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameViewModel.kt b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameViewModel.kt index 83509c1a0f..2fdd56295c 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameViewModel.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/addusername/AddUsernameViewModel.kt @@ -88,7 +88,9 @@ class AddUsernameViewModel( is AddUsernameScreenEvents.ReservationCompleted -> applyReservationCompleted(state, event, stateEmitter) is AddUsernameScreenEvents.LearnMoreClicked -> stateEmitter(state.copy(dialogs = state.dialogs.copy(learnMore = true))) is AddUsernameScreenEvents.LearnMoreDialogDismissed -> stateEmitter(state.copy(dialogs = state.dialogs.copy(learnMore = false))) - is AddUsernameScreenEvents.SkipClicked -> applySkipClicked(parentEventEmitter) + is AddUsernameScreenEvents.SkipClicked -> stateEmitter(state.copy(dialogs = state.dialogs.copy(confirmSkip = true))) + is AddUsernameScreenEvents.SkipConfirmed -> applySkipConfirmed(parentEventEmitter) + is AddUsernameScreenEvents.SkipDialogDismissed -> stateEmitter(state.copy(dialogs = state.dialogs.copy(confirmSkip = false))) is AddUsernameScreenEvents.NextClicked -> applyNextClicked(state, parentEventEmitter, stateEmitter) is AddUsernameScreenEvents.NetworkErrorDialogDismissed -> applyDialogDismissed(state, stateEmitter) { it.copy(networkError = false) } is AddUsernameScreenEvents.UnknownErrorDialogDismissed -> applyDialogDismissed(state, stateEmitter) { it.copy(unknownError = false) } @@ -222,7 +224,7 @@ class AddUsernameViewModel( } } - private fun applySkipClicked(parentEventEmitter: (RegistrationFlowEvent) -> Unit) { + private fun applySkipConfirmed(parentEventEmitter: (RegistrationFlowEvent) -> Unit) { Log.i(TAG, "Skipping username creation.") parentEventEmitter(RegistrationFlowEvent.RegistrationComplete) } diff --git a/feature/registration/src/main/res/values/strings.xml b/feature/registration/src/main/res/values/strings.xml index fca2734abf..2d27ef1d83 100644 --- a/feature/registration/src/main/res/values/strings.xml +++ b/feature/registration/src/main/res/values/strings.xml @@ -741,6 +741,14 @@ This number can\'t be 00. Enter a digit between 1–9 Numbers with more than 2 digits can\'t start with 0 + + Are you sure? + + Without a username people won\'t be able to find you on Signal. + + Cancel + + Continue diff --git a/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameScreenTest.kt b/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameScreenTest.kt index c28e2b2f18..7d9f7df773 100644 --- a/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameScreenTest.kt +++ b/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameScreenTest.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.performTextReplacement import androidx.test.core.app.ApplicationProvider import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.containsExactly import assertk.assertions.isEqualTo import assertk.assertions.isNotNull import org.junit.Rule @@ -22,6 +24,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.signal.core.ui.CoreUiDependenciesRule +import org.signal.core.ui.compose.Dialogs import org.signal.core.ui.compose.theme.SignalTheme import org.signal.libsignal.usernames.Username import org.signal.registration.test.TestTags @@ -113,4 +116,40 @@ class AddUsernameScreenTest { assertThat(emittedEvent).isEqualTo(AddUsernameScreenEvents.SkipClicked) } + + @Test + fun `confirming the skip dialog emits SkipConfirmed`() { + val emittedEvents = mutableListOf() + + composeTestRule.setContent { + SignalTheme { + AddUsernameScreen( + state = AddUsernameState(dialogs = AddUsernameState.Dialogs(confirmSkip = true)), + onEvent = { emittedEvents.add(it) } + ) + } + } + + composeTestRule.onNodeWithTag(Dialogs.TEST_TAG_ALERT_DIALOG_CONFIRM_BUTTON).performClick() + + assertThat(emittedEvents).contains(AddUsernameScreenEvents.SkipConfirmed) + } + + @Test + fun `cancelling the skip dialog emits SkipDialogDismissed`() { + val emittedEvents = mutableListOf() + + composeTestRule.setContent { + SignalTheme { + AddUsernameScreen( + state = AddUsernameState(dialogs = AddUsernameState.Dialogs(confirmSkip = true)), + onEvent = { emittedEvents.add(it) } + ) + } + } + + composeTestRule.onNodeWithTag(Dialogs.TEST_TAG_ALERT_DIALOG_DISMISS_BUTTON).performClick() + + assertThat(emittedEvents).containsExactly(AddUsernameScreenEvents.SkipDialogDismissed) + } } diff --git a/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameViewModelTest.kt b/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameViewModelTest.kt index 0b417ad53f..c3a4e048c3 100644 --- a/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameViewModelTest.kt +++ b/feature/registration/src/test/java/org/signal/registration/screens/addusername/AddUsernameViewModelTest.kt @@ -354,10 +354,32 @@ class AddUsernameViewModelTest { } @Test - fun `SkipClicked completes registration`() = runTest(testDispatcher) { + fun `SkipClicked shows the confirmation dialog without completing registration`() = runTest(testDispatcher) { viewModel.onEvent(AddUsernameScreenEvents.SkipClicked) advanceUntilIdle() + assertThat(viewModel.state.value.dialogs.confirmSkip).isTrue() + assertThat(parentEvents).isEmpty() + } + + @Test + fun `dismissing the skip confirmation dialog keeps the user on the screen`() = runTest(testDispatcher) { + viewModel.onEvent(AddUsernameScreenEvents.SkipClicked) + advanceUntilIdle() + viewModel.onEvent(AddUsernameScreenEvents.SkipDialogDismissed) + advanceUntilIdle() + + assertThat(viewModel.state.value.dialogs.confirmSkip).isFalse() + assertThat(parentEvents).isEmpty() + } + + @Test + fun `SkipConfirmed completes registration`() = runTest(testDispatcher) { + viewModel.onEvent(AddUsernameScreenEvents.SkipClicked) + advanceUntilIdle() + viewModel.onEvent(AddUsernameScreenEvents.SkipConfirmed) + advanceUntilIdle() + assertThat(parentEvents).containsExactly(RegistrationFlowEvent.RegistrationComplete) } }