diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts
index 5088e3aaca..fef68ecaba 100644
--- a/src/alf/atoms.ts
+++ b/src/alf/atoms.ts
@@ -154,6 +154,12 @@ export const atoms = {
align_end: {
alignItems: 'flex-end',
},
+ align_baseline: {
+ alignItems: 'baseline',
+ },
+ align_stretch: {
+ alignItems: 'stretch',
+ },
self_auto: {
alignSelf: 'auto',
},
diff --git a/src/screens/Login/ChooseAccountForm.tsx b/src/screens/Login/ChooseAccountForm.tsx
new file mode 100644
index 0000000000..99d1beb891
--- /dev/null
+++ b/src/screens/Login/ChooseAccountForm.tsx
@@ -0,0 +1,183 @@
+import React from 'react'
+import {ScrollView, TouchableOpacity, View} from 'react-native'
+import {Trans, msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import flattenReactChildren from 'react-keyed-flatten-children'
+
+import {useAnalytics} from 'lib/analytics/analytics'
+import {UserAvatar} from '../../view/com/util/UserAvatar'
+import {colors} from 'lib/styles'
+import {styles} from '../../view/com/auth/login/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'
+import {Button} from '#/components/Button'
+import {atoms as a, useTheme} from '#/alf'
+import {Text} from '#/components/Typography'
+import {ChevronRight_Stroke2_Corner0_Rounded as Chevron} from '#/components/icons/Chevron'
+import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
+
+function Group({children}: {children: React.ReactNode}) {
+ const t = useTheme()
+ return (
+
+ {flattenReactChildren(children).map((child, i) => {
+ return React.isValidElement(child) ? (
+
+ {i > 0 ? (
+
+ ) : null}
+ {React.cloneElement(child, {
+ // @ts-ignore
+ style: {
+ borderRadius: 0,
+ borderWidth: 0,
+ },
+ })}
+
+ ) : null
+ })}
+
+ )
+}
+
+function AccountItem({
+ account,
+ onSelect,
+ isCurrentAccount,
+}: {
+ account: SessionAccount
+ onSelect: (account: SessionAccount) => void
+ isCurrentAccount: boolean
+}) {
+ const t = useTheme()
+ const {_} = useLingui()
+ const {data: profile} = useProfileQuery({did: account.did})
+
+ const onPress = React.useCallback(() => {
+ onSelect(account)
+ }, [account, onSelect])
+
+ return (
+
+
+
+
+
+
+
+ {profile?.displayName || account.handle}{' '}
+
+ {account.handle}
+
+ {isCurrentAccount ? (
+
+ ) : (
+
+ )}
+
+
+ )
+}
+export const ChooseAccountForm = ({
+ onSelectAccount,
+ onPressBack,
+}: {
+ onSelectAccount: (account?: SessionAccount) => void
+ onPressBack: () => void
+}) => {
+ const {track, screen} = useAnalytics()
+ const {_} = useLingui()
+ const t = useTheme()
+ 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 (
+
+
+ Sign in as...
+
+
+ {accounts.map(account => (
+
+ ))}
+ onSelectAccount(undefined)}
+ accessibilityRole="button"
+ accessibilityLabel={_(msg`Login to account that is not listed`)}
+ accessibilityHint="">
+
+
+ Other account
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/screens/Login/index.tsx b/src/screens/Login/index.tsx
new file mode 100644
index 0000000000..f2cfde550b
--- /dev/null
+++ b/src/screens/Login/index.tsx
@@ -0,0 +1,166 @@
+import React from 'react'
+import {useAnalytics} from '#/lib/analytics/analytics'
+import {useLingui} from '@lingui/react'
+import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout'
+import {SessionAccount, useSession} from '#/state/session'
+import {DEFAULT_SERVICE} from '#/lib/constants'
+import {useLoggedOutView} from '#/state/shell/logged-out'
+import {useServiceQuery} from '#/state/queries/service'
+import {msg} from '@lingui/macro'
+import {logger} from '#/logger'
+import {atoms as a} from '#/alf'
+import {KeyboardAvoidingView} from 'react-native'
+import {ChooseAccountForm} from './ChooseAccountForm'
+import {ForgotPasswordForm} from '#/view/com/auth/login/ForgotPasswordForm'
+import {SetNewPasswordForm} from '#/view/com/auth/login/SetNewPasswordForm'
+import {PasswordUpdatedForm} from '#/view/com/auth/login/PasswordUpdatedForm'
+import {LoginForm} from '#/view/com/auth/login/LoginForm'
+
+enum Forms {
+ Login,
+ ChooseAccount,
+ ForgotPassword,
+ SetNewPassword,
+ PasswordUpdated,
+}
+
+export const Login = ({onPressBack}: {onPressBack: () => void}) => {
+ const {_} = useLingui()
+
+ const {accounts} = useSession()
+ const {track} = useAnalytics()
+ const {requestedAccountSwitchTo} = useLoggedOutView()
+ const requestedAccount = accounts.find(
+ acc => acc.did === requestedAccountSwitchTo,
+ )
+
+ const [error, setError] = React.useState('')
+ const [serviceUrl, setServiceUrl] = React.useState(
+ requestedAccount?.service || DEFAULT_SERVICE,
+ )
+ const [initialHandle, setInitialHandle] = React.useState(
+ requestedAccount?.handle || '',
+ )
+ const [currentForm, setCurrentForm] = React.useState(
+ 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)
+ }
+
+ React.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)
+ }
+
+ let content = null
+ let title = ''
+ let description = ''
+
+ switch (currentForm) {
+ case Forms.Login:
+ title = _(msg`Sign in`)
+ description = _(msg`Enter your username and password`)
+ content = (
+
+ )
+ break
+ case Forms.ChooseAccount:
+ title = _(msg`Sign in`)
+ description = _(msg`Select from an existing account`)
+ content = (
+
+ )
+ break
+ case Forms.ForgotPassword:
+ title = _(msg`Forgot Password`)
+ description = _(msg`Let's get your password reset!`)
+ content = (
+
+ )
+ break
+ case Forms.SetNewPassword:
+ title = _(msg`Forgot Password`)
+ description = _(msg`Let's get your password reset!`)
+ content = (
+
+ )
+ break
+ case Forms.PasswordUpdated:
+ title = _(msg`Password updated`)
+ description = _(msg`You can now sign in with your new password.`)
+ content =
+ break
+ }
+
+ return (
+
+
+ {content}
+
+
+ )
+}
diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx
index 603abbab2d..58604ec9ee 100644
--- a/src/view/com/auth/LoggedOut.tsx
+++ b/src/view/com/auth/LoggedOut.tsx
@@ -5,16 +5,16 @@ 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 {isIOS, isNative} from '#/platform/detection'
+import {Login} from '#/screens/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 {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {
useLoggedOutView,
useLoggedOutViewControls,
diff --git a/src/view/com/auth/login/ChooseAccountForm.tsx b/src/view/com/auth/login/ChooseAccountForm.tsx
deleted file mode 100644
index 32cd8315d3..0000000000
--- 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 (
-
-
-
-
-
-
-
- {profile?.displayName || account.handle}{' '}
-
-
- {account.handle}
-
-
- {isCurrentAccount ? (
-
- ) : (
-
- )}
-
-
- )
-}
-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 (
-
-
- Sign in as...
-
- {accounts.map(account => (
-
- ))}
- onSelectAccount(undefined)}
- accessibilityRole="button"
- accessibilityLabel={_(msg`Login to account that is not listed`)}
- accessibilityHint="">
-
-
-
- Other account
-
-
-
-
-
-
-
-
- Back
-
-
-
-
-
- )
-}
diff --git a/src/view/com/auth/login/Login.tsx b/src/view/com/auth/login/Login.tsx
deleted file mode 100644
index bc931ac04c..0000000000
--- 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('')
- const [serviceUrl, setServiceUrl] = useState(
- requestedAccount?.service || DEFAULT_SERVICE,
- )
- const [initialHandle, setInitialHandle] = useState(
- requestedAccount?.handle || '',
- )
- const [currentForm, setCurrentForm] = useState(
- 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 (
-
- {currentForm === Forms.Login ? (
-
-
-
- ) : undefined}
- {currentForm === Forms.ChooseAccount ? (
-
-
-
- ) : undefined}
- {currentForm === Forms.ForgotPassword ? (
-
-
-
- ) : undefined}
- {currentForm === Forms.SetNewPassword ? (
-
-
-
- ) : undefined}
- {currentForm === Forms.PasswordUpdated ? (
-
-
-
- ) : undefined}
-
- )
-}