diff --git a/native/account/registration/username-selection.react.js b/native/account/registration/username-selection.react.js --- a/native/account/registration/username-selection.react.js +++ b/native/account/registration/username-selection.react.js @@ -1,17 +1,34 @@ // @flow import * as React from 'react'; -import { Text } from 'react-native'; +import { View, Text } from 'react-native'; + +import { + exactSearchUser, + exactSearchUserActionTypes, +} from 'lib/actions/user-actions.js'; +import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; +import { validUsernameRegex } from 'lib/shared/account-utils.js'; +import { + useServerCall, + useDispatchActionPromise, +} from 'lib/utils/action-utils.js'; import RegistrationButtonContainer from './registration-button-container.react.js'; import RegistrationButton from './registration-button.react.js'; import RegistrationContainer from './registration-container.react.js'; import RegistrationContentContainer from './registration-content-container.react.js'; import type { RegistrationNavigationProp } from './registration-navigator.react.js'; +import RegistrationTextInput from './registration-text-input.react.js'; import type { CoolOrNerdMode } from './registration-types.js'; import type { NavigationRoute } from '../../navigation/route-names.js'; +import { useSelector } from '../../redux/redux-utils.js'; import { useStyles } from '../../themes/colors.js'; +const exactSearchUserLoadingStatusSelector = createLoadingStatusSelector( + exactSearchUserActionTypes, +); + export type UsernameSelectionParams = { +userSelections: { +coolOrNerdMode: CoolOrNerdMode, @@ -19,25 +36,120 @@ }, }; +type UsernameError = 'username_invalid' | 'username_taken'; + type Props = { +navigation: RegistrationNavigationProp<'UsernameSelection'>, +route: NavigationRoute<'UsernameSelection'>, }; // eslint-disable-next-line no-unused-vars function UsernameSelection(props: Props): React.Node { - const onProceed = React.useCallback(() => {}, []); + const [username, setUsername] = React.useState(''); + const validUsername = username.search(validUsernameRegex) > -1; + const [usernameError, setUsernameError] = React.useState(); const styles = useStyles(unboundStyles); + const checkUsernameValidity = React.useCallback(() => { + if (validUsername) { + return true; + } + setUsernameError('username_invalid'); + return false; + }, [validUsername]); + + const exactSearchUserCall = useServerCall(exactSearchUser); + const dispatchActionPromise = useDispatchActionPromise(); + const onProceed = React.useCallback(async () => { + if (!checkUsernameValidity()) { + return; + } + + const searchPromise = exactSearchUserCall(username); + dispatchActionPromise(exactSearchUserActionTypes, searchPromise); + const { userInfo } = await searchPromise; + + if (userInfo) { + setUsernameError('username_taken'); + return; + } + + setUsernameError(undefined); + }, [ + checkUsernameValidity, + username, + exactSearchUserCall, + dispatchActionPromise, + ]); + + const exactSearchUserCallLoading = useSelector( + state => exactSearchUserLoadingStatusSelector(state) === 'loading', + ); + let buttonVariant = 'disabled'; + if (exactSearchUserCallLoading) { + buttonVariant = 'loading'; + } else if (validUsername) { + buttonVariant = 'enabled'; + } + + let errorText; + if (usernameError === 'username_invalid') { + errorText = ( + <> + Usernames must: + + {'1. '} + + Be at least one character long. + + + + {'2. '} + + Start with either a letter or a number. + + + + {'3. '} + + Contain only letters, numbers, or the characters “-” and “_”. + + + + ); + } else if (usernameError === 'username_taken') { + errorText = ( + + Username taken. Please try another one + + ); + } + return ( Pick a username + + {errorText} @@ -50,6 +162,24 @@ color: 'panelForegroundLabel', paddingBottom: 16, }, + error: { + marginTop: 16, + }, + errorText: { + fontFamily: 'Arial', + fontSize: 15, + lineHeight: 20, + color: 'redText', + }, + listItem: { + flexDirection: 'row', + }, + listItemNumber: { + fontWeight: 'bold', + }, + listItemContent: { + flexShrink: 1, + }, }; export default UsernameSelection;