From 0070e0da846c862648a404b5435ae0cad4c5cbf5 Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Fri, 30 Jul 2021 03:38:11 +0300 Subject: [PATCH] ToastProvider --- src/components/App.js | 2 - src/components/toast/styles.js | 13 +++ src/context/ToastContext.js | 143 +++++++++++++++++++++++ src/index.js | 5 +- src/pages/notifications/Notifications.js | 88 +++----------- src/pages/notifications/styles.js | 13 --- 6 files changed, 175 insertions(+), 89 deletions(-) create mode 100644 src/context/ToastContext.js diff --git a/src/components/App.js b/src/components/App.js index efb1554..7f6ca2f 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,6 +1,5 @@ import React, { useEffect } from "react"; import { HashRouter, Route, Switch, Redirect } from "react-router-dom"; -import ToastContainer from "./Toast/ToastContainer"; // components import Layout from "./Layout/Layout"; @@ -35,7 +34,6 @@ export default function App() { - ); diff --git a/src/components/toast/styles.js b/src/components/toast/styles.js index 456f118..abd2d12 100644 --- a/src/components/toast/styles.js +++ b/src/components/toast/styles.js @@ -9,5 +9,18 @@ export default makeStyles((theme) => ({ notificationCloseButton: { position: "absolute", right: theme.spacing(2) + }, + notificationComponent: { + paddingRight: theme.spacing(4) + }, + progress: { + visibility: "hidden" + }, + notification: { + display: "flex", + alignItems: "center", + background: "transparent", + boxShadow: "none", + overflow: "visible" } })); diff --git a/src/context/ToastContext.js b/src/context/ToastContext.js new file mode 100644 index 0000000..80ef189 --- /dev/null +++ b/src/context/ToastContext.js @@ -0,0 +1,143 @@ +import React, { useReducer, useMemo, useCallback } from "react"; +import { toast } from "react-toastify"; +import useStyles from "../components/Toast/styles"; +import ToastContainer from "../components/Toast/ToastContainer"; +import Notification from "../components/Notification"; + +const positions = [ + toast.POSITION.TOP_LEFT, + toast.POSITION.TOP_CENTER, + toast.POSITION.TOP_RIGHT, + toast.POSITION.BOTTOM_LEFT, + toast.POSITION.BOTTOM_CENTER, + toast.POSITION.BOTTOM_RIGHT +]; + +const ToastStateContext = React.createContext(); +const ToastDispatchContext = React.createContext(); + +const toastInitialState = { position: 2 }; +const toastReducer = (state, action) => { + switch (action.type) { + case "onNotificationPositionChange": + return { ...state, position: action.payload.position }; + default: { + return state; + } + } +}; + +export const toastDispatchActions = (dispatch) => ({ + changeNotificationPosition: (position) => + dispatch({ type: "onNotificationPositionChange", payload: { position } }) +}); + +const ToastProvider = ({ children }) => { + // var [errorToastId, setErrorToastId] = useState(null); + const [state, dispatch] = useReducer(toastReducer, toastInitialState); + const dispatchActions = useMemo(() => toastDispatchActions(dispatch), [ + dispatch + ]); + + const classes = useStyles(); + + const sendNotification = useCallback( + (componentProps, options) => { + return toast( + , + options + ); + }, + [classes] + ); + + const notify = useCallback( + (message, type, extraButtonClick) => { + // if (errorToastId && type === "error") return; + + let componentProps; + + switch (type) { + case "info": + componentProps = { + type: "feedback", + message, + variant: "contained", + color: "primary" + }; + break; + case "error": + componentProps = { + type: "message", + message, + variant: "contained", + color: "secondary", + extraButton: "Resend", + extraButtonClick + }; + break; + default: + componentProps = { + type: "shipped", + message, + variant: "contained", + color: "success" + }; + } + + const toastId = sendNotification(componentProps, { + type, + position: positions[state.position], + progressClassName: classes.progress, + // onClose: type === "error" && (() => setErrorToastId(null)), + className: classes.notification + }); + + // if (type === "error") setErrorToastId(toastId); + + return toastId; + }, + [sendNotification, classes, state.position] + ); + + const actions = useMemo( + () => ({ + ...dispatchActions, + info: (message) => notify(message, "info"), + notify + }), + [dispatchActions, notify] + ); + + return ( + + + <> + {children} + + + + + ); +}; + +const useToast = () => { + const context = React.useContext(ToastDispatchContext); + if (context === undefined) { + throw new Error("useToast must be used within a ToastProvider"); + } + return context; +}; + +const useToastState = () => { + var context = React.useContext(ToastStateContext); + if (context === undefined) { + throw new Error("useToastState must be used within a ToastProvider"); + } + return context; +}; + +export { ToastProvider, useToast, useToastState }; diff --git a/src/index.js b/src/index.js index 139a7fe..38be7a5 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,7 @@ import App from "./components/App"; import * as serviceWorker from "./serviceWorker"; import { LayoutProvider } from "./context/LayoutContext"; import { UserProvider } from "./context/UserContext"; +import { ToastProvider } from "./context/ToastContext"; import "./utils/i18n"; ReactDOM.render( @@ -15,7 +16,9 @@ ReactDOM.render( Loading...}> - + + + diff --git a/src/pages/notifications/Notifications.js b/src/pages/notifications/Notifications.js index 80ff429..496fa79 100644 --- a/src/pages/notifications/Notifications.js +++ b/src/pages/notifications/Notifications.js @@ -15,20 +15,15 @@ import PageTitle from "../../components/PageTitle/PageTitle"; import Notification from "../../components/Notification"; import { Typography, Button } from "../../components/Wrappers/Wrappers"; -const positions = [ - toast.POSITION.TOP_LEFT, - toast.POSITION.TOP_CENTER, - toast.POSITION.TOP_RIGHT, - toast.POSITION.BOTTOM_LEFT, - toast.POSITION.BOTTOM_CENTER, - toast.POSITION.BOTTOM_RIGHT -]; +// context +import { useToast, useToastState } from "../../context/ToastContext"; export default function NotificationsPage(props) { - var classes = useStyles(); + const classes = useStyles(); + const { notify, changeNotificationPosition } = useToast(); + const { position: notificationsPosition } = useToastState(); // local - var [notificationsPosition, setNotificationPosition] = useState(2); var [errorToastId, setErrorToastId] = useState(null); return ( @@ -123,7 +118,7 @@ export default function NotificationsPage(props) {