diff options
Diffstat (limited to 'FrontEnd/src/views/timeline/TimelinePostEdit.tsx')
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostEdit.tsx | 267 |
1 files changed, 0 insertions, 267 deletions
diff --git a/FrontEnd/src/views/timeline/TimelinePostEdit.tsx b/FrontEnd/src/views/timeline/TimelinePostEdit.tsx deleted file mode 100644 index 38e72264..00000000 --- a/FrontEnd/src/views/timeline/TimelinePostEdit.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import * as React from "react"; -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 "@/utilities/base64"; - -import BlobImage from "../common/BlobImage"; -import LoadingButton from "../common/button/LoadingButton"; -import PopupMenu from "../common/menu/PopupMenu"; -import MarkdownPostEdit from "./MarkdownPostEdit"; -import TimelinePostEditCard from "./TimelinePostEditCard"; -import IconButton from "../common/button/IconButton"; - -import "./TimelinePostEdit.css"; - -interface TimelinePostEditTextProps { - text: string; - disabled: boolean; - onChange: (text: string) => void; - className?: string; - style?: React.CSSProperties; -} - -const TimelinePostEditText: React.FC<TimelinePostEditTextProps> = (props) => { - const { text, disabled, onChange, className, style } = props; - - return ( - <textarea - value={text} - disabled={disabled} - onChange={(event) => { - onChange(event.target.value); - }} - className={className} - style={style} - /> - ); -}; - -interface TimelinePostEditImageProps { - onSelect: (file: File | null) => void; - disabled: boolean; -} - -const TimelinePostEditImage: React.FC<TimelinePostEditImageProps> = (props) => { - const { onSelect, disabled } = props; - - const { t } = useTranslation(); - - const [file, setFile] = React.useState<File | null>(null); - const [error, setError] = React.useState<boolean>(false); - - const onInputChange: React.ChangeEventHandler<HTMLInputElement> = (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 ( - <> - <input - type="file" - onChange={onInputChange} - accept="image/*" - disabled={disabled} - className="mx-3 my-1" - /> - {file != null && !error && ( - <BlobImage - blob={file} - className="timeline-post-edit-image" - onLoad={() => onSelect(file)} - onError={() => { - onSelect(null); - setError(true); - }} - /> - )} - {error ? <div className="text-danger">{t("loadImageError")}</div> : null} - </> - ); -}; - -type PostKind = "text" | "markdown" | "image"; - -const postKindIconMap: Record<PostKind, string> = { - text: "fonts", - markdown: "markdown", - image: "image", -}; - -export interface TimelinePostEditProps { - className?: string; - style?: React.CSSProperties; - timeline: HttpTimelineInfo; - onPosted: (newPost: HttpTimelinePostInfo) => void; -} - -const TimelinePostEdit: React.FC<TimelinePostEditProps> = (props) => { - const { timeline, style, className, onPosted } = props; - - const { t } = useTranslation(); - - const [process, setProcess] = React.useState<boolean>(false); - - const [kind, setKind] = React.useState<Exclude<PostKind, "markdown">>("text"); - const [showMarkdown, setShowMarkdown] = React.useState<boolean>(false); - - const [text, setText] = React.useState<string>(""); - const [image, setImage] = React.useState<File | null>(null); - - const draftTextLocalStorageKey = `timeline.${timeline.owner.username}.${timeline.nameV2}.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<void> => { - 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.owner.username, timeline.nameV2, { - dataList: [requestData], - }) - .then( - (data) => { - if (kind === "text") { - setText(""); - window.localStorage.removeItem(draftTextLocalStorageKey); - } - setProcess(false); - setKind("text"); - onPosted(data); - }, - () => { - setProcess(false); - onPostError(); - }, - ); - }; - - return ( - <TimelinePostEditCard className={className} style={style}> - {showMarkdown ? ( - <MarkdownPostEdit - className="cru-fill-parent" - onClose={() => setShowMarkdown(false)} - owner={timeline.owner.username} - timeline={timeline.nameV2} - onPosted={onPosted} - onPostError={onPostError} - /> - ) : ( - <div className="row"> - <div className="col px-1 py-1"> - {(() => { - if (kind === "text") { - return ( - <TimelinePostEditText - className="cru-fill-parent timeline-post-edit" - text={text} - disabled={process} - onChange={(t) => { - setText(t); - window.localStorage.setItem(draftTextLocalStorageKey, t); - }} - /> - ); - } else if (kind === "image") { - return ( - <TimelinePostEditImage - onSelect={setImage} - disabled={process} - /> - ); - } - })()} - </div> - <div className="col col-auto align-self-end m-1"> - <div className="d-block cru-text-center mt-1 mb-2"> - <PopupMenu - items={(["text", "image", "markdown"] as const).map((kind) => ({ - type: "button", - text: `timeline.post.type.${kind}`, - iconClassName: postKindIconMap[kind], - onClick: () => { - if (kind === "markdown") { - setShowMarkdown(true); - } else { - setKind(kind); - } - }, - }))} - > - <IconButton large icon={postKindIconMap[kind]} /> - </PopupMenu> - </div> - <LoadingButton - onClick={() => void onSend()} - disabled={!canSend} - loading={process} - > - {t("timeline.send")} - </LoadingButton> - </div> - </div> - )} - </TimelinePostEditCard> - ); -}; - -export default TimelinePostEdit; |