Add confirm dialog when skipping username during numberless reg.

This commit is contained in:
Greyson Parrelli
2026-09-09 16:37:43 -04:00
committed by Cody Henthorne
parent 4b5749f843
commit 823c540a6e
7 changed files with 105 additions and 4 deletions
@@ -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() {
@@ -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()
@@ -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. */
@@ -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)
}
@@ -741,6 +741,14 @@
<string name="AddUsernameScreen__this_number_cant_be_00">This number can\'t be 00. Enter a digit between 19</string>
<!-- Validation error shown when the number the user entered after their username has a leading zero. -->
<string name="AddUsernameScreen__this_number_cant_start_with_0">Numbers with more than 2 digits can\'t start with 0</string>
<!-- Title of the dialog confirming the user wants to skip choosing a username. -->
<string name="AddUsernameScreen__are_you_sure">Are you sure?</string>
<!-- Body of the dialog confirming the user wants to skip choosing a username. -->
<string name="AddUsernameScreen__without_a_username_people_wont_be_able_to_find_you">Without a username people won\'t be able to find you on Signal.</string>
<!-- Button in the skip-username confirmation dialog that returns to the screen without skipping. -->
<string name="AddUsernameScreen__cancel">Cancel</string>
<!-- Button in the skip-username confirmation dialog that skips choosing a username. -->
<string name="AddUsernameScreen__continue">Continue</string>
<!-- ContactSupportDialog -->
<!-- Title of the dialog asking the user whether they want to attach a debug log to their support request -->
@@ -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<AddUsernameScreenEvents>()
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<AddUsernameScreenEvents>()
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)
}
}
@@ -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)
}
}