about summary refs log tree commit diff
path: root/src/view/com/auth
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/auth')
-rw-r--r--src/view/com/auth/HomeLoggedOutCTA.tsx21
-rw-r--r--src/view/com/auth/LoggedOut.tsx41
-rw-r--r--src/view/com/auth/SplashScreen.tsx174
-rw-r--r--src/view/com/auth/SplashScreen.web.tsx280
-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
-rw-r--r--src/view/com/auth/login/ChooseAccountForm.tsx158
-rw-r--r--src/view/com/auth/login/ForgotPasswordForm.tsx228
-rw-r--r--src/view/com/auth/login/Login.tsx164
-rw-r--r--src/view/com/auth/login/LoginForm.tsx298
-rw-r--r--src/view/com/auth/login/PasswordUpdatedForm.tsx48
-rw-r--r--src/view/com/auth/login/SetNewPasswordForm.tsx211
-rw-r--r--src/view/com/auth/login/styles.ts118
-rw-r--r--src/view/com/auth/onboarding/RecommendedFollowsItem.tsx23
-rw-r--r--src/view/com/auth/onboarding/WelcomeMobile.tsx6
-rw-r--r--src/view/com/auth/server-input/index.tsx8
21 files changed, 316 insertions, 2841 deletions
diff --git a/src/view/com/auth/HomeLoggedOutCTA.tsx b/src/view/com/auth/HomeLoggedOutCTA.tsx
index f796d8bae..4c8c35da7 100644
--- a/src/view/com/auth/HomeLoggedOutCTA.tsx
+++ b/src/view/com/auth/HomeLoggedOutCTA.tsx
@@ -1,14 +1,15 @@
 import React from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {Trans, msg} from '@lingui/macro'
-import {ScrollView} from '../util/Views'
-import {Text} from '../util/text/Text'
+
 import {usePalette} from '#/lib/hooks/usePalette'
-import {colors, s} from '#/lib/styles'
-import {TextLink} from '../util/Link'
 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {colors, s} from '#/lib/styles'
 import {useLoggedOutViewControls} from '#/state/shell/logged-out'
