/* eslint-disable @next/next/no-img-element */ "use client"; import { UserInfo } from "@/ldap"; import React, { HTMLInputTypeAttribute, InputHTMLAttributes } from "react"; import styles from "./AboutMe.module.css"; import AvatarChanger from "@/components/AvatarChanger"; import Input from "@/components/Input"; import { Form, Formik, FormikHelpers } from "formik"; import { AboutMeFormValues, PasswordUpdateFormValues, aboutMeSchema, passwordUpdateSchema } from "@/schemas"; import PrettyForm from "@/components/PrettyForm"; import Toast from "@/components/Toast"; type UpdateResponse = { ok: boolean; error?: string; }; async function fileAsBase64(f: File) { const reader = new FileReader(); reader.readAsArrayBuffer(f); return new Promise<string>((resolve, reject) => { reader.onload = () => { const result = reader.result as ArrayBuffer; const buffer = Buffer.from(result); resolve(buffer.toString("base64")); }; reader.onerror = () => reject(reader.error); }); } export default function AboutMe({ info }: { info: UserInfo }) { const [globalError, setGlobalError] = React.useState<string | null>(null); const [madeProfileChanges, setMadeChanges] = React.useState(false); const [madePasswordChanges, setMadePasswordChanges] = React.useState(false); const initialValues: AboutMeFormValues = { username: info.username, displayName: info.displayName, email: info.email, avatar: info.avatar }; async function handleFormSubmit( { displayName, email, avatar }: AboutMeFormValues, { setSubmitting }: FormikHelpers<AboutMeFormValues> ) { setMadeChanges(false); setSubmitting(true); const req = await fetch("/api/update", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ displayName, email, avatar: avatar != null ? avatar.split(",")[1] : null }) }); setSubmitting(false); try { const res: UpdateResponse = await req.json(); if (!res.ok && res.error !== null) { switch (res.error) { case "avatarBig": break; } } setMadeChanges(true); } catch { console.error(req); } } const [passwordError, setPasswordError] = React.useState<string | null>(null); const initialPasswordValues: PasswordUpdateFormValues = { password: "", newPassword: "", confirmPassword: "" }; async function handlePasswordSubmit( { password, newPassword }: PasswordUpdateFormValues, { setFieldError, setSubmitting }: FormikHelpers<PasswordUpdateFormValues> ) { setMadePasswordChanges(false); setSubmitting(true); const req = await fetch("/api/changePassword", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ currentPassword: password, newPassword: newPassword }) }); setSubmitting(false); try { const res: UpdateResponse = await req.json(); if (!res.ok && res.error !== null) { switch (res.error) { case "incorrectPassword": setFieldError("password", "Incorrect password."); break; } } setMadePasswordChanges(true); } catch { console.error(req); } } return ( <div className={styles.content}> <h2 className={styles.userName}>{info.username}</h2> <PrettyForm globalError={globalError}> <Formik initialValues={initialValues} onSubmit={handleFormSubmit} validationSchema={aboutMeSchema} > {({ isSubmitting }) => ( <Form className={styles.profileGrid}> {madeProfileChanges ? <Toast>Saved your changes.</Toast> : null} <Input type="text" name="username" label="Username" defaultValue={info.username} disabled title="You can't change your username." /> <Input type="text" name="displayName" label="Display name" defaultValue={info.displayName} /> <Input type="email" name="email" label="Email" defaultValue={info.email} /> <Input type="file" name="avatar" label="Avatar" accept="image/png, image/jpeg" customRender={(fieldProps) => ( <AvatarChanger currentAvatarBlob={fieldProps.field.value} onChange={(newBlob) => fieldProps.form.setFieldValue("avatar", newBlob) } /> )} /> <input type="submit" value="Save" className={styles.fancyInput} disabled={isSubmitting} /> </Form> )} </Formik> </PrettyForm> <hr className={styles.divider} /> <h2 className={styles.header}>Change password</h2> <PrettyForm globalError={passwordError}> <Formik initialValues={initialPasswordValues} onSubmit={handlePasswordSubmit} validationSchema={passwordUpdateSchema} > {({ isSubmitting }) => ( <Form> {madePasswordChanges ? ( <Toast>Changed your password.</Toast> ) : null} <Input type="password" name="password" label="Current" minLength={12} required /> <Input type="password" name="newPassword" label="New" minLength={12} required /> <Input type="password" name="confirmPassword" label="Confirm" minLength={12} required /> <input type="submit" value="Save" className={styles.fancyInput} disabled={isSubmitting} /> </Form> )} </Formik> </PrettyForm> <hr className={styles.divider} /> <input type="button" value="Log out" className={styles.logout} onClick={async () => { document.cookie = "ticket=; expires=" + new Date().toUTCString() + "; path=/"; window.location.href = "/"; }} /> </div> ); }