mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Add client-side username check in auth flow
Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
<string name="display_name_error">От 1 до 64 символов</string>
|
<string name="display_name_error">От 1 до 64 символов</string>
|
||||||
<string name="fill_all_fields">Заполните все поля</string>
|
<string name="fill_all_fields">Заполните все поля</string>
|
||||||
<string name="username_length_error">Имя пользователя — от 3 до 20 символов</string>
|
<string name="username_length_error">Имя пользователя — от 3 до 20 символов</string>
|
||||||
|
<string name="username_chars_error">Имя пользователя может содержать только английские буквы, цифры, дефисы и подчеркивания</string>
|
||||||
<string name="password_length_error">Пароль — от 5 до 50 символов</string>
|
<string name="password_length_error">Пароль — от 5 до 50 символов</string>
|
||||||
<string name="passwords_dont_match">Пароли не совпадают</string>
|
<string name="passwords_dont_match">Пароли не совпадают</string>
|
||||||
<string name="auth_welcome_title">Добро пожаловать в FromChat</string>
|
<string name="auth_welcome_title">Добро пожаловать в FromChat</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<!-- Validation Errors -->
|
<!-- Validation Errors -->
|
||||||
<string name="fill_all_fields">Please fill in every field</string>
|
<string name="fill_all_fields">Please fill in every field</string>
|
||||||
<string name="username_length_error">Username must be 3 to 20 characters</string>
|
<string name="username_length_error">Username must be 3 to 20 characters</string>
|
||||||
|
<string name="username_chars_error">Username can only contain English letters, numbers, hyphens and underscores</string>
|
||||||
<string name="password_length_error">Password must be 5 to 50 characters</string>
|
<string name="password_length_error">Password must be 5 to 50 characters</string>
|
||||||
<string name="passwords_dont_match">The two passwords don’t match</string>
|
<string name="passwords_dont_match">The two passwords don’t match</string>
|
||||||
<string name="auth_welcome_title">Welcome to FromChat</string>
|
<string name="auth_welcome_title">Welcome to FromChat</string>
|
||||||
|
|||||||
@@ -42,8 +42,13 @@ import ru.fromchat.ui.components.expressiveStepFieldColors
|
|||||||
import ru.fromchat.ui.components.trackImeScrollTarget
|
import ru.fromchat.ui.components.trackImeScrollTarget
|
||||||
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
||||||
import ru.fromchat.username
|
import ru.fromchat.username
|
||||||
|
import ru.fromchat.username_chars_error
|
||||||
import ru.fromchat.username_length_error
|
import ru.fromchat.username_length_error
|
||||||
|
|
||||||
|
/** Matches backend `is_valid_username`: English letters, digits, hyphen, underscore. */
|
||||||
|
private fun isAllowedUsernameChar(ch: Char) =
|
||||||
|
ch in 'a'..'z' || ch in 'A'..'Z' || ch in '0'..'9' || ch == '_' || ch == '-'
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
internal fun usernameStepPage(
|
internal fun usernameStepPage(
|
||||||
@@ -59,9 +64,11 @@ internal fun usernameStepPage(
|
|||||||
|
|
||||||
val fillAll = stringResource(Res.string.fill_all_fields)
|
val fillAll = stringResource(Res.string.fill_all_fields)
|
||||||
val usernameLenError = stringResource(Res.string.username_length_error)
|
val usernameLenError = stringResource(Res.string.username_length_error)
|
||||||
|
val usernameCharsError = stringResource(Res.string.username_chars_error)
|
||||||
val serverFail = stringResource(Res.string.auth_server_connect_failed)
|
val serverFail = stringResource(Res.string.auth_server_connect_failed)
|
||||||
val unexpected = stringResource(Res.string.error_unexpected)
|
val unexpected = stringResource(Res.string.error_unexpected)
|
||||||
val nextLabel = stringResource(Res.string.settings_next)
|
val nextLabel = stringResource(Res.string.settings_next)
|
||||||
|
val hasProhibitedChars = username.trim().any { !isAllowedUsernameChar(it) }
|
||||||
|
|
||||||
return ExpressiveStepPage(
|
return ExpressiveStepPage(
|
||||||
hero = ExpressiveHeroSpec(
|
hero = ExpressiveHeroSpec(
|
||||||
@@ -90,6 +97,12 @@ internal fun usernameStepPage(
|
|||||||
.trackImeScrollTarget(imeScroll, ExpressiveStepLazyListIndices.STEPS_BODY)
|
.trackImeScrollTarget(imeScroll, ExpressiveStepLazyListIndices.STEPS_BODY)
|
||||||
.padding(horizontal = SettingsStepHorizontalPadding),
|
.padding(horizontal = SettingsStepHorizontalPadding),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
|
isError = hasProhibitedChars,
|
||||||
|
supportingText = if (hasProhibitedChars) {
|
||||||
|
{ Text(usernameCharsError) }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
colors = expressiveStepFieldColors(),
|
colors = expressiveStepFieldColors(),
|
||||||
shape = SettingsPasswordOutlineFieldShape,
|
shape = SettingsPasswordOutlineFieldShape,
|
||||||
)
|
)
|
||||||
@@ -98,7 +111,7 @@ internal fun usernameStepPage(
|
|||||||
button = {
|
button = {
|
||||||
ActionButton(
|
ActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (busy) return@ActionButton
|
if (busy || hasProhibitedChars) return@ActionButton
|
||||||
val trimmed = username.trim()
|
val trimmed = username.trim()
|
||||||
if (trimmed.isBlank()) {
|
if (trimmed.isBlank()) {
|
||||||
onSnackbar(fillAll, null)
|
onSnackbar(fillAll, null)
|
||||||
@@ -136,7 +149,7 @@ internal fun usernameStepPage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = !busy,
|
enabled = !busy && !hasProhibitedChars,
|
||||||
loading = busy,
|
loading = busy,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user