import { useRouter } from "next/router"; import { memo, useCallback, useEffect, useRef, useState } from "react"; import { notificationService, NotificationType, } from "../services/notification.service.js"; import styles from "../styles/exos/Notifications.module.scss"; import { MdDone, MdError, MdInfo } from "react-icons/md"; import { BiError } from "react-icons/bi"; function NotificationComp({ notification, classes, removeAlert, mount, fadeIn, }) { const notifIcon = { [NotificationType.Success]: , [NotificationType.Warning]: , [NotificationType.Info]: , [NotificationType.Error]: , }; return (
{ removeAlert(notification); }} >
{notifIcon[notification.type]} {notification.title}
{notification.message}
); } const Notification = memo(NotificationComp); export default function Notifications({ id = "default-notif", fade = true }) { const mounted = useRef(false); const router = useRouter(); const [notification, setNotif] = useState([]); useEffect(() => { mounted.current = true; // subscribe to new alert notifications const subscription = notificationService.onAlert(id).subscribe((notif) => { // clear alerts when an empty alert is received if (!notif.message) { setNotif((alerts) => { // filter out alerts without 'keepAfterRouteChange' flag const filteredAlerts = alerts.filter((x) => x.keepAfterRouteChange); // remove 'keepAfterRouteChange' flag on the rest return omit(filteredAlerts, "keepAfterRouteChange"); }); } else { // add alert to array with unique id notif.itemId = Math.random(); addNotif(notif); // auto close alert if required if (notif.autoClose) { setTimeout(() => removeAlert(notif), 3000); } } }); // clear alerts on location change const clearAlerts = () => notificationService.clear(id); router.events.on("routeChangeStart", clearAlerts); // clean up function that runs when the component unmounts return () => { mounted.current = false; // unsubscribe to avoid memory leaks subscription.unsubscribe(); router.events.off("routeChangeStart", clearAlerts); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); function omit(arr, key) { return arr.map((obj) => { const { [key]: omitted, ...rest } = obj; return rest; }); } function addNotif(notif) { if (notification.length > 5) { removeAlert(notification[0]); } setNotif((alerts) => [...alerts, notif]); } function removeAlertFunc(notif) { if (!mounted.current) return; if (fade) { // fade out alert setNotif((alerts) => alerts.map((x) => x.itemId === notif.itemId ? { ...x, fade: true } : x ) ); // remove alert after faded out setTimeout(() => { setNotif((alerts) => alerts.filter((x) => x.itemId !== notif.itemId)); }, 500); } else { // remove alert setNotif((alerts) => alerts.filter((x) => x.itemId !== notif.itemId)); } } const removeAlert = useCallback(removeAlertFunc, [fade]); function cssClasses(notif) { if (!notif) return; const classes = [styles["notif"], styles["notif-dismissable"]]; const notifTypeClass = { [NotificationType.Success]: styles["notif-success"], [NotificationType.Error]: styles["notif-danger"], [NotificationType.Info]: styles["notif-info"], [NotificationType.Warning]: styles["notif-warning"], }; classes.push(notifTypeClass[notif.type]); if (notif.fade) { classes.push("fade"); } return classes.join(" "); } useEffect(() => { if (!mounted.current) return; if (notification.length > 5) { removeAlert(notification[0]); } }, [notification, removeAlert]); if (!notification.length) return null; return (
{notification.map((notification, index) => { return ( ); })}
); }