aboutsummaryrefslogtreecommitdiff
path: root/Timeline/ClientApp/src/app/common/AlertHost.tsx
blob: e22354fa871067c97ee3e9d77333606cd4e28c4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import React, { useCallback } from "react";
import { Alert } from "reactstrap";
import without from "lodash/without";
import concat from "lodash/concat";

import {
  alertService,
  AlertInfoEx,
  kAlertHostId,
  AlertInfo,
} from "./alert-service";
import { useTranslation } from "react-i18next";

interface AutoCloseAlertProps {
  alert: AlertInfo;
  close: () => void;
}

export const AutoCloseAlert: React.FC<AutoCloseAlertProps> = (props) => {
  const { alert } = props;
  const { dismissTime } = alert;

  const { t } = useTranslation();

  React.useEffect(() => {
    const tag =
      dismissTime === "never"
        ? null
        : typeof dismissTime === "number"
        ? window.setTimeout(props.close, dismissTime)
        : window.setTimeout(props.close, 5000);
    return () => {
      if (tag != null) {
        window.clearTimeout(tag);
      }
    };
  }, [dismissTime, props.close]);

  return (
    <Alert className="m-3" color={alert.type ?? "primary"} toggle={props.close}>
      {(() => {
        const { message } = alert;
        if (typeof message === "function") {
          const Message = message;
          return <Message />;
        } else if (typeof message === "object" && message.type === "i18n") {
          return t(message.key);
        } else return alert.message;
      })()}
    </Alert>
  );
};

// oh what a bad name!
interface AlertInfoExEx extends AlertInfoEx {
  close: () => void;
}

export const AlertHost: React.FC = () => {
  const [alerts, setAlerts] = React.useState<AlertInfoExEx[]>([]);

  // 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 (
    <div id={kAlertHostId} className="alert-container">
      {alerts.map((alert) => {
        return (
          <AutoCloseAlert key={alert.id} alert={alert} close={alert.close} />
        );
      })}
    </div>
  );
};

export default AlertHost;