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