Newer
Older
taehui / qwilight-fe / src / app / [language] / note / components / NoteView.tsx
@Taehui Taehui on 7 Jul 6 KB v1.0.0
import CommentView from "@/app/[language]/note/components/CommentView";
import useGetComment from "@/app/[language]/note/query/useGetComment";
import usePostToil from "@/app/[language]/note/query/usePostToil";
import w0 from "@/assets/w0.png";
import w1 from "@/assets/w1.png";
import w2 from "@/assets/w2.png";
import w4 from "@/assets/w4.png";
import w5 from "@/assets/w5.png";
import w6 from "@/assets/w6.png";
import w7 from "@/assets/w7.png";
import w8 from "@/assets/w8.png";
import AvatarPlaceText from "@/components/AvatarPlaceText";
import LevelText from "@/components/LevelText";
import { GetNoteAPI } from "@/type/wwwAPI";
import {
  formatText,
  getDefaultAvatarID,
  getGenreText,
} from "@/utilities/Utility";
import { useTranslations } from "next-intl";
import Image from "next/image";
import { useMemo, useState } from "react";
import { Placeholder } from "react-bootstrap";
import Collapse from "react-bootstrap/Collapse";
import Col from "react-bootstrap/esm/Col";
import Row from "react-bootstrap/Row";
import Spinner from "react-bootstrap/Spinner";
import Stack from "react-bootstrap/Stack";
import { Item, Menu, useContextMenu } from "react-contexify";
import Swal from "sweetalert2";
import { useSiteStore } from "@/state/Stores";

const ws = [w0, w1, w2, "", w4, w5, w6, w7, w8];

export default function NoteView({
  noteID,
  artist,
  title,
  genre,
  levelText,
  level,
  topCount,
  totalCount,
  fittedText,
  wantAvatarID,
  wantAvatarName,
  handled,
}: GetNoteAPI["notes"][number] & {
  fittedText?: string;
  wantAvatarID?: string;
  wantAvatarName?: string;
  handled?: number;
}) {
  const t = useTranslations();

  const [isCommentOpened, setCommentOpened] = useState(false);

  const { show: viewToilInput } = useContextMenu({
    id: `toil-${noteID}`,
  });

  const { mutateAsync: postToil } = usePostToil();

  const { siteAvatarID } = useSiteStore();
  const {
    data: { comments, totalComments },
    isFetched: isCommentLoaded,
  } = useGetComment(noteID, isCommentOpened, wantAvatarID);

  const avatarPlaceMe = useMemo(() => {
    const avatarID = getDefaultAvatarID(siteAvatarID);
    return comments.find((comment) => comment.avatarID == avatarID);
  }, [comments, siteAvatarID])?.avatarPlace;

  return (
    <>
      <Stack gap={2}>
        <Row
          className="route flex-nowrap"
          onClick={() => {
            setCommentOpened((prevState) => !prevState);
          }}
          onContextMenu={(event) => {
            event.preventDefault();
            viewToilInput({ event, props: { noteID } });
          }}
        >
          <Col className="cc">
            <Stack gap={2}>
              <Stack gap={2} direction="horizontal">
                <LevelText
                  level={level}
                  levelText={levelText}
                  className="ellipsis"
                />
                <span className="ellipsis">{title}</span>
                {isCommentOpened && !isCommentLoaded && <Spinner size="sm" />}
              </Stack>
              <Stack gap={2} direction="horizontal">
                <span className="ellipsis artist">{artist}</span>
                <span className="ellipsis genre">{getGenreText(genre)}</span>
              </Stack>
              {typeof topCount === "number" && (
                <span className="ellipsis fittedText">
                  {t("topCountText", {
                    topCount: formatText(topCount),
                  })}
                </span>
              )}
              {typeof totalCount === "number" && (
                <span className="ellipsis fittedText">
                  {t("totalCountText", {
                    totalCount: formatText(totalCount),
                  })}
                </span>
              )}
              {fittedText && (
                <span className="ellipsis fittedText">{fittedText}</span>
              )}
            </Stack>
          </Col>
          {typeof handled === "number" && (
            <Col xs="auto">
              <Image src={ws[handled]} width={10} height={60} alt="" />
            </Col>
          )}
        </Row>
        <Row>
          <Collapse in={isCommentOpened}>
            <Stack gap={2}>
              {typeof avatarPlaceMe === "number" && (
                <AvatarPlaceText
                  avatarPlace={avatarPlaceMe + 1}
                  avatarCount={totalComments}
                />
              )}
              {comments.map(
                ({
                  date,
                  avatarID,
                  avatarName,
                  stand,
                  band,
                  point,
                  commentary,
                  isBand1,
                  judgmentMode,
                  hitPointsMode,
                  isPaused,
                  handled,
                  avatarPlace,
                }) => (
                  <CommentView
                    key={avatarID}
                    date={date}
                    avatarID={avatarID}
                    avatarName={avatarName}
                    stand={stand}
                    band={band}
                    point={point}
                    isBand1={isBand1}
                    commentary={commentary}
                    isTargetAvatar={
                      !!(wantAvatarID && wantAvatarID === avatarID) ||
                      !!(
                        wantAvatarName &&
                        avatarName
                          .toLowerCase()
                          .includes(wantAvatarName.toLowerCase())
                      )
                    }
                    judgmentMode={judgmentMode}
                    hitPointsMode={hitPointsMode}
                    isPaused={isPaused}
                    handled={handled}
                    avatarPlace={avatarPlace + 1}
                  />
                ),
              )}
            </Stack>
          </Collapse>
        </Row>
      </Stack>

      <Menu id={`toil-${noteID}`}>
        <Item
          onClick={async ({ props: { noteID } }) => {
            const { isConfirmed, value } = await Swal.fire({
              title: t("toilNoteFileText"),
              input: "text",
            });
            if (isConfirmed) {
              await postToil({ noteID, commentary: value });
            }
          }}
        >
          {t("toilNoteFile")}
        </Item>
      </Menu>
    </>
  );
}

export const NoteViewLoading = () => {
  return (
    <Stack gap={2}>
      <Placeholder xs={12} size="lg" bg="secondary" animation="wave" />
      <Placeholder xs={6} size="lg" bg="secondary" animation="wave" />
    </Stack>
  );
};