import React, {useState} from 'react' import { ActivityIndicator, SafeAreaView, StyleSheet, TouchableOpacity, View, } from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import * as EmailValidator from 'email-validator' import {logger} from '#/logger' import {useModalControls} from '#/state/modals' import {useAgent, useSession} from '#/state/session' import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {cleanError, isNetworkError} from 'lib/strings/errors' import {checkAndFormatResetCode} from 'lib/strings/password' import {colors, s} from 'lib/styles' import {isAndroid, isWeb} from 'platform/detection' import {ErrorMessage} from '../util/error/ErrorMessage' import {Button} from '../util/forms/Button' import {Text} from '../util/text/Text' import {ScrollView} from './util' import {TextInput} from './util' enum Stages { RequestCode, ChangePassword, Done, } export const snapPoints = isAndroid ? ['90%'] : ['45%'] export function Component() { const pal = usePalette('default') const {currentAccount} = useSession() const agent = useAgent() const {_} = useLingui() const [stage, setStage] = useState(Stages.RequestCode) const [isProcessing, setIsProcessing] = useState(false) const [resetCode, setResetCode] = useState('') const [newPassword, setNewPassword] = useState('') const [error, setError] = useState('') const {isMobile} = useWebMediaQueries() const {closeModal} = useModalControls() const onRequestCode = async () => { if ( !currentAccount?.email || !EmailValidator.validate(currentAccount.email) ) { return setError(_(msg`Your email appears to be invalid.`)) } setError('') setIsProcessing(true) try { await agent.com.atproto.server.requestPasswordReset({ email: currentAccount.email, }) setStage(Stages.ChangePassword) } catch (e: any) { const errMsg = e.toString() logger.warn('Failed to request password reset', {error: e}) if (isNetworkError(e)) { setError( _( msg`Unable to contact your service. Please check your Internet connection.`, ), ) } else { setError(cleanError(errMsg)) } } finally { setIsProcessing(false) } } const onChangePassword = async () => { const formattedCode = checkAndFormatResetCode(resetCode) // TODO Better password strength check if (!formattedCode || !newPassword) { setError( _( msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, ), ) return } setError('') setIsProcessing(true) try { await agent.com.atproto.server.resetPassword({ token: formattedCode, password: newPassword, }) setStage(Stages.Done) } catch (e: any) { const errMsg = e.toString() logger.warn('Failed to set new password', {error: e}) if (isNetworkError(e)) { setError( 'Unable to contact your service. Please check your Internet connection.', ) } else { setError(cleanError(errMsg)) } } finally { setIsProcessing(false) } } const onBlur = () => { const formattedCode = checkAndFormatResetCode(resetCode) if (!formattedCode) { setError( _( msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, ), ) return } setResetCode(formattedCode) } return ( {stage !== Stages.Done ? _(msg`Change Password`) : _(msg`Password Changed`)} {stage === Stages.RequestCode ? ( If you want to change your password, we will send you a code to verify that this is your account. ) : stage === Stages.ChangePassword ? ( Enter the code you received to change your password. ) : ( Your password has been changed successfully! )} {stage === Stages.RequestCode && ( setStage(Stages.ChangePassword)} accessibilityRole="button" accessibilityLabel={_(msg`Go to next`)} accessibilityHint={_(msg`Navigates to the next screen`)}> Already have a code? )} {stage === Stages.ChangePassword && ( setError('')} onBlur={onBlur} accessible={true} accessibilityLabel={_(msg`Reset Code`)} accessibilityHint="" autoCapitalize="none" autoCorrect={false} autoComplete="off" /> )} {error ? ( ) : undefined} {isProcessing ? ( ) : ( {stage === Stages.RequestCode && (