+import {TextLink} from '../util/Link'
+import {Text} from '../util/text/Text'
+import {ScrollView} from '../util/Views'
 
 export function HomeLoggedOutCTA() {
   const pal = usePalette('default')
@@ -52,7 +53,9 @@ export function HomeLoggedOutCTA() {
           onPress={showCreateAccount}
           accessibilityRole="button"
           accessibilityLabel={_(msg`Create new account`)}
-          accessibilityHint="Opens flow to create a new Bluesky account">
+          accessibilityHint={_(
+            msg`Opens flow to create a new Bluesky account`,
+          )}>
           <Text
             style={[
               s.white,
@@ -68,14 +71,16 @@ export function HomeLoggedOutCTA() {
           onPress={showSignIn}
           accessibilityRole="button"
           accessibilityLabel={_(msg`Sign in`)}
-          accessibilityHint="Opens flow to sign into your existing Bluesky account">
+          accessibilityHint={_(
+            msg`Opens flow to sign into your existing Bluesky account`,
+          )}>
           <Text
             style={[
               pal.text,
               styles.btnLabel,
               isMobile && styles.btnLabelMobile,
             ]}>
-            <Trans>Sign In</Trans>
+            <Trans>Sign in</Trans>
           </Text>
         </TouchableOpacity>
       </View>
diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx
index 603abbab2..c8c81dd77 100644
--- a/src/view/com/auth/LoggedOut.tsx
+++ b/src/view/com/auth/LoggedOut.tsx
@@ -1,27 +1,28 @@
 import React from 'react'
-import {View, Pressable} from 'react-native'
+import {Pressable, View} from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {Trans, msg} from '@lingui/macro'
 import {useNavigation} from '@react-navigation/native'
 
-import {isIOS, isNative} from 'platform/detection'
-import {Login} from 'view/com/auth/login/Login'
-import {CreateAccount} from 'view/com/auth/create/CreateAccount'
-import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
-import {s} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {SplashScreen} from './SplashScreen'
-import {useSetMinimalShellMode} from '#/state/shell/minimal-mode'
-import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
+import {useAnalytics} from '#/lib/analytics/analytics'
+import {usePalette} from '#/lib/hooks/usePalette'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {logEvent} from '#/lib/statsig/statsig'
+import {s} from '#/lib/styles'
+import {isIOS, isNative} from '#/platform/detection'
+import {useSession} from '#/state/session'
 import {
   useLoggedOutView,
   useLoggedOutViewControls,
 } from '#/state/shell/logged-out'
-import {useSession} from '#/state/session'
-import {Text} from '#/view/com/util/text/Text'
+import {useSetMinimalShellMode} from '#/state/shell/minimal-mode'
 import {NavigationProp} from 'lib/routes/types'
+import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
+import {Text} from '#/view/com/util/text/Text'
+import {Login} from '#/screens/Login'
+import {Signup} from '#/screens/Signup'
+import {SplashScreen} from './SplashScreen'
 
 enum ScreenState {
   S_LoginOrCreateAccount,
@@ -133,10 +134,14 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
 
         {screenState === ScreenState.S_LoginOrCreateAccount ? (
           <SplashScreen
-            onPressSignin={() => setScreenState(ScreenState.S_Login)}
-            onPressCreateAccount={() =>
+            onPressSignin={() => {
+              setScreenState(ScreenState.S_Login)
+              logEvent('splash:signInPressed', {})
+            }}
+            onPressCreateAccount={() => {
               setScreenState(ScreenState.S_CreateAccount)
-            }
+              logEvent('splash:createAccountPressed', {})
+            }}
           />
         ) : undefined}
         {screenState === ScreenState.S_Login ? (
@@ -148,7 +153,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/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
index ffd07d945..763b01dfa 100644
--- a/src/view/com/auth/SplashScreen.tsx
+++ b/src/view/com/auth/SplashScreen.tsx
@@ -1,14 +1,21 @@
 import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {Text} from 'view/com/util/text/Text'
-import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
-import {s, colors} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {CenteredView} from '../util/Views'
-import {Trans, msg} from '@lingui/macro'
+import {View} from 'react-native'
+import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+
+import {sanitizeAppLanguageSetting} from '#/locale/helpers'
+import {APP_LANGUAGES} from '#/locale/languages'
+import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
 import {Logo} from '#/view/icons/Logo'
 import {Logotype} from '#/view/icons/Logotype'
+import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
+import {Text} from '#/components/Typography'
+import {CenteredView} from '../util/Views'
 
 export const SplashScreen = ({
   onPressSignin,
@@ -17,82 +24,121 @@ export const SplashScreen = ({
   onPressSignin: () => void
   onPressCreateAccount: () => void
 }) => {
-  const pal = usePalette('default')
+  const t = useTheme()
   const {_} = useLingui()
 
+  const langPrefs = useLanguagePrefs()
+  const setLangPrefs = useLanguagePrefsApi()
+  const insets = useSafeAreaInsets()
+
+  const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage)
+
+  const onChangeAppLanguage = React.useCallback(
+    (value: Parameters<PickerSelectProps['onValueChange']>[0]) => {
+      if (!value) return
+      if (sanitizedLang !== value) {
+        setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value))
+      }
+    },
+    [sanitizedLang, setLangPrefs],
+  )
+
   return (
-    <CenteredView style={[styles.container, pal.view]}>
+    <CenteredView style={[a.h_full, a.flex_1]}>
       <ErrorBoundary>
-        <View style={styles.hero}>
+        <View style={[{flex: 1}, a.justify_center, a.align_center]}>
           <Logo width={92} fill="sky" />
 
-          <View style={{paddingTop: 40, paddingBottom: 6}}>
-            <Logotype width={161} fill={pal.text.color} />
+          <View style={[a.pb_sm, a.pt_5xl]}>
+            <Logotype width={161} fill={t.atoms.text.color} />
           </View>
 
-          <Text type="lg-medium" style={[pal.textLight]}>
+          <Text
+            style={[a.text_md, a.font_semibold, t.atoms.text_contrast_medium]}>
             <Trans>What's up?</Trans>
           </Text>
         </View>
-        <View testID="signinOrCreateAccount" style={styles.btns}>
-          <TouchableOpacity
+        <View testID="signinOrCreateAccount">
+          <Button
             testID="createAccountButton"
-            style={[styles.btn, {backgroundColor: colors.blue3}]}
             onPress={onPressCreateAccount}
             accessibilityRole="button"
-            accessibilityLabel={_(msg`Create new account`)}
-            accessibilityHint="Opens flow to create a new Bluesky account">
-            <Text style={[s.white, styles.btnLabel]}>
+            label={_(msg`Create new account`)}
+            accessibilityHint={_(
+              msg`Opens flow to create a new Bluesky account`,
+            )}
+            style={[a.mx_xl, a.mb_xl]}
+            size="large"
+            variant="solid"
+            color="primary">
+            <ButtonText>
               <Trans>Create a new account</Trans>
-            </Text>
-          </TouchableOpacity>
-          <TouchableOpacity
+            </ButtonText>
+          </Button>
+          <Button
             testID="signInButton"
-            style={[styles.btn, pal.btn]}
             onPress={onPressSignin}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Sign in`)}
-            accessibilityHint="Opens flow to sign into your existing Bluesky account">
-            <Text style={[pal.text, styles.btnLabel]}>
-              <Trans>Sign In</Trans>
-            </Text>
-          </TouchableOpacity>
+            label={_(msg`Sign in`)}
+            accessibilityHint={_(
+              msg`Opens flow to sign into your existing Bluesky account`,
+            )}
+            style={[a.mx_xl, a.mb_xl]}
+            size="large"
+            variant="solid"
+            color="secondary">
+            <ButtonText>
+              <Trans>Sign in</Trans>
+            </ButtonText>
+          </Button>
         </View>
+        <View
+          style={[
+            a.px_lg,
+            a.pt_md,
+            a.pb_2xl,
+            a.justify_center,
+            a.align_center,
+          ]}>
+          <View style={a.relative}>
+            <RNPickerSelect
+              placeholder={{}}
+              value={sanitizedLang}
+              onValueChange={onChangeAppLanguage}
+              items={APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => ({
+                label: l.name,
+                value: l.code2,
+                key: l.code2,
+              }))}
+              useNativeAndroidPickerStyle={false}
+              style={{
+                inputAndroid: {
+                  color: t.atoms.text_contrast_medium.color,
+                  fontSize: 16,
+                  paddingRight: 12 + 4,
+                },
+                inputIOS: {
+                  color: t.atoms.text.color,
+                  fontSize: 16,
+                  paddingRight: 12 + 4,
+                },
+              }}
+            />
+
+            <View
+              style={[
+                a.absolute,
+                a.inset_0,
+                {left: 'auto'},
+                {pointerEvents: 'none'},
+                a.align_center,
+                a.justify_center,
+              ]}>
+              <ChevronDown fill={t.atoms.text.color} size="xs" />
+            </View>
+          </View>
+        </View>
+        <View style={{height: insets.bottom}} />
       </ErrorBoundary>
     </CenteredView>
   )
 }
-
-const styles = StyleSheet.create({
-  container: {
-    height: '100%',
-  },
-  hero: {
-    flex: 2,
-    justifyContent: 'center',
-    alignItems: 'center',
-  },
-  btns: {
-    paddingBottom: 40,
-  },
-  title: {
-    textAlign: 'center',
-    fontSize: 68,
-    fontWeight: 'bold',
-  },
-  subtitle: {
-    textAlign: 'center',
-    fontSize: 42,
-    fontWeight: 'bold',
-  },
-  btn: {
-    borderRadius: 32,
-    paddingVertical: 16,
-    marginBottom: 20,
-    marginHorizontal: 20,
-  },
-  btnLabel: {
-    textAlign: 'center',
-    fontSize: 21,
-  },
-})
diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx
index 8ef64099f..cdb72cc04 100644
--- a/src/view/com/auth/SplashScreen.web.tsx
+++ b/src/view/com/auth/SplashScreen.web.tsx
@@ -1,17 +1,22 @@
 import React from 'react'
-import {StyleSheet, TouchableOpacity, View, Pressable} from 'react-native'
+import {Pressable, View} from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Text} from 'view/com/util/text/Text'
-import {TextLink} from '../util/Link'
-import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
-import {s, colors} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {CenteredView} from '../util/Views'
-import {isWeb} from 'platform/detection'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {sanitizeAppLanguageSetting} from '#/locale/helpers'
+import {APP_LANGUAGES} from '#/locale/languages'
+import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {Trans} from '@lingui/macro'
 import {Logo} from '#/view/icons/Logo'
 import {Logotype} from '#/view/icons/Logotype'
+import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
+import {InlineLink} from '#/components/Link'
+import {Text} from '#/components/Typography'
+import {CenteredView} from '../util/Views'
 
 export const SplashScreen = ({
   onDismiss,
@@ -22,10 +27,9 @@ export const SplashScreen = ({
   onPressSignin: () => void
   onPressCreateAccount: () => void
 }) => {
-  const pal = usePalette('default')
-  const {isTabletOrMobile} = useWebMediaQueries()
-  const styles = useStyles()
-  const isMobileWeb = isWeb && isTabletOrMobile
+  const {_} = useLingui()
+  const t = useTheme()
+  const {isTabletOrMobile: isMobileWeb} = useWebMediaQueries()
 
   return (
     <>
@@ -44,153 +48,165 @@ export const SplashScreen = ({
             icon="x"
             size={24}
             style={{
-              color: String(pal.text.color),
+              color: String(t.atoms.text.color),
             }}
           />
         </Pressable>
       )}
 
-      <CenteredView style={[styles.container, pal.view]}>
+      <CenteredView style={[a.h_full, a.flex_1]}>
         <View
           testID="noSessionView"
           style={[
-            styles.containerInner,
-            isMobileWeb && styles.containerInnerMobile,
-            pal.border,
-            {alignItems: 'center'},
+            a.h_full,
+            a.justify_center,
+            // @ts-ignore web only
+            {paddingBottom: '20vh'},
+            isMobileWeb && a.pb_5xl,
+            t.atoms.border_contrast_medium,
+            a.align_center,
+            a.gap_5xl,
           ]}>
           <ErrorBoundary>
-            <Logo width={92} fill="sky" />
+            <View style={[a.justify_center, a.align_center]}>
+              <Logo width={92} fill="sky" />
+
+              <View style={[a.pb_sm, a.pt_5xl]}>
+                <Logotype width={161} fill={t.atoms.text.color} />
+              </View>
 
-            <View style={{paddingTop: 40, paddingBottom: 20}}>
-              <Logotype width={161} fill={pal.text.color} />
+              <Text
+                style={[
+                  a.text_md,
+                  a.font_semibold,
+                  t.atoms.text_contrast_medium,
+                ]}>
+                <Trans>What's up?</Trans>
+              </Text>
             </View>
 
-            <View testID="signinOrCreateAccount" style={styles.btns}>
-              <TouchableOpacity
+            <View
+              testID="signinOrCreateAccount"
+              style={[a.w_full, {maxWidth: 320}]}>
+              <Button
                 testID="createAccountButton"
-                style={[styles.btn, {backgroundColor: colors.blue3}]}
                 onPress={onPressCreateAccount}
-                // TODO: web accessibility
-                accessibilityRole="button">
-                <Text style={[s.white, styles.btnLabel]}>
+                accessibilityRole="button"
+                label={_(msg`Create new account`)}
+                accessibilityHint={_(
+                  msg`Opens flow to create a new Bluesky account`,
+                )}
+                style={[a.mx_xl, a.mb_xl]}
+                size="large"
+                variant="solid"
+                color="primary">
+                <ButtonText>
                   <Trans>Create a new account</Trans>
-                </Text>
-              </TouchableOpacity>
-              <TouchableOpacity
+                </ButtonText>
+              </Button>
+              <Button
                 testID="signInButton"
-                style={[styles.btn, pal.btn]}
                 onPress={onPressSignin}
-                // TODO: web accessibility
-                accessibilityRole="button">
-                <Text style={[pal.text, styles.btnLabel]}>
-                  <Trans>Sign In</Trans>
-                </Text>
-              </TouchableOpacity>
+                label={_(msg`Sign in`)}
+                accessibilityHint={_(
+                  msg`Opens flow to sign into your existing Bluesky account`,
+                )}
+                style={[a.mx_xl, a.mb_xl]}
+                size="large"
+                variant="solid"
+                color="secondary">
+                <ButtonText>
+                  <Trans>Sign in</Trans>
+                </ButtonText>
+              </Button>
             </View>
           </ErrorBoundary>
         </View>
-        <Footer styles={styles} />
+        <Footer />
       </CenteredView>
     </>
   )
 }
 
-function Footer({styles}: {styles: ReturnType<typeof useStyles>}) {
-  const pal = usePalette('default')
+function Footer() {
+  const t = useTheme()
+
+  const langPrefs = useLanguagePrefs()
+  const setLangPrefs = useLanguagePrefsApi()
+
+  const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage)
+
+  const onChangeAppLanguage = React.useCallback(
+    (ev: React.ChangeEvent<HTMLSelectElement>) => {
+      const value = ev.target.value
+
+      if (!value) return
+      if (sanitizedLang !== value) {
+        setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value))
+      }
+    },
+    [sanitizedLang, setLangPrefs],
+  )
 
   return (
-    <View style={[styles.footer, pal.view, pal.border]}>
-      <TextLink
-        href="https://bsky.social"
-        text="Business"
-        style={[styles.footerLink, pal.link]}
-      />
-      <TextLink
-        href="https://bsky.social/about/blog"
-        text="Blog"
-        style={[styles.footerLink, pal.link]}
-      />
-      <TextLink
-        href="https://bsky.social/about/join"
-        text="Jobs"
-        style={[styles.footerLink, pal.link]}
-      />
+    <View
+      style={[
+        a.absolute,
+        a.inset_0,
+        {top: 'auto'},
+        a.p_xl,
+        a.border_t,
+        a.flex_row,
+        a.flex_wrap,
+        a.gap_xl,
+        a.flex_1,
+        t.atoms.border_contrast_medium,
+      ]}>
+      <InlineLink to="https://bsky.social">
+        <Trans>Business</Trans>
+      </InlineLink>
+      <InlineLink to="https://bsky.social/about/blog">
+        <Trans>Blog</Trans>
+      </InlineLink>
+      <InlineLink to="https://bsky.social/about/join">
+        <Trans>Jobs</Trans>
+      </InlineLink>
+
+      <View style={a.flex_1} />
+
+      <View style={[a.flex_row, a.gap_sm, a.align_center, a.flex_shrink]}>
+        <Text aria-hidden={true} style={t.atoms.text_contrast_medium}>
+          {APP_LANGUAGES.find(l => l.code2 === sanitizedLang)?.name}
+        </Text>
+        <ChevronDown
+          fill={t.atoms.text.color}
+          size="xs"
+          style={a.flex_shrink}
+        />
+
+        <select
+          value={sanitizedLang}
+          onChange={onChangeAppLanguage}
+          style={{
+            cursor: 'pointer',
+            MozAppearance: 'none',
+            WebkitAppearance: 'none',
+            appearance: 'none',
+            position: 'absolute',
+            inset: 0,
+            width: '100%',
+            color: 'transparent',
+            background: 'transparent',
+            border: 0,
+            padding: 0,
+          }}>
+          {APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => (
+            <option key={l.code2} value={l.code2}>
+              {l.name}
+            </option>
+          ))}
+        </select>
+      </View>
     </View>
   )
 }
-const useStyles = () => {
-  return StyleSheet.create({
-    container: {
-      height: '100%',
-    },
-    containerInner: {
-      height: '100%',
-      justifyContent: 'center',
-      // @ts-ignore web only
-      paddingBottom: '20vh',
-      paddingHorizontal: 20,
-    },
-    containerInnerMobile: {
-      paddingBottom: 50,
-    },
-    title: {
-      textAlign: 'center',
-      color: colors.blue3,
-      fontSize: 68,
-      fontWeight: 'bold',
-      paddingBottom: 10,
-    },
-    titleMobile: {
-      textAlign: 'center',
-      color: colors.blue3,
-      fontSize: 58,
-      fontWeight: 'bold',
-    },
-    subtitle: {
-      textAlign: 'center',
-      color: colors.gray5,
-      fontSize: 52,
-      fontWeight: 'bold',
-      paddingBottom: 30,
-    },
-    subtitleMobile: {
-      textAlign: 'center',
-      color: colors.gray5,
-      fontSize: 42,
-      fontWeight: 'bold',
-      paddingBottom: 30,
-    },
-    btns: {
-      gap: 10,
-      justifyContent: 'center',
-      paddingBottom: 40,
-    },
-    btn: {
-      borderRadius: 30,
-      paddingHorizontal: 24,
-      paddingVertical: 12,
-      minWidth: 220,
-    },
-    btnLabel: {
-      textAlign: 'center',
-      fontSize: 18,
-    },
-    notice: {
-      paddingHorizontal: 40,
-      textAlign: 'center',
-    },
-    footer: {
-      position: 'absolute',
-      left: 0,
-      right: 0,
-      bottom: 0,
-      padding: 20,
-      borderTopWidth: 1,
-      flexDirection: 'row',
-    },
-    footerLink: {
-      marginRight: 20,
-    },
-  })
-}
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)
-}
diff --git a/src/view/com/auth/login/ChooseAccountForm.tsx b/src/view/com/auth/login/ChooseAccountForm.tsx
deleted file mode 100644
index 32cd8315d..000000000
--- a/src/view/com/auth/login/ChooseAccountForm.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import React from 'react'
-import {ScrollView, TouchableOpacity, View} from 'react-native'
-import {
-  FontAwesomeIcon,
-  FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {Text} from '../../util/text/Text'
-import {UserAvatar} from '../../util/UserAvatar'
-import {s, colors} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {styles} from './styles'
-import {useSession, useSessionApi, SessionAccount} from '#/state/session'
-import {useProfileQuery} from '#/state/queries/profile'
-import {useLoggedOutViewControls} from '#/state/shell/logged-out'
-import * as Toast from '#/view/com/util/Toast'
-
-function AccountItem({
-  account,
-  onSelect,
-  isCurrentAccount,
-}: {
-  account: SessionAccount
-  onSelect: (account: SessionAccount) => void
-  isCurrentAccount: boolean
-}) {
-  const pal = usePalette('default')
-  const {_} = useLingui()
-  const {data: profile} = useProfileQuery({did: account.did})
-
-  const onPress = React.useCallback(() => {
-    onSelect(account)
-  }, [account, onSelect])
-
-  return (
-    <TouchableOpacity
-      testID={`chooseAccountBtn-${account.handle}`}
-      key={account.did}
-      style={[pal.view, pal.border, styles.account]}
-      onPress={onPress}
-      accessibilityRole="button"
-      accessibilityLabel={_(msg`Sign in as ${account.handle}`)}
-      accessibilityHint={_(msg`Double tap to sign in`)}>
-      <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
-        <View style={s.p10}>
-          <UserAvatar avatar={profile?.avatar} size={30} />
-        </View>
-        <Text style={styles.accountText}>
-          <Text type="lg-bold" style={pal.text}>
-            {profile?.displayName || account.handle}{' '}
-          </Text>
-          <Text type="lg" style={[pal.textLight]}>
-            {account.handle}
-          </Text>
-        </Text>
-        {isCurrentAccount ? (
-          <FontAwesomeIcon
-            icon="check"
-            size={16}
-            style={[{color: colors.green3} as FontAwesomeIconStyle, s.mr10]}
-          />
-        ) : (
-          <FontAwesomeIcon
-            icon="angle-right"
-            size={16}
-            style={[pal.text, s.mr10]}
-          />
-        )}
-      </View>
-    </TouchableOpacity>
-  )
-}
-export const ChooseAccountForm = ({
-  onSelectAccount,
-  onPressBack,
-}: {
-  onSelectAccount: (account?: SessionAccount) => void
-  onPressBack: () => void
-}) => {
-  const {track, screen} = useAnalytics()
-  const pal = usePalette('default')
-  const {_} = useLingui()
-  const {accounts, currentAccount} = useSession()
-  const {initSession} = useSessionApi()
-  const {setShowLoggedOut} = useLoggedOutViewControls()
-
-  React.useEffect(() => {
-    screen('Choose Account')
-  }, [screen])
-
-  const onSelect = React.useCallback(
-    async (account: SessionAccount) => {
-      if (account.accessJwt) {
-        if (account.did === currentAccount?.did) {
-          setShowLoggedOut(false)
-          Toast.show(_(msg`Already signed in as @${account.handle}`))
-        } else {
-          await initSession(account)
-          track('Sign In', {resumedSession: true})
-          setTimeout(() => {
-            Toast.show(_(msg`Signed in as @${account.handle}`))
-          }, 100)
-        }
-      } else {
-        onSelectAccount(account)
-      }
-    },
-    [currentAccount, track, initSession, onSelectAccount, setShowLoggedOut, _],
-  )
-
-  return (
-    <ScrollView testID="chooseAccountForm" style={styles.maxHeight}>
-      <Text
-        type="2xl-medium"
-        style={[pal.text, styles.groupLabel, s.mt5, s.mb10]}>
-        <Trans>Sign in as...</Trans>
-      </Text>
-      {accounts.map(account => (
-        <AccountItem
-          key={account.did}
-          account={account}
-          onSelect={onSelect}
-          isCurrentAccount={account.did === currentAccount?.did}
-        />
-      ))}
-      <TouchableOpacity
-        testID="chooseNewAccountBtn"
-        style={[pal.view, pal.border, styles.account, styles.accountLast]}
-        onPress={() => onSelectAccount(undefined)}
-        accessibilityRole="button"
-        accessibilityLabel={_(msg`Login to account that is not listed`)}
-        accessibilityHint="">
-        <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
-          <Text style={[styles.accountText, styles.accountTextOther]}>
-            <Text type="lg" style={pal.text}>
-              <Trans>Other account</Trans>
-            </Text>
-          </Text>
-          <FontAwesomeIcon
-            icon="angle-right"
-            size={16}
-            style={[pal.text, s.mr10]}
-          />
-        </View>
-      </TouchableOpacity>
-      <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}>
-        <TouchableOpacity onPress={onPressBack} accessibilityRole="button">
-          <Text type="xl" style={[pal.link, s.pl5]}>
-            <Trans>Back</Trans>
-          </Text>
-        </TouchableOpacity>
-        <View style={s.flex1} />
-      </View>
-    </ScrollView>
-  )
-}
diff --git a/src/view/com/auth/login/ForgotPasswordForm.tsx b/src/view/com/auth/login/ForgotPasswordForm.tsx
deleted file mode 100644
index 322da2b8f..000000000
--- a/src/view/com/auth/login/ForgotPasswordForm.tsx
+++ /dev/null
@@ -1,228 +0,0 @@
-import React, {useState, useEffect} from 'react'
-import {
-  ActivityIndicator,
-  Keyboard,
-  TextInput,
-  TouchableOpacity,
-  View,
-} from 'react-native'
-import {
-  FontAwesomeIcon,
-  FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
-import {ComAtprotoServerDescribeServer} from '@atproto/api'
-import * as EmailValidator from 'email-validator'
-import {BskyAgent} from '@atproto/api'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {Text} from '../../util/text/Text'
-import {s} from 'lib/styles'
-import {toNiceDomain} from 'lib/strings/url-helpers'
-import {isNetworkError} from 'lib/strings/errors'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useTheme} from 'lib/ThemeContext'
-import {cleanError} from 'lib/strings/errors'
-import {logger} from '#/logger'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {styles} from './styles'
-import {useDialogControl} from '#/components/Dialog'
-
-import {ServerInputDialog} from '../server-input'
-
-type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
-
-export const ForgotPasswordForm = ({
-  error,
-  serviceUrl,
-  serviceDescription,
-  setError,
-  setServiceUrl,
-  onPressBack,
-  onEmailSent,
-}: {
-  error: string
-  serviceUrl: string
-  serviceDescription: ServiceDescription | undefined
-  setError: (v: string) => void
-  setServiceUrl: (v: string) => void
-  onPressBack: () => void
-  onEmailSent: () => void
-}) => {
-  const pal = usePalette('default')
-  const theme = useTheme()
-  const [isProcessing, setIsProcessing] = useState<boolean>(false)
-  const [email, setEmail] = useState<string>('')
-  const {screen} = useAnalytics()
-  const {_} = useLingui()
-  const serverInputControl = useDialogControl()
-
-  useEffect(() => {
-    screen('Signin:ForgotPassword')
-  }, [screen])
-
-  const onPressSelectService = React.useCallback(() => {
-    serverInputControl.open()
-    Keyboard.dismiss()
-  }, [serverInputControl])
-
-  const onPressNext = async () => {
-    if (!EmailValidator.validate(email)) {
-      return setError(_(msg`Your email appears to be invalid.`))
-    }
-
-    setError('')
-    setIsProcessing(true)
-
-    try {
-      const agent = new BskyAgent({service: serviceUrl})
-      await agent.com.atproto.server.requestPasswordReset({email})
-      onEmailSent()
-    } catch (e: any) {
-      const errMsg = e.toString()
-      logger.warn('Failed to request password reset', {error: e})
-      setIsProcessing(false)
-      if (isNetworkError(e)) {
-        setError(
-          _(
-            msg`Unable to contact your service. Please check your Internet connection.`,
-          ),
-        )
-      } else {
-        setError(cleanError(errMsg))
-      }
-    }
-  }
-
-  return (
-    <>
-      <View>
-        <ServerInputDialog
-          control={serverInputControl}
-          onSelect={setServiceUrl}
-        />
-        <Text type="title-lg" style={[pal.text, styles.screenTitle]}>
-          <Trans>Reset password</Trans>
-        </Text>
-        <Text type="md" style={[pal.text, styles.instructions]}>
-          <Trans>
-            Enter the email you used to create your account. We'll send you a
-            "reset code" so you can set a new password.
-          </Trans>
-        </Text>
-        <View
-          testID="forgotPasswordView"
-          style={[pal.borderDark, pal.view, styles.group]}>
-          <TouchableOpacity
-            testID="forgotPasswordSelectServiceButton"
-            style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}
-            onPress={onPressSelectService}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Hosting provider`)}
-            accessibilityHint={_(
-              msg`Sets hosting provider for password reset`,
-            )}>
-            <FontAwesomeIcon
-              icon="globe"
-              style={[pal.textLight, styles.groupContentIcon]}
-            />
-            <Text style={[pal.text, styles.textInput]} numberOfLines={1}>
-              {toNiceDomain(serviceUrl)}
-            </Text>
-            <View style={[pal.btn, styles.textBtnFakeInnerBtn]}>
-              <FontAwesomeIcon
-                icon="pen"
-                size={12}
-                style={pal.text as FontAwesomeIconStyle}
-              />
-            </View>
-          </TouchableOpacity>
-          <View style={[pal.borderDark, styles.groupContent]}>
-            <FontAwesomeIcon
-              icon="envelope"
-              style={[pal.textLight, styles.groupContentIcon]}
-            />
-            <TextInput
-              testID="forgotPasswordEmail"
-              style={[pal.text, styles.textInput]}
-              placeholder={_(msg`Email address`)}
-              placeholderTextColor={pal.colors.textLight}
-              autoCapitalize="none"
-              autoFocus
-              autoCorrect={false}
-              keyboardAppearance={theme.colorScheme}
-              value={email}
-              onChangeText={setEmail}
-              editable={!isProcessing}
-              accessibilityLabel={_(msg`Email`)}
-              accessibilityHint={_(msg`Sets email for password reset`)}
-            />
-          </View>
-        </View>
-        {error ? (
-          <View style={styles.error}>
-            <View style={styles.errorIcon}>
-              <FontAwesomeIcon icon="exclamation" style={s.white} size={10} />
-            </View>
-            <View style={s.flex1}>
-              <Text style={[s.white, s.bold]}>{error}</Text>
-            </View>
-          </View>
-        ) : undefined}
-        <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}>
-          <TouchableOpacity onPress={onPressBack} accessibilityRole="button">
-            <Text type="xl" style={[pal.link, s.pl5]}>
-              <Trans>Back</Trans>
-            </Text>
-          </TouchableOpacity>
-          <View style={s.flex1} />
-          {!serviceDescription || isProcessing ? (
-            <ActivityIndicator />
-          ) : !email ? (
-            <Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}>
-              <Trans>Next</Trans>
-            </Text>
-          ) : (
-            <TouchableOpacity
-              testID="newPasswordButton"
-              onPress={onPressNext}
-              accessibilityRole="button"
-              accessibilityLabel={_(msg`Go to next`)}
-              accessibilityHint={_(msg`Navigates to the next screen`)}>
-              <Text type="xl-bold" style={[pal.link, s.pr5]}>
-                <Trans>Next</Trans>
-              </Text>
-            </TouchableOpacity>
-          )}
-          {!serviceDescription || isProcessing ? (
-            <Text type="xl" style={[pal.textLight, s.pl10]}>
-              <Trans>Processing...</Trans>
-            </Text>
-          ) : undefined}
-        </View>
-        <View
-          style={[
-            s.flexRow,
-            s.alignCenter,
-            s.mt20,
-            s.mb20,
-            pal.border,
-            s.borderBottom1,
-            {alignSelf: 'center', width: '90%'},
-          ]}
-        />
-        <View style={[s.flexRow, s.justifyCenter]}>
-          <TouchableOpacity
-            testID="skipSendEmailButton"
-            onPress={onEmailSent}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Go to next`)}
-            accessibilityHint={_(msg`Navigates to the next screen`)}>
-            <Text type="xl" style={[pal.link, s.pr5]}>
-              <Trans>Already have a code?</Trans>
-            </Text>
-          </TouchableOpacity>
-        </View>
-      </View>
-    </>
-  )
-}
diff --git a/src/view/com/auth/login/Login.tsx b/src/view/com/auth/login/Login.tsx
deleted file mode 100644
index bc931ac04..000000000
--- a/src/view/com/auth/login/Login.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import React, {useState, useEffect} from 'react'
-import {KeyboardAvoidingView} from 'react-native'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {LoggedOutLayout} from 'view/com/util/layouts/LoggedOutLayout'
-import {DEFAULT_SERVICE} from '#/lib/constants'
-import {usePalette} from 'lib/hooks/usePalette'
-import {logger} from '#/logger'
-import {ChooseAccountForm} from './ChooseAccountForm'
-import {LoginForm} from './LoginForm'
-import {ForgotPasswordForm} from './ForgotPasswordForm'
-import {SetNewPasswordForm} from './SetNewPasswordForm'
-import {PasswordUpdatedForm} from './PasswordUpdatedForm'
-import {useLingui} from '@lingui/react'
-import {msg} from '@lingui/macro'
-import {useSession, SessionAccount} from '#/state/session'
-import {useServiceQuery} from '#/state/queries/service'
-import {useLoggedOutView} from '#/state/shell/logged-out'
-
-enum Forms {
-  Login,
-  ChooseAccount,
-  ForgotPassword,
-  SetNewPassword,
-  PasswordUpdated,
-}
-
-export const Login = ({onPressBack}: {onPressBack: () => void}) => {
-  const {_} = useLingui()
-  const pal = usePalette('default')
-
-  const {accounts} = useSession()
-  const {track} = useAnalytics()
-  const {requestedAccountSwitchTo} = useLoggedOutView()
-  const requestedAccount = accounts.find(
-    a => a.did === requestedAccountSwitchTo,
-  )
-
-  const [error, setError] = useState<string>('')
-  const [serviceUrl, setServiceUrl] = useState<string>(
-    requestedAccount?.service || DEFAULT_SERVICE,
-  )
-  const [initialHandle, setInitialHandle] = useState<string>(
-    requestedAccount?.handle || '',
-  )
-  const [currentForm, setCurrentForm] = useState<Forms>(
-    requestedAccount
-      ? Forms.Login
-      : accounts.length
-      ? Forms.ChooseAccount
-      : Forms.Login,
-  )
-
-  const {
-    data: serviceDescription,
-    error: serviceError,
-    refetch: refetchService,
-  } = useServiceQuery(serviceUrl)
-
-  const onSelectAccount = (account?: SessionAccount) => {
-    if (account?.service) {
-      setServiceUrl(account.service)
-    }
-    setInitialHandle(account?.handle || '')
-    setCurrentForm(Forms.Login)
-  }
-
-  const gotoForm = (form: Forms) => () => {
-    setError('')
-    setCurrentForm(form)
-  }
-
-  useEffect(() => {
-    if (serviceError) {
-      setError(
-        _(
-          msg`Unable to contact your service. Please check your Internet connection.`,
-        ),
-      )
-      logger.warn(`Failed to fetch service description for ${serviceUrl}`, {
-        error: String(serviceError),
-      })
-    } else {
-      setError('')
-    }
-  }, [serviceError, serviceUrl, _])
-
-  const onPressRetryConnect = () => refetchService()
-  const onPressForgotPassword = () => {
-    track('Signin:PressedForgotPassword')
-    setCurrentForm(Forms.ForgotPassword)
-  }
-
-  return (
-    <KeyboardAvoidingView testID="signIn" behavior="padding" style={pal.view}>
-      {currentForm === Forms.Login ? (
-        <LoggedOutLayout
-          leadin=""
-          title={_(msg`Sign in`)}
-          description={_(msg`Enter your username and password`)}>
-          <LoginForm
-            error={error}
-            serviceUrl={serviceUrl}
-            serviceDescription={serviceDescription}
-            initialHandle={initialHandle}
-            setError={setError}
-            setServiceUrl={setServiceUrl}
-            onPressBack={onPressBack}
-            onPressForgotPassword={onPressForgotPassword}
-            onPressRetryConnect={onPressRetryConnect}
-          />
-        </LoggedOutLayout>
-      ) : undefined}
-      {currentForm === Forms.ChooseAccount ? (
-        <LoggedOutLayout
-          leadin=""
-          title={_(msg`Sign in as...`)}
-          description={_(msg`Select from an existing account`)}>
-          <ChooseAccountForm
-            onSelectAccount={onSelectAccount}
-            onPressBack={onPressBack}
-          />
-        </LoggedOutLayout>
-      ) : undefined}
-      {currentForm === Forms.ForgotPassword ? (
-        <LoggedOutLayout
-          leadin=""
-          title={_(msg`Forgot Password`)}
-          description={_(msg`Let's get your password reset!`)}>
-          <ForgotPasswordForm
-            error={error}
-            serviceUrl={serviceUrl}
-            serviceDescription={serviceDescription}
-            setError={setError}
-            setServiceUrl={setServiceUrl}
-            onPressBack={gotoForm(Forms.Login)}
-            onEmailSent={gotoForm(Forms.SetNewPassword)}
-          />
-        </LoggedOutLayout>
-      ) : undefined}
-      {currentForm === Forms.SetNewPassword ? (
-        <LoggedOutLayout
-          leadin=""
-          title={_(msg`Forgot Password`)}
-          description={_(msg`Let's get your password reset!`)}>
-          <SetNewPasswordForm
-            error={error}
-            serviceUrl={serviceUrl}
-            setError={setError}
-            onPressBack={gotoForm(Forms.ForgotPassword)}
-            onPasswordSet={gotoForm(Forms.PasswordUpdated)}
-          />
-        </LoggedOutLayout>
-      ) : undefined}
-      {currentForm === Forms.PasswordUpdated ? (
-        <LoggedOutLayout
-          leadin=""
-          title={_(msg`Password updated`)}
-          description={_(msg`You can now sign in with your new password.`)}>
-          <PasswordUpdatedForm onPressNext={gotoForm(Forms.Login)} />
-        </LoggedOutLayout>
-      ) : undefined}
-    </KeyboardAvoidingView>
-  )
-}
diff --git a/src/view/com/auth/login/LoginForm.tsx b/src/view/com/auth/login/LoginForm.tsx
deleted file mode 100644
index e480de7a4..000000000
--- a/src/view/com/auth/login/LoginForm.tsx
+++ /dev/null
@@ -1,298 +0,0 @@
-import React, {useState, useRef} from 'react'
-import {
-  ActivityIndicator,
-  Keyboard,
-  TextInput,
-  TouchableOpacity,
-  View,
-} from 'react-native'
-import {
-  FontAwesomeIcon,
-  FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
-import {ComAtprotoServerDescribeServer} from '@atproto/api'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {Text} from '../../util/text/Text'
-import {s} from 'lib/styles'
-import {createFullHandle} from 'lib/strings/handles'
-import {toNiceDomain} from 'lib/strings/url-helpers'
-import {isNetworkError} from 'lib/strings/errors'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useTheme} from 'lib/ThemeContext'
-import {useSessionApi} from '#/state/session'
-import {cleanError} from 'lib/strings/errors'
-import {logger} from '#/logger'
-import {Trans, msg} from '@lingui/macro'
-import {styles} from './styles'
-import {useLingui} from '@lingui/react'
-import {useDialogControl} from '#/components/Dialog'
-
-import {ServerInputDialog} from '../server-input'
-
-type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
-
-export const LoginForm = ({
-  error,
-  serviceUrl,
-  serviceDescription,
-  initialHandle,
-  setError,
-  setServiceUrl,
-  onPressRetryConnect,
-  onPressBack,
-  onPressForgotPassword,
-}: {
-  error: string
-  serviceUrl: string
-  serviceDescription: ServiceDescription | undefined
-  initialHandle: string
-  setError: (v: string) => void
-  setServiceUrl: (v: string) => void
-  onPressRetryConnect: () => void
-  onPressBack: () => void
-  onPressForgotPassword: () => void
-}) => {
-  const {track} = useAnalytics()
-  const pal = usePalette('default')
-  const theme = useTheme()
-  const [isProcessing, setIsProcessing] = useState<boolean>(false)
-  const [identifier, setIdentifier] = useState<string>(initialHandle)
-  const [password, setPassword] = useState<string>('')
-  const passwordInputRef = useRef<TextInput>(null)
-  const {_} = useLingui()
-  const {login} = useSessionApi()
-  const serverInputControl = useDialogControl()
-
-  const onPressSelectService = () => {
-    serverInputControl.open()
-    Keyboard.dismiss()
-    track('Signin:PressedSelectService')
-  }
-
-  const onPressNext = async () => {
-    Keyboard.dismiss()
-    setError('')
-    setIsProcessing(true)
-
-    try {
-      // try to guess the handle if the user just gave their own username
-      let fullIdent = identifier
-      if (
-        !identifier.includes('@') && // not an email
-        !identifier.includes('.') && // not a domain
-        serviceDescription &&
-        serviceDescription.availableUserDomains.length > 0
-      ) {
-        let matched = false
-        for (const domain of serviceDescription.availableUserDomains) {
-          if (fullIdent.endsWith(domain)) {
-            matched = true
-          }
-        }
-        if (!matched) {
-          fullIdent = createFullHandle(
-            identifier,
-            serviceDescription.availableUserDomains[0],
-          )
-        }
-      }
-
-      // TODO remove double login
-      await login({
-        service: serviceUrl,
-        identifier: fullIdent,
-        password,
-      })
-    } catch (e: any) {
-      const errMsg = e.toString()
-      setIsProcessing(false)
-      if (errMsg.includes('Authentication Required')) {
-        logger.info('Failed to login due to invalid credentials', {
-          error: errMsg,
-        })
-        setError(_(msg`Invalid username or password`))
-      } else if (isNetworkError(e)) {
-        logger.warn('Failed to login due to network error', {error: errMsg})
-        setError(
-          _(
-            msg`Unable to contact your service. Please check your Internet connection.`,
-          ),
-        )
-      } else {
-        logger.warn('Failed to login', {error: errMsg})
-        setError(cleanError(errMsg))
-      }
-    }
-  }
-
-  const isReady = !!serviceDescription && !!identifier && !!password
-  return (
-    <View testID="loginForm">
-      <ServerInputDialog
-        control={serverInputControl}
-        onSelect={setServiceUrl}
-      />
-
-      <Text type="sm-bold" style={[pal.text, styles.groupLabel]}>
-        <Trans>Sign into</Trans>
-      </Text>
-      <View style={[pal.borderDark, styles.group]}>
-        <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
-          <FontAwesomeIcon
-            icon="globe"
-            style={[pal.textLight, styles.groupContentIcon]}
-          />
-          <TouchableOpacity
-            testID="loginSelectServiceButton"
-            style={styles.textBtn}
-            onPress={onPressSelectService}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Select service`)}
-            accessibilityHint={_(msg`Sets server for the Bluesky client`)}>
-            <Text type="xl" style={[pal.text, styles.textBtnLabel]}>
-              {toNiceDomain(serviceUrl)}
-            </Text>
-            <View style={[pal.btn, styles.textBtnFakeInnerBtn]}>
-              <FontAwesomeIcon
-                icon="pen"
-                size={12}
-                style={pal.textLight as FontAwesomeIconStyle}
-              />
-            </View>
-          </TouchableOpacity>
-        </View>
-      </View>
-      <Text type="sm-bold" style={[pal.text, styles.groupLabel]}>
-        <Trans>Account</Trans>
-      </Text>
-      <View style={[pal.borderDark, styles.group]}>
-        <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
-          <FontAwesomeIcon
-            icon="at"
-            style={[pal.textLight, styles.groupContentIcon]}
-          />
-          <TextInput
-            testID="loginUsernameInput"
-            style={[pal.text, styles.textInput]}
-            placeholder={_(msg`Username or email address`)}
-            placeholderTextColor={pal.colors.textLight}
-            autoCapitalize="none"
-            autoFocus
-            autoCorrect={false}
-            autoComplete="username"
-            returnKeyType="next"
-            textContentType="username"
-            onSubmitEditing={() => {
-              passwordInputRef.current?.focus()
-            }}
-            blurOnSubmit={false} // prevents flickering due to onSubmitEditing going to next field
-            keyboardAppearance={theme.colorScheme}
-            value={identifier}
-            onChangeText={str =>
-              setIdentifier((str || '').toLowerCase().trim())
-            }
-            editable={!isProcessing}
-            accessibilityLabel={_(msg`Username or email address`)}
-            accessibilityHint={_(
-              msg`Input the username or email address you used at signup`,
-            )}
-          />
-        </View>
-        <View style={[pal.borderDark, styles.groupContent]}>
-          <FontAwesomeIcon
-            icon="lock"
-            style={[pal.textLight, styles.groupContentIcon]}
-          />
-          <TextInput
-            testID="loginPasswordInput"
-            ref={passwordInputRef}
-            style={[pal.text, styles.textInput]}
-            placeholder="Password"
-            placeholderTextColor={pal.colors.textLight}
-            autoCapitalize="none"
-            autoCorrect={false}
-            autoComplete="password"
-            returnKeyType="done"
-            enablesReturnKeyAutomatically={true}
-            keyboardAppearance={theme.colorScheme}
-            secureTextEntry={true}
-            textContentType="password"
-            clearButtonMode="while-editing"
-            value={password}
-            onChangeText={setPassword}
-            onSubmitEditing={onPressNext}
-            blurOnSubmit={false} // HACK: https://github.com/facebook/react-native/issues/21911#issuecomment-558343069 Keyboard blur behavior is now handled in onSubmitEditing
-            editable={!isProcessing}
-            accessibilityLabel={_(msg`Password`)}
-            accessibilityHint={
-              identifier === ''
-                ? _(msg`Input your password`)
-                : _(msg`Input the password tied to ${identifier}`)
-            }
-          />
-          <TouchableOpacity
-            testID="forgotPasswordButton"
-            style={styles.textInputInnerBtn}
-            onPress={onPressForgotPassword}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Forgot password`)}
-            accessibilityHint={_(msg`Opens password reset form`)}>
-            <Text style={pal.link}>
-              <Trans>Forgot</Trans>
-            </Text>
-          </TouchableOpacity>
-        </View>
-      </View>
-      {error ? (
-        <View style={styles.error}>
-          <View style={styles.errorIcon}>
-            <FontAwesomeIcon icon="exclamation" style={s.white} size={10} />
-          </View>
-          <View style={s.flex1}>
-            <Text style={[s.white, s.bold]}>{error}</Text>
-          </View>
-        </View>
-      ) : undefined}
-      <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}>
-        <TouchableOpacity onPress={onPressBack} accessibilityRole="button">
-          <Text type="xl" style={[pal.link, s.pl5]}>
-            <Trans>Back</Trans>
-          </Text>
-        </TouchableOpacity>
-        <View style={s.flex1} />
-        {!serviceDescription && error ? (
-          <TouchableOpacity
-            testID="loginRetryButton"
-            onPress={onPressRetryConnect}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Retry`)}
-            accessibilityHint={_(msg`Retries login`)}>
-            <Text type="xl-bold" style={[pal.link, s.pr5]}>
-              <Trans>Retry</Trans>
-            </Text>
-          </TouchableOpacity>
-        ) : !serviceDescription ? (
-          <>
-            <ActivityIndicator />
-            <Text type="xl" style={[pal.textLight, s.pl10]}>
-              <Trans>Connecting...</Trans>
-            </Text>
-          </>
-        ) : isProcessing ? (
-          <ActivityIndicator />
-        ) : isReady ? (
-          <TouchableOpacity
-            testID="loginNextButton"
-            onPress={onPressNext}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Go to next`)}
-            accessibilityHint={_(msg`Navigates to the next screen`)}>
-            <Text type="xl-bold" style={[pal.link, s.pr5]}>
-              <Trans>Next</Trans>
-            </Text>
-          </TouchableOpacity>
-        ) : undefined}
-      </View>
-    </View>
-  )
-}
diff --git a/src/view/com/auth/login/PasswordUpdatedForm.tsx b/src/view/com/auth/login/PasswordUpdatedForm.tsx
deleted file mode 100644
index 71f750b14..000000000
--- a/src/view/com/auth/login/PasswordUpdatedForm.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import React, {useEffect} from 'react'
-import {TouchableOpacity, View} from 'react-native'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {Text} from '../../util/text/Text'
-import {s} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {styles} from './styles'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-export const PasswordUpdatedForm = ({
-  onPressNext,
-}: {
-  onPressNext: () => void
-}) => {
-  const {screen} = useAnalytics()
-  const pal = usePalette('default')
-  const {_} = useLingui()
-
-  useEffect(() => {
-    screen('Signin:PasswordUpdatedForm')
-  }, [screen])
-
-  return (
-    <>
-      <View>
-        <Text type="title-lg" style={[pal.text, styles.screenTitle]}>
-          <Trans>Password updated!</Trans>
-        </Text>
-        <Text type="lg" style={[pal.text, styles.instructions]}>
-          <Trans>You can now sign in with your new password.</Trans>
-        </Text>
-        <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}>
-          <View style={s.flex1} />
-          <TouchableOpacity
-            onPress={onPressNext}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Close alert`)}
-            accessibilityHint={_(msg`Closes password update alert`)}>
-            <Text type="xl-bold" style={[pal.link, s.pr5]}>
-              <Trans>Okay</Trans>
-            </Text>
-          </TouchableOpacity>
-        </View>
-      </View>
-    </>
-  )
-}
diff --git a/src/view/com/auth/login/SetNewPasswordForm.tsx b/src/view/com/auth/login/SetNewPasswordForm.tsx
deleted file mode 100644
index 6d1584c86..000000000
--- a/src/view/com/auth/login/SetNewPasswordForm.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-import React, {useState, useEffect} from 'react'
-import {
-  ActivityIndicator,
-  TextInput,
-  TouchableOpacity,
-  View,
-} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {BskyAgent} from '@atproto/api'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {Text} from '../../util/text/Text'
-import {s} from 'lib/styles'
-import {isNetworkError} from 'lib/strings/errors'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useTheme} from 'lib/ThemeContext'
-import {cleanError} from 'lib/strings/errors'
-import {checkAndFormatResetCode} from 'lib/strings/password'
-import {logger} from '#/logger'
-import {styles} from './styles'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-export const SetNewPasswordForm = ({
-  error,
-  serviceUrl,
-  setError,
-  onPressBack,
-  onPasswordSet,
-}: {
-  error: string
-  serviceUrl: string
-  setError: (v: string) => void
-  onPressBack: () => void
-  onPasswordSet: () => void
-}) => {
-  const pal = usePalette('default')
-  const theme = useTheme()
-  const {screen} = useAnalytics()
-  const {_} = useLingui()
-
-  useEffect(() => {
-    screen('Signin:SetNewPasswordForm')
-  }, [screen])
-
-  const [isProcessing, setIsProcessing] = useState<boolean>(false)
-  const [resetCode, setResetCode] = useState<string>('')
-  const [password, setPassword] = useState<string>('')
-
-  const onPressNext = async () => {
-    // Check that the code is correct. We do this again just incase the user enters the code after their pw and we
-    // don't get to call onBlur first
-    const formattedCode = checkAndFormatResetCode(resetCode)
-    // TODO Better password strength check
-    if (!formattedCode || !password) {
-      setError(
-        _(
-          msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`,
-        ),
-      )
-      return
-    }
-
-    setError('')
-    setIsProcessing(true)
-
-    try {
-      const agent = new BskyAgent({service: serviceUrl})
-      await agent.com.atproto.server.resetPassword({
-        token: formattedCode,
-        password,
-      })
-      onPasswordSet()
-    } catch (e: any) {
-      const errMsg = e.toString()
-      logger.warn('Failed to set new password', {error: e})
-      setIsProcessing(false)
-      if (isNetworkError(e)) {
-        setError(
-          'Unable to contact your service. Please check your Internet connection.',
-        )
-      } else {
-        setError(cleanError(errMsg))
-      }
-    }
-  }
-
-  const onBlur = () => {
-    const formattedCode = checkAndFormatResetCode(resetCode)
-    if (!formattedCode) {
-      setError(
-        _(
-          msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`,
-        ),
-      )
-      return
-    }
-    setResetCode(formattedCode)
-  }
-
-  return (
-    <>
-      <View>
-        <Text type="title-lg" style={[pal.text, styles.screenTitle]}>
-          <Trans>Set new password</Trans>
-        </Text>
-        <Text type="lg" style={[pal.text, styles.instructions]}>
-          <Trans>
-            You will receive an email with a "reset code." Enter that code here,
-            then enter your new password.
-          </Trans>
-        </Text>
-        <View
-          testID="newPasswordView"
-          style={[pal.view, pal.borderDark, styles.group]}>
-          <View
-            style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
-            <FontAwesomeIcon
-              icon="ticket"
-              style={[pal.textLight, styles.groupContentIcon]}
-            />
-            <TextInput
-              testID="resetCodeInput"
-              style={[pal.text, styles.textInput]}
-              placeholder={_(msg`Reset code`)}
-              placeholderTextColor={pal.colors.textLight}
-              autoCapitalize="none"
-              autoCorrect={false}
-              keyboardAppearance={theme.colorScheme}
-              autoComplete="off"
-              value={resetCode}
-              onChangeText={setResetCode}
-              onFocus={() => setError('')}
-              onBlur={onBlur}
-              editable={!isProcessing}
-              accessible={true}
-              accessibilityLabel={_(msg`Reset code`)}
-              accessibilityHint={_(
-                msg`Input code sent to your email for password reset`,
-              )}
-            />
-          </View>
-          <View style={[pal.borderDark, styles.groupContent]}>
-            <FontAwesomeIcon
-              icon="lock"
-              style={[pal.textLight, styles.groupContentIcon]}
-            />
-            <TextInput
-              testID="newPasswordInput"
-              style={[pal.text, styles.textInput]}
-              placeholder={_(msg`New password`)}
-              placeholderTextColor={pal.colors.textLight}
-              autoCapitalize="none"
-              autoCorrect={false}
-              autoComplete="new-password"
-              keyboardAppearance={theme.colorScheme}
-              secureTextEntry
-              value={password}
-              onChangeText={setPassword}
-              editable={!isProcessing}
-              accessible={true}
-              accessibilityLabel={_(msg`Password`)}
-              accessibilityHint={_(msg`Input new password`)}
-            />
-          </View>
-        </View>
-        {error ? (
-          <View style={styles.error}>
-            <View style={styles.errorIcon}>
-              <FontAwesomeIcon icon="exclamation" style={s.white} size={10} />
-            </View>
-            <View style={s.flex1}>
-              <Text style={[s.white, s.bold]}>{error}</Text>
-            </View>
-          </View>
-        ) : undefined}
-        <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}>
-          <TouchableOpacity onPress={onPressBack} accessibilityRole="button">
-            <Text type="xl" style={[pal.link, s.pl5]}>
-              <Trans>Back</Trans>
-            </Text>
-          </TouchableOpacity>
-          <View style={s.flex1} />
-          {isProcessing ? (
-            <ActivityIndicator />
-          ) : !resetCode || !password ? (
-            <Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}>
-              <Trans>Next</Trans>
-            </Text>
-          ) : (
-            <TouchableOpacity
-              testID="setNewPasswordButton"
-              // Check the code before running the callback
-              onPress={onPressNext}
-              accessibilityRole="button"
-              accessibilityLabel={_(msg`Go to next`)}
-              accessibilityHint={_(msg`Navigates to the next screen`)}>
-              <Text type="xl-bold" style={[pal.link, s.pr5]}>
-                <Trans>Next</Trans>
-              </Text>
-            </TouchableOpacity>
-          )}
-          {isProcessing ? (
-            <Text type="xl" style={[pal.textLight, s.pl10]}>
-              <Trans>Updating...</Trans>
-            </Text>
-          ) : undefined}
-        </View>
-      </View>
-    </>
-  )
-}
diff --git a/src/view/com/auth/login/styles.ts b/src/view/com/auth/login/styles.ts
deleted file mode 100644
index 9dccc2803..000000000
--- a/src/view/com/auth/login/styles.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import {StyleSheet} from 'react-native'
-import {colors} from 'lib/styles'
-import {isWeb} from '#/platform/detection'
-
-export const styles = StyleSheet.create({
-  screenTitle: {
-    marginBottom: 10,
-    marginHorizontal: 20,
-  },
-  instructions: {
-    marginBottom: 20,
-    marginHorizontal: 20,
-  },
-  group: {
-    borderWidth: 1,
-    borderRadius: 10,
-    marginBottom: 20,
-    marginHorizontal: 20,
-  },
-  groupLabel: {
-    paddingHorizontal: 20,
-    paddingBottom: 5,
-  },
-  groupContent: {
-    borderTopWidth: 1,
-    flexDirection: 'row',
-    alignItems: 'center',
-  },
-  noTopBorder: {
-    borderTopWidth: 0,
-  },
-  groupContentIcon: {
-    marginLeft: 10,
-  },
-  account: {
-    borderTopWidth: 1,
-    paddingHorizontal: 20,
-    paddingVertical: 4,
-  },
-  accountLast: {
-    borderBottomWidth: 1,
-    marginBottom: 20,
-    paddingVertical: 8,
-  },
-  textInput: {
-    flex: 1,
-    width: '100%',
-    paddingVertical: 10,
-    paddingHorizontal: 12,
-    fontSize: 17,
-    letterSpacing: 0.25,
-    fontWeight: '400',
-    borderRadius: 10,
-  },
-  textInputInnerBtn: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    paddingVertical: 6,
-    paddingHorizontal: 8,
-    marginHorizontal: 6,
-  },
-  textBtn: {
-    flexDirection: 'row',
-    flex: 1,
-    alignItems: 'center',
-  },
-  textBtnLabel: {
-    flex: 1,
-    paddingVertical: 10,
-    paddingHorizontal: 12,
-  },
-  textBtnFakeInnerBtn: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    borderRadius: 6,
-    paddingVertical: 6,
-    paddingHorizontal: 8,
-    marginHorizontal: 6,
-  },
-  accountText: {
-    flex: 1,
-    flexDirection: 'row',
-    alignItems: 'baseline',
-    paddingVertical: 10,
-  },
-  accountTextOther: {
-    paddingLeft: 12,
-  },
-  error: {
-    backgroundColor: colors.red4,
-    flexDirection: 'row',
-    alignItems: 'center',
-    marginTop: -5,
-    marginHorizontal: 20,
-    marginBottom: 15,
-    borderRadius: 8,
-    paddingHorizontal: 8,
-    paddingVertical: 8,
-  },
-  errorIcon: {
-    borderWidth: 1,
-    borderColor: colors.white,
-    color: colors.white,
-    borderRadius: 30,
-    width: 16,
-    height: 16,
-    alignItems: 'center',
-    justifyContent: 'center',
-    marginRight: 5,
-  },
-  dimmed: {opacity: 0.5},
-
-  maxHeight: {
-    // @ts-ignore web only -prf
-    maxHeight: isWeb ? '100vh' : undefined,
-    height: !isWeb ? '100%' : undefined,
-  },
-})
diff --git a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx
index 07001068c..dba3f8c56 100644
--- a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx
@@ -1,6 +1,6 @@
 import React from 'react'
 import {View, StyleSheet, ActivityIndicator} from 'react-native'
-import {ProfileModeration, AppBskyActorDefs} from '@atproto/api'
+import {ModerationDecision, AppBskyActorDefs} from '@atproto/api'
 import {Button} from '#/view/com/util/forms/Button'
 import {usePalette} from 'lib/hooks/usePalette'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
@@ -11,14 +11,15 @@ import {Text} from 'view/com/util/text/Text'
 import Animated, {FadeInRight} from 'react-native-reanimated'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {useAnalytics} from 'lib/analytics/analytics'
-import {Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {Trans, msg} from '@lingui/macro'
 import {Shadow, useProfileShadow} from '#/state/cache/profile-shadow'
 import {useProfileFollowMutationQueue} from '#/state/queries/profile'
 import {logger} from '#/logger'
 
 type Props = {
   profile: AppBskyActorDefs.ProfileViewBasic
-  moderation: ProfileModeration
+  moderation: ModerationDecision
   onFollowStateChange: (props: {
     did: string
     following: boolean
@@ -56,13 +57,13 @@ export function RecommendedFollowsItem({
   )
 }
 
-export function ProfileCard({
+function ProfileCard({
   profile,
   onFollowStateChange,
   moderation,
 }: {
   profile: Shadow<AppBskyActorDefs.ProfileViewBasic>
-  moderation: ProfileModeration
+  moderation: ModerationDecision
   onFollowStateChange: (props: {
     did: string
     following: boolean
@@ -70,9 +71,13 @@ export function ProfileCard({
 }) {
   const {track} = useAnalytics()
   const pal = usePalette('default')
+  const {_} = useLingui()
   const [addingMoreSuggestions, setAddingMoreSuggestions] =
     React.useState(false)
-  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
+  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
+    profile,
+    'RecommendedFollowsItem',
+  )
 
   const onToggleFollow = React.useCallback(async () => {
     try {
@@ -110,7 +115,7 @@ export function ProfileCard({
           <UserAvatar
             size={40}
             avatar={profile.avatar}
-            moderation={moderation.avatar}
+            moderation={moderation.ui('avatar')}
           />
         </View>
         <View style={styles.layoutContent}>
@@ -121,7 +126,7 @@ export function ProfileCard({
             lineHeight={1.2}>
             {sanitizeDisplayName(
               profile.displayName || sanitizeHandle(profile.handle),
-              moderation.profile,
+              moderation.ui('displayName'),
             )}
           </Text>
           <Text type="xl" style={[pal.textLight]} numberOfLines={1}>
@@ -133,7 +138,7 @@ export function ProfileCard({
           type={profile.viewer?.following ? 'default' : 'inverted'}
           labelStyle={styles.followButton}
           onPress={onToggleFollow}
-          label={profile.viewer?.following ? 'Unfollow' : 'Follow'}
+          label={profile.viewer?.following ? _(msg`Unfollow`) : _(msg`Follow`)}
         />
       </View>
       {profile.description ? (
diff --git a/src/view/com/auth/onboarding/WelcomeMobile.tsx b/src/view/com/auth/onboarding/WelcomeMobile.tsx
index 5de1a7817..b8659d56c 100644
--- a/src/view/com/auth/onboarding/WelcomeMobile.tsx
+++ b/src/view/com/auth/onboarding/WelcomeMobile.tsx
@@ -6,7 +6,8 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Button} from 'view/com/util/forms/Button'
 import {ViewHeader} from 'view/com/util/ViewHeader'
-import {Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {Trans, msg} from '@lingui/macro'
 
 type Props = {
   next: () => void
@@ -15,6 +16,7 @@ type Props = {
 
 export function WelcomeMobile({next, skip}: Props) {
   const pal = usePalette('default')
+  const {_} = useLingui()
 
   return (
     <View style={[styles.container]} testID="welcomeOnboarding">
@@ -91,7 +93,7 @@ export function WelcomeMobile({next, skip}: Props) {
 
       <Button
         onPress={next}
-        label="Continue"
+        label={_(msg`Continue`)}
         testID="continueBtn"
         style={[styles.buttonContainer]}
         labelStyle={styles.buttonText}
diff --git a/src/view/com/auth/server-input/index.tsx b/src/view/com/auth/server-input/index.tsx
index a70621973..b26ac1dcb 100644
--- a/src/view/com/auth/server-input/index.tsx
+++ b/src/view/com/auth/server-input/index.tsx
@@ -2,7 +2,7 @@ import React from 'react'
 import {View} from 'react-native'
 import {useLingui} from '@lingui/react'
 import {Trans, msg} from '@lingui/macro'
-import {PROD_SERVICE} from 'lib/constants'
+import {BSKY_SERVICE} from 'lib/constants'
 import * as persisted from '#/state/persisted'
 
 import {atoms as a, useBreakpoints, useTheme} from '#/alf'
@@ -26,7 +26,7 @@ export function ServerInputDialog({
   const [pdsAddressHistory, setPdsAddressHistory] = React.useState<string[]>(
     persisted.get('pdsAddressHistory') || [],
   )
-  const [fixedOption, setFixedOption] = React.useState([PROD_SERVICE])
+  const [fixedOption, setFixedOption] = React.useState([BSKY_SERVICE])
   const [customAddress, setCustomAddress] = React.useState('')
 
   const onClose = React.useCallback(() => {
@@ -86,7 +86,7 @@ export function ServerInputDialog({
             label="Preferences"
             values={fixedOption}
             onChange={setFixedOption}>
-            <ToggleButton.Button name={PROD_SERVICE} label={_(msg`Bluesky`)}>
+            <ToggleButton.Button name={BSKY_SERVICE} label={_(msg`Bluesky`)}>
               {_(msg`Bluesky`)}
             </ToggleButton.Button>
             <ToggleButton.Button
@@ -115,7 +115,7 @@ export function ServerInputDialog({
                   testID="customServerTextInput"
                   value={customAddress}
                   onChangeText={setCustomAddress}
-                  label={_(msg`my-server.com`)}
+                  label="my-server.com"
                   accessibilityLabelledBy="address-input-label"
                   autoCapitalize="none"
                   keyboardType="url"