forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React, {useState} from 'react'
2import {Keyboard, View} from 'react-native'
3import {type ComAtprotoServerDescribeServer} from '@atproto/api'
4import {msg} from '@lingui/core/macro'
5import {useLingui} from '@lingui/react'
6import {Trans} from '@lingui/react/macro'
7import * as EmailValidator from 'email-validator'
8
9import {cleanError, isNetworkError} from '#/lib/strings/errors'
10import {logger} from '#/logger'
11import {Agent} from '#/state/session/agent'
12import {atoms as a, useTheme, web} from '#/alf'
13import {Button, ButtonIcon, ButtonText} from '#/components/Button'
14import {FormError} from '#/components/forms/FormError'
15import {HostingProvider} from '#/components/forms/HostingProvider'
16import * as TextField from '#/components/forms/TextField'
17import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At'
18import {Loader} from '#/components/Loader'
19import {Text} from '#/components/Typography'
20import {IS_WEB} from '#/env'
21import {FormContainer} from './FormContainer'
22
23type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
24
25export const ForgotPasswordForm = ({
26 error,
27 serviceUrl,
28 serviceDescription,
29 setError,
30 setServiceUrl,
31 onPressBack,
32 onEmailSent,
33}: {
34 error: string
35 serviceUrl: string
36 serviceDescription: ServiceDescription | undefined
37 setError: (v: string) => void
38 setServiceUrl: (v: string) => void
39 onPressBack: () => void
40 onEmailSent: () => void
41}) => {
42 const t = useTheme()
43 const [isProcessing, setIsProcessing] = useState<boolean>(false)
44 const [email, setEmail] = useState<string>('')
45 const {_} = useLingui()
46
47 const onPressSelectService = React.useCallback(() => {
48 Keyboard.dismiss()
49 }, [])
50
51 const onPressNext = async () => {
52 if (!EmailValidator.validate(email)) {
53 return setError(_(msg`Your email appears to be invalid.`))
54 }
55
56 setError('')
57 setIsProcessing(true)
58
59 try {
60 const agent = new Agent(null, {service: serviceUrl})
61 await agent.com.atproto.server.requestPasswordReset({email})
62 onEmailSent()
63 } catch (e: any) {
64 const errMsg = e.toString()
65 logger.warn('Failed to request password reset', {error: e})
66 setIsProcessing(false)
67 if (isNetworkError(e)) {
68 setError(
69 _(
70 msg`Unable to contact your service. Please check your Internet connection.`,
71 ),
72 )
73 } else {
74 setError(cleanError(errMsg))
75 }
76 }
77 }
78
79 return (
80 <FormContainer
81 testID="forgotPasswordForm"
82 titleText={<Trans>Reset password</Trans>}>
83 <View>
84 <TextField.LabelText>
85 <Trans>Hosting provider</Trans>
86 </TextField.LabelText>
87 <HostingProvider
88 serviceUrl={serviceUrl}
89 onSelectServiceUrl={setServiceUrl}
90 onOpenDialog={onPressSelectService}
91 />
92 </View>
93 <View>
94 <TextField.LabelText>
95 <Trans>Email address</Trans>
96 </TextField.LabelText>
97 <TextField.Root>
98 <TextField.Icon icon={At} />
99 <TextField.Input
100 testID="forgotPasswordEmail"
101 label={_(msg`Enter your email address`)}
102 autoCapitalize="none"
103 autoFocus
104 autoCorrect={false}
105 autoComplete="email"
106 value={email}
107 onChangeText={setEmail}
108 editable={!isProcessing}
109 accessibilityHint={_(msg`Sets email for password reset`)}
110 />
111 </TextField.Root>
112 </View>
113
114 <Text style={[t.atoms.text_contrast_high, a.leading_snug]}>
115 <Trans>
116 Enter the email you used to create your account. We'll send you a
117 "reset code" so you can set a new password.
118 </Trans>
119 </Text>
120
121 <FormError error={error} />
122
123 <View style={[web([a.flex_row, a.align_center]), a.pt_md]}>
124 {IS_WEB && (
125 <>
126 <Button
127 label={_(msg`Back`)}
128 color="secondary"
129 size="large"
130 onPress={onPressBack}>
131 <ButtonText>
132 <Trans>Back</Trans>
133 </ButtonText>
134 </Button>
135 <View style={a.flex_1} />
136 </>
137 )}
138 {!serviceDescription ? (
139 <Button
140 label={_(msg`Connecting to service...`)}
141 size="large"
142 color="secondary"
143 disabled>
144 <ButtonIcon icon={Loader} />
145 <ButtonText>Connecting...</ButtonText>
146 </Button>
147 ) : (
148 <Button
149 label={_(msg`Next`)}
150 accessibilityHint={_(msg`Navigates to the next screen`)}
151 color="primary"
152 size="large"
153 onPress={onPressNext}
154 disabled={isProcessing}>
155 <ButtonText>
156 <Trans>Next</Trans>
157 </ButtonText>
158 {isProcessing && <ButtonIcon icon={Loader} />}
159 </Button>
160 )}
161 </View>
162 <View
163 style={[
164 t.atoms.border_contrast_medium,
165 a.border_t,
166 a.pt_xl,
167 a.mt_md,
168 a.flex_row,
169 a.justify_center,
170 ]}>
171 <Button
172 testID="skipSendEmailButton"
173 onPress={onEmailSent}
174 label={_(msg`Go to next`)}
175 accessibilityHint={_(msg`Navigates to the next screen`)}
176 size="large"
177 variant="ghost"
178 color="secondary">
179 <ButtonText>
180 <Trans>Already have a code?</Trans>
181 </ButtonText>
182 </Button>
183 </View>
184 </FormContainer>
185 )
186}