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