mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {useRef} from 'react'
2import {KeyboardAvoidingView} from 'react-native'
3import {LayoutAnimationConfig} from 'react-native-reanimated'
4import {msg} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
6
7import {DEFAULT_SERVICE} from '#/lib/constants'
8import {logEvent} from '#/lib/statsig/statsig'
9import {logger} from '#/logger'
10import {useServiceQuery} from '#/state/queries/service'
11import {type SessionAccount, useSession} from '#/state/session'
12import {useLoggedOutView} from '#/state/shell/logged-out'
13import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout'
14import {ForgotPasswordForm} from '#/screens/Login/ForgotPasswordForm'
15import {LoginForm} from '#/screens/Login/LoginForm'
16import {PasswordUpdatedForm} from '#/screens/Login/PasswordUpdatedForm'
17import {SetNewPasswordForm} from '#/screens/Login/SetNewPasswordForm'
18import {atoms as a} from '#/alf'
19import {ChooseAccountForm} from './ChooseAccountForm'
20import {ScreenTransition} from './ScreenTransition'
21
22enum Forms {
23 Login,
24 ChooseAccount,
25 ForgotPassword,
26 SetNewPassword,
27 PasswordUpdated,
28}
29
30export const Login = ({onPressBack}: {onPressBack: () => void}) => {
31 const {_} = useLingui()
32 const failedAttemptCountRef = useRef(0)
33 const startTimeRef = useRef(Date.now())
34
35 const {accounts} = useSession()
36 const {requestedAccountSwitchTo} = useLoggedOutView()
37 const requestedAccount = accounts.find(
38 acc => acc.did === requestedAccountSwitchTo,
39 )
40
41 const [error, setError] = React.useState<string>('')
42 const [serviceUrl, setServiceUrl] = React.useState<string>(
43 requestedAccount?.service || DEFAULT_SERVICE,
44 )
45 const [initialHandle, setInitialHandle] = React.useState<string>(
46 requestedAccount?.handle || '',
47 )
48 const [currentForm, setCurrentForm] = React.useState<Forms>(
49 requestedAccount
50 ? Forms.Login
51 : accounts.length
52 ? Forms.ChooseAccount
53 : Forms.Login,
54 )
55
56 const {
57 data: serviceDescription,
58 error: serviceError,
59 refetch: refetchService,
60 } = useServiceQuery(serviceUrl)
61
62 const onSelectAccount = (account?: SessionAccount) => {
63 if (account?.service) {
64 setServiceUrl(account.service)
65 }
66 setInitialHandle(account?.handle || '')
67 setCurrentForm(Forms.Login)
68 }
69
70 const gotoForm = (form: Forms) => {
71 setError('')
72 setCurrentForm(form)
73 }
74
75 React.useEffect(() => {
76 if (serviceError) {
77 setError(
78 _(
79 msg`Unable to contact your service. Please check your Internet connection.`,
80 ),
81 )
82 logger.warn(`Failed to fetch service description for ${serviceUrl}`, {
83 error: String(serviceError),
84 })
85 logEvent('signin:hostingProviderFailedResolution', {})
86 } else {
87 setError('')
88 }
89 }, [serviceError, serviceUrl, _])
90
91 const onPressForgotPassword = () => {
92 setCurrentForm(Forms.ForgotPassword)
93 logEvent('signin:forgotPasswordPressed', {})
94 }
95
96 const handlePressBack = () => {
97 onPressBack()
98 logEvent('signin:backPressed', {
99 failedAttemptsCount: failedAttemptCountRef.current,
100 })
101 }
102
103 const onAttemptSuccess = () => {
104 logEvent('signin:success', {
105 isUsingCustomProvider: serviceUrl !== DEFAULT_SERVICE,
106 timeTakenSeconds: Math.round((Date.now() - startTimeRef.current) / 1000),
107 failedAttemptsCount: failedAttemptCountRef.current,
108 })
109 setCurrentForm(Forms.Login)
110 }
111
112 const onAttemptFailed = () => {
113 failedAttemptCountRef.current += 1
114 }
115
116 let content = null
117 let title = ''
118 let description = ''
119
120 switch (currentForm) {
121 case Forms.Login:
122 title = _(msg`Sign in`)
123 description = _(msg`Enter your username and password`)
124 content = (
125 <LoginForm
126 error={error}
127 serviceUrl={serviceUrl}
128 serviceDescription={serviceDescription}
129 initialHandle={initialHandle}
130 setError={setError}
131 onAttemptFailed={onAttemptFailed}
132 onAttemptSuccess={onAttemptSuccess}
133 setServiceUrl={setServiceUrl}
134 onPressBack={() =>
135 accounts.length ? gotoForm(Forms.ChooseAccount) : handlePressBack()
136 }
137 onPressForgotPassword={onPressForgotPassword}
138 onPressRetryConnect={refetchService}
139 />
140 )
141 break
142 case Forms.ChooseAccount:
143 title = _(msg`Sign in`)
144 description = _(msg`Select from an existing account`)
145 content = (
146 <ChooseAccountForm
147 onSelectAccount={onSelectAccount}
148 onPressBack={handlePressBack}
149 />
150 )
151 break
152 case Forms.ForgotPassword:
153 title = _(msg`Forgot Password`)
154 description = _(msg`Let's get your password reset!`)
155 content = (
156 <ForgotPasswordForm
157 error={error}
158 serviceUrl={serviceUrl}
159 serviceDescription={serviceDescription}
160 setError={setError}
161 setServiceUrl={setServiceUrl}
162 onPressBack={() => gotoForm(Forms.Login)}
163 onEmailSent={() => gotoForm(Forms.SetNewPassword)}
164 />
165 )
166 break
167 case Forms.SetNewPassword:
168 title = _(msg`Forgot Password`)
169 description = _(msg`Let's get your password reset!`)
170 content = (
171 <SetNewPasswordForm
172 error={error}
173 serviceUrl={serviceUrl}
174 setError={setError}
175 onPressBack={() => gotoForm(Forms.ForgotPassword)}
176 onPasswordSet={() => gotoForm(Forms.PasswordUpdated)}
177 />
178 )
179 break
180 case Forms.PasswordUpdated:
181 title = _(msg`Password updated`)
182 description = _(msg`You can now sign in with your new password.`)
183 content = (
184 <PasswordUpdatedForm onPressNext={() => gotoForm(Forms.Login)} />
185 )
186 break
187 }
188
189 return (
190 <KeyboardAvoidingView testID="signIn" behavior="padding" style={a.flex_1}>
191 <LoggedOutLayout
192 leadin=""
193 title={title}
194 description={description}
195 scrollable>
196 <LayoutAnimationConfig skipEntering skipExiting>
197 <ScreenTransition key={currentForm}>{content}</ScreenTransition>
198 </LayoutAnimationConfig>
199 </LoggedOutLayout>
200 </KeyboardAvoidingView>
201 )
202}