import scss from "@/app/[language]/forum/components/TextView.module.scss"; import useGetHit from "@/app/[language]/forum/query/useGetHit"; import AvatarDrawing from "@/components/AvatarDrawing"; import { getHitTexts, tag } from "@/utilities/Utility"; import { sanitize } from "isomorphic-dompurify"; import { useTranslations } from "next-intl"; import { useEffect, useMemo, useState } from "react"; import Col from "react-bootstrap/Col"; import Row from "react-bootstrap/Row"; import Stack from "react-bootstrap/Stack"; import { getDatetime } from "taehui-lib/date"; export default function TextView({ title, text, avatarID, avatarName, date, hitCount, }: { title: string; text: string; avatarID: string; avatarName: string; date?: string; hitCount?: number; }) { const hitTexts = useMemo(() => getHitTexts(text), [text]); const [textHTMLContents, setTextHTMLContents] = useState(""); const t = useTranslations(); const { data: hit, isFetched: isHitLoaded } = useGetHit(hitTexts); useEffect(() => { setTextHTMLContents( sanitize(tag(t, text, {}, scss.essay), { ADD_ATTR: ["target"], }), ); }, [t, text]); useEffect(() => { if (isHitLoaded) { setTextHTMLContents( sanitize( tag( t, text, Object.fromEntries(hitTexts.map((hitText, i) => [hitText, hit[i]])), scss.essay, ), { ADD_ATTR: ["target"], }, ), ); } }, [hit, hitTexts, isHitLoaded, t, text]); return ( <> <Row> <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> <Col> <Stack gap={2}> <span>{avatarName}</span> <span>{title}</span> </Stack> </Col> <Col xs="auto"> <Stack gap={2} className="text-end"> <span>{getDatetime(date)}</span> {typeof hitCount === "number" && ( <span>{t("hitCount", { hitCount })}</span> )} </Stack> </Col> </Row> <hr /> <span style={{ lineBreak: "anywhere", whiteSpace: "break-spaces" }} dangerouslySetInnerHTML={{ __html: textHTMLContents, }} /> </> ); }