import React from "react"; import classnames from "classnames"; import { useTranslation } from "react-i18next"; import { UiLogicError } from "@/common"; import { getHttpTimelineClient, HttpTimelineInfo, HttpTimelinePostInfo, HttpTimelinePostPostRequestData, } from "@/http/timeline"; import { pushAlert } from "@/services/alert"; import { base64 } from "@/http/common"; import BlobImage from "../common/BlobImage"; import LoadingButton from "../common/button/LoadingButton"; import { PopupMenu } from "../common/Menu"; import Card from "../common/Card"; import MarkdownPostEdit from "./MarkdownPostEdit"; import TimelineLine from "./TimelineLine"; import "./TimelinePostEdit.css"; interface TimelinePostEditTextProps { text: string; disabled: boolean; onChange: (text: string) => void; className?: string; style?: React.CSSProperties; } const TimelinePostEditText: React.FC = (props) => { const { text, disabled, onChange, className, style } = props; return ( { onChange(event.target.value); }} className={className} style={style} /> ); }; interface TimelinePostEditImageProps { onSelect: (file: File | null) => void; disabled: boolean; } const TimelinePostEditImage: React.FC = (props) => { const { onSelect, disabled } = props; const { t } = useTranslation(); const [file, setFile] = React.useState(null); const [error, setError] = React.useState(false); const onInputChange: React.ChangeEventHandler = (e) => { setError(false); const files = e.target.files; if (files == null || files.length === 0) { setFile(null); onSelect(null); } else { setFile(files[0]); } }; React.useEffect(() => { return () => { onSelect(null); }; }, [onSelect]); return ( <> {file != null && !error && ( onSelect(file)} onError={() => { onSelect(null); setError(true); }} /> )} {error ?
{t("loadImageError")}
: null} ); }; type PostKind = "text" | "markdown" | "image"; const postKindIconClassNameMap: Record = { text: "bi-fonts", markdown: "bi-markdown", image: "bi-image", }; export interface TimelinePostEditProps { className?: string; style?: React.CSSProperties; timeline: HttpTimelineInfo; onPosted: (newPost: HttpTimelinePostInfo) => void; } const TimelinePostEdit: React.FC = (props) => { const { timeline, style, className, onPosted } = props; const { t } = useTranslation(); const [process, setProcess] = React.useState(false); const [kind, setKind] = React.useState>("text"); const [showMarkdown, setShowMarkdown] = React.useState(false); const [text, setText] = React.useState(""); const [image, setImage] = React.useState(null); const draftTextLocalStorageKey = `timeline.${timeline.name}.postDraft.text`; React.useEffect(() => { setText(window.localStorage.getItem(draftTextLocalStorageKey) ?? ""); }, [draftTextLocalStorageKey]); const canSend = (kind === "text" && text.length !== 0) || (kind === "image" && image != null); const onPostError = (): void => { pushAlert({ type: "danger", message: "timeline.sendPostFailed", }); }; const onSend = async (): Promise => { setProcess(true); let requestData: HttpTimelinePostPostRequestData; switch (kind) { case "text": requestData = { contentType: "text/plain", data: await base64(text), }; break; case "image": if (image == null) { throw new UiLogicError( "Content type is image but image blob is null." ); } requestData = { contentType: image.type, data: await base64(image), }; break; default: throw new UiLogicError("Unknown content type."); } getHttpTimelineClient() .postPost(timeline.name, { dataList: [requestData], }) .then( (data) => { if (kind === "text") { setText(""); window.localStorage.removeItem(draftTextLocalStorageKey); } setProcess(false); setKind("text"); onPosted(data); }, (_) => { setProcess(false); onPostError(); } ); }; return (
{showMarkdown ? ( setShowMarkdown(false)} timeline={timeline.name} onPosted={onPosted} onPostError={onPostError} /> ) : (
{(() => { if (kind === "text") { return ( { setText(t); window.localStorage.setItem( draftTextLocalStorageKey, t ); }} /> ); } else if (kind === "image") { return ( ); } })()}
({ type: "button", text: `timeline.post.type.${kind}`, iconClassName: postKindIconClassNameMap[kind], onClick: () => { if (kind === "markdown") { setShowMarkdown(true); } else { setKind(kind); } }, }) )} >
{t("timeline.send")}
)}
); }; export default TimelinePostEdit;