add toasts

This commit is contained in:
Skip R. 2023-04-26 20:22:49 -07:00
parent 2acdeb8c95
commit 38eb8866b3
4 changed files with 76 additions and 13 deletions

View File

@ -14,21 +14,13 @@ import {
passwordUpdateSchema passwordUpdateSchema
} from "@/schemas"; } from "@/schemas";
import PrettyForm from "@/components/PrettyForm"; import PrettyForm from "@/components/PrettyForm";
import Toast from "@/components/Toast";
type UpdateResponse = { type UpdateResponse = {
ok: boolean; ok: boolean;
error?: string; error?: string;
}; };
// TODO skip do your magic
type InputProps = {
label: string;
name: string;
type: HTMLInputTypeAttribute;
error?: string;
displayImage?: string;
} & InputHTMLAttributes<HTMLInputElement>;
async function fileAsBase64(f: File) { async function fileAsBase64(f: File) {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsArrayBuffer(f); reader.readAsArrayBuffer(f);
@ -44,6 +36,9 @@ async function fileAsBase64(f: File) {
export default function AboutMe({ info }: { info: UserInfo }) { export default function AboutMe({ info }: { info: UserInfo }) {
const [globalError, setGlobalError] = React.useState<string | null>(null); const [globalError, setGlobalError] = React.useState<string | null>(null);
const [madeProfileChanges, setMadeChanges] = React.useState(false);
const [madePasswordChanges, setMadePasswordChanges] = React.useState(false);
const initialValues: AboutMeFormValues = { const initialValues: AboutMeFormValues = {
username: info.username, username: info.username,
displayName: info.displayName, displayName: info.displayName,
@ -55,6 +50,7 @@ export default function AboutMe({ info }: { info: UserInfo }) {
{ displayName, email, avatar }: AboutMeFormValues, { displayName, email, avatar }: AboutMeFormValues,
{ setSubmitting }: FormikHelpers<AboutMeFormValues> { setSubmitting }: FormikHelpers<AboutMeFormValues>
) { ) {
setMadeChanges(false);
setSubmitting(true); setSubmitting(true);
const req = await fetch("/api/update", { const req = await fetch("/api/update", {
method: "POST", method: "POST",
@ -78,6 +74,7 @@ export default function AboutMe({ info }: { info: UserInfo }) {
break; break;
} }
} }
setMadeChanges(true);
} catch { } catch {
console.error(req); console.error(req);
} }
@ -94,7 +91,7 @@ export default function AboutMe({ info }: { info: UserInfo }) {
{ password, newPassword }: PasswordUpdateFormValues, { password, newPassword }: PasswordUpdateFormValues,
{ setFieldError, setSubmitting }: FormikHelpers<PasswordUpdateFormValues> { setFieldError, setSubmitting }: FormikHelpers<PasswordUpdateFormValues>
) { ) {
console.log(password, newPassword); setMadePasswordChanges(false);
setSubmitting(true); setSubmitting(true);
const req = await fetch("/api/changePassword", { const req = await fetch("/api/changePassword", {
method: "POST", method: "POST",
@ -118,6 +115,7 @@ export default function AboutMe({ info }: { info: UserInfo }) {
break; break;
} }
} }
setMadePasswordChanges(true);
} catch { } catch {
console.error(req); console.error(req);
} }
@ -134,6 +132,7 @@ export default function AboutMe({ info }: { info: UserInfo }) {
> >
{({ isSubmitting }) => ( {({ isSubmitting }) => (
<Form className={styles.profileGrid}> <Form className={styles.profileGrid}>
{madeProfileChanges ? <Toast>Saved your changes.</Toast> : null}
<Input <Input
type="text" type="text"
name="username" name="username"
@ -180,7 +179,6 @@ export default function AboutMe({ info }: { info: UserInfo }) {
)} )}
</Formik> </Formik>
</PrettyForm> </PrettyForm>
<hr className={styles.divider} /> <hr className={styles.divider} />
<h2 className={styles.header}>Change password</h2> <h2 className={styles.header}>Change password</h2>
<PrettyForm globalError={passwordError}> <PrettyForm globalError={passwordError}>
@ -190,7 +188,10 @@ export default function AboutMe({ info }: { info: UserInfo }) {
validationSchema={passwordUpdateSchema} validationSchema={passwordUpdateSchema}
> >
{({ isSubmitting }) => ( {({ isSubmitting }) => (
<Form className={styles.profileGrid}> <Form>
{madePasswordChanges ? (
<Toast>Changed your password.</Toast>
) : null}
<Input <Input
type="password" type="password"
name="password" name="password"
@ -225,7 +226,6 @@ export default function AboutMe({ info }: { info: UserInfo }) {
)} )}
</Formik> </Formik>
</PrettyForm> </PrettyForm>
<hr className={styles.divider} /> <hr className={styles.divider} />
<input <input
type="button" type="button"

View File

@ -0,0 +1,32 @@
.toast {
display: flex;
align-items: center;
padding: 1rem;
background-color: var(--bg-dark);
margin: 1rem 0;
border-radius: 0.25rem;
animation-name: pop-in;
animation-timing-function: ease-in-out;
animation-duration: 250ms;
animation-iteration-count: 1;;
}
.toast svg {
height: 0.9em;
margin-right: 0.5rem;
}
@keyframes pop-in {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}

12
src/components/Toast.tsx Normal file
View File

@ -0,0 +1,12 @@
import React from "react";
import styles from "./Toast.module.css";
import CheckIcon from "./icons/CheckIcon";
export default function Toast({ children }: { children: React.ReactNode }) {
return (
<div className={styles.toast}>
<CheckIcon />
{children}
</div>
);
}

View File

@ -0,0 +1,19 @@
export default function CheckIcon() {
return (
<svg
viewBox="0 0 128 128"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="check" fill="currentColor" fill-rule="nonzero">
<path
d="M107.782834,4.91476009 C111.16323,-0.155833631 118.014111,-1.52600976 123.084704,1.85438606 C128.155298,5.23478187 129.525474,12.0856625 126.145078,17.1562562 L64.5253312,123.085877 C60.662855,128.879591 52.465466,129.691293 47.5417556,124.767582 L3.23188204,89.4577087 C-1.07729401,85.1485327 -1.07729401,78.1619779 3.23188204,73.8528018 C7.54105809,69.5436258 14.5276129,69.5436258 18.8367889,73.8528018 L53.6283699,99.643429 L107.782834,4.91476009 Z"
id="Path-4"
></path>
</g>
</g>
</svg>
);
}