From 0bc8a2ca2e25ac6fe9ef6b89d1bf9b8066b6f6bf Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 22 Feb 2021 15:53:28 +0800 Subject: ... --- .../app/views/timeline-common/TimelinePostEdit.tsx | 143 +++++++++------------ 1 file changed, 59 insertions(+), 84 deletions(-) (limited to 'FrontEnd/src') diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx index 09d066cc..05bd9ed6 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx @@ -1,7 +1,7 @@ import React from "react"; import clsx from "clsx"; import { useTranslation } from "react-i18next"; -import { Button, Spinner, Row, Col, Form } from "react-bootstrap"; +import { Row, Col, Form } from "react-bootstrap"; import { UiLogicError } from "@/common"; @@ -15,50 +15,31 @@ import { import { pushAlert } from "@/services/alert"; import { base64 } from "@/http/common"; +import BlobImage from "../common/BlobImage"; +import LoadingButton from "../common/LoadingButton"; + interface TimelinePostEditImageProps { - onSelect: (blob: Blob | null) => void; + onSelect: (file: File | 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); + const [error, setError] = React.useState(false); - 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]); - } + const onInputChange: React.ChangeEventHandler = (e) => { + setError(false); + const files = e.target.files; + if (files == null || files.length === 0) { + setFile(null); onSelect(null); - setError(null); - }, - [onSelect] - ); - - const onImgLoad = React.useCallback(() => { - onSelect(file); - }, [onSelect, file]); - - const onImgError = React.useCallback(() => { - setError("loadImageError"); - }, []); + } else { + setFile(files[0]); + } + }; return ( <> @@ -68,15 +49,20 @@ const TimelinePostEditImage: React.FC = (props) => { accept="image/*" className="mx-3 my-1 d-inline-block" /> - {fileUrl && error == null && ( - onSelect(file)} + onError={() => { + onSelect(null); + setError(true); + }} /> )} - {error != null &&
{t(error)}
} + {error != null && ( +
{t("loadImageError")}
+ )} ); }; @@ -93,10 +79,10 @@ const TimelinePostEdit: React.FC = (props) => { const { t } = useTranslation(); - const [state, setState] = React.useState<"input" | "process">("input"); + const [process, setProcess] = React.useState(false); const [kind, setKind] = React.useState<"text" | "image">("text"); const [text, setText] = React.useState(""); - const [imageBlob, setImageBlob] = React.useState(null); + const [image, setImage] = React.useState(null); const draftLocalStorageKey = `timeline.${timeline.name}.postDraft`; @@ -104,7 +90,9 @@ const TimelinePostEdit: React.FC = (props) => { setText(window.localStorage.getItem(draftLocalStorageKey) ?? ""); }, [draftLocalStorageKey]); - const canSend = kind === "text" || (kind === "image" && imageBlob != null); + const canSend = + (kind === "text" && text.length !== 0) || + (kind === "image" && image != null); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const containerRef = React.useRef(null!); @@ -128,11 +116,11 @@ const TimelinePostEdit: React.FC = (props) => { const toggleKind = React.useCallback(() => { setKind((oldKind) => (oldKind === "text" ? "image" : "text")); - setImageBlob(null); + setImage(null); }, []); const onSend = async (): Promise => { - setState("process"); + setProcess(true); let requestData: HttpTimelinePostPostRequestData; switch (kind) { @@ -143,14 +131,14 @@ const TimelinePostEdit: React.FC = (props) => { }; break; case "image": - if (imageBlob == null) { + if (image == null) { throw new UiLogicError( "Content type is image but image blob is null." ); } requestData = { - contentType: imageBlob.type, - data: await base64(imageBlob), + contentType: image.type, + data: await base64(image), }; break; default: @@ -167,7 +155,7 @@ const TimelinePostEdit: React.FC = (props) => { setText(""); window.localStorage.removeItem(draftLocalStorageKey); } - setState("input"); + setProcess(false); setKind("text"); onPosted(data); }, @@ -176,15 +164,11 @@ const TimelinePostEdit: React.FC = (props) => { type: "danger", message: "timeline.sendPostFailed", }); - setState("input"); + setProcess(false); } ); }; - const onImageSelect = React.useCallback((blob: Blob | null) => { - setImageBlob(blob); - }, []); - return (
= (props) => { as="textarea" className="w-100 h-100 timeline-post-edit" value={text} - disabled={state === "process"} + disabled={process} onChange={(event: React.ChangeEvent) => { const value = event.currentTarget.value; setText(value); @@ -205,37 +189,28 @@ const TimelinePostEdit: React.FC = (props) => { }} /> ) : ( - + )} - {(() => { - if (state === "input") { - return ( - <> -
- -
- - - ); - } else { - return ; - } - })()} +
+ +
+ + {t("timeline.send")} +
-- cgit v1.2.3