aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/views/timeline/TimelinePostEdit.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'FrontEnd/src/views/timeline/TimelinePostEdit.tsx')
-rw-r--r--FrontEnd/src/views/timeline/TimelinePostEdit.tsx267
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;