about summary refs log tree commit diff
path: root/src/view/com/auth/create
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/auth/create')
-rw-r--r--src/view/com/auth/create/CreateAccount.tsx219
-rw-r--r--src/view/com/auth/create/Policies.tsx114
-rw-r--r--src/view/com/auth/create/Step1.tsx283
-rw-r--r--src/view/com/auth/create/Step2.tsx307
-rw-r--r--src/view/com/auth/create/Step3.tsx62
-rw-r--r--src/view/com/auth/create/StepHeader.tsx44
-rw-r--r--src/view/com/auth/create/state.ts350
7 files changed, 0 insertions, 1379 deletions
diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx
deleted file mode 100644
index 5d452736a..000000000
--- a/src/view/com/auth/create/CreateAccount.tsx
+++ /dev/null
@@ -1,219 +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 {useOnboardingDispatch} from '#/state/shell'
-import {useSessionApi} from '#/state/session'
-import {useCreateAccount, submit} from './state'
-import {useServiceQuery} from '#/state/queries/service'
-import {
-  usePreferencesSetBirthDateMutation,
-  useSetSaveFeedsMutation,
-  DEFAULT_PROD_FEEDS,
-} from '#/state/queries/preferences'
-import {FEEDBACK_FORM_URL, HITSLOP_10, IS_PROD} 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'
-
-export function CreateAccount({onPressBack}: {onPressBack: () => void}) {
-  const {screen} = useAnalytics()
-  const pal = usePalette('default')
-  const {_} = useLingui()
-  const [uiState, uiDispatch] = useCreateAccount()
-  const onboardingDispatch = useOnboardingDispatch()
-  const {createAccount} = useSessionApi()
-  const {mutate: setBirthDate} = usePreferencesSetBirthDateMutation()
-  const {mutate: setSavedFeeds} = useSetSaveFeedsMutation()
-  const {isTabletOrDesktop} = useWebMediaQueries()
-
-  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 < 3) {
-      uiDispatch({type: 'next'})
-    } else {
-      try {
-        await submit({
-          onboardingDispatch,
-          createAccount,
-          uiState,
-          uiDispatch,
-          _,
-        })
-        setBirthDate({birthDate: uiState.birthDate})
-        if (IS_PROD(uiState.serviceUrl)) {
-          setSavedFeeds(DEFAULT_PROD_FEEDS)
-        }
-      } catch {
-        // dont need to handle here
-      }
-    }
-  }, [
-    uiState,
-    uiDispatch,
-    onboardingDispatch,
-    createAccount,
-    setBirthDate,
-    setSavedFeeds,
-    _,
-  ])
-
-  // 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
deleted file mode 100644
index 2c7d60818..000000000
--- a/src/view/com/auth/create/Policies.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import React from 'react'
-import {StyleSheet, View} from 'react-native'
-import {
-  FontAwesomeIcon,
-  FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
-import {ComAtprotoServerDescribeServer} from '@atproto/api'
-import {TextLink} from '../../util/Link'
-import {Text} from '../../util/text/Text'
-import {s, colors} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-
-type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
-
-export const Policies = ({
-  serviceDescription,
-  needsGuardian,
-}: {
-  serviceDescription: ServiceDescription
-  needsGuardian: boolean
-}) => {
-  const pal = usePalette('default')
-  if (!serviceDescription) {
-    return <View />
-  }
-  const tos = validWebLink(serviceDescription.links?.termsOfService)
-  const pp = validWebLink(serviceDescription.links?.privacyPolicy)
-  if (!tos && !pp) {
-    return (
-      <View style={[styles.policies, {flexDirection: 'row'}]}>
-        <View
-          style={[
-            styles.errorIcon,
-            {borderColor: pal.colors.text, marginTop: 1},
-          ]}>
-          <FontAwesomeIcon
-            icon="exclamation"
-            style={pal.textLight as FontAwesomeIconStyle}
-            size={10}
-          />
-        </View>
-        <Text style={[pal.textLight, s.pl5, s.flex1]}>
-          This service has not provided terms of service or a privacy policy.
-        </Text>
-      </View>
-    )
-  }
-  const els = []
-  if (tos) {
-    els.push(
-      <TextLink
-        key="tos"
-        href={tos}
-        text="Terms of Service"
-        style={[pal.link, s.underline]}
-      />,
-    )
-  }
-  if (pp) {
-    els.push(
-      <TextLink
-        key="pp"
-        href={pp}
-        text="Privacy Policy"
-        style={[pal.link, s.underline]}
-      />,
-    )
-  }
-  if (els.length === 2) {
-    els.splice(
-      1,
-      0,
-      <Text key="and" style={pal.textLight}>
-        {' '}
-        and{' '}
-      </Text>,
-    )
-  }
-  return (
-    <View style={styles.policies}>
-      <Text style={pal.textLight}>
-        By creating an account you agree to the {els}.
-      </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>
-      )}
-    </View>
-  )
-}
-
-function validWebLink(url?: string): string | undefined {
-  return url && (url.startsWith('http://') || url.startsWith('https://'))
-    ? url
-    : undefined
-}
-
-const styles = StyleSheet.create({
-  policies: {
-    flexDirection: 'column',
-    gap: 8,
-  },
-  errorIcon: {
-    borderWidth: 1,
-    borderColor: colors.white,
-    borderRadius: 30,
-    width: 16,
-    height: 16,
-    alignItems: 'center',
-    justifyContent: 'center',
-  },
-})
diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx
deleted file mode 100644
index a7abbfaa8..000000000
--- a/src/view/com/auth/create/Step1.tsx
+++ /dev/null
@@ -1,283 +0,0 @@
-import React from 'react'
-import {
-  ActivityIndicator,
-  Keyboard,
-  StyleSheet,
-  TouchableOpacity,
-  TouchableWithoutFeedback,
-  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 {useModalControls} from '#/state/modals'
-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 {openModal} = useModalControls()
-  const serverInputControl = useDialogControl()
-
-  const onPressSelectService = React.useCallback(() => {
-    serverInputControl.open()
-    Keyboard.dismiss()
-  }, [serverInputControl])
-
-  const onPressWaitlist = React.useCallback(() => {
-    openModal({name: 'waitlist'})
-  }, [openModal])
-
-  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`)} />
-
-      <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.inviteCode && uiState.isInviteCodeRequired ? (
-            <View style={[s.flexRow, s.alignCenter]}>
-              <Text style={pal.text}>
-                <Trans>Don't have an invite code?</Trans>{' '}
-              </Text>
-              <TouchableWithoutFeedback
-                onPress={onPressWaitlist}
-                accessibilityLabel={_(msg`Join the waitlist.`)}
-                accessibilityHint="">
-                <View style={styles.touchable}>
-                  <Text style={pal.link}>
-                    <Trans>Join the waitlist.</Trans>
-                  </Text>
-                </View>
-              </TouchableWithoutFeedback>
-            </View>
-          ) : (
-            <>
-              <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)}
-                />
-              )}
-            </>
-          )}
-        </>
-      )}
-      {uiState.error ? (
-        <ErrorMessage message={uiState.error} style={styles.error} />
-      ) : undefined}
-    </View>
-  )
-}
-
-const styles = StyleSheet.create({
-  error: {
-    borderRadius: 6,
-    marginTop: 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 2e16b13bb..000000000
--- a/src/view/com/auth/create/Step2.tsx
+++ /dev/null
@@ -1,307 +0,0 @@
-import React from 'react'
-import {
-  ActivityIndicator,
-  StyleSheet,
-  TouchableWithoutFeedback,
-  View,
-} from 'react-native'
-import RNPickerSelect from 'react-native-picker-select'
-import {
-  CreateAccountState,
-  CreateAccountDispatch,
-  requestVerificationCode,
-} from './state'
-import {Text} from 'view/com/util/text/Text'
-import {StepHeader} from './StepHeader'
-import {s} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {TextInput} from '../util/TextInput'
-import {Button} from '../../util/forms/Button'
-import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
-import {isAndroid, isWeb} from 'platform/detection'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import parsePhoneNumber from 'libphonenumber-js'
-import {COUNTRY_CODES} from '#/lib/country-codes'
-import {
-  FontAwesomeIcon,
-  FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
-import {HITSLOP_10} from '#/lib/constants'
-
-export function Step2({
-  uiState,
-  uiDispatch,
-}: {
-  uiState: CreateAccountState
-  uiDispatch: CreateAccountDispatch
-}) {
-  const pal = usePalette('default')
-  const {_} = useLingui()
-  const {isMobile} = useWebMediaQueries()
-
-  const onPressRequest = React.useCallback(() => {
-    const phoneNumber = parsePhoneNumber(
-      uiState.verificationPhone,
-      uiState.phoneCountry,
-    )
-    if (phoneNumber && phoneNumber.isValid()) {
-      requestVerificationCode({uiState, uiDispatch, _})
-    } else {
-      uiDispatch({
-        type: 'set-error',
-        value: _(
-          msg`There's something wrong with this number. Please choose your country and enter your full phone number!`,
-        ),
-      })
-    }
-  }, [uiState, uiDispatch, _])
-
-  const onPressRetry = React.useCallback(() => {
-    uiDispatch({type: 'set-has-requested-verification-code', value: false})
-  }, [uiDispatch])
-
-  const phoneNumberFormatted = React.useMemo(
-    () =>
-      uiState.hasRequestedVerificationCode
-        ? parsePhoneNumber(
-            uiState.verificationPhone,
-            uiState.phoneCountry,
-          )?.formatInternational()
-        : '',
-    [
-      uiState.hasRequestedVerificationCode,
-      uiState.verificationPhone,
-      uiState.phoneCountry,
-    ],
-  )
-
-  return (
-    <View>
-      <StepHeader uiState={uiState} title={_(msg`SMS verification`)} />
-
-      {!uiState.hasRequestedVerificationCode ? (
-        <>
-          <View style={s.pb10}>
-            <Text
-              type="md-medium"
-              style={[pal.text, s.mb2]}
-              nativeID="phoneCountry">
-              <Trans>Country</Trans>
-            </Text>
-            <View
-              style={[
-                {position: 'relative'},
-                isAndroid && {
-                  borderWidth: 1,
-                  borderColor: pal.border.borderColor,
-                  borderRadius: 4,
-                },
-              ]}>
-              <RNPickerSelect
-                placeholder={{}}
-                value={uiState.phoneCountry}
-                onValueChange={value =>
-                  uiDispatch({type: 'set-phone-country', value})
-                }
-                items={COUNTRY_CODES.filter(l => Boolean(l.code2)).map(l => ({
-                  label: l.name,
-                  value: l.code2,
-                  key: l.code2,
-                }))}
-                style={{
-                  inputAndroid: {
-                    backgroundColor: pal.view.backgroundColor,
-                    color: pal.text.color,
-                    fontSize: 21,
-                    letterSpacing: 0.5,
-                    fontWeight: '500',
-                    paddingHorizontal: 14,
-                    paddingVertical: 8,
-                    borderRadius: 4,
-                  },
-                  inputIOS: {
-                    backgroundColor: pal.view.backgroundColor,
-                    color: pal.text.color,
-                    fontSize: 14,
-                    letterSpacing: 0.5,
-                    fontWeight: '500',
-                    paddingHorizontal: 14,
-                    paddingVertical: 8,
-                    borderWidth: 1,
-                    borderColor: pal.border.borderColor,
-                    borderRadius: 4,
-                  },
-                  inputWeb: {
-                    // @ts-ignore web only
-                    cursor: 'pointer',
-                    '-moz-appearance': 'none',
-                    '-webkit-appearance': 'none',
-                    appearance: 'none',
-                    outline: 0,
-                    borderWidth: 1,
-                    borderColor: pal.border.borderColor,
-                    backgroundColor: pal.view.backgroundColor,
-                    color: pal.text.color,
-                    fontSize: 14,
-                    letterSpacing: 0.5,
-                    fontWeight: '500',
-                    paddingHorizontal: 14,
-                    paddingVertical: 8,
-                    borderRadius: 4,
-                  },
-                }}
-                accessibilityLabel={_(msg`Select your phone's country`)}
-                accessibilityHint=""
-                accessibilityLabelledBy="phoneCountry"
-              />
-              <View
-                style={{
-                  position: 'absolute',
-                  top: 1,
-                  right: 1,
-                  bottom: 1,
-                  width: 40,
-                  pointerEvents: 'none',
-                  alignItems: 'center',
-                  justifyContent: 'center',
-                }}>
-                <FontAwesomeIcon
-                  icon="chevron-down"
-                  style={pal.text as FontAwesomeIconStyle}
-                />
-              </View>
-            </View>
-          </View>
-
-          <View style={s.pb20}>
-            <Text
-              type="md-medium"
-              style={[pal.text, s.mb2]}
-              nativeID="phoneNumber">
-              <Trans>Phone number</Trans>
-            </Text>
-            <TextInput
-              testID="phoneInput"
-              icon="phone"
-              placeholder={_(msg`Enter your phone number`)}
-              value={uiState.verificationPhone}
-              editable
-              onChange={value =>
-                uiDispatch({type: 'set-verification-phone', value})
-              }
-              accessibilityLabel={_(msg`Email`)}
-              accessibilityHint={_(
-                msg`Input phone number for SMS verification`,
-              )}
-              accessibilityLabelledBy="phoneNumber"
-              keyboardType="phone-pad"
-              autoCapitalize="none"
-              autoComplete="tel"
-              autoCorrect={false}
-              autoFocus={true}
-            />
-            <Text type="sm" style={[pal.textLight, s.mt5]}>
-              <Trans>
-                Please enter a phone number that can receive SMS text messages.
-              </Trans>
-            </Text>
-          </View>
-
-          <View style={isMobile ? {} : {flexDirection: 'row'}}>
-            {uiState.isProcessing ? (
-              <ActivityIndicator />
-            ) : (
-              <Button
-                testID="requestCodeBtn"
-                type="primary"
-                label={_(msg`Request code`)}
-                labelStyle={isMobile ? [s.flex1, s.textCenter, s.f17] : []}
-                style={
-                  isMobile ? {paddingVertical: 12, paddingHorizontal: 20} : {}
-                }
-                onPress={onPressRequest}
-              />
-            )}
-          </View>
-        </>
-      ) : (
-        <>
-          <View style={s.pb20}>
-            <View
-              style={[
-                s.flexRow,
-                s.mb5,
-                s.alignCenter,
-                {justifyContent: 'space-between'},
-              ]}>
-              <Text
-                type="md-medium"
-                style={pal.text}
-                nativeID="verificationCode">
-                <Trans>Verification code</Trans>{' '}
-              </Text>
-              <TouchableWithoutFeedback
-                onPress={onPressRetry}
-                accessibilityLabel={_(msg`Retry.`)}
-                accessibilityHint=""
-                hitSlop={HITSLOP_10}>
-                <View style={styles.touchable}>
-                  <Text
-                    type="md-medium"
-                    style={pal.link}
-                    nativeID="verificationCode">
-                    <Trans>Retry</Trans>
-                  </Text>
-                </View>
-              </TouchableWithoutFeedback>
-            </View>
-            <TextInput
-              testID="codeInput"
-              icon="hashtag"
-              placeholder={_(msg`XXXXXX`)}
-              value={uiState.verificationCode}
-              editable
-              onChange={value =>
-                uiDispatch({type: 'set-verification-code', value})
-              }
-              accessibilityLabel={_(msg`Email`)}
-              accessibilityHint={_(
-                msg`Input the verification code we have texted to you`,
-              )}
-              accessibilityLabelledBy="verificationCode"
-              keyboardType="phone-pad"
-              autoCapitalize="none"
-              autoComplete="one-time-code"
-              textContentType="oneTimeCode"
-              autoCorrect={false}
-              autoFocus={true}
-            />
-            <Text type="sm" style={[pal.textLight, s.mt5]}>
-              <Trans>
-                Please enter the verification code sent to{' '}
-                {phoneNumberFormatted}.
-              </Trans>
-            </Text>
-          </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'}),
-  },
-})
diff --git a/src/view/com/auth/create/Step3.tsx b/src/view/com/auth/create/Step3.tsx
deleted file mode 100644
index 3a52abf80..000000000
--- a/src/view/com/auth/create/Step3.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React from 'react'
-import {StyleSheet, 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} from 'lib/strings/handles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-/** STEP 3: Your user handle
- * @field User handle
- */
-export function Step3({
-  uiState,
-  uiDispatch,
-}: {
-  uiState: CreateAccountState
-  uiDispatch: CreateAccountDispatch
-}) {
-  const pal = usePalette('default')
-  const {_} = useLingui()
-  return (
-    <View>
-      <StepHeader uiState={uiState} title={_(msg`Your user handle`)} />
-      <View style={s.pb10}>
-        <TextInput
-          testID="handleInput"
-          icon="at"
-          placeholder="e.g. alice"
-          value={uiState.handle}
-          editable
-          autoFocus
-          autoComplete="off"
-          autoCorrect={false}
-          onChange={value => uiDispatch({type: 'set-handle', value})}
-          // 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>
-      {uiState.error ? (
-        <ErrorMessage message={uiState.error} style={styles.error} />
-      ) : undefined}
-    </View>
-  )
-}
-
-const styles = StyleSheet.create({
-  error: {
-    borderRadius: 6,
-  },
-})
diff --git a/src/view/com/auth/create/StepHeader.tsx b/src/view/com/auth/create/StepHeader.tsx
deleted file mode 100644
index af6bf5478..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 = uiState.isPhoneVerificationRequired ? 3 : 2
-  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,
-  },
-})
diff --git a/src/view/com/auth/create/state.ts b/src/view/com/auth/create/state.ts
deleted file mode 100644
index e8a7cd4ed..000000000
--- a/src/view/com/auth/create/state.ts
+++ /dev/null
@@ -1,350 +0,0 @@
-import {useReducer} from 'react'
-import {
-  ComAtprotoServerDescribeServer,
-  ComAtprotoServerCreateAccount,
-  BskyAgent,
-} from '@atproto/api'
-import {I18nContext, useLingui} from '@lingui/react'
-import {msg} from '@lingui/macro'
-import * as EmailValidator from 'email-validator'
-import {getAge} from 'lib/strings/time'
-import {logger} from '#/logger'
-import {createFullHandle} from '#/lib/strings/handles'
-import {cleanError} from '#/lib/strings/errors'
-import {DispatchContext as OnboardingDispatchContext} from '#/state/shell/onboarding'
-import {ApiContext as SessionApiContext} from '#/state/session'
-import {DEFAULT_SERVICE} from '#/lib/constants'
-import parsePhoneNumber, {CountryCode} from 'libphonenumber-js'
-
-export type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
-const DEFAULT_DATE = new Date(Date.now() - 60e3 * 60 * 24 * 365 * 20) // default to 20 years ago
-
-export type CreateAccountAction =
-  | {type: 'set-step'; value: number}
-  | {type: 'set-error'; value: string | undefined}
-  | {type: 'set-processing'; value: boolean}
-  | {type: 'set-service-url'; value: string}
-  | {type: 'set-service-description'; value: ServiceDescription | undefined}
-  | {type: 'set-user-domain'; value: string}
-  | {type: 'set-invite-code'; value: string}
-  | {type: 'set-email'; value: string}
-  | {type: 'set-password'; value: string}
-  | {type: 'set-phone-country'; value: CountryCode}
-  | {type: 'set-verification-phone'; value: string}
-  | {type: 'set-verification-code'; value: string}
-  | {type: 'set-has-requested-verification-code'; value: boolean}
-  | {type: 'set-handle'; value: string}
-  | {type: 'set-birth-date'; value: Date}
-  | {type: 'next'}
-  | {type: 'back'}
-
-export interface CreateAccountState {
-  // state
-  step: number
-  error: string | undefined
-  isProcessing: boolean
-  serviceUrl: string
-  serviceDescription: ServiceDescription | undefined
-  userDomain: string
-  inviteCode: string
-  email: string
-  password: string
-  phoneCountry: CountryCode
-  verificationPhone: string
-  verificationCode: string
-  hasRequestedVerificationCode: boolean
-  handle: string
-  birthDate: Date
-
-  // computed
-  canBack: boolean
-  canNext: boolean
-  isInviteCodeRequired: boolean
-  isPhoneVerificationRequired: boolean
-}
-
-export type CreateAccountDispatch = (action: CreateAccountAction) => void
-
-export function useCreateAccount() {
-  const {_} = useLingui()
-  return useReducer(createReducer({_}), {
-    step: 1,
-    error: undefined,
-    isProcessing: false,
-    serviceUrl: DEFAULT_SERVICE,
-    serviceDescription: undefined,
-    userDomain: '',
-    inviteCode: '',
-    email: '',
-    password: '',
-    phoneCountry: 'US',
-    verificationPhone: '',
-    verificationCode: '',
-    hasRequestedVerificationCode: false,
-    handle: '',
-    birthDate: DEFAULT_DATE,
-
-    canBack: false,
-    canNext: false,
-    isInviteCodeRequired: false,
-    isPhoneVerificationRequired: false,
-  })
-}
-
-export async function requestVerificationCode({
-  uiState,
-  uiDispatch,
-  _,
-}: {
-  uiState: CreateAccountState
-  uiDispatch: CreateAccountDispatch
-  _: I18nContext['_']
-}) {
-  const phoneNumber = parsePhoneNumber(
-    uiState.verificationPhone,
-    uiState.phoneCountry,
-  )?.number
-  if (!phoneNumber) {
-    return
-  }
-  uiDispatch({type: 'set-error', value: ''})
-  uiDispatch({type: 'set-processing', value: true})
-  uiDispatch({type: 'set-verification-phone', value: phoneNumber})
-  try {
-    const agent = new BskyAgent({service: uiState.serviceUrl})
-    await agent.com.atproto.temp.requestPhoneVerification({
-      phoneNumber,
-    })
-    uiDispatch({type: 'set-has-requested-verification-code', value: true})
-  } catch (e: any) {
-    logger.error(
-      `Failed to request sms verification code (${e.status} status)`,
-      {message: e},
-    )
-    uiDispatch({type: 'set-error', value: cleanError(e.toString())})
-  }
-  uiDispatch({type: 'set-processing', value: false})
-}
-
-export async function submit({
-  createAccount,
-  onboardingDispatch,
-  uiState,
-  uiDispatch,
-  _,
-}: {
-  createAccount: SessionApiContext['createAccount']
-  onboardingDispatch: OnboardingDispatchContext
-  uiState: CreateAccountState
-  uiDispatch: CreateAccountDispatch
-  _: I18nContext['_']
-}) {
-  if (!uiState.email) {
-    uiDispatch({type: 'set-step', value: 1})
-    return uiDispatch({
-      type: 'set-error',
-      value: _(msg`Please enter your email.`),
-    })
-  }
-  if (!EmailValidator.validate(uiState.email)) {
-    uiDispatch({type: 'set-step', value: 1})
-    return uiDispatch({
-      type: 'set-error',
-      value: _(msg`Your email appears to be invalid.`),
-    })
-  }
-  if (!uiState.password) {
-    uiDispatch({type: 'set-step', value: 1})
-    return uiDispatch({
-      type: 'set-error',
-      value: _(msg`Please choose your password.`),
-    })
-  }
-  if (
-    uiState.isPhoneVerificationRequired &&
-    (!uiState.verificationPhone || !uiState.verificationCode)
-  ) {
-    uiDispatch({type: 'set-step', value: 2})
-    return uiDispatch({
-      type: 'set-error',
-      value: _(msg`Please enter the code you received by SMS.`),
-    })
-  }
-  if (!uiState.handle) {
-    uiDispatch({type: 'set-step', value: 3})
-    return uiDispatch({
-      type: 'set-error',
-      value: _(msg`Please choose your handle.`),
-    })
-  }
-  uiDispatch({type: 'set-error', value: ''})
-  uiDispatch({type: 'set-processing', value: true})
-
-  try {
-    onboardingDispatch({type: 'start'}) // start now to avoid flashing the wrong view
-    await createAccount({
-      service: uiState.serviceUrl,
-      email: uiState.email,
-      handle: createFullHandle(uiState.handle, uiState.userDomain),
-      password: uiState.password,
-      inviteCode: uiState.inviteCode.trim(),
-      verificationPhone: uiState.verificationPhone.trim(),
-      verificationCode: uiState.verificationCode.trim(),
-    })
-  } catch (e: any) {
-    onboardingDispatch({type: 'skip'}) // undo starting the onboard
-    let errMsg = e.toString()
-    if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) {
-      errMsg = _(
-        msg`Invite code not accepted. Check that you input it correctly and try again.`,
-      )
-      uiDispatch({type: 'set-step', value: 1})
-    } else if (e.error === 'InvalidPhoneVerification') {
-      uiDispatch({type: 'set-step', value: 2})
-    }
-
-    if ([400, 429].includes(e.status)) {
-      logger.warn('Failed to create account', {message: e})
-    } else {
-      logger.error(`Failed to create account (${e.status} status)`, {
-        message: e,
-      })
-    }
-
-    uiDispatch({type: 'set-processing', value: false})
-    uiDispatch({type: 'set-error', value: cleanError(errMsg)})
-    throw e
-  }
-}
-
-export function is13(state: CreateAccountState) {
-  return getAge(state.birthDate) >= 13
-}
-
-export function is18(state: CreateAccountState) {
-  return getAge(state.birthDate) >= 18
-}
-
-function createReducer({_}: {_: I18nContext['_']}) {
-  return function reducer(
-    state: CreateAccountState,
-    action: CreateAccountAction,
-  ): CreateAccountState {
-    switch (action.type) {
-      case 'set-step': {
-        return compute({...state, step: action.value})
-      }
-      case 'set-error': {
-        return compute({...state, error: action.value})
-      }
-      case 'set-processing': {
-        return compute({...state, isProcessing: action.value})
-      }
-      case 'set-service-url': {
-        return compute({
-          ...state,
-          serviceUrl: action.value,
-          serviceDescription:
-            state.serviceUrl !== action.value
-              ? undefined
-              : state.serviceDescription,
-        })
-      }
-      case 'set-service-description': {
-        return compute({
-          ...state,
-          serviceDescription: action.value,
-          userDomain: action.value?.availableUserDomains[0] || '',
-        })
-      }
-      case 'set-user-domain': {
-        return compute({...state, userDomain: action.value})
-      }
-      case 'set-invite-code': {
-        return compute({...state, inviteCode: action.value})
-      }
-      case 'set-email': {
-        return compute({...state, email: action.value})
-      }
-      case 'set-password': {
-        return compute({...state, password: action.value})
-      }
-      case 'set-phone-country': {
-        return compute({...state, phoneCountry: action.value})
-      }
-      case 'set-verification-phone': {
-        return compute({
-          ...state,
-          verificationPhone: action.value,
-          hasRequestedVerificationCode: false,
-        })
-      }
-      case 'set-verification-code': {
-        return compute({...state, verificationCode: action.value.trim()})
-      }
-      case 'set-has-requested-verification-code': {
-        return compute({...state, hasRequestedVerificationCode: action.value})
-      }
-      case 'set-handle': {
-        return compute({...state, handle: action.value})
-      }
-      case 'set-birth-date': {
-        return compute({...state, birthDate: action.value})
-      }
-      case 'next': {
-        if (state.step === 1) {
-          if (!is13(state)) {
-            return compute({
-              ...state,
-              error: _(
-                msg`Unfortunately, you do not meet the requirements to create an account.`,
-              ),
-            })
-          }
-        }
-        let increment = 1
-        if (state.step === 1 && !state.isPhoneVerificationRequired) {
-          increment = 2
-        }
-        return compute({...state, error: '', step: state.step + increment})
-      }
-      case 'back': {
-        let decrement = 1
-        if (state.step === 3 && !state.isPhoneVerificationRequired) {
-          decrement = 2
-        }
-        return compute({...state, error: '', step: state.step - decrement})
-      }
-    }
-  }
-}
-
-function compute(state: CreateAccountState): CreateAccountState {
-  let canNext = true
-  if (state.step === 1) {
-    canNext =
-      !!state.serviceDescription &&
-      (!state.isInviteCodeRequired || !!state.inviteCode) &&
-      !!state.email &&
-      !!state.password
-  } else if (state.step === 2) {
-    canNext =
-      !state.isPhoneVerificationRequired ||
-      (!!state.verificationPhone &&
-        isValidVerificationCode(state.verificationCode))
-  } else if (state.step === 3) {
-    canNext = !!state.handle
-  }
-  return {
-    ...state,
-    canBack: state.step > 1,
-    canNext,
-    isInviteCodeRequired: !!state.serviceDescription?.inviteCodeRequired,
-    isPhoneVerificationRequired:
-      !!state.serviceDescription?.phoneVerificationRequired,
-  }
-}
-
-function isValidVerificationCode(str: string): boolean {
-  return /[0-9]{6}/.test(str)
-}