production-taskbar-client / src / renderer / features / informing / InformingPage.jsx
InformingPage.jsx
Raw
/* eslint-disable react/no-danger */
import "./InformingPage.less";

import { ipcRenderer } from "electron";
import React, { useCallback, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import { Button } from "antd";
import _ from "lodash";

import {
  clearNotification,
  newNotification,
  decrementCloseTimer,
} from "./informingSlice";
import useInformingWebsocket from "./useInformingWebsocket";
import { useSendInformingMutation } from "../../apis/backendWs";

const isDev = process.env.NODE_ENV === "development";

const kInformingChannel = "informing";
const kInputEventChannel = "input-event";

export default function InformingPage() {
  const dispatch = useDispatch();
  const { t } = useTranslation();
  const { data } = useInformingWebsocket();
  const [sendInforming, { data: sendData }] = useSendInformingMutation();

  const nId = useSelector((state) => state.informing.notificationId);
  const content = useSelector((state) => state.informing.content);
  const closeDelay = useSelector((state) => state.informing.closeDelay);
  const closeTimer = useSelector((state) => state.informing.closeTimer);
  const isOverlay = useSelector((state) => state.informing.isOverlay);
  const title = useSelector((state) => state.informing.title);
  const needConfirmation = useSelector(
    (state) => state.informing.needConfirmation
  );

  useEffect(() => {
    if (!_.isEmpty(data)) {
      dispatch(newNotification(data));
    }
  }, [dispatch, data]);

  const informingHandler = useCallback(
    (_e, { event }) => {
      if (event === "hidden") dispatch(clearNotification());
    },
    [dispatch]
  );

  useEffect(() => {
    document.title = "";
    ipcRenderer.on(kInformingChannel, informingHandler);
    return function cleanup() {
      ipcRenderer.removeListener(kInputEventChannel, informingHandler);
    };
  }, [informingHandler]);

  const inputHandler = useCallback(
    (_event, userId) => {
      if (userId && nId) {
        sendInforming({
          type: "notification.confirm",
          notification_id: nId,
          user_id: userId,
        });
      }
    },
    [sendInforming, nId]
  );

  useEffect(() => {
    ipcRenderer.invoke(kInformingChannel, {
      event: "set-closable",
      data: !needConfirmation || !isOverlay,
    });
    if (needConfirmation) ipcRenderer.on(kInputEventChannel, inputHandler);
    return function cleanup() {
      if (needConfirmation)
        ipcRenderer.removeListener(kInputEventChannel, inputHandler);
    };
  }, [inputHandler, needConfirmation, isOverlay]);

  useEffect(() => {
    if (title) document.title = title;
    else document.title = t("informing.defaultTitle");
  }, [t, title]);

  useEffect(() => {
    if (sendData) {
      sendInforming(null); // reset sendData state
      dispatch(clearNotification());
    }
  }, [dispatch, sendData, sendInforming]);

  useEffect(() => {
    let closeTimeout;
    let closeInterval;
    if (!content) window.close();
    else {
      // show window
      ipcRenderer.invoke(kInformingChannel, {
        event: "show",
        data: { isOverlay },
      });
      // close timeout
      closeTimeout = setTimeout(
        () => dispatch(clearNotification()),
        closeDelay * 60 * 1000
      );
      // close timeout
      closeInterval = setInterval(
        () => dispatch(decrementCloseTimer(1)),
        60 * 1000
      );
    }
    return function cleanup() {
      clearTimeout(closeTimeout);
      clearInterval(closeInterval);
    };
  }, [dispatch, content, closeDelay, isOverlay]);

  return (
    <div
      className={
        isOverlay ? "informing-page overflow-hidden" : "informing-page"
      }
    >
      {content ? (
        <>
          <div dangerouslySetInnerHTML={{ __html: content }} />
          <div className="informing-page__footer">
            {(() => {
              if (isOverlay)
                return (
                  <div className="informing-page__close-timer">
                    {t("informing.closeTimer", { closeTimer })}
                  </div>
                );
              if (needConfirmation)
                return (
                  <Button
                    key="informing-page-close"
                    className="informing-page__close-button"
                    disabled
                  >
                    {typeof sendData === "boolean" && !sendData
                      ? t("informing.closeConfirmButtonError")
                      : t("informing.closeConfirmButton")}
                  </Button>
                );
              return (
                <Button
                  key="informing-page-close"
                  className="informing-page__close-button"
                  onClick={() => dispatch(clearNotification())}
                >
                  {t("informing.closeButton")}
                </Button>
              );
            })()}
          </div>
        </>
      ) : null}
      {isDev && !isOverlay ? (
        <Button
          key="informing-page-devtools"
          className="devtools-button"
          onClick={() =>
            ipcRenderer.invoke(kInformingChannel, {
              event: "open-devtools",
            })
          }
        >
          Devtools
        </Button>
      ) : null}
    </div>
  );
}