diff options
author | crupest <crupest@outlook.com> | 2020-10-27 19:21:35 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-10-27 19:21:35 +0800 |
commit | ac769e656b122ff569c3f1534701b71e00fed586 (patch) | |
tree | 72966645ff1e25139d3995262e1c4349f2c14733 /FrontEnd/src/app/views/common/alert | |
parent | 14e5848c23c643cea9b5d709770747d98c3d75e2 (diff) | |
download | timeline-ac769e656b122ff569c3f1534701b71e00fed586.tar.gz timeline-ac769e656b122ff569c3f1534701b71e00fed586.tar.bz2 timeline-ac769e656b122ff569c3f1534701b71e00fed586.zip |
Split front and back end.
Diffstat (limited to 'FrontEnd/src/app/views/common/alert')
-rw-r--r-- | FrontEnd/src/app/views/common/alert/AlertHost.tsx | 101 | ||||
-rw-r--r-- | FrontEnd/src/app/views/common/alert/alert.sass | 15 |
2 files changed, 116 insertions, 0 deletions
diff --git a/FrontEnd/src/app/views/common/alert/AlertHost.tsx b/FrontEnd/src/app/views/common/alert/AlertHost.tsx new file mode 100644 index 00000000..c74f18e2 --- /dev/null +++ b/FrontEnd/src/app/views/common/alert/AlertHost.tsx @@ -0,0 +1,101 @@ +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<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" + variant={alert.type ?? "primary"} + onClose={props.close} + dismissible + > + {(() => { + 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; +} + +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; diff --git a/FrontEnd/src/app/views/common/alert/alert.sass b/FrontEnd/src/app/views/common/alert/alert.sass new file mode 100644 index 00000000..c3560b87 --- /dev/null +++ b/FrontEnd/src/app/views/common/alert/alert.sass @@ -0,0 +1,15 @@ +.alert-container
+ position: fixed
+ z-index: $zindex-popover
+
+@include media-breakpoint-up(sm)
+ .alert-container
+ bottom: 0
+ right: 0
+
+@include media-breakpoint-down(sm)
+ .alert-container
+ bottom: 0
+ right: 0
+ left: 0
+ text-align: center
|