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