import React, { useCallback } from "react"; import without from "lodash/without"; import concat from "lodash/concat"; import { useTranslation } from "react-i18next"; import { Alert } from "react-bootstrap"; import { alertService, AlertInfoEx, kAlertHostId, AlertInfo, } from "@/services/alert"; interface AutoCloseAlertProps { alert: AlertInfo; close: () => void; } export const AutoCloseAlert: React.FC = (props) => { const { alert } = props; const { dismissTime } = alert; const { t } = useTranslation(); const timerTag = React.useRef(null); React.useEffect(() => { const tag = dismissTime === "never" ? null : typeof dismissTime === "number" ? window.setTimeout(props.close, dismissTime) : window.setTimeout(props.close, 5000); timerTag.current = tag; return () => { if (tag != null) { window.clearTimeout(tag); } }; }, [dismissTime, props.close]); return ( { const { current: tag } = timerTag; if (tag != null) { window.clearTimeout(tag); } }} onClose={props.close} dismissible > {(() => { const { message } = alert; if (typeof message === "function") { const Message = message; return ; } else if (typeof message === "object" && message.type === "i18n") { return t(message.key); } else return alert.message; })()} ); }; // oh what a bad name! interface AlertInfoExEx extends AlertInfoEx { close: () => void; } const AlertHost: React.FC = () => { const [alerts, setAlerts] = React.useState([]); // react guarantee that state setters are stable, so we don't need to add it to dependency list const consume = useCallback((alert: AlertInfoEx): void => { const alertEx: AlertInfoExEx = { ...alert, close: () => { setAlerts((oldAlerts) => { return without(oldAlerts, alertEx); }); }, }; setAlerts((oldAlerts) => { return concat(oldAlerts, alertEx); }); }, []); React.useEffect(() => { alertService.registerConsumer(consume); return () => { alertService.unregisterConsumer(consume); }; }, [consume]); return (
{alerts.map((alert) => { return ( ); })}
); }; export default AlertHost;