ToastProvider
parent
f9a07e5fcb
commit
0070e0da84
|
@ -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() {
|
|||
<PublicRoute path="/login" component={Login} />
|
||||
<Route component={Error} />
|
||||
</Switch>
|
||||
<ToastContainer />
|
||||
</HashRouter>
|
||||
);
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -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(
|
||||
<Notification
|
||||
{...componentProps}
|
||||
className={classes.notificationComponent}
|
||||
/>,
|
||||
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 (
|
||||
<ToastStateContext.Provider value={state}>
|
||||
<ToastDispatchContext.Provider value={actions}>
|
||||
<>
|
||||
{children}
|
||||
<ToastContainer />
|
||||
</>
|
||||
</ToastDispatchContext.Provider>
|
||||
</ToastStateContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
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 };
|
|
@ -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(
|
|||
<ThemeProvider theme={Themes.default}>
|
||||
<CssBaseline />
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<App />
|
||||
<ToastProvider>
|
||||
<App />
|
||||
</ToastProvider>
|
||||
</Suspense>
|
||||
</ThemeProvider>
|
||||
</UserProvider>
|
||||
|
|
|
@ -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) {
|
|||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => handleNotificationCall("info")}
|
||||
onClick={() => notify("Info message", "info")}
|
||||
className={classnames(classes.notificationCallButton)}
|
||||
>
|
||||
Info Message
|
||||
|
@ -131,7 +126,14 @@ export default function NotificationsPage(props) {
|
|||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={() => handleNotificationCall("error")}
|
||||
onClick={() => {
|
||||
const toastId = notify(
|
||||
"Error message",
|
||||
"error",
|
||||
retryErrorNotification
|
||||
);
|
||||
setErrorToastId(toastId);
|
||||
}}
|
||||
className={classnames(classes.notificationCallButton)}
|
||||
>
|
||||
Error + Retry Message
|
||||
|
@ -139,7 +141,7 @@ export default function NotificationsPage(props) {
|
|||
<Button
|
||||
variant="contained"
|
||||
color="success"
|
||||
onClick={() => handleNotificationCall("success")}
|
||||
onClick={() => notify("Success message", "success")}
|
||||
className={classnames(classes.notificationCallButton)}
|
||||
>
|
||||
Success Message
|
||||
|
@ -334,17 +336,6 @@ export default function NotificationsPage(props) {
|
|||
</>
|
||||
);
|
||||
|
||||
// #############################################################
|
||||
function sendNotification(componentProps, options) {
|
||||
return toast(
|
||||
<Notification
|
||||
{...componentProps}
|
||||
className={classes.notificationComponent}
|
||||
/>,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
function retryErrorNotification() {
|
||||
var componentProps = {
|
||||
type: "message",
|
||||
|
@ -356,54 +347,5 @@ export default function NotificationsPage(props) {
|
|||
render: <Notification {...componentProps} />,
|
||||
type: "success"
|
||||
});
|
||||
setErrorToastId(null);
|
||||
}
|
||||
|
||||
function handleNotificationCall(notificationType) {
|
||||
var componentProps;
|
||||
|
||||
if (errorToastId && notificationType === "error") return;
|
||||
|
||||
switch (notificationType) {
|
||||
case "info":
|
||||
componentProps = {
|
||||
type: "feedback",
|
||||
message: "New user feedback received",
|
||||
variant: "contained",
|
||||
color: "primary"
|
||||
};
|
||||
break;
|
||||
case "error":
|
||||
componentProps = {
|
||||
type: "message",
|
||||
message: "Message was not sent!",
|
||||
variant: "contained",
|
||||
color: "secondary",
|
||||
extraButton: "Resend",
|
||||
extraButtonClick: retryErrorNotification
|
||||
};
|
||||
break;
|
||||
default:
|
||||
componentProps = {
|
||||
type: "shipped",
|
||||
message: "The item was shipped",
|
||||
variant: "contained",
|
||||
color: "success"
|
||||
};
|
||||
}
|
||||
|
||||
var toastId = sendNotification(componentProps, {
|
||||
type: notificationType,
|
||||
position: positions[notificationsPosition],
|
||||
progressClassName: classes.progress,
|
||||
onClose: notificationType === "error" && (() => setErrorToastId(null)),
|
||||
className: classes.notification
|
||||
});
|
||||
|
||||
if (notificationType === "error") setErrorToastId(toastId);
|
||||
}
|
||||
|
||||
function changeNotificationPosition(positionId) {
|
||||
setNotificationPosition(positionId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,19 +55,6 @@ export default makeStyles((theme) => ({
|
|||
notificationItem: {
|
||||
marginTop: theme.spacing(2)
|
||||
},
|
||||
progress: {
|
||||
visibility: "hidden"
|
||||
},
|
||||
notification: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
background: "transparent",
|
||||
boxShadow: "none",
|
||||
overflow: "visible"
|
||||
},
|
||||
notificationComponent: {
|
||||
paddingRight: theme.spacing(4)
|
||||
},
|
||||
widgetHeader: {
|
||||
paddingBottom: 8
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue