/* eslint-disable @next/next/no-img-element */ "use client"; import { UserInfo } from "@/ldap"; import React from "react"; import styles from "./AboutMe.module.css"; import AvatarChanger from "@/components/AvatarChanger"; import Input, { Label } 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"; import { AuthProviderState } from "@/auth/AuthProvider"; import inputStyles from "@/components/Input.module.css"; import Connection from "@/components/Connection"; import DiscordIcon from "@/components/icons/DiscordIcon"; import GitHubIcon from "@/components/icons/GitHubIcon"; import TailscaleIcon from "@/components/icons/TailscaleIcon"; import MigaduIcon from "@/components/icons/MigaduIcon"; type UpdateResponse = { ok: boolean; error?: string; }; export default function AboutMe({ info, providers: [discordState, githubState] }: { info: UserInfo; providers: AuthProviderState[]; }) { // TODO: Reimplement password changing. 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}> <PrettyForm globalError={globalError}> <Formik initialValues={initialValues} onSubmit={handleFormSubmit} validationSchema={aboutMeSchema} > {({ isSubmitting }) => ( <Form className={styles.profileGrid}> <div className={styles.profileTower}> <Input type="file" name="avatar" customRender={(fieldProps) => ( <AvatarChanger currentAvatarBlob={fieldProps.field.value} onChange={(newBlob) => fieldProps.form.setFieldValue("avatar", newBlob) } vertical /> )} /> </div> <div> <h2 className={styles.userName}>{info.username}</h2> <div className={styles.rightGrid}> <div> {madeProfileChanges ? ( <Toast>Saved your changes.</Toast> ) : null} <Input type="text" name="username" label="Username" defaultValue={info.username} disabled hint="This can’t be changed." /> <Input type="text" name="displayName" label="Display name" defaultValue={info.displayName} /> <Input type="email" name="email" label="Email" defaultValue={info.email} /> <div className={inputStyles.formRow}> <button type="button">Change Password</button> </div> <div className={inputStyles.formRow}> <button type="button" onClick={async () => { document.cookie = "ticket=; expires=" + new Date().toUTCString() + "; path=/"; window.location.href = "/"; }} > Log out </button> </div> <input type="submit" value="Save" className={styles.fancyInput} disabled={isSubmitting} /> </div> <div className={styles.connections}> <Label>Connections</Label> <Connection service="Discord" authState={discordState} icon={DiscordIcon} /> <Connection service="GitHub" authState={githubState} icon={GitHubIcon} /> <Connection service="Tailscale" icon={TailscaleIcon} unavailable /> <Connection service="Migadu" icon={MigaduIcon} unavailable /> </div> </div> </div> </Form> )} </Formik> </PrettyForm> {/*<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> <h2 className={styles.header}>Connections</h2> <div className={styles.authProviderList}> {providers.map((provider) => ( <AuthProviderEntry provider={provider} key={provider.name} /> ))} </div> <input type="button" value="Log out" className={styles.logout} onClick={async () => { document.cookie = "ticket=; expires=" + new Date().toUTCString() + "; path=/"; window.location.href = "/"; }} />*/} </div> ); }