From 1b9d1ee7d5d1179f5c3cfc728f638c710b86f22b Mon Sep 17 00:00:00 2001 From: minhtrannhat Date: Mon, 4 Sep 2023 16:13:20 -0400 Subject: [PATCH] Feat(frontend): Toasts --- frontend/src/App.tsx | 13 ++++++-- frontend/src/ToastContext.tsx | 49 ++++++++++++++++++++++++++++++ frontend/src/components/Title.tsx | 2 +- frontend/src/components/Toasts.tsx | 47 ++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 frontend/src/ToastContext.tsx create mode 100644 frontend/src/components/Toasts.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 11abe31..a78248b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -24,6 +24,10 @@ import { AuthContextProvider } from "./AuthContext"; // React router import Router from "./Router"; +// Toasts +import Toasts from "./components/Toasts"; +import { ToastContextProvider } from "./ToastContext"; + const queryClient = new QueryClient(); const App = () => { @@ -35,9 +39,12 @@ const App = () => { Todo - - - + + + + + + diff --git a/frontend/src/ToastContext.tsx b/frontend/src/ToastContext.tsx new file mode 100644 index 0000000..a764bd3 --- /dev/null +++ b/frontend/src/ToastContext.tsx @@ -0,0 +1,49 @@ +import { AlertColor } from "@mui/material/Alert"; + +import React, { createContext, useState } from "react"; + +export interface IToast { + category?: AlertColor; + key: number; + message: string; +} + +interface IToastContext { + addToast: (message: string, category: AlertColor | undefined) => void; + setToasts: React.Dispatch>; + toasts: IToast[]; +} + +export const ToastContext = createContext({ + addToast: () => {}, + setToasts: () => {}, + toasts: [], +}); + +interface IProps { + children?: React.ReactNode; +} + +export const ToastContextProvider = ({ children }: IProps) => { + const [toasts, setToasts] = useState([]); + + const addToast = ( + message: string, + category: AlertColor | undefined = undefined, + ) => { + setToasts((prev) => [ + ...prev, + { + category, + key: new Date().getTime(), + message, + }, + ]); + }; + + return ( + + {children} + + ); +}; diff --git a/frontend/src/components/Title.tsx b/frontend/src/components/Title.tsx index cc5f2cc..9ac6d85 100644 --- a/frontend/src/components/Title.tsx +++ b/frontend/src/components/Title.tsx @@ -8,7 +8,7 @@ interface IProps { const Title = ({ title }: IProps) => ( <> - Tozo | {title} + Todo | {title} {title} diff --git a/frontend/src/components/Toasts.tsx b/frontend/src/components/Toasts.tsx new file mode 100644 index 0000000..4c73fa5 --- /dev/null +++ b/frontend/src/components/Toasts.tsx @@ -0,0 +1,47 @@ +import Alert from "@mui/material/Alert"; +import Snackbar from "@mui/material/Snackbar"; +import React, { useContext, useEffect, useState } from "react"; + +import { ToastContext, IToast } from "src/ToastContext"; + +const Toasts = () => { + const { toasts, setToasts } = useContext(ToastContext); + const [open, setOpen] = useState(false); + const [currentToast, setCurrentToast] = useState(); + + useEffect(() => { + if (!open && toasts.length) { + setCurrentToast(toasts[0]); + setToasts((prev) => prev.slice(1)); + setOpen(true); + } + }, [open, setCurrentToast, setOpen, setToasts, toasts]); + + const onClose = (event?: React.SyntheticEvent | Event, reason?: string) => { + if (reason !== "clickaway") { + setOpen(false); + } + }; + + return ( + setCurrentToast(undefined), + }} + > + + {currentToast?.message} + + + ); +}; + +export default Toasts;