diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/auth/LoggedOut.tsx | 4 | ||||
-rw-r--r-- | src/view/com/auth/create/CaptchaWebView.tsx | 14 | ||||
-rw-r--r-- | src/view/com/auth/create/CreateAccount.tsx | 230 | ||||
-rw-r--r-- | src/view/com/auth/create/Policies.tsx | 14 | ||||
-rw-r--r-- | src/view/com/auth/create/Step1.tsx | 261 | ||||
-rw-r--r-- | src/view/com/auth/create/Step2.tsx | 140 | ||||
-rw-r--r-- | src/view/com/auth/create/Step3.tsx | 114 | ||||
-rw-r--r-- | src/view/com/auth/create/StepHeader.tsx | 44 |
8 files changed, 20 insertions, 801 deletions
diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx index 58604ec9e..b22bbb7fe 100644 --- a/src/view/com/auth/LoggedOut.tsx +++ b/src/view/com/auth/LoggedOut.tsx @@ -7,7 +7,7 @@ import {useNavigation} from '@react-navigation/native' import {isIOS, isNative} from '#/platform/detection' import {Login} from '#/screens/Login' -import {CreateAccount} from '#/view/com/auth/create/CreateAccount' +import {Signup} from '#/screens/Signup' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {s} from '#/lib/styles' import {usePalette} from '#/lib/hooks/usePalette' @@ -148,7 +148,7 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) { /> ) : undefined} {screenState === ScreenState.S_CreateAccount ? ( - <CreateAccount + <Signup onPressBack={() => setScreenState(ScreenState.S_LoginOrCreateAccount) } diff --git a/src/view/com/auth/create/CaptchaWebView.tsx b/src/view/com/auth/create/CaptchaWebView.tsx index b0de8b4a4..06b605e4d 100644 --- a/src/view/com/auth/create/CaptchaWebView.tsx +++ b/src/view/com/auth/create/CaptchaWebView.tsx @@ -2,7 +2,7 @@ import React from 'react' import {WebView, WebViewNavigation} from 'react-native-webview' import {ShouldStartLoadRequest} from 'react-native-webview/lib/WebViewTypes' import {StyleSheet} from 'react-native' -import {CreateAccountState} from 'view/com/auth/create/state' +import {SignupState} from '#/screens/Signup/state' const ALLOWED_HOSTS = [ 'bsky.social', @@ -17,24 +17,24 @@ const ALLOWED_HOSTS = [ export function CaptchaWebView({ url, stateParam, - uiState, + state, onSuccess, onError, }: { url: string stateParam: string - uiState?: CreateAccountState + state?: SignupState onSuccess: (code: string) => void onError: () => void }) { const redirectHost = React.useMemo(() => { - if (!uiState?.serviceUrl) return 'bsky.app' + if (!state?.serviceUrl) return 'bsky.app' - return uiState?.serviceUrl && - new URL(uiState?.serviceUrl).host === 'staging.bsky.dev' + return state?.serviceUrl && + new URL(state?.serviceUrl).host === 'staging.bsky.dev' ? 'staging.bsky.app' : 'bsky.app' - }, [uiState?.serviceUrl]) + }, [state?.serviceUrl]) const wasSuccessful = React.useRef(false) diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx deleted file mode 100644 index d193802fe..000000000 --- a/src/view/com/auth/create/CreateAccount.tsx +++ /dev/null @@ -1,230 +0,0 @@ -import React from 'react' -import { - ActivityIndicator, - ScrollView, - StyleSheet, - TouchableOpacity, - View, -} from 'react-native' -import {useAnalytics} from 'lib/analytics/analytics' -import {Text} from '../../util/text/Text' -import {LoggedOutLayout} from 'view/com/util/layouts/LoggedOutLayout' -import {s} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useCreateAccount, useSubmitCreateAccount} from './state' -import {useServiceQuery} from '#/state/queries/service' -import {FEEDBACK_FORM_URL, HITSLOP_10} from '#/lib/constants' - -import {Step1} from './Step1' -import {Step2} from './Step2' -import {Step3} from './Step3' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {TextLink} from '../../util/Link' -import {getAgent} from 'state/session' -import {createFullHandle, validateHandle} from 'lib/strings/handles' - -export function CreateAccount({onPressBack}: {onPressBack: () => void}) { - const {screen} = useAnalytics() - const pal = usePalette('default') - const {_} = useLingui() - const [uiState, uiDispatch] = useCreateAccount() - const {isTabletOrDesktop} = useWebMediaQueries() - const submit = useSubmitCreateAccount(uiState, uiDispatch) - - React.useEffect(() => { - screen('CreateAccount') - }, [screen]) - - // fetch service info - // = - - const { - data: serviceInfo, - isFetching: serviceInfoIsFetching, - error: serviceInfoError, - refetch: refetchServiceInfo, - } = useServiceQuery(uiState.serviceUrl) - - React.useEffect(() => { - if (serviceInfo) { - uiDispatch({type: 'set-service-description', value: serviceInfo}) - uiDispatch({type: 'set-error', value: ''}) - } else if (serviceInfoError) { - uiDispatch({ - type: 'set-error', - value: _( - msg`Unable to contact your service. Please check your Internet connection.`, - ), - }) - } - }, [_, uiDispatch, serviceInfo, serviceInfoError]) - - // event handlers - // = - - const onPressBackInner = React.useCallback(() => { - if (uiState.canBack) { - uiDispatch({type: 'back'}) - } else { - onPressBack() - } - }, [uiState, uiDispatch, onPressBack]) - - const onPressNext = React.useCallback(async () => { - if (!uiState.canNext) { - return - } - - if (uiState.step === 2) { - if (!validateHandle(uiState.handle, uiState.userDomain).overall) { - return - } - - uiDispatch({type: 'set-processing', value: true}) - try { - const res = await getAgent().resolveHandle({ - handle: createFullHandle(uiState.handle, uiState.userDomain), - }) - - if (res.data.did) { - uiDispatch({ - type: 'set-error', - value: _(msg`That handle is already taken.`), - }) - return - } - } catch (e) { - // Don't need to handle - } finally { - uiDispatch({type: 'set-processing', value: false}) - } - - if (!uiState.isCaptchaRequired) { - try { - await submit() - } catch { - // dont need to handle here - } - // We don't need to go to the next page if there wasn't a captcha required - return - } - } - - uiDispatch({type: 'next'}) - }, [ - uiState.canNext, - uiState.step, - uiState.isCaptchaRequired, - uiState.handle, - uiState.userDomain, - uiDispatch, - _, - submit, - ]) - - // rendering - // = - - return ( - <LoggedOutLayout - leadin="" - title={_(msg`Create Account`)} - description={_(msg`We're so excited to have you join us!`)}> - <ScrollView - testID="createAccount" - style={pal.view} - keyboardShouldPersistTaps="handled" - keyboardDismissMode="on-drag"> - <View style={styles.stepContainer}> - {uiState.step === 1 && ( - <Step1 uiState={uiState} uiDispatch={uiDispatch} /> - )} - {uiState.step === 2 && ( - <Step2 uiState={uiState} uiDispatch={uiDispatch} /> - )} - {uiState.step === 3 && ( - <Step3 uiState={uiState} uiDispatch={uiDispatch} /> - )} - </View> - <View style={[s.flexRow, s.pl20, s.pr20]}> - <TouchableOpacity - onPress={onPressBackInner} - testID="backBtn" - accessibilityRole="button" - hitSlop={HITSLOP_10}> - <Text type="xl" style={pal.link}> - <Trans>Back</Trans> - </Text> - </TouchableOpacity> - <View style={s.flex1} /> - {uiState.canNext ? ( - <TouchableOpacity - testID="nextBtn" - onPress={onPressNext} - accessibilityRole="button" - hitSlop={HITSLOP_10}> - {uiState.isProcessing ? ( - <ActivityIndicator /> - ) : ( - <Text type="xl-bold" style={[pal.link, s.pr5]}> - <Trans>Next</Trans> - </Text> - )} - </TouchableOpacity> - ) : serviceInfoError ? ( - <TouchableOpacity - testID="retryConnectBtn" - onPress={() => refetchServiceInfo()} - accessibilityRole="button" - accessibilityLabel={_(msg`Retry`)} - accessibilityHint="" - accessibilityLiveRegion="polite" - hitSlop={HITSLOP_10}> - <Text type="xl-bold" style={[pal.link, s.pr5]}> - <Trans>Retry</Trans> - </Text> - </TouchableOpacity> - ) : serviceInfoIsFetching ? ( - <> - <ActivityIndicator color="#fff" /> - <Text type="xl" style={[pal.text, s.pr5]}> - <Trans>Connecting...</Trans> - </Text> - </> - ) : undefined} - </View> - - <View style={styles.stepContainer}> - <View - style={[ - s.flexRow, - s.alignCenter, - pal.viewLight, - {borderRadius: 8, paddingHorizontal: 14, paddingVertical: 12}, - ]}> - <Text type="md" style={pal.textLight}> - <Trans>Having trouble?</Trans>{' '} - </Text> - <TextLink - type="md" - style={pal.link} - text={_(msg`Contact support`)} - href={FEEDBACK_FORM_URL({email: uiState.email})} - /> - </View> - </View> - - <View style={{height: isTabletOrDesktop ? 50 : 400}} /> - </ScrollView> - </LoggedOutLayout> - ) -} - -const styles = StyleSheet.create({ - stepContainer: { - paddingHorizontal: 20, - paddingVertical: 20, - }, -}) diff --git a/src/view/com/auth/create/Policies.tsx b/src/view/com/auth/create/Policies.tsx index 2c7d60818..dc3c9c174 100644 --- a/src/view/com/auth/create/Policies.tsx +++ b/src/view/com/auth/create/Policies.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {StyleSheet, View} from 'react-native' +import {Linking, StyleSheet, View} from 'react-native' import { FontAwesomeIcon, FontAwesomeIconStyle, @@ -15,9 +15,11 @@ type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema export const Policies = ({ serviceDescription, needsGuardian, + under13, }: { serviceDescription: ServiceDescription needsGuardian: boolean + under13: boolean }) => { const pal = usePalette('default') if (!serviceDescription) { @@ -53,6 +55,7 @@ export const Policies = ({ href={tos} text="Terms of Service" style={[pal.link, s.underline]} + onPress={() => Linking.openURL(tos)} />, ) } @@ -63,6 +66,7 @@ export const Policies = ({ href={pp} text="Privacy Policy" style={[pal.link, s.underline]} + onPress={() => Linking.openURL(pp)} />, ) } @@ -81,12 +85,16 @@ export const Policies = ({ <Text style={pal.textLight}> By creating an account you agree to the {els}. </Text> - {needsGuardian && ( + {under13 ? ( + <Text style={[pal.textLight, s.bold]}> + You must be 13 years of age or older to sign up. + </Text> + ) : needsGuardian ? ( <Text style={[pal.textLight, s.bold]}> If you are not yet an adult according to the laws of your country, your parent or legal guardian must read these Terms on your behalf. </Text> - )} + ) : undefined} </View> ) } diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx deleted file mode 100644 index 1f6852f8c..000000000 --- a/src/view/com/auth/create/Step1.tsx +++ /dev/null @@ -1,261 +0,0 @@ -import React from 'react' -import { - ActivityIndicator, - Keyboard, - StyleSheet, - TouchableOpacity, - View, -} from 'react-native' -import {CreateAccountState, CreateAccountDispatch, is18} from './state' -import {Text} from 'view/com/util/text/Text' -import {DateInput} from 'view/com/util/forms/DateInput' -import {StepHeader} from './StepHeader' -import {s} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' -import {TextInput} from '../util/TextInput' -import {Policies} from './Policies' -import {ErrorMessage} from 'view/com/util/error/ErrorMessage' -import {isWeb} from 'platform/detection' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {logger} from '#/logger' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import {useDialogControl} from '#/components/Dialog' - -import {ServerInputDialog} from '../server-input' -import {toNiceDomain} from '#/lib/strings/url-helpers' - -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 Step1({ - uiState, - uiDispatch, -}: { - uiState: CreateAccountState - uiDispatch: CreateAccountDispatch -}) { - const pal = usePalette('default') - const {_} = useLingui() - const serverInputControl = useDialogControl() - - const onPressSelectService = React.useCallback(() => { - serverInputControl.open() - Keyboard.dismiss() - }, [serverInputControl]) - - const birthDate = React.useMemo(() => { - return sanitizeDate(uiState.birthDate) - }, [uiState.birthDate]) - - return ( - <View> - <ServerInputDialog - control={serverInputControl} - onSelect={url => uiDispatch({type: 'set-service-url', value: url})} - /> - <StepHeader uiState={uiState} title={_(msg`Your account`)} /> - - {uiState.error ? ( - <ErrorMessage message={uiState.error} style={styles.error} /> - ) : undefined} - - <View style={s.pb20}> - <Text type="md-medium" style={[pal.text, s.mb2]}> - <Trans>Hosting provider</Trans> - </Text> - <View style={[pal.border, {borderWidth: 1, borderRadius: 6}]}> - <View - style={[ - pal.borderDark, - {flexDirection: 'row', alignItems: 'center'}, - ]}> - <FontAwesomeIcon - icon="globe" - style={[pal.textLight, {marginLeft: 14}]} - /> - <TouchableOpacity - testID="selectServiceButton" - style={{ - flexDirection: 'row', - flex: 1, - alignItems: 'center', - }} - onPress={onPressSelectService} - accessibilityRole="button" - accessibilityLabel={_(msg`Select service`)} - accessibilityHint={_(msg`Sets server for the Bluesky client`)}> - <Text - type="xl" - style={[ - pal.text, - { - flex: 1, - paddingVertical: 10, - paddingRight: 12, - paddingLeft: 10, - }, - ]}> - {toNiceDomain(uiState.serviceUrl)} - </Text> - <View - style={[ - pal.btn, - { - flexDirection: 'row', - alignItems: 'center', - borderRadius: 6, - paddingVertical: 6, - paddingHorizontal: 8, - marginHorizontal: 6, - }, - ]}> - <FontAwesomeIcon - icon="pen" - size={12} - style={pal.textLight as FontAwesomeIconStyle} - /> - </View> - </TouchableOpacity> - </View> - </View> - </View> - - {!uiState.serviceDescription ? ( - <ActivityIndicator /> - ) : ( - <> - {uiState.isInviteCodeRequired && ( - <View style={s.pb20}> - <Text type="md-medium" style={[pal.text, s.mb2]}> - <Trans>Invite code</Trans> - </Text> - <TextInput - testID="inviteCodeInput" - icon="ticket" - placeholder={_(msg`Required for this provider`)} - value={uiState.inviteCode} - editable - onChange={value => uiDispatch({type: 'set-invite-code', value})} - accessibilityLabel={_(msg`Invite code`)} - accessibilityHint={_(msg`Input invite code to proceed`)} - autoCapitalize="none" - autoComplete="off" - autoCorrect={false} - autoFocus={true} - /> - </View> - )} - - {!uiState.isInviteCodeRequired || uiState.inviteCode ? ( - <> - <View style={s.pb20}> - <Text - type="md-medium" - style={[pal.text, s.mb2]} - nativeID="email"> - <Trans>Email address</Trans> - </Text> - <TextInput - testID="emailInput" - icon="envelope" - placeholder={_(msg`Enter your email address`)} - value={uiState.email} - editable - onChange={value => uiDispatch({type: 'set-email', value})} - accessibilityLabel={_(msg`Email`)} - accessibilityHint={_(msg`Input email for Bluesky account`)} - accessibilityLabelledBy="email" - autoCapitalize="none" - autoComplete="email" - autoCorrect={false} - autoFocus={!uiState.isInviteCodeRequired} - /> - </View> - - <View style={s.pb20}> - <Text - type="md-medium" - style={[pal.text, s.mb2]} - nativeID="password"> - <Trans>Password</Trans> - </Text> - <TextInput - testID="passwordInput" - icon="lock" - placeholder={_(msg`Choose your password`)} - value={uiState.password} - editable - secureTextEntry - onChange={value => uiDispatch({type: 'set-password', value})} - accessibilityLabel={_(msg`Password`)} - accessibilityHint={_(msg`Set password`)} - accessibilityLabelledBy="password" - autoCapitalize="none" - autoComplete="new-password" - autoCorrect={false} - /> - </View> - - <View style={s.pb20}> - <Text - type="md-medium" - style={[pal.text, s.mb2]} - nativeID="birthDate"> - <Trans>Your birth date</Trans> - </Text> - <DateInput - handleAsUTC - testID="birthdayInput" - value={birthDate} - onChange={value => - uiDispatch({type: 'set-birth-date', value}) - } - buttonType="default-light" - buttonStyle={[pal.border, styles.dateInputButton]} - buttonLabelType="lg" - accessibilityLabel={_(msg`Birthday`)} - accessibilityHint={_(msg`Enter your birth date`)} - accessibilityLabelledBy="birthDate" - /> - </View> - - {uiState.serviceDescription && ( - <Policies - serviceDescription={uiState.serviceDescription} - needsGuardian={!is18(uiState)} - /> - )} - </> - ) : undefined} - </> - )} - </View> - ) -} - -const styles = StyleSheet.create({ - error: { - borderRadius: 6, - marginBottom: 10, - }, - dateInputButton: { - borderWidth: 1, - borderRadius: 6, - paddingVertical: 14, - }, - // @ts-expect-error: Suppressing error due to incomplete `ViewStyle` type definition in react-native-web, missing `cursor` prop as discussed in https://github.com/necolas/react-native-web/issues/832. - touchable: { - ...(isWeb && {cursor: 'pointer'}), - }, -}) diff --git a/src/view/com/auth/create/Step2.tsx b/src/view/com/auth/create/Step2.tsx deleted file mode 100644 index 5c262977f..000000000 --- a/src/view/com/auth/create/Step2.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import React from 'react' -import {View} from 'react-native' -import {CreateAccountState, CreateAccountDispatch} from './state' -import {Text} from 'view/com/util/text/Text' -import {StepHeader} from './StepHeader' -import {s} from 'lib/styles' -import {TextInput} from '../util/TextInput' -import { - createFullHandle, - IsValidHandle, - validateHandle, -} from 'lib/strings/handles' -import {usePalette} from 'lib/hooks/usePalette' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {atoms as a, useTheme} from '#/alf' -import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' -import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times' -import {useFocusEffect} from '@react-navigation/native' - -/** STEP 3: Your user handle - * @field User handle - */ -export function Step2({ - uiState, - uiDispatch, -}: { - uiState: CreateAccountState - uiDispatch: CreateAccountDispatch -}) { - const pal = usePalette('default') - const {_} = useLingui() - const t = useTheme() - - const [validCheck, setValidCheck] = React.useState<IsValidHandle>({ - handleChars: false, - frontLength: false, - totalLength: true, - overall: false, - }) - - useFocusEffect( - React.useCallback(() => { - setValidCheck(validateHandle(uiState.handle, uiState.userDomain)) - - // Disabling this, because we only want to run this when we focus the screen - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []), - ) - - const onHandleChange = React.useCallback( - (value: string) => { - if (uiState.error) { - uiDispatch({type: 'set-error', value: ''}) - } - - setValidCheck(validateHandle(value, uiState.userDomain)) - uiDispatch({type: 'set-handle', value}) - }, - [uiDispatch, uiState.error, uiState.userDomain], - ) - - return ( - <View> - <StepHeader uiState={uiState} title={_(msg`Your user handle`)} /> - <View style={s.pb10}> - <View style={s.mb20}> - <TextInput - testID="handleInput" - icon="at" - placeholder="e.g. alice" - value={uiState.handle} - editable - autoFocus - autoComplete="off" - autoCorrect={false} - onChange={onHandleChange} - // TODO: Add explicit text label - accessibilityLabel={_(msg`User handle`)} - accessibilityHint={_(msg`Input your user handle`)} - /> - <Text type="lg" style={[pal.text, s.pl5, s.pt10]}> - <Trans>Your full handle will be</Trans>{' '} - <Text type="lg-bold" style={pal.text}> - @{createFullHandle(uiState.handle, uiState.userDomain)} - </Text> - </Text> - </View> - <View - style={[ - a.w_full, - a.rounded_sm, - a.border, - a.p_md, - a.gap_sm, - t.atoms.border_contrast_low, - ]}> - {uiState.error ? ( - <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> - <IsValidIcon valid={false} /> - <Text style={[t.atoms.text, a.text_md, a.flex]}> - {uiState.error} - </Text> - </View> - ) : undefined} - <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> - <IsValidIcon valid={validCheck.handleChars} /> - <Text style={[t.atoms.text, a.text_md, a.flex]}> - <Trans>May only contain letters and numbers</Trans> - </Text> - </View> - <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> - <IsValidIcon - valid={validCheck.frontLength && validCheck.totalLength} - /> - {!validCheck.totalLength ? ( - <Text style={[t.atoms.text]}> - <Trans>May not be longer than 253 characters</Trans> - </Text> - ) : ( - <Text style={[t.atoms.text, a.text_md]}> - <Trans>Must be at least 3 characters</Trans> - </Text> - )} - </View> - </View> - </View> - </View> - ) -} - -function IsValidIcon({valid}: {valid: boolean}) { - const t = useTheme() - - if (!valid) { - return <Times size="md" style={{color: t.palette.negative_500}} /> - } - - return <Check size="md" style={{color: t.palette.positive_700}} /> -} diff --git a/src/view/com/auth/create/Step3.tsx b/src/view/com/auth/create/Step3.tsx deleted file mode 100644 index 53fdfdde8..000000000 --- a/src/view/com/auth/create/Step3.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from 'react' -import {ActivityIndicator, StyleSheet, View} from 'react-native' -import { - CreateAccountState, - CreateAccountDispatch, - useSubmitCreateAccount, -} from './state' -import {StepHeader} from './StepHeader' -import {ErrorMessage} from 'view/com/util/error/ErrorMessage' -import {isWeb} from 'platform/detection' -import {msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {nanoid} from 'nanoid/non-secure' -import {CaptchaWebView} from 'view/com/auth/create/CaptchaWebView' -import {useTheme} from 'lib/ThemeContext' -import {createFullHandle} from 'lib/strings/handles' - -const CAPTCHA_PATH = '/gate/signup' - -export function Step3({ - uiState, - uiDispatch, -}: { - uiState: CreateAccountState - uiDispatch: CreateAccountDispatch -}) { - const {_} = useLingui() - const theme = useTheme() - const submit = useSubmitCreateAccount(uiState, uiDispatch) - - const [completed, setCompleted] = React.useState(false) - - const stateParam = React.useMemo(() => nanoid(15), []) - const url = React.useMemo(() => { - const newUrl = new URL(uiState.serviceUrl) - newUrl.pathname = CAPTCHA_PATH - newUrl.searchParams.set( - 'handle', - createFullHandle(uiState.handle, uiState.userDomain), - ) - newUrl.searchParams.set('state', stateParam) - newUrl.searchParams.set('colorScheme', theme.colorScheme) - - console.log(newUrl) - - return newUrl.href - }, [ - uiState.serviceUrl, - uiState.handle, - uiState.userDomain, - stateParam, - theme.colorScheme, - ]) - - const onSuccess = React.useCallback( - (code: string) => { - setCompleted(true) - submit(code) - }, - [submit], - ) - - const onError = React.useCallback(() => { - uiDispatch({ - type: 'set-error', - value: _(msg`Error receiving captcha response.`), - }) - }, [_, uiDispatch]) - - return ( - <View> - <StepHeader uiState={uiState} title={_(msg`Complete the challenge`)} /> - <View style={[styles.container, completed && styles.center]}> - {!completed ? ( - <CaptchaWebView - url={url} - stateParam={stateParam} - uiState={uiState} - onSuccess={onSuccess} - onError={onError} - /> - ) : ( - <ActivityIndicator size="large" /> - )} - </View> - - {uiState.error ? ( - <ErrorMessage message={uiState.error} style={styles.error} /> - ) : undefined} - </View> - ) -} - -const styles = StyleSheet.create({ - error: { - borderRadius: 6, - marginTop: 10, - }, - // @ts-expect-error: Suppressing error due to incomplete `ViewStyle` type definition in react-native-web, missing `cursor` prop as discussed in https://github.com/necolas/react-native-web/issues/832. - touchable: { - ...(isWeb && {cursor: 'pointer'}), - }, - container: { - minHeight: 500, - width: '100%', - paddingBottom: 20, - overflow: 'hidden', - }, - center: { - alignItems: 'center', - justifyContent: 'center', - }, -}) diff --git a/src/view/com/auth/create/StepHeader.tsx b/src/view/com/auth/create/StepHeader.tsx deleted file mode 100644 index a98b392d8..000000000 --- a/src/view/com/auth/create/StepHeader.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react' -import {StyleSheet, View} from 'react-native' -import {Text} from 'view/com/util/text/Text' -import {usePalette} from 'lib/hooks/usePalette' -import {Trans} from '@lingui/macro' -import {CreateAccountState} from './state' - -export function StepHeader({ - uiState, - title, - children, -}: React.PropsWithChildren<{uiState: CreateAccountState; title: string}>) { - const pal = usePalette('default') - const numSteps = 3 - return ( - <View style={styles.container}> - <View> - <Text type="lg" style={[pal.textLight]}> - {uiState.step === 3 ? ( - <Trans>Last step!</Trans> - ) : ( - <Trans> - Step {uiState.step} of {numSteps} - </Trans> - )} - </Text> - - <Text style={[pal.text]} type="title-xl"> - {title} - </Text> - </View> - {children} - </View> - ) -} - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - marginBottom: 20, - }, -}) |