import React, { FormEventHandler, useCallback, useEffect, useMemo, useState } from 'react'; import { Box, Button, Dialog, Input, Overlay, OverlayBackdrop, OverlayCenter, Spinner, Text, color, config, } from 'folds'; import { useNavigate } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; import { AuthDict, AuthType, MatrixError, createClient } from 'matrix-js-sdk'; import { useAutoDiscoveryInfo } from '../../../hooks/useAutoDiscoveryInfo'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { useAuthServer } from '../../../hooks/useAuthServer'; import { usePasswordEmail } from '../../../hooks/usePasswordEmail'; import { PasswordInput } from '../../../components/password-input'; import { ConfirmPasswordMatch } from '../../../components/ConfirmPasswordMatch'; import { FieldError } from '../FiledError'; import { UIAFlowOverlay } from '../../../components/UIAFlowOverlay'; import { EmailStageDialog } from '../../../components/uia-stages'; import { ResetPasswordResult, resetPassword } from './resetPasswordUtil'; import { getLoginPath, withSearchParam } from '../../pathUtils'; import { LoginPathSearchParams } from '../../paths'; import { getUIAError, getUIAErrorCode } from '../../../utils/matrix-uia'; type FormData = { email: string; password: string; clientSecret: string; }; function ResetPasswordComplete({ email }: { email?: string }) { const server = useAuthServer(); const navigate = useNavigate(); const handleClick = () => { const path = getLoginPath(server); if (email) { navigate(withSearchParam(path, { email })); return; } navigate(path); }; return ( }> Password has been reset successfully. Please login with your new password. ); } type PasswordResetFormProps = { defaultEmail?: string; }; export function PasswordResetForm({ defaultEmail }: PasswordResetFormProps) { const server = useAuthServer(); const serverDiscovery = useAutoDiscoveryInfo(); const baseUrl = serverDiscovery['m.homeserver'].base_url; const mx = useMemo(() => createClient({ baseUrl }), [baseUrl]); const [formData, setFormData] = useState(); const [passwordEmailState, passwordEmail] = usePasswordEmail(mx); const [resetPasswordState, handleResetPassword] = useAsyncCallback< ResetPasswordResult, MatrixError, [AuthDict, string] >(useCallback(async (authDict, newPassword) => resetPassword(mx, authDict, newPassword), [mx])); const [ongoingAuthData, resetPasswordResult] = resetPasswordState.status === AsyncStatus.Success ? resetPasswordState.data : []; const resetPasswordError = resetPasswordState.status === AsyncStatus.Error ? resetPasswordState.error : undefined; const flowErrorCode = ongoingAuthData && getUIAErrorCode(ongoingAuthData); const flowError = ongoingAuthData && getUIAError(ongoingAuthData); let waitingToVerifyEmail = true; if (resetPasswordResult) waitingToVerifyEmail = false; if (ongoingAuthData && flowErrorCode === undefined) waitingToVerifyEmail = false; if (resetPasswordError) waitingToVerifyEmail = false; if (resetPasswordState.status === AsyncStatus.Loading) waitingToVerifyEmail = false; // We only support UIA m.login.password stage for reset password // So we will assume to process it as soon as // we have 401 with no error on initial request. useEffect(() => { if (formData && ongoingAuthData && !flowErrorCode) { handleResetPassword( { type: AuthType.Password, identifier: { type: 'm.id.thirdparty', medium: 'email', address: formData.email, }, password: formData.password, }, formData.password, ); } }, [ongoingAuthData, flowErrorCode, formData, handleResetPassword]); const handleSubmit: FormEventHandler = (evt) => { evt.preventDefault(); const { emailInput, passwordInput, confirmPasswordInput } = evt.target as HTMLFormElement & { emailInput: HTMLInputElement; passwordInput: HTMLInputElement; confirmPasswordInput: HTMLInputElement; }; const email = emailInput.value.trim(); const password = passwordInput.value; const confirmPassword = confirmPasswordInput.value; if (!email) { emailInput.focus(); return; } if (password !== confirmPassword) return; const clientSecret = mx.generateClientSecret(); passwordEmail(email, clientSecret); setFormData({ email, password, clientSecret, }); }; const handleCancel = () => { window.location.reload(); }; const handleSubmitRequest = useCallback( (authDict: AuthDict) => { if (!formData) return; const { password } = formData; handleResetPassword(authDict, password); }, [formData, handleResetPassword], ); return ( Homeserver {server} will send you an email to let you reset your password. Email {passwordEmailState.status === AsyncStatus.Error && ( )} {(match, doMatch, passRef, confPassRef) => ( <> New Password Confirm Password )} {resetPasswordError && ( )} {resetPasswordResult && } {passwordEmailState.status === AsyncStatus.Success && formData && waitingToVerifyEmail && ( )} } > ); }