Newer
Older
taehui / qwilight-fe / src / app / [language] / site / components / SiteYellView.tsx
@taehui taehui on 21 Sep 14 KB v1.0.0
import scss from "@/app/[language]/site/components/SiteYellView.module.scss";
import wsAPI from "@/app/[language]/site/lib/wsAPI";
import {
  AbilitySiteYell,
  CommentSiteYell,
  InviteSiteYell,
  LevelSiteYell,
  OnSiteYellInput,
  SiteYellItem,
  TVSiteYell,
} from "@/app/[language]/site/type";
import tv0 from "@/assets/tv0.svg";
import tv1 from "@/assets/tv1.png";
import AvatarDrawing from "@/components/AvatarDrawing";
import AvatarTitle from "@/components/AvatarTitle";
import HitPointsModeText from "@/components/HitPointsModeText";
import LevelText from "@/components/LevelText";

import { useSiteStore } from "@/state/Stores";
import {
  formatText,
  getAbilityUpText,
  getGenreText,
} from "@/utilities/Utility";
import { sanitize } from "isomorphic-dompurify";
import { observer } from "mobx-react-lite";
import { useTranslations } from "next-intl";
import Image from "next/image";
import Link from "next/link";
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
import Stack from "react-bootstrap/Stack";

const EventPB = require("@/Event_pb");

export default observer(
  ({
    siteYellItem,
    onSiteYellInput,
  }: {
    siteYellItem: SiteYellItem;
    onSiteYellInput: OnSiteYellInput;
  }) => {
    const { siteYellVariety, date, avatarID, avatarName, siteYell } =
      siteYellItem;
    const { saveTraffic } = useSiteStore();
    const t = useTranslations();

    const doSiteYell = (siteYell: string) => {
      siteYell = siteYell.replace(" ", " ");
      const m = siteYell.match(/(http|https|mailto|rtmp):\/\/[^ ]+/i);
      if (m != null) {
        const t = m[0];

        if (
          !saveTraffic &&
          t.match(/(\.aac|\.flac|\.mid|\.midi|\.mp3|\.ogg|\.wav|\.wma)($|\?)/i)
        ) {
          return siteYell.replace(
            t,
            `<audio class="${scss.siteYell}" src="${t}" controls></audio>`,
          );
        }

        if (
          !saveTraffic &&
          t.match(/(\.bmp|\.gif|\.heic|\.jpeg|\.jpg|\.png|\.webp)($|\?)/i)
        ) {
          return siteYell.replace(
            t,
            `<a href="${t}" target="_blank">
              <img class="${scss.siteYell} rounded border" src="${t}" alt="">
            </a>`,
          );
        }

        if (
          !saveTraffic &&
          t.match(/(\.avi|\.mkv|\.mp4|\.mpeg|\.mpg|\.wmv)($|\?)/i)
        ) {
          return siteYell.replace(
            t,
            `<video class="${scss.siteYell}" src="${t}" controls></video>`,
          );
        }

        return siteYell.replace(t, `<a href="${t}" target="_blank">${t}</a>`);
      }

      return siteYell;
    };

    switch (siteYellVariety) {
      case "@Enter":
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className={`cc ${scss.enter}`}>{t("siteYellEnter")}</span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      case "@Quit":
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className={`cc ${scss.quit}`}>{t("siteYellQuit")}</span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      case "@Site":
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className={`cc ${scss.enter}`}>
                  {t("siteYellNewSite")}
                </span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      case "@Net":
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className={`cc ${scss.enter}`}>
                  {t("siteYellNewNetSite")}
                </span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      case "@Notify":
        return (
          <Row className="flex-nowrap">
            <Col className="cc">
              <Stack gap={2}>
                <span>{t("siteYellTaehui")}</span>
                <span
                  dangerouslySetInnerHTML={{
                    __html: doSiteYell(siteYell as string),
                  }}
                />
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      case "@Invite": {
        const { siteID, siteName, avatarName } = siteYell as InviteSiteYell;
        return (
          <Row
            className="rc route"
            onClick={() => {
              wsAPI.send({
                eventID: EventPB.Event.EventID.ENTER_SITE,
                text: JSON.stringify({
                  siteID,
                  siteCipher: "",
                }),
              });
            }}
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className="cc">{t("siteYellInvite", { siteName })}</span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      }
      case "@TV":
        const { href, title, text } = siteYell as TVSiteYell;
        return (
          <Row
            className="flex-nowrap"
            as={Link}
            href={href}
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            <Col xs="auto">
              {href.startsWith("https://www.twitch.tv") && (
                <Image src={tv0} alt="" width={60} height={60} />
              )}
              {href.startsWith("https://chzzk.naver.com") && (
                <Image src={tv1} alt="" width={60} height={60} />
              )}
            </Col>
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={text}
                  className="cc"
                />
                <span className="cc">{t("siteYellTV", { title })}</span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      case "@Wiped":
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className="cc">{t("wipedSiteYell")}</span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      case "@Comment": {
        const {
          avatarID,
          avatarName,
          title,
          artist,
          genre,
          level,
          levelText,
          stand,
          hitPointsMode,
        } = siteYell as CommentSiteYell;
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <Stack gap={2} direction="horizontal">
                  <LevelText
                    level={level}
                    levelText={levelText}
                    className="cc"
                  />
                  <span className="cc">{title}</span>
                </Stack>
                <Stack gap={2} direction="horizontal">
                  <span className="cc artist">{artist}</span>
                  <span className="cc genre">{getGenreText(genre)}</span>
                </Stack>
              </Stack>
            </Col>
            <Col xs="auto">
              <Stack gap={2} className="text-end">
                <span className="date">{date}</span>
                <HitPointsModeText
                  hitPointsMode={hitPointsMode}
                  text={t("textStand", { value: formatText(stand) })}
                />
              </Stack>
            </Col>
          </Row>
        );
      }
      case "@Ability": {
        const { avatarID, avatarName } = siteYell as AbilitySiteYell;
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col>
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className="cc">
                  {getAbilityUpText(siteYell as AbilitySiteYell)}
                </span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      }
      case "@Level": {
        const { avatarID, avatarName, title } = siteYell as LevelSiteYell;
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <AvatarTitle
                  avatarID={avatarID}
                  avatarName={avatarName}
                  className="cc"
                />
                <span className="cc">{t("wwwLevelClearText", { title })}</span>
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      }
      case "":
        return <>{siteYell}</>;
      case null:
        return (
          <Row
            className="flex-nowrap"
            onContextMenu={(e) => {
              e.preventDefault();
              if (avatarID) {
                onSiteYellInput(e, avatarID);
              }
            }}
          >
            {avatarID && (
              <Col xs="auto">
                <AvatarDrawing avatarID={avatarID} />
              </Col>
            )}
            <Col className="cc">
              <Stack gap={2}>
                <Stack gap={2} direction="horizontal">
                  <AvatarTitle
                    avatarID={avatarID}
                    avatarName={avatarName}
                    className="cc"
                  />
                </Stack>
                <span
                  className="cc"
                  dangerouslySetInnerHTML={{
                    __html: doSiteYell(sanitize(siteYell as string)),
                  }}
                />
              </Stack>
            </Col>
            <Col xs="auto">
              <span className="date">{date}</span>
            </Col>
          </Row>
        );
      default:
        return null;
    }
  },
);