import {useState} from 'react' import {useWindowDimensions, View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import * as EmailValidator from 'email-validator' import {cleanError, isNetworkError} from '#/lib/strings/errors' import {checkAndFormatResetCode} from '#/lib/strings/password' import {logger} from '#/logger' import {isNative} from '#/platform/detection' import {useAgent, useSession} from '#/state/session' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {android, atoms as a, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' enum Stages { RequestCode = 'RequestCode', ChangePassword = 'ChangePassword', Done = 'Done', } export function ChangePasswordDialog({ control, }: { control: Dialog.DialogControlProps }) { const {height} = useWindowDimensions() return ( ) } function Inner() { const {_} = useLingui() const {currentAccount} = useSession() const agent = useAgent() const control = Dialog.useDialogContext() const [stage, setStage] = useState(Stages.RequestCode) const [isProcessing, setIsProcessing] = useState(false) const [resetCode, setResetCode] = useState('') const [newPassword, setNewPassword] = useState('') const [error, setError] = useState('') const uiStrings = { RequestCode: { title: _(msg`Change your password`), message: _( msg`If you want to change your password, we will send you a code to verify that this is your account.`, ), }, ChangePassword: { title: _(msg`Enter code`), message: _( msg`Please enter the code you received and the new password you would like to use.`, ), }, Done: { title: _(msg`Password changed`), message: _( msg`Your password has been changed successfully! Please use your new password when you sign in to Bluesky from now on.`, ), }, } 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) { if (isNetworkError(e)) { setError( _( msg`Unable to contact your service. Please check your internet connection and try again.`, ), ) } else { logger.error('Failed to request password reset', {safeMessage: e}) setError(cleanError(e)) } } finally { setIsProcessing(false) } } const onChangePassword = async () => { const formattedCode = checkAndFormatResetCode(resetCode) if (!formattedCode) { setError( _( msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, ), ) return } if (!newPassword) { setError( _(msg`Please enter a password. It must be at least 8 characters long.`), ) return } if (newPassword.length < 8) { setError(_(msg`Password must be at least 8 characters long.`)) return } setError('') setIsProcessing(true) try { await agent.com.atproto.server.resetPassword({ token: formattedCode, password: newPassword, }) setStage(Stages.Done) } catch (e: any) { if (isNetworkError(e)) { setError( _( msg`Unable to contact your service. Please check your internet connection and try again.`, ), ) } else if (e?.toString().includes('Token is invalid')) { setError(_(msg`This confirmation code is not valid. Please try again.`)) } else { logger.error('Failed to set new password', {safeMessage: e}) setError(cleanError(e)) } } finally { setIsProcessing(false) } } const onBlur = () => { const formattedCode = checkAndFormatResetCode(resetCode) if (!formattedCode) { return } setResetCode(formattedCode) } return ( {uiStrings[stage].title} {error ? ( ) : null} {uiStrings[stage].message} {stage === Stages.ChangePassword && ( Confirmation code New password )} {stage === Stages.RequestCode ? ( <> {isNative && ( )} ) : stage === Stages.ChangePassword ? ( <> ) : stage === Stages.Done ? ( ) : null} ) }