diff --git a/src/lib/strings/handles.ts b/src/lib/strings/handles.ts index bc07b32ec6..052dab3774 100644 --- a/src/lib/strings/handles.ts +++ b/src/lib/strings/handles.ts @@ -25,28 +25,32 @@ export function sanitizeHandle(handle: string, prefix = ''): string { return isInvalidHandle(handle) ? '⚠Invalid Handle' : `${prefix}${handle}` } -export interface IsValidHandle { - handleChars: boolean - hyphenStartOrEnd: boolean - frontLength: boolean - totalLength: boolean - overall: boolean -} +type ValidHandleResult = + | 'charsError' + | 'hyphenError' + | 'frontLengthError' + | 'totalLengthError' + | 'valid' // More checks from https://github.com/bluesky-social/atproto/blob/main/packages/pds/src/handle/index.ts#L72 -export function validateHandle(str: string, userDomain: string): IsValidHandle { +export function validateHandle( + str: string, + userDomain: string, +): ValidHandleResult { const fullHandle = createFullHandle(str, userDomain) - const results = { - handleChars: - !str || (VALIDATE_REGEX.test(fullHandle) && !str.includes('.')), - hyphenStartOrEnd: !str.startsWith('-') && !str.endsWith('-'), - frontLength: str.length >= 3, - totalLength: fullHandle.length <= 253, + if (str.length < 3) { + return 'frontLengthError' + } + if (fullHandle.length > 253) { + return 'totalLengthError' + } + if (str.startsWith('-') || str.endsWith('-')) { + return 'hyphenError' + } + if (!str || !VALIDATE_REGEX.test(fullHandle) || str.includes('.')) { + return 'charsError' } - return { - ...results, - overall: !Object.values(results).includes(false), - } + return 'valid' } diff --git a/src/screens/Signup/StepHandle.tsx b/src/screens/Signup/StepHandle.tsx index 2266f43879..f7d097848f 100644 --- a/src/screens/Signup/StepHandle.tsx +++ b/src/screens/Signup/StepHandle.tsx @@ -2,41 +2,20 @@ import React from 'react' import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useFocusEffect} from '@react-navigation/native' -import { - createFullHandle, - IsValidHandle, - validateHandle, -} from '#/lib/strings/handles' +import {createFullHandle} from '#/lib/strings/handles' import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {useSignupContext} from '#/screens/Signup/state' -import {atoms as a, useTheme} from '#/alf' +import {atoms as a} from '#/alf' +import {FormError} from '#/components/forms/FormError' import * as TextField from '#/components/forms/TextField' import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At' -import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' -import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times' import {Text} from '#/components/Typography' export function StepHandle() { const {_} = useLingui() - const t = useTheme() const {state, dispatch} = useSignupContext() - const [validCheck, setValidCheck] = React.useState({ - handleChars: false, - hyphenStartOrEnd: false, - frontLength: false, - totalLength: true, - overall: false, - }) - - useFocusEffect( - React.useCallback(() => { - setValidCheck(validateHandle(state.handle, state.userDomain)) - }, [state.handle, state.userDomain]), - ) - const onHandleChange = React.useCallback( (value: string) => { if (state.error) { @@ -75,61 +54,8 @@ export function StepHandle() { @{createFullHandle(state.handle, state.userDomain)} - - - {state.error ? ( - - - {state.error} - - ) : undefined} - {validCheck.hyphenStartOrEnd ? ( - - - - Only contains letters, numbers, and hyphens - - - ) : ( - - - - Doesn't begin or end with a hyphen - - - )} - - - {!validCheck.totalLength ? ( - - No longer than 253 characters - - ) : ( - - At least 3 characters - - )} - - + ) } - -function IsValidIcon({valid}: {valid: boolean}) { - const t = useTheme() - if (!valid) { - return - } - return -} diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx index 6758f7fa14..8bfc32fb5c 100644 --- a/src/screens/Signup/index.tsx +++ b/src/screens/Signup/index.tsx @@ -7,7 +7,7 @@ import {useLingui} from '@lingui/react' import {useAnalytics} from '#/lib/analytics/analytics' import {FEEDBACK_FORM_URL} from '#/lib/constants' import {logEvent} from '#/lib/statsig/statsig' -import {createFullHandle} from '#/lib/strings/handles' +import {createFullHandle, validateHandle} from '#/lib/strings/handles' import {useServiceQuery} from '#/state/queries/service' import {useAgent} from '#/state/session' import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout' @@ -74,6 +74,39 @@ export function Signup({onPressBack}: {onPressBack: () => void}) { const onNextPress = React.useCallback(async () => { if (state.activeStep === SignupStep.HANDLE) { try { + const isValidHandle = validateHandle(state.handle, state.userDomain) + + switch (isValidHandle) { + case 'charsError': + dispatch({ + type: 'setError', + value: _( + msg`Handle may only contain letters, numbers, and hyphens`, + ), + }) + return + case 'totalLengthError': + dispatch({ + type: 'setError', + value: _(msg`Handle may not be longer than 253 characters`), + }) + return + case 'frontLengthError': + dispatch({ + type: 'setError', + value: _(msg`Handle should be at least 3 characters`), + }) + return + case 'hyphenError': + dispatch({ + type: 'setError', + value: _(msg`Handle may not begin or end with a hyphen`), + }) + return + case 'valid': + break + } + dispatch({type: 'setIsLoading', value: true}) const res = await getAgent().resolveHandle({ diff --git a/src/screens/Signup/state.ts b/src/screens/Signup/state.ts index 86a144368f..dc6509d9a7 100644 --- a/src/screens/Signup/state.ts +++ b/src/screens/Signup/state.ts @@ -10,7 +10,7 @@ import * as EmailValidator from 'email-validator' import {DEFAULT_SERVICE, IS_PROD_SERVICE} from '#/lib/constants' import {cleanError} from '#/lib/strings/errors' -import {createFullHandle, validateHandle} from '#/lib/strings/handles' +import {createFullHandle} from '#/lib/strings/handles' import {getAge} from '#/lib/strings/time' import {logger} from '#/logger' import { @@ -175,11 +175,8 @@ export function reducer(s: SignupState, a: SignupAction): SignupState { isValidEmail break } - case SignupStep.HANDLE: { - next.canNext = - !!next.handle && validateHandle(next.handle, next.userDomain).overall - break - } + default: + next.canNext = true } logger.debug('signup', next)