import { useEffect, useLayoutEffect, useRef, useState } from "react"; import { useParams } from "react-router-dom"; import { Button, Col, Collapse, Input, ListGroup, Modal, ModalBody, Row, TabContent, TabPane, } from "reactstrap"; import { toast } from "react-toastify"; import { observer } from "mobx-react-lite"; import { useTranslation } from "react-i18next"; import { useTo, useWindowArea } from "taehui-ts/fe-utility"; import { getMillis } from "taehui-ts/date"; import { useAvatarStore, useForumStore, useTaehuiStore } from "src/Stores"; import { wwwAXIOS } from "src/Www"; import TextView from "src/forum/TextView"; import AutoEssayTitleView from "src/forum//AutoEssayTitleView"; import { AutoEssayViewLoading } from "src/Loading"; const PostEssayView = observer(({ mode }: { mode: "w" | "m" }) => { const forumStore = useForumStore(); const { forumTitle, title, text, setTitle, setText, autoEssayID, setAutoEssayID, setAutoEssays, autoEssays, } = forumStore; const { titleViewHeight, avatarViewHeight } = useTaehuiStore(); const { totem, taehuiAvatarID, taehuiAvatarName } = useAvatarStore(); const { t } = useTranslation(); const [isEdit, setEdit] = useState(true); const [testText, setTestText] = useState(""); const [isFileUploading, setFileUploading] = useState(false); const [textViewHeight, setTextViewHeight] = useState(100); const [isAutoEssayOpened, setAutoEssayOpened] = useState(false); const [isAutoEssayViewLoading, setAutoEssayViewLoading] = useState(false); const { forumID, essayID } = useParams<{ forumID: string; essayID: string | undefined; }>(); const editView = useRef<HTMLDivElement>(null); const textView = useRef<HTMLTextAreaElement>(null); const inputView = useRef<HTMLDivElement>(null); const { windowHeight } = useWindowArea(); const to = useTo(); const setTag = (tag: string) => { const { current } = textView; if (current) { const { selectionStart, selectionEnd } = current; const t = text.substring(selectionStart, selectionEnd); const tag0 = "<" + tag + ">"; const tag1 = "</" + tag + ">"; if (t.startsWith(tag0) || t.endsWith(tag1)) { setText( text.substring(0, selectionStart) + text.substring( selectionStart + tag0.length, selectionStart + t.length - tag1.length, ) + text.substring(selectionEnd), ); setTimeout(() => { current.selectionStart = selectionStart; current.selectionEnd = selectionEnd - tag0.length - tag1.length; current.focus(); }, 0); } else { setText( text.substring(0, selectionStart) + tag0 + text.substring(selectionStart, selectionEnd) + tag1 + text.substring(selectionEnd), ); setTimeout(() => { current.selectionStart = selectionStart; current.selectionEnd = selectionEnd + tag0.length + tag1.length; current.focus(); }, 0); } } }; useLayoutEffect(() => { if (!totem) { to(`/forum/${forumID}`); } }, [forumID, to, totem]); useEffect(() => { setTextViewHeight( windowHeight - titleViewHeight - avatarViewHeight - (editView.current?.clientHeight ?? 0) - (inputView.current?.clientHeight ?? 0), ); }, [avatarViewHeight, titleViewHeight, windowHeight]); useEffect(() => { if (mode === "w") { setTitle(""); setText(""); } }, [mode, setText, setTitle]); useEffect(() => { if (isAutoEssayOpened) { setAutoEssayViewLoading(true); (async () => { if (forumID && (await setAutoEssays(forumID, totem))) { setAutoEssayViewLoading(false); } })(); } }, [forumID, setAutoEssays, isAutoEssayOpened, totem]); useEffect(() => { const postAutoEssayID = setInterval(async () => { const { title, text } = forumStore; if (title && text) { if (autoEssayID === undefined) { const { status, data: { autoEssayID }, } = await wwwAXIOS.post( `/autoEssay/${forumID}`, { title, text, }, { headers: { millis: getMillis(), totem, }, }, ); if (status === 201) { toast.success(t("postedAutoEssay")); setAutoEssayID(autoEssayID); } } else { const { status } = await wwwAXIOS.put( `/autoEssay/${autoEssayID}`, { title, text, }, { headers: { millis: getMillis(), totem, }, }, ); if (status === 204) { toast.success(t("postedAutoEssay")); } } } }, 60000); return () => { clearInterval(postAutoEssayID); }; }, [autoEssayID, forumID, forumStore, setAutoEssayID, t, totem]); useLayoutEffect(() => { if (!isEdit) { setTestText(text); } }, [isEdit, text]); return ( <> {isEdit && ( <div ref={editView}> <Row className="g-0"> <Col className="m-1"> <Input invalid={!title} valid={!!title} placeholder={t("title")} value={title} onChange={({ target: { value } }) => { setTitle(value); }} /> </Col> </Row> <Row className="g-0"> <Col className="m-1" xs="auto"> <Button onClick={() => { const inputElement = document.createElement("input"); inputElement.type = "file"; inputElement.accept = "audio/*,image/*,video/*"; inputElement.addEventListener( "change", async ({ target }) => { const file = (target as HTMLInputElement).files?.[0]; if (file) { try { setFileUploading(true); const form = new FormData(); form.append("data", file); const { data, status } = await wwwAXIOS.post( "/file", form, { headers: { millis: getMillis(), totem, }, }, ); if (status === 201) { const { current } = textView; if (current) { const { selectionStart } = current; const textBefore = text.substring( 0, selectionStart, ); const textLater = text.substring( selectionStart, text.length, ); if ( data.fileName.match( /^.*\.(bmp|gif|jpeg|jpg|png|webp)$/i, ) ) { setText( `${textBefore}<img src="${data.fileName}">${textLater}`, ); } else if ( data.fileName.match( /^.*\.(aif|aiff|asf|flac|m4a|mid|midi|mp2|mp3|ogg|opus|raw|wav|wma)$/i, ) ) { setText( `${textBefore}<audio src="${data.fileName}" controls></audio>${textLater}`, ); } else if ( data.fileName.match( /^.*\.(avi|flv|m1v|mkv|mov|mp4|mpeg|mpg|webm|wmv)$/i, ) ) { setText( `${textBefore}<video src="${data.fileName}" controls></video>${textLater}`, ); } else { setText( `${textBefore}<a href="${data.fileName}">${file.name}</a>${textLater}`, ); } } } } finally { setFileUploading(false); } } }, ); inputElement.click(); }} color="info" > {t("fileUpload")} </Button> </Col> <Col className="m-1" xs="auto"> <Button onClick={() => { setAutoEssayOpened((prevState) => !prevState); }} color={isAutoEssayOpened ? "secondary" : "primary"} > {t("autoEssays")} </Button> </Col> </Row> <Collapse isOpen={isAutoEssayOpened}> {isAutoEssayViewLoading ? ( <AutoEssayViewLoading /> ) : ( <ListGroup> {autoEssays.map((autoEssay) => ( <AutoEssayTitleView key={autoEssay.autoEssayID} autoEssay={autoEssay} /> ))} </ListGroup> )} </Collapse> </div> )} <Row className="g-0"> <Col className="m-1"> <TabContent activeTab={isEdit ? 1 : 2}> <TabPane tabId={1}> <Input type="textarea" className="form-control" placeholder={t("text")} value={text} onChange={({ target: { value } }) => { setText(value); }} innerRef={textView} style={{ height: textViewHeight }} /> </TabPane> <TabPane tabId={2}> <TextView forumTitle={forumTitle} title={title} text={testText} avatarID={taehuiAvatarID} avatarName={taehuiAvatarName} /> </TabPane> </TabContent> </Col> </Row> <div ref={inputView}> <Row className="g-0"> <Col className="m-1" xs="auto"> <Button onClick={() => { setTag("strong"); }} color="info" > <strong>{t("textTag")}</strong> </Button> </Col> <Col className="m-1" xs="auto"> <Button onClick={() => { setTag("i"); }} color="info" > <i>{t("textTag")}</i> </Button> </Col> <Col className="m-1" xs="auto"> <Button onClick={() => { setTag("u"); }} color="info" > <u>{t("textTag")}</u> </Button> </Col> <Col className="m-1" xs="auto"> <Button onClick={() => { setTag("s"); }} color="info" > <s>{t("textTag")}</s> </Button> </Col> </Row> <Row className="g-0"> <Col className="m-1"> <Button color={isEdit ? "secondary" : "primary"} onClick={() => { setEdit((prevState) => !prevState); }} > {t("viewEditedEssay")} </Button> </Col> {mode === "w" && ( <Col className="m-1" xs="auto"> <Button color="success" onClick={async () => { if (title && text) { const { status, data } = await wwwAXIOS.post( `/essay/${forumID}`, { title, text, }, { headers: { millis: getMillis(), totem, }, }, ); if (status === 201) { to(`/forum/${forumID}/${data.essayID}`); } } else { toast.error(t("failedValidation")); } }} > {t("postEssay")} </Button> </Col> )} {mode === "m" && ( <Col className="m-1" xs="auto"> <Button color="warning" onClick={async () => { if (title && text) { const { status } = await wwwAXIOS.put( `/essay/${essayID}`, { title, text, }, { headers: { millis: getMillis(), totem, }, }, ); if (status === 204) { to(`/forum/${forumID}/${essayID}`); } } else { toast.error(t("failedValidation")); } }} > {t("doModifyEssay")} </Button> </Col> )} <Col className="m-1" xs="auto"> <Button color="danger" onClick={() => { to(`/forum/${forumID}`); }} > {t("quit")} </Button> </Col> </Row> </div> <Modal isOpen={isFileUploading}> <ModalBody> <span>{t("fileUploading")}</span> </ModalBody> </Modal> </> ); }); export default PostEssayView;