2023-04-26 13:56:59 -04:00
|
|
|
/* 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";
|
2023-04-26 22:59:17 -04:00
|
|
|
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";
|
2023-04-26 23:22:49 -04:00
|
|
|
import Toast from "@/components/Toast";
|
2023-04-26 13:56:59 -04:00
|
|
|
|
|
|
|
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 }) {
|
2023-04-26 22:59:17 -04:00
|
|
|
const [globalError, setGlobalError] = React.useState<string | null>(null);
|
2023-04-26 23:22:49 -04:00
|
|
|
const [madeProfileChanges, setMadeChanges] = React.useState(false);
|
|
|
|
const [madePasswordChanges, setMadePasswordChanges] = React.useState(false);
|
|
|
|
|
2023-04-26 22:59:17 -04:00
|
|
|
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>
|
|
|
|
) {
|
2023-04-26 23:22:49 -04:00
|
|
|
setMadeChanges(false);
|
2023-04-26 22:59:17 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2023-04-26 23:22:49 -04:00
|
|
|
setMadeChanges(true);
|
2023-04-26 22:59:17 -04:00
|
|
|
} 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>
|
|
|
|
) {
|
2023-04-26 23:22:49 -04:00
|
|
|
setMadePasswordChanges(false);
|
2023-04-26 22:59:17 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2023-04-26 23:22:49 -04:00
|
|
|
setMadePasswordChanges(true);
|
2023-04-26 22:59:17 -04:00
|
|
|
} catch {
|
|
|
|
console.error(req);
|
|
|
|
}
|
|
|
|
}
|
2023-04-26 13:56:59 -04:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={styles.content}>
|
2023-04-26 22:59:17 -04:00
|
|
|
<h2 className={styles.userName}>{info.username}</h2>
|
|
|
|
<PrettyForm globalError={globalError}>
|
|
|
|
<Formik
|
|
|
|
initialValues={initialValues}
|
|
|
|
onSubmit={handleFormSubmit}
|
|
|
|
validationSchema={aboutMeSchema}
|
|
|
|
>
|
|
|
|
{({ isSubmitting }) => (
|
|
|
|
<Form className={styles.profileGrid}>
|
2023-04-26 23:22:49 -04:00
|
|
|
{madeProfileChanges ? <Toast>Saved your changes.</Toast> : null}
|
2023-04-26 22:59:17 -04:00
|
|
|
<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>
|
2023-04-26 13:56:59 -04:00
|
|
|
<hr className={styles.divider} />
|
|
|
|
<h2 className={styles.header}>Change password</h2>
|
2023-04-26 22:59:17 -04:00
|
|
|
<PrettyForm globalError={passwordError}>
|
|
|
|
<Formik
|
|
|
|
initialValues={initialPasswordValues}
|
|
|
|
onSubmit={handlePasswordSubmit}
|
|
|
|
validationSchema={passwordUpdateSchema}
|
|
|
|
>
|
|
|
|
{({ isSubmitting }) => (
|
2023-04-26 23:22:49 -04:00
|
|
|
<Form>
|
|
|
|
{madePasswordChanges ? (
|
|
|
|
<Toast>Changed your password.</Toast>
|
|
|
|
) : null}
|
2023-04-26 22:59:17 -04:00
|
|
|
<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>
|
2023-04-26 23:32:52 -04:00
|
|
|
|
|
|
|
<hr className={styles.divider} />
|
|
|
|
<h2 className={styles.header}>Connections</h2>
|
|
|
|
<div>
|
|
|
|
<p>discord: {info.discordId}</p>
|
|
|
|
<p>github: {info.githubId}</p>
|
|
|
|
</div>
|
|
|
|
|
2023-04-26 21:21:28 -04:00
|
|
|
<hr className={styles.divider} />
|
2023-04-26 22:59:17 -04:00
|
|
|
<input
|
|
|
|
type="button"
|
|
|
|
value="Log out"
|
|
|
|
className={styles.logout}
|
|
|
|
onClick={async () => {
|
|
|
|
document.cookie =
|
|
|
|
"ticket=; expires=" + new Date().toUTCString() + "; path=/";
|
|
|
|
window.location.href = "/";
|
|
|
|
}}
|
|
|
|
/>
|
2023-04-26 13:56:59 -04:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|