import PropTypes from 'prop-types'; import { createContext, useCallback, useEffect, useMemo, useRef } from 'react'; import { DEFAULT_NOTIF_TIMEOUT } from '../constants'; import { defaultTransition } from '../helpers/animationPropsHelper'; import useContextHook from './useContextHook'; import useStateWithCallback from '../hooks/useStateWithCallback'; const Context = createContext(null); Context.displayName = 'Notification'; export const NOTIF_TYPES = { SUCCESS: 'success', ERROR: 'error', INFO: 'info' }; const defaultNotifOptions = { asPortal: false, timeout: DEFAULT_NOTIF_TIMEOUT, withTimeout: true }; export const Provider = ({ children }) => { const [notif, setNotif] = useStateWithCallback(null); const timeoutID = useRef(); const dismissNotif = useCallback(() => { setNotif(null); clearTimeout(timeoutID.current); }, [setNotif]); const notify = useCallback( ( message, type, { asPortal = defaultNotifOptions.asPortal, timeout = defaultNotifOptions.timeout, withTimeout = defaultNotifOptions.withTimeout } = defaultNotifOptions ) => { const notifProps = { asPortal, message, timeout, type, withTimeout }; let shouldDismissNotif = false; setNotif( (prevNotif) => { if (prevNotif) { shouldDismissNotif = true; clearTimeout(timeoutID.current); return null; } else { return notifProps; } }, () => { if (shouldDismissNotif) { if (withTimeout) setTimeout(() => setNotif(notifProps), defaultTransition * 1000); else setNotif(notifProps); } } ); }, [setNotif] ); const notifyError = useCallback( (message, options) => notify(message, NOTIF_TYPES.ERROR, options), [notify] ); const notifySuccess = useCallback( (message, options) => notify(message, NOTIF_TYPES.SUCCESS, options), [notify] ); const notifyInfo = useCallback( (message, options) => notify(message, NOTIF_TYPES.INFO, options), [notify] ); useEffect(() => { if (notif && notif.withTimeout && notif.timeout >= 0) { timeoutID.current = setTimeout(dismissNotif, notif.timeout); } return () => clearTimeout(timeoutID.current); }, [dismissNotif, notif]); const value = useMemo( () => ({ dismissNotif, notif, notifyError, notifyInfo, notifySuccess }), [dismissNotif, notif, notifyError, notifySuccess, notifyInfo] ); return {children}; }; Provider.propTypes = { children: PropTypes.node.isRequired }; export const useNotif = () => useContextHook(Context);