Newer
Older
taehui / qwilight-fe / src / app / [language] / site / components / SiteYellItem.tsx
@Taehui Taehui on 18 Mar 12 KB 2024-03-18 오전 9:54
import scss from "@/app/[language]/site/components/SiteYellItem.module.scss";
import wsAPI from "@/app/[language]/site/lib/wsAPI";
import {
  AbilitySiteYell,
  CommentSiteYell,
  InviteSiteYell,
  LevelSiteYell,
  OnSiteYellInput,
  SiteYell,
  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 { useSiteStore } from "@/state/Stores";
import {
  formatText,
  getAbilityUpText,
  getGenreText,
  getHitPointsClass,
} 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 { Col, Row } from "reactstrap";

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

export default observer(
  ({
    data,
    onSiteYellInput,
  }: {
    data: SiteYell;
    onSiteYellInput: OnSiteYellInput;
  }) => {
    const { siteYellVariety, date, avatarID, avatarName, siteYell } = data;
    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.file}" 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.file}" src="${t}" alt="">
            </a>`,
          );
        }

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

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

      return siteYell;
    };

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