import React, {useRef} from 'react' import {type TextInput, View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import * as EmailValidator from 'email-validator' import type tldts from 'tldts' import {isEmailMaybeInvalid} from '#/lib/strings/email' import {logger} from '#/logger' import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {is13, is18, useSignupContext} from '#/screens/Signup/state' import {Policies} from '#/screens/Signup/StepInfo/Policies' import {atoms as a, native} from '#/alf' import * as DateField from '#/components/forms/DateField' import {type DateFieldRef} from '#/components/forms/DateField/types' import {FormError} from '#/components/forms/FormError' import {HostingProvider} from '#/components/forms/HostingProvider' import * as TextField from '#/components/forms/TextField' import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '#/components/icons/Envelope' import {Lock_Stroke2_Corner0_Rounded as Lock} from '#/components/icons/Lock' import {Ticket_Stroke2_Corner0_Rounded as Ticket} from '#/components/icons/Ticket' import {Loader} from '#/components/Loader' import {usePreemptivelyCompleteActivePolicyUpdate} from '#/components/PolicyUpdateOverlay/usePreemptivelyCompleteActivePolicyUpdate' import {BackNextButtons} from '../BackNextButtons' function sanitizeDate(date: Date): Date { if (!date || date.toString() === 'Invalid Date') { logger.error(`Create account: handled invalid date for birthDate`, { hasDate: !!date, }) return new Date() } return date } export function StepInfo({ onPressBack, isServerError, refetchServer, isLoadingStarterPack, }: { onPressBack: () => void isServerError: boolean refetchServer: () => void isLoadingStarterPack: boolean }) { const {_} = useLingui() const {state, dispatch} = useSignupContext() const preemptivelyCompleteActivePolicyUpdate = usePreemptivelyCompleteActivePolicyUpdate() const inviteCodeValueRef = useRef(state.inviteCode) const emailValueRef = useRef(state.email) const prevEmailValueRef = useRef(state.email) const passwordValueRef = useRef(state.password) const emailInputRef = useRef(null) const passwordInputRef = useRef(null) const birthdateInputRef = useRef(null) const [hasWarnedEmail, setHasWarnedEmail] = React.useState(false) const tldtsRef = React.useRef() React.useEffect(() => { // @ts-expect-error - valid path import('tldts/dist/index.cjs.min.js').then(tldts => { tldtsRef.current = tldts }) // This will get used in the avatar creator a few steps later, so lets preload it now // @ts-expect-error - valid path import('react-native-view-shot/src/index') }, []) const onNextPress = () => { const inviteCode = inviteCodeValueRef.current const email = emailValueRef.current const emailChanged = prevEmailValueRef.current !== email const password = passwordValueRef.current if (!is13(state.dateOfBirth)) { return } if (state.serviceDescription?.inviteCodeRequired && !inviteCode) { return dispatch({ type: 'setError', value: _(msg`Please enter your invite code.`), field: 'invite-code', }) } if (!email) { return dispatch({ type: 'setError', value: _(msg`Please enter your email.`), field: 'email', }) } if (!EmailValidator.validate(email)) { return dispatch({ type: 'setError', value: _(msg`Your email appears to be invalid.`), field: 'email', }) } if (emailChanged && tldtsRef.current) { if (isEmailMaybeInvalid(email, tldtsRef.current)) { prevEmailValueRef.current = email setHasWarnedEmail(true) return dispatch({ type: 'setError', value: _( msg`Please double-check that you have entered your email address correctly.`, ), }) } } else if (hasWarnedEmail) { setHasWarnedEmail(false) } prevEmailValueRef.current = email if (!password) { return dispatch({ type: 'setError', value: _(msg`Please choose your password.`), field: 'password', }) } if (password.length < 8) { return dispatch({ type: 'setError', value: _(msg`Your password must be at least 8 characters long.`), field: 'password', }) } preemptivelyCompleteActivePolicyUpdate() dispatch({type: 'setInviteCode', value: inviteCode}) dispatch({type: 'setEmail', value: email}) dispatch({type: 'setPassword', value: password}) dispatch({type: 'next'}) logger.metric( 'signup:nextPressed', { activeStep: state.activeStep, }, {statsig: true}, ) } return ( dispatch({type: 'setServiceUrl', value: v})} /> {state.isLoading || isLoadingStarterPack ? ( ) : state.serviceDescription ? ( <> {state.serviceDescription.inviteCodeRequired && ( Invite code { inviteCodeValueRef.current = value.trim() if ( state.errorField === 'invite-code' && value.trim().length > 0 ) { dispatch({type: 'clearError'}) } }} label={_(msg`Required for this provider`)} defaultValue={state.inviteCode} autoCapitalize="none" autoComplete="email" keyboardType="email-address" returnKeyType="next" submitBehavior={native('submit')} onSubmitEditing={native(() => emailInputRef.current?.focus(), )} /> )} Email { emailValueRef.current = value.trim() if (hasWarnedEmail) { setHasWarnedEmail(false) } if ( state.errorField === 'email' && value.trim().length > 0 && EmailValidator.validate(value.trim()) ) { dispatch({type: 'clearError'}) } }} label={_(msg`Enter your email address`)} defaultValue={state.email} autoCapitalize="none" autoComplete="email" keyboardType="email-address" returnKeyType="next" submitBehavior={native('submit')} onSubmitEditing={native(() => passwordInputRef.current?.focus(), )} /> Password { passwordValueRef.current = value if (state.errorField === 'password' && value.length >= 8) { dispatch({type: 'clearError'}) } }} label={_(msg`Choose your password`)} defaultValue={state.password} secureTextEntry autoComplete="new-password" autoCapitalize="none" returnKeyType="next" submitBehavior={native('blurAndSubmit')} onSubmitEditing={native(() => birthdateInputRef.current?.focus(), )} passwordRules="minlength: 8;" /> Your birth date { dispatch({ type: 'setDateOfBirth', value: sanitizeDate(new Date(date)), }) }} label={_(msg`Date of birth`)} accessibilityHint={_(msg`Select your date of birth`)} maximumDate={new Date()} /> ) : undefined} ) }