import { useEffect, useMemo, useState } from "react"; import { Badge, Col, Row } from "reactstrap"; import { sanitize } from "dompurify"; import { useTranslation } from "react-i18next"; import { sprintf } from "sprintf-js"; import { getDatetime } from "taehui-ts/date"; import { getHitTexts, tag } from "src/Utility"; import { wwwAXIOS } from "src/Www"; import AvatarTitle from "src/AvatarTitle"; import scss from "src/forum/TextView.module.scss"; const TextView = ({ forumTitle, title, text, avatarID, avatarName, date, hitCount, }: { forumTitle: string; title: string; text: string; avatarID: string; avatarName: string; date?: string; hitCount?: number; }) => { const hitTexts = useMemo(() => getHitTexts(text), [text]); const [textHTMLContents, setTextHTMLContents] = useState(""); const { t } = useTranslation(); useEffect(() => { (async () => { setTextHTMLContents( sanitize(tag(t, text, {}, scss.essay), { ADD_ATTR: ["target"], }), ); const hits = ( await Promise.all( hitTexts.map(async (hitText) => wwwAXIOS.get<number>(`/file/${hitText}/hit`), ), ) ) .filter(({ status }) => status === 200) .map(({ data }) => data); setTextHTMLContents( sanitize( tag( t, text, Object.fromEntries( hitTexts.map((hitText, i) => [hitText, hits[i]]), ), scss.essay, ), { ADD_ATTR: ["target"], }, ), ); })(); }, [hitTexts, t, text]); return ( <> <Row className="g-0"> <Col className="m-1"> <Badge>{forumTitle}</Badge> </Col> </Row> <Row className="g-0"> <AvatarTitle avatarID={avatarID} avatarName={avatarName}> <span>{title}</span> </AvatarTitle> <Col className="m-1" xs="auto"> {hitCount !== undefined && ( <> <span>{getDatetime(date)}</span> <br /> <span>{sprintf(t("hitCount"), hitCount)}</span> </> )} </Col> </Row> <Row className="g-0"> <Col className="m-1"> <span className="ln" dangerouslySetInnerHTML={{ __html: textHTMLContents, }} /> </Col> </Row> </> ); }; export default TextView;