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
  | 
