/** * The password change modal. * * @module */ import { useState } from "react"; import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; import { API_URL } from "../../config/api.ts"; import { VALIDATION } from "../../../shared/validation.ts"; import { useAuth } from "../../hooks/auth/useAuth.ts"; import { Modal } from "../shared/Modal.tsx"; import { expectOk } from "../../utils/shared/api.ts"; import { FormActions } from "../form/FormActions.tsx"; import { FormError } from "../form/FormError.tsx"; import { FormProvider } from "react-hook-form"; import { SubmitButton } from "../form/SubmitButton.tsx"; import { TextField } from "../form/TextField.tsx"; import { useApiForm } from "../../hooks/form/useApiForm.ts"; /** The props of {@link ChangePasswordModal}. */ export interface ChangePasswordModalProps { /** Called to close it. */ onClose: () => void; } /** The form's fields. */ interface Values { currentPassword: string; newPassword: string; confirmPassword: string; } /** * Renders the form that changes the user's password, given the current one. The * change signs out every other session, and this one keeps the fresh tokens it * is handed. */ export function ChangePasswordModal( { onClose }: ChangePasswordModalProps, ): React.JSX.Element { const { authFetch, updateSession } = useAuth(); const [done, setDone] = useState(false); const form = useApiForm({ mode: "onChange", defaultValues: { currentPassword: "", newPassword: "", confirmPassword: "", }, }); const onSubmit = form.submit(async ({ currentPassword, newPassword }) => { // The change signs out every session opened with the old password, this // one included, so keep the fresh tokens it hands back. const tokens = await expectOk<{ token: string; mediaToken: string }>( await authFetch(`${API_URL}/api/users/me/change-password`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ currentPassword, newPassword }), }), ); updateSession(tokens); setDone(true); }); return ( {done ? (

Password changed successfully.

) : (
name="currentPassword" type="password" label={t`Current password`} autoComplete="current-password" autoFocus rules={{ required: true }} /> name="newPassword" type="password" label={t`New password`} autoComplete="new-password" maxLength={VALIDATION.PASSWORD_MAX} rules={{ required: true, minLength: { value: VALIDATION.PASSWORD_MIN, message: t`At least ${VALIDATION.PASSWORD_MIN} characters`, }, }} /> name="confirmPassword" type="password" label={t`Confirm new password`} autoComplete="new-password" rules={{ required: true, validate: (value, values) => value === values.newPassword || t`Passwords do not match`, }} /> Saving…}> Change password
)}
); }