import React from "react"; import clsx from "clsx"; import { useTranslation } from "react-i18next"; import Svg from "react-inlinesvg"; import { Button, Spinner, Row, Col, Form } from "react-bootstrap"; import textIcon from "bootstrap-icons/icons/card-text.svg"; import imageIcon from "bootstrap-icons/icons/image.svg"; import { UiLogicError } from "@/common"; import { pushAlert } from "@/services/alert"; import { TimelineCreatePostRequest } from "@/services/timeline"; interface TimelinePostEditImageProps { onSelect: (blob: Blob | null) => void; } const TimelinePostEditImage: React.FC = (props) => { const { onSelect } = props; const { t } = useTranslation(); const [file, setFile] = React.useState(null); const [fileUrl, setFileUrl] = React.useState(null); const [error, setError] = React.useState(null); React.useEffect(() => { if (file != null) { const url = URL.createObjectURL(file); setFileUrl(url); return () => { URL.revokeObjectURL(url); }; } }, [file]); const onInputChange: React.ChangeEventHandler = React.useCallback( (e) => { const files = e.target.files; if (files == null || files.length === 0) { setFile(null); setFileUrl(null); } else { setFile(files[0]); } onSelect(null); setError(null); }, [onSelect] ); const onImgLoad = React.useCallback(() => { onSelect(file); }, [onSelect, file]); const onImgError = React.useCallback(() => { setError("loadImageError"); }, []); return ( <> {fileUrl && error == null && ( )} {error != null &&
{t(error)}
} ); }; export type TimelinePostSendCallback = ( content: TimelineCreatePostRequest ) => Promise; export interface TimelinePostEditProps { className?: string; onPost: TimelinePostSendCallback; onHeightChange?: (height: number) => void; timelineUniqueId: string; } const TimelinePostEdit: React.FC = (props) => { const { onPost } = props; const { t } = useTranslation(); const [state, setState] = React.useState<"input" | "process">("input"); const [kind, setKind] = React.useState<"text" | "image">("text"); const [text, setText] = React.useState(""); const [imageBlob, setImageBlob] = React.useState(null); const draftLocalStorageKey = `timeline.${props.timelineUniqueId}.postDraft`; React.useEffect(() => { setText(window.localStorage.getItem(draftLocalStorageKey) ?? ""); }, [draftLocalStorageKey]); const canSend = kind === "text" || (kind === "image" && imageBlob != null); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const containerRef = React.useRef(null!); const notifyHeightChange = (): void => { if (props.onHeightChange) { props.onHeightChange(containerRef.current.clientHeight); } }; React.useEffect(() => { if (props.onHeightChange) { props.onHeightChange(containerRef.current.clientHeight); } return () => { if (props.onHeightChange) { props.onHeightChange(0); } }; }); const toggleKind = React.useCallback(() => { setKind((oldKind) => (oldKind === "text" ? "image" : "text")); setImageBlob(null); }, []); const onSend = React.useCallback(() => { setState("process"); const req: TimelineCreatePostRequest = (() => { switch (kind) { case "text": return { content: { type: "text", text: text, }, } as TimelineCreatePostRequest; case "image": if (imageBlob == null) { throw new UiLogicError( "Content type is image but image blob is null." ); } return { content: { type: "image", data: imageBlob, }, } as TimelineCreatePostRequest; default: throw new UiLogicError("Unknown content type."); } })(); onPost(req).then( (_) => { if (kind === "text") { setText(""); window.localStorage.removeItem(draftLocalStorageKey); } setState("input"); setKind("text"); }, (_) => { pushAlert({ type: "danger", message: t("timeline.sendPostFailed"), }); setState("input"); } ); }, [onPost, kind, text, imageBlob, t, draftLocalStorageKey]); const onImageSelect = React.useCallback((blob: Blob | null) => { setImageBlob(blob); }, []); return (
{kind === "text" ? ( ) => { const value = event.currentTarget.value; setText(value); window.localStorage.setItem(draftLocalStorageKey, value); }} /> ) : ( )} {(() => { if (state === "input") { return ( <>
); } else { return ; } })()}
); }; export default TimelinePostEdit;