ToastProvider
parent
f9a07e5fcb
commit
0070e0da84
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { HashRouter, Route, Switch, Redirect } from "react-router-dom";
|
import { HashRouter, Route, Switch, Redirect } from "react-router-dom";
|
||||||
import ToastContainer from "./Toast/ToastContainer";
|
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import Layout from "./Layout/Layout";
|
import Layout from "./Layout/Layout";
|
||||||
|
@ -35,7 +34,6 @@ export default function App() {
|
||||||
<PublicRoute path="/login" component={Login} />
|
<PublicRoute path="/login" component={Login} />
|
||||||
<Route component={Error} />
|
<Route component={Error} />
|
||||||
</Switch>
|
</Switch>
|
||||||
<ToastContainer />
|
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,18 @@ export default makeStyles((theme) => ({
|
||||||
notificationCloseButton: {
|
notificationCloseButton: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
right: theme.spacing(2)
|
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 * as serviceWorker from "./serviceWorker";
|
||||||
import { LayoutProvider } from "./context/LayoutContext";
|
import { LayoutProvider } from "./context/LayoutContext";
|
||||||
import { UserProvider } from "./context/UserContext";
|
import { UserProvider } from "./context/UserContext";
|
||||||
|
import { ToastProvider } from "./context/ToastContext";
|
||||||
import "./utils/i18n";
|
import "./utils/i18n";
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
|
@ -15,7 +16,9 @@ ReactDOM.render(
|
||||||
<ThemeProvider theme={Themes.default}>
|
<ThemeProvider theme={Themes.default}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Suspense fallback={<div>Loading...</div>}>
|
<Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<ToastProvider>
|
||||||
<App />
|
<App />
|
||||||
|
</ToastProvider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</UserProvider>
|
</UserProvider>
|
||||||
|
|
|
@ -15,20 +15,15 @@ import PageTitle from "../../components/PageTitle/PageTitle";
|
||||||
import Notification from "../../components/Notification";
|
import Notification from "../../components/Notification";
|
||||||
import { Typography, Button } from "../../components/Wrappers/Wrappers";
|
import { Typography, Button } from "../../components/Wrappers/Wrappers";
|
||||||
|
|
||||||
const positions = [
|
// context
|
||||||
toast.POSITION.TOP_LEFT,
|
import { useToast, useToastState } from "../../context/ToastContext";
|
||||||
toast.POSITION.TOP_CENTER,
|
|
||||||
toast.POSITION.TOP_RIGHT,
|
|
||||||
toast.POSITION.BOTTOM_LEFT,
|
|
||||||
toast.POSITION.BOTTOM_CENTER,
|
|
||||||
toast.POSITION.BOTTOM_RIGHT
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function NotificationsPage(props) {
|
export default function NotificationsPage(props) {
|
||||||
var classes = useStyles();
|
const classes = useStyles();
|
||||||
|
const { notify, changeNotificationPosition } = useToast();
|
||||||
|
const { position: notificationsPosition } = useToastState();
|
||||||
|
|
||||||
// local
|
// local
|
||||||
var [notificationsPosition, setNotificationPosition] = useState(2);
|
|
||||||
var [errorToastId, setErrorToastId] = useState(null);
|
var [errorToastId, setErrorToastId] = useState(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -123,7 +118,7 @@ export default function NotificationsPage(props) {
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => handleNotificationCall("info")}
|
onClick={() => notify("Info message", "info")}
|
||||||
className={classnames(classes.notificationCallButton)}
|
className={classnames(classes.notificationCallButton)}
|
||||||
>
|
>
|
||||||
Info Message
|
Info Message
|
||||||
|
@ -131,7 +126,14 @@ export default function NotificationsPage(props) {
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onClick={() => handleNotificationCall("error")}
|
onClick={() => {
|
||||||
|
const toastId = notify(
|
||||||
|
"Error message",
|
||||||
|
"error",
|
||||||
|
retryErrorNotification
|
||||||
|
);
|
||||||
|
setErrorToastId(toastId);
|
||||||
|
}}
|
||||||
className={classnames(classes.notificationCallButton)}
|
className={classnames(classes.notificationCallButton)}
|
||||||
>
|
>
|
||||||
Error + Retry Message
|
Error + Retry Message
|
||||||
|
@ -139,7 +141,7 @@ export default function NotificationsPage(props) {
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="success"
|
color="success"
|
||||||
onClick={() => handleNotificationCall("success")}
|
onClick={() => notify("Success message", "success")}
|
||||||
className={classnames(classes.notificationCallButton)}
|
className={classnames(classes.notificationCallButton)}
|
||||||
>
|
>
|
||||||
Success Message
|
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() {
|
function retryErrorNotification() {
|
||||||
var componentProps = {
|
var componentProps = {
|
||||||
type: "message",
|
type: "message",
|
||||||
|
@ -356,54 +347,5 @@ export default function NotificationsPage(props) {
|
||||||
render: <Notification {...componentProps} />,
|
render: <Notification {...componentProps} />,
|
||||||
type: "success"
|
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: {
|
notificationItem: {
|
||||||
marginTop: theme.spacing(2)
|
marginTop: theme.spacing(2)
|
||||||
},
|
},
|
||||||
progress: {
|
|
||||||
visibility: "hidden"
|
|
||||||
},
|
|
||||||
notification: {
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
background: "transparent",
|
|
||||||
boxShadow: "none",
|
|
||||||
overflow: "visible"
|
|
||||||
},
|
|
||||||
notificationComponent: {
|
|
||||||
paddingRight: theme.spacing(4)
|
|
||||||
},
|
|
||||||
widgetHeader: {
|
widgetHeader: {
|
||||||
paddingBottom: 8
|
paddingBottom: 8
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue