mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {useState} from 'react'
2import {ActivityIndicator, View} from 'react-native'
3import {msg, Trans} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5
6import {logEvent} from '#/lib/statsig/statsig'
7import {isNetworkError} from '#/lib/strings/errors'
8import {cleanError} from '#/lib/strings/errors'
9import {checkAndFormatResetCode} from '#/lib/strings/password'
10import {logger} from '#/logger'
11import {Agent} from '#/state/session/agent'
12import {atoms as a, useTheme} from '#/alf'
13import {Button, ButtonText} from '#/components/Button'
14import {FormError} from '#/components/forms/FormError'
15import * as TextField from '#/components/forms/TextField'
16import {Lock_Stroke2_Corner0_Rounded as Lock} from '#/components/icons/Lock'
17import {Ticket_Stroke2_Corner0_Rounded as Ticket} from '#/components/icons/Ticket'
18import {Text} from '#/components/Typography'
19import {FormContainer} from './FormContainer'
20
21export const SetNewPasswordForm = ({
22 error,
23 serviceUrl,
24 setError,
25 onPressBack,
26 onPasswordSet,
27}: {
28 error: string
29 serviceUrl: string
30 setError: (v: string) => void
31 onPressBack: () => void
32 onPasswordSet: () => void
33}) => {
34 const {_} = useLingui()
35 const t = useTheme()
36
37 const [isProcessing, setIsProcessing] = useState<boolean>(false)
38 const [resetCode, setResetCode] = useState<string>('')
39 const [password, setPassword] = useState<string>('')
40
41 const onPressNext = async () => {
42 // Check that the code is correct. We do this again just incase the user enters the code after their pw and we
43 // don't get to call onBlur first
44 const formattedCode = checkAndFormatResetCode(resetCode)
45
46 if (!formattedCode) {
47 setError(
48 _(
49 msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`,
50 ),
51 )
52 logEvent('signin:passwordResetFailure', {})
53 return
54 }
55
56 // TODO Better password strength check
57 if (!password) {
58 setError(_(msg`Please enter a password.`))
59 return
60 }
61
62 setError('')
63 setIsProcessing(true)
64
65 try {
66 const agent = new Agent(null, {service: serviceUrl})
67 await agent.com.atproto.server.resetPassword({
68 token: formattedCode,
69 password,
70 })
71 onPasswordSet()
72 logEvent('signin:passwordResetSuccess', {})
73 } catch (e: any) {
74 const errMsg = e.toString()
75 logger.warn('Failed to set new password', {error: e})
76 logEvent('signin:passwordResetFailure', {})
77 setIsProcessing(false)
78 if (isNetworkError(e)) {
79 setError(
80 _(
81 msg`Unable to contact your service. Please check your Internet connection.`,
82 ),
83 )
84 } else {
85 setError(cleanError(errMsg))
86 }
87 }
88 }
89
90 const onBlur = () => {
91 const formattedCode = checkAndFormatResetCode(resetCode)
92 if (!formattedCode) {
93 setError(
94 _(
95 msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`,
96 ),
97 )
98 return
99 }
100 setResetCode(formattedCode)
101 }
102
103 return (
104 <FormContainer
105 testID="setNewPasswordForm"
106 titleText={<Trans>Set new password</Trans>}>
107 <Text style={[a.leading_snug, a.mb_sm]}>
108 <Trans>
109 You will receive an email with a "reset code." Enter that code here,
110 then enter your new password.
111 </Trans>
112 </Text>
113
114 <View>
115 <TextField.LabelText>
116 <Trans>Reset code</Trans>
117 </TextField.LabelText>
118 <TextField.Root>
119 <TextField.Icon icon={Ticket} />
120 <TextField.Input
121 testID="resetCodeInput"
122 label={_(msg`Looks like XXXXX-XXXXX`)}
123 autoCapitalize="none"
124 autoFocus={true}
125 autoCorrect={false}
126 autoComplete="off"
127 value={resetCode}
128 onChangeText={setResetCode}
129 onFocus={() => setError('')}
130 onBlur={onBlur}
131 editable={!isProcessing}
132 accessibilityHint={_(
133 msg`Input code sent to your email for password reset`,
134 )}
135 />
136 </TextField.Root>
137 </View>
138
139 <View>
140 <TextField.LabelText>
141 <Trans>New password</Trans>
142 </TextField.LabelText>
143 <TextField.Root>
144 <TextField.Icon icon={Lock} />
145 <TextField.Input
146 testID="newPasswordInput"
147 label={_(msg`Enter a password`)}
148 autoCapitalize="none"
149 autoCorrect={false}
150 autoComplete="password"
151 returnKeyType="done"
152 secureTextEntry={true}
153 textContentType="password"
154 clearButtonMode="while-editing"
155 value={password}
156 onChangeText={setPassword}
157 onSubmitEditing={onPressNext}
158 editable={!isProcessing}
159 accessibilityHint={_(msg`Input new password`)}
160 />
161 </TextField.Root>
162 </View>
163
164 <FormError error={error} />
165
166 <View style={[a.flex_row, a.align_center, a.pt_lg]}>
167 <Button
168 label={_(msg`Back`)}
169 variant="solid"
170 color="secondary"
171 size="large"
172 onPress={onPressBack}>
173 <ButtonText>
174 <Trans>Back</Trans>
175 </ButtonText>
176 </Button>
177 <View style={a.flex_1} />
178 {isProcessing ? (
179 <ActivityIndicator />
180 ) : (
181 <Button
182 label={_(msg`Next`)}
183 variant="solid"
184 color="primary"
185 size="large"
186 onPress={onPressNext}>
187 <ButtonText>
188 <Trans>Next</Trans>
189 </ButtonText>
190 </Button>
191 )}
192 {isProcessing ? (
193 <Text style={[t.atoms.text_contrast_high, a.pl_md]}>
194 <Trans>Updating...</Trans>
195 </Text>
196 ) : undefined}
197 </View>
198 </FormContainer>
199 )
200}