Bluesky app fork with some witchin' additions 馃挮
at main 186 lines 5.5 kB view raw
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}