forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {useCallback, useState} from 'react'
2import {View} from 'react-native'
3import {msg} from '@lingui/core/macro'
4import {useLingui} from '@lingui/react'
5import {Trans} from '@lingui/react/macro'
6
7import {logger} from '#/logger'
8import {type SessionAccount, useSession, useSessionApi} from '#/state/session'
9import {useLoggedOutViewControls} from '#/state/shell/logged-out'
10import {atoms as a, web} from '#/alf'
11import {AccountList} from '#/components/AccountList'
12import {Button, ButtonText} from '#/components/Button'
13import * as TextField from '#/components/forms/TextField'
14import * as Toast from '#/components/Toast'
15import {useAnalytics} from '#/analytics'
16import {IS_WEB} from '#/env'
17import {FormContainer} from './FormContainer'
18
19export const ChooseAccountForm = ({
20 onSelectAccount,
21 onPressBack,
22}: {
23 onSelectAccount: (account?: SessionAccount) => void
24 onPressBack: () => void
25}) => {
26 const [pendingDid, setPendingDid] = useState<string | null>(null)
27 const {_} = useLingui()
28 const ax = useAnalytics()
29 const {currentAccount} = useSession()
30 const {resumeSession} = useSessionApi()
31 const {setShowLoggedOut} = useLoggedOutViewControls()
32
33 const onSelect = useCallback(
34 async (account: SessionAccount) => {
35 if (pendingDid) {
36 // The session API isn't resilient to race conditions so let's just ignore this.
37 return
38 }
39 if (!account.isOauthSession && !account.accessJwt) {
40 // Move to login form.
41 onSelectAccount(account)
42 return
43 }
44 if (account.did === currentAccount?.did) {
45 setShowLoggedOut(false)
46 Toast.show(_(msg`Already signed in as @${account.handle}`))
47 return
48 }
49 try {
50 setPendingDid(account.did)
51 await Promise.race([
52 resumeSession(account, true),
53 new Promise<never>((_, reject) =>
54 setTimeout(
55 () => reject(new Error('Session resume timed out')),
56 15_000,
57 ),
58 ),
59 ])
60 ax.metric('account:loggedIn', {
61 logContext: 'ChooseAccountForm',
62 withPassword: false,
63 })
64 Toast.show(_(msg`Signed in as @${account.handle}`))
65 } catch (e: any) {
66 logger.error('choose account: initSession failed', {
67 message: e instanceof Error ? e.message : 'Unknown error',
68 })
69 Toast.show(_(msg`Sign in failed. Please try again.`))
70 // Move to login form.
71 onSelectAccount(account)
72 } finally {
73 setPendingDid(null)
74 }
75 },
76 [
77 currentAccount,
78 resumeSession,
79 pendingDid,
80 onSelectAccount,
81 setShowLoggedOut,
82 _,
83 ax,
84 ],
85 )
86
87 return (
88 <FormContainer
89 testID="chooseAccountForm"
90 titleText={<Trans>Select account</Trans>}
91 style={web([a.py_2xl])}>
92 <View>
93 {IS_WEB && (
94 <TextField.LabelText>
95 <Trans>Sign in as...</Trans>
96 </TextField.LabelText>
97 )}
98 <AccountList
99 onSelectAccount={onSelect}
100 onSelectOther={() => onSelectAccount()}
101 pendingDid={pendingDid}
102 />
103 </View>
104 {IS_WEB && (
105 <View style={[a.flex_row]}>
106 <Button
107 label={_(msg`Back`)}
108 color="secondary"
109 size="large"
110 onPress={onPressBack}>
111 <ButtonText>{_(msg`Back`)}</ButtonText>
112 </Button>
113 <View style={[a.flex_1]} />
114 </View>
115 )}
116 </FormContainer>
117 )
118}