diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java index 0b9d608ade..39899d9ed0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java @@ -23,14 +23,14 @@ import androidx.navigation.fragment.NavHostFragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.textfield.TextInputLayout; +import org.signal.core.util.EditTextUtil; +import org.signal.core.util.concurrent.LifecycleDisposable; import org.thoughtcrime.securesms.LoggingFragment; import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; import org.thoughtcrime.securesms.databinding.UsernameEditFragmentBinding; -import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.util.FragmentResultContract; -import org.signal.core.util.concurrent.LifecycleDisposable; import org.thoughtcrime.securesms.util.UsernameUtil; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton; @@ -157,58 +157,27 @@ public class UsernameEditFragment extends LoggingFragment { binding.root.setLayoutTransition(ANIMATED_LAYOUT); - switch (state.usernameStatus) { - case NONE: - usernameInputWrapper.setError(null); - break; - case TOO_SHORT: - case TOO_LONG: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_NICKNAME_LENGTH, UsernameUtil.MAX_NICKNAME_LENGTH)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); + CharSequence error = switch (state.usernameStatus) { + case NONE -> null; + case TOO_SHORT, TOO_LONG -> getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_NICKNAME_LENGTH, UsernameUtil.MAX_NICKNAME_LENGTH); + case INVALID_CHARACTERS -> getString(R.string.UsernameEditFragment_usernames_can_only_include); + case CANNOT_START_WITH_NUMBER -> getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number); + case INVALID_GENERIC -> getString(R.string.UsernameEditFragment_username_is_invalid); + case TAKEN -> getString(R.string.UsernameEditFragment_this_username_is_taken); + case DISCRIMINATOR_HAS_INVALID_CHARACTERS, DISCRIMINATOR_NOT_AVAILABLE -> getString(R.string.UsernameEditFragment__this_username_is_not_available_try_another_number); + case DISCRIMINATOR_TOO_LONG -> getString(R.string.UsernameEditFragment__invalid_username_enter_a_maximum_of_d_digits, UsernameUtil.MAX_DISCRIMINATOR_LENGTH); + case DISCRIMINATOR_TOO_SHORT -> getString(R.string.UsernameEditFragment__invalid_username_enter_a_minimum_of_d_digits, UsernameUtil.MIN_DISCRIMINATOR_LENGTH); + }; - break; - case INVALID_CHARACTERS: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_can_only_include)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); + int colorRes = error != null ? R.color.signal_colorError : R.color.signal_colorPrimary; + int color = ContextCompat.getColor(requireContext(), colorRes); - break; - case CANNOT_START_WITH_NUMBER: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case INVALID_GENERIC: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_username_is_invalid)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case TAKEN: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_taken)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case DISCRIMINATOR_HAS_INVALID_CHARACTERS: - case DISCRIMINATOR_NOT_AVAILABLE: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment__this_username_is_not_available_try_another_number)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case DISCRIMINATOR_TOO_LONG: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment__invalid_username_enter_a_maximum_of_d_digits, UsernameUtil.MAX_DISCRIMINATOR_LENGTH)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case DISCRIMINATOR_TOO_SHORT: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment__invalid_username_enter_a_minimum_of_d_digits, UsernameUtil.MIN_DISCRIMINATOR_LENGTH)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - } - - CharSequence error = usernameInputWrapper.getError(); + binding.usernameTextFocusedStroke.setBackgroundColor(color); + binding.usernameTextWrapper.setHintTextColor(ColorStateList.valueOf(color)); + EditTextUtil.setCursorColor(binding.usernameText, color); + EditTextUtil.setCursorColor(binding.discriminatorText, color); binding.usernameError.setVisibility(error != null ? View.VISIBLE : View.GONE); - binding.usernameError.setText(usernameInputWrapper.getError()); - + binding.usernameError.setText(error); binding.root.setLayoutTransition(STATIC_LAYOUT); } @@ -224,9 +193,7 @@ public class UsernameEditFragment extends LoggingFragment { if (usernameState.getUsername() != null) { binding.summary.setText(usernameState.getUsername().getUsername()); binding.summary.setAlpha(1f); - } else if (usernameState instanceof UsernameState.Loading) { - binding.summary.setAlpha(0.5f); - } else { + } else if (!(usernameState instanceof UsernameState.Loading)) { binding.summary.setText(R.string.UsernameEditFragment__choose_your_username); binding.summary.setAlpha(1f); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt index a80fb91bda..23939372ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt @@ -83,23 +83,11 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr ) } - val invalidReason: InvalidReason? = checkUsername(nickname) - - if (invalidReason != null) { - // We only want to show actual errors after debouncing. But we also don't want to allow users to submit names with errors. - // So we disable submit, but we don't show an error yet. - State( - buttonState = ButtonState.SUBMIT_DISABLED, - usernameStatus = UsernameStatus.NONE, - usernameState = state.usernameState - ) - } else { - State( - buttonState = ButtonState.SUBMIT_DISABLED, - usernameStatus = UsernameStatus.NONE, - usernameState = state.usernameState - ) - } + State( + buttonState = ButtonState.SUBMIT_DISABLED, + usernameStatus = UsernameStatus.NONE, + usernameState = state.usernameState + ) } stateMachineStore.update { @@ -270,8 +258,6 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr return } - uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, UsernameState.Loading) } - val isDiscriminatorSetByUser = state is UsernameEditStateMachine.UserEnteredDiscriminator || state is UsernameEditStateMachine.UserEnteredNicknameAndDiscriminator val discriminator = if (isDiscriminatorSetByUser) { state.discriminator @@ -281,16 +267,17 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr val discriminatorInvalidReason = checkDiscriminator(discriminator) if (isDiscriminatorSetByUser && discriminatorInvalidReason != null) { - uiState.update { s -> - State( + uiState.update { uiState -> + uiState.copy( buttonState = ButtonState.SUBMIT_DISABLED, - usernameStatus = mapDiscriminatorError(discriminatorInvalidReason), - usernameState = s.usernameState + usernameStatus = mapDiscriminatorError(discriminatorInvalidReason) ) } return } + uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, UsernameState.Loading) } + disposables += UsernameRepository.reserveUsername(nickname, discriminator).subscribe { result: Result -> result.either( onSuccess = { reserved: UsernameState.Reserved -> diff --git a/app/src/main/res/drawable/username_edit_box_fill.xml b/app/src/main/res/drawable/username_edit_box_fill.xml new file mode 100644 index 0000000000..3f1672c78c --- /dev/null +++ b/app/src/main/res/drawable/username_edit_box_fill.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/username_edit_fragment.xml b/app/src/main/res/layout/username_edit_fragment.xml index fd596cbc0b..bc4f5b0a04 100644 --- a/app/src/main/res/layout/username_edit_fragment.xml +++ b/app/src/main/res/layout/username_edit_fragment.xml @@ -39,7 +39,7 @@ android:id="@+id/username_box_fill" android:layout_width="0dp" android:layout_height="0dp" - android:background="@color/signal_colorSurfaceVariant" + android:background="@drawable/username_edit_box_fill" app:layout_constraintBottom_toBottomOf="@id/username_text_wrapper" app:layout_constraintEnd_toEndOf="@id/discriminator_text" app:layout_constraintStart_toStartOf="@id/username_text_wrapper" @@ -92,9 +92,10 @@ android:imeOptions="actionDone" android:importantForAutofill="no" android:inputType="number" + android:maxLength="19" android:maxLines="1" - android:paddingHorizontal="16dp" android:minHeight="48dp" + android:paddingHorizontal="16dp" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="@id/username_text_wrapper" app:layout_constraintEnd_toEndOf="parent" @@ -105,12 +106,13 @@ android:layout_width="16dp" android:layout_height="16dp" android:layout_marginBottom="10dp" + android:layout_marginEnd="16dp" app:indicatorColor="@color/signal_colorOnSurfaceVariant" app:indicatorSize="16dp" app:layout_constraintBottom_toBottomOf="@id/username_text_wrapper" app:layout_constraintEnd_toStartOf="@id/discriminator_text" app:trackColor="@color/transparent" - app:trackThickness="1dp" /> + app:trackThickness="1.75dp" />