Newer
Older
taehui / taehui-fe / src / forum / PostEssayView.tsx
@Taehui Taehui on 13 Mar 12 KB v1.0.0
import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
import {
  Button,
  Col,
  Collapse,
  Input,
  ListGroup,
  Modal,
  ModalBody,
  Row,
  TabContent,
  TabPane,
} from "reactstrap";
import { toast } from "react-toastify";
import { observer } from "mobx-react-lite";
import { useTranslation } from "react-i18next";
import { useTo, useWindowArea } from "taehui-ts/fe-utility";

import { useAvatarStore, useForumStore, useTaehuiStore } from "src/Stores";
import TextView from "src/forum/TextView";
import AutoEssayTitleView from "src/forum//AutoEssayTitleView";
import { AutoEssayViewLoading } from "src/Loading";
import useGetAutoEssay from "src/forum/useGetAutoEssay";
import usePostAutoEssay from "src/forum/usePostAutoEssay";
import usePutAutoEssay from "src/forum/usePutAutoEssay";
import withTotem from "src/withTotem";
import usePutEssay from "src/forum/usePutEssay";
import usePostFile from "src/forum/usePostFile";
import useGetEssay from "src/forum/useGetEssay";
import usePostEssay from "src/forum/usePostEssay";

export default withTotem(
  observer(() => {
    const {
      isTitleTextFilled,
      title,
      text,
      setTitle,
      setText,
      autoEssayID,
      setAutoEssayID,
    } = useForumStore();
    const { titleViewHeight, avatarViewHeight } = useTaehuiStore();
    const { taehuiAvatarID, taehuiAvatarName } = useAvatarStore();

    const { t } = useTranslation();
    const [isEdit, setEdit] = useState(true);
    const [testText, setTestText] = useState("");
    const [textViewHeight, setTextViewHeight] = useState(100);
    const [isAutoEssayOpened, setAutoEssayOpened] = useState(false);

    type PostEssayViewParams = {
      forumID: string;
      essayID: string | undefined;
    };
    const { forumID, essayID } =
      useParams<PostEssayViewParams>() as PostEssayViewParams;

    const { isFetched: isAutoEssayLoaded, data: autoEssay } = useGetAutoEssay(
      forumID,
      isAutoEssayOpened,
    );

    const editView = useRef<HTMLDivElement>(null);
    const textView = useRef<HTMLTextAreaElement>(null);
    const inputView = useRef<HTMLDivElement>(null);

    const { mutateAsync: postAutoEssay } = usePostAutoEssay();
    const { mutateAsync: putAutoEssay } = usePutAutoEssay();
    const { mutateAsync: putEssay } = usePutEssay();
    const { isPending, mutateAsync: postFile } = usePostFile();
    const { mutateAsync: postEssay } = usePostEssay();

    const {
      data: essay,
      data: { forumTitle },
      isFetched: isEssayLoaded,
    } = useGetEssay(essayID);

    useEffect(() => {
      if (isEssayLoaded) {
        setTitle(essay.title);
        setText(essay.text);
      }
    }, [essay, isEssayLoaded, setText, setTitle]);

    const { windowHeight } = useWindowArea();

    const to = useTo();

    const setTag = (tag: string) => {
      const { current } = textView;
      if (current) {
        const { selectionStart, selectionEnd } = current;
        const t = text.substring(selectionStart, selectionEnd);
        const tag0 = "<" + tag + ">";
        const tag1 = "</" + tag + ">";
        if (t.startsWith(tag0) || t.endsWith(tag1)) {
          setText(
            text.substring(0, selectionStart) +
              text.substring(
                selectionStart + tag0.length,
                selectionStart + t.length - tag1.length,
              ) +
              text.substring(selectionEnd),
          );
          setTimeout(() => {
            current.selectionStart = selectionStart;
            current.selectionEnd = selectionEnd - tag0.length - tag1.length;
            current.focus();
          }, 0);
        } else {
          setText(
            text.substring(0, selectionStart) +
              tag0 +
              text.substring(selectionStart, selectionEnd) +
              tag1 +
              text.substring(selectionEnd),
          );
          setTimeout(() => {
            current.selectionStart = selectionStart;
            current.selectionEnd = selectionEnd + tag0.length + tag1.length;
            current.focus();
          }, 0);
        }
      }
    };

    useEffect(() => {
      setTextViewHeight(
        windowHeight -
          titleViewHeight -
          avatarViewHeight -
          (editView.current?.clientHeight ?? 0) -
          (inputView.current?.clientHeight ?? 0),
      );
    }, [avatarViewHeight, titleViewHeight, windowHeight]);

    useEffect(() => {
      if (!essayID) {
        setTitle("");
        setText("");
      }
    }, [essayID, setText, setTitle]);

    useEffect(() => {
      const postAutoEssaysID = setInterval(async () => {
        if (isTitleTextFilled) {
          if (autoEssayID === undefined) {
            const { autoEssayID } = await postAutoEssay({
              forumID,
              title,
              text,
            });
            setAutoEssayID(autoEssayID);
          } else {
            await putAutoEssay({ autoEssayID, title, text });
          }
        }
      }, 60000);

      return () => {
        clearInterval(postAutoEssaysID);
      };
    }, [
      autoEssayID,
      forumID,
      isTitleTextFilled,
      postAutoEssay,
      putAutoEssay,
      setAutoEssayID,
      t,
      text,
      title,
    ]);

    useLayoutEffect(() => {
      if (!isEdit) {
        setTestText(text);
      }
    }, [isEdit, text]);

    return (
      <>
        {isEdit && (
          <div ref={editView}>
            <Row className="g-0">
              <Col className="m-1">
                <Input
                  invalid={!title}
                  valid={!!title}
                  placeholder={t("title")}
                  value={title}
                  onChange={({ target: { value } }) => {
                    setTitle(value);
                  }}
                />
              </Col>
            </Row>
            <Row className="g-0">
              <Col className="m-1" xs="auto">
                <Button
                  onClick={() => {
                    const inputElement = document.createElement("input");
                    inputElement.type = "file";
                    inputElement.accept = "audio/*,image/*,video/*";
                    inputElement.addEventListener(
                      "change",
                      async ({ target }) => {
                        const file = (target as HTMLInputElement).files?.[0];
                        const { current } = textView;
                        if (file && current) {
                          const text = await postFile({
                            file,
                            textView: current,
                          });
                          if (text) {
                            setText(text);
                          }
                        }
                      },
                    );
                    inputElement.click();
                  }}
                  color="info"
                >
                  {t("fileUpload")}
                </Button>
              </Col>
              <Col className="m-1" xs="auto">
                <Button
                  onClick={() => {
                    setAutoEssayOpened((prevState) => !prevState);
                  }}
                  color={isAutoEssayOpened ? "secondary" : "primary"}
                >
                  {t("autoEssays")}
                </Button>
              </Col>
            </Row>
            <Row className="g-0">
              <Col className="m-1">
                <Collapse isOpen={isAutoEssayOpened}>
                  {isAutoEssayLoaded ? (
                    <ListGroup>
                      {autoEssay.map((autoEssay) => (
                        <AutoEssayTitleView
                          key={autoEssay.autoEssayID}
                          autoEssay={autoEssay}
                        />
                      ))}
                    </ListGroup>
                  ) : (
                    <AutoEssayViewLoading />
                  )}
                </Collapse>
              </Col>
            </Row>
          </div>
        )}
        <Row className="g-0">
          <Col className="m-1">
            <TabContent activeTab={isEdit ? 1 : 2}>
              <TabPane tabId={1}>
                <Input
                  type="textarea"
                  className="form-control"
                  placeholder={t("text")}
                  value={text}
                  onChange={({ target: { value } }) => {
                    setText(value);
                  }}
                  innerRef={textView}
                  style={{ height: textViewHeight }}
                />
              </TabPane>
              <TabPane tabId={2}>
                <TextView
                  forumTitle={forumTitle}
                  title={title}
                  text={testText}
                  avatarID={taehuiAvatarID}
                  avatarName={taehuiAvatarName}
                />
              </TabPane>
            </TabContent>
          </Col>
        </Row>
        <div ref={inputView}>
          <Row className="g-0">
            <Col className="m-1" xs="auto">
              <Button
                onClick={() => {
                  setTag("strong");
                }}
                color="info"
              >
                <strong>{t("textTag")}</strong>
              </Button>
            </Col>
            <Col className="m-1" xs="auto">
              <Button
                onClick={() => {
                  setTag("i");
                }}
                color="info"
              >
                <i>{t("textTag")}</i>
              </Button>
            </Col>
            <Col className="m-1" xs="auto">
              <Button
                onClick={() => {
                  setTag("u");
                }}
                color="info"
              >
                <u>{t("textTag")}</u>
              </Button>
            </Col>
            <Col className="m-1" xs="auto">
              <Button
                onClick={() => {
                  setTag("s");
                }}
                color="info"
              >
                <s>{t("textTag")}</s>
              </Button>
            </Col>
          </Row>
          <Row className="g-0">
            <Col className="m-1">
              <Button
                color={isEdit ? "secondary" : "primary"}
                onClick={() => {
                  setEdit((prevState) => !prevState);
                }}
              >
                {t("viewEditedEssay")}
              </Button>
            </Col>
            {essayID ? (
              <Col className="m-1" xs="auto">
                <Button
                  color="warning"
                  onClick={async () => {
                    if (title && text) {
                      await putEssay({ essayID, title, text });
                      to(`/forum/${forumID}/${essayID}`);
                    } else {
                      toast.error(t("failedValidation"));
                    }
                  }}
                >
                  {t("doModifyEssay")}
                </Button>
              </Col>
            ) : (
              <Col className="m-1" xs="auto">
                <Button
                  color="success"
                  onClick={async () => {
                    if (title && text) {
                      await postEssay({
                        forumID,
                        title,
                        text,
                      });
                      to(`/forum/${forumID}/${essayID}`);
                    } else {
                      toast.error(t("failedValidation"));
                    }
                  }}
                >
                  {t("postEssay")}
                </Button>
              </Col>
            )}
            <Col className="m-1" xs="auto">
              <Button
                color="danger"
                onClick={() => {
                  to(`/forum/${forumID}`);
                }}
              >
                {t("quit")}
              </Button>
            </Col>
          </Row>
        </div>
        <Modal isOpen={isPending}>
          <ModalBody>
            <span>{t("fileUploading")}</span>
          </ModalBody>
        </Modal>
      </>
    );
  }),
);