From 40b4871c3f7bfe04f332ae7fb687fd7d9ae34734 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 14 Sep 2023 23:47:16 +0800 Subject: ... --- FrontEnd/src/pages/timeline/edit/ImagePostEdit.css | 4 + FrontEnd/src/pages/timeline/edit/ImagePostEdit.tsx | 36 ++++ .../src/pages/timeline/edit/MarkdownPostEdit.css | 34 ++++ .../src/pages/timeline/edit/MarkdownPostEdit.tsx | 216 +++++++++++++++++++++ .../src/pages/timeline/edit/PlainTextPostEdit.css | 18 ++ .../src/pages/timeline/edit/PlainTextPostEdit.tsx | 26 +++ .../pages/timeline/edit/TimelinePostCreateView.css | 33 ++++ .../pages/timeline/edit/TimelinePostCreateView.tsx | 200 +++++++++++++++++++ 8 files changed, 567 insertions(+) create mode 100644 FrontEnd/src/pages/timeline/edit/ImagePostEdit.css create mode 100644 FrontEnd/src/pages/timeline/edit/ImagePostEdit.tsx create mode 100644 FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.css create mode 100644 FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx create mode 100644 FrontEnd/src/pages/timeline/edit/PlainTextPostEdit.css create mode 100644 FrontEnd/src/pages/timeline/edit/PlainTextPostEdit.tsx create mode 100644 FrontEnd/src/pages/timeline/edit/TimelinePostCreateView.css create mode 100644 FrontEnd/src/pages/timeline/edit/TimelinePostCreateView.tsx (limited to 'FrontEnd/src/pages/timeline/edit') diff --git a/FrontEnd/src/pages/timeline/edit/ImagePostEdit.css b/FrontEnd/src/pages/timeline/edit/ImagePostEdit.css new file mode 100644 index 00000000..3d5e895c --- /dev/null +++ b/FrontEnd/src/pages/timeline/edit/ImagePostEdit.css @@ -0,0 +1,4 @@ +.timeline-post-create-image { + max-width: 100px; + max-height: 100px; +} diff --git a/FrontEnd/src/pages/timeline/edit/ImagePostEdit.tsx b/FrontEnd/src/pages/timeline/edit/ImagePostEdit.tsx new file mode 100644 index 00000000..d25d04b4 --- /dev/null +++ b/FrontEnd/src/pages/timeline/edit/ImagePostEdit.tsx @@ -0,0 +1,36 @@ +import classNames from "classnames"; + +import BlobImage from "~/src/components/BlobImage"; + +interface TimelinePostEditImageProps { + file: File; + onChange: (file: File | null) => void; + disabled: boolean; + className?: string; +} + +export default function ImagePostEdit(props: TimelinePostEditImageProps) { + const { file, onChange, disabled, className } = props; + + return ( +
+ { + const files = e.target.files; + if (files == null || files.length === 0) { + onChange(null); + } else { + onChange(files[0]); + } + }} + className="mx-3 my-1" + /> + {file && } +
+ ); +} diff --git a/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.css b/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.css new file mode 100644 index 00000000..33a77943 --- /dev/null +++ b/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.css @@ -0,0 +1,34 @@ +.timeline-markdown-post-edit-page { + overflow: auto; + max-height: 300px; +} + +.timeline-post-create-markdown-edit-area { + border: 1px solid var(--cru-clickable-primary-normal-color); + border-top: none; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + padding: 0.6em; +} + +.timeline-post-create-markdown-edit-area:hover { + border: 1px solid var(--cru-clickable-primary-normal-color); + border-top: none; +} + +.timeline-markdown-post-edit-image-container { + position: relative; + text-align: center; + margin-bottom: 1em; +} + +.timeline-markdown-post-edit-image { + max-width: 100%; + max-height: 200px; +} + +.timeline-markdown-post-edit-image-delete-button { + position: absolute; + right: 10px; + top: 2px; +} diff --git a/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx b/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx new file mode 100644 index 00000000..d10d3f2d --- /dev/null +++ b/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx @@ -0,0 +1,216 @@ +import * as React from "react"; +import classnames from "classnames"; +import { useTranslation } from "react-i18next"; + +import { + getHttpTimelineClient, + HttpTimelinePostInfo, +} from "~src/http/timeline"; + +import TimelinePostBuilder from "~src/services/TimelinePostBuilder"; + +import FlatButton from "~src/components/button/FlatButton"; +import { TabPages } from "~src/components/tab"; +import ConfirmDialog from "~src/components/dialog/ConfirmDialog"; +import Spinner from "~src/components/Spinner"; +import IconButton from "~src/components/button/IconButton"; +import { DialogProvider, useDialog } from "~src/components/dialog"; + +import "./MarkdownPostEdit.css"; + +export interface MarkdownPostEditProps { + owner: string; + timeline: string; + onPosted: (post: HttpTimelinePostInfo) => void; + onPostError: () => void; + onClose: () => void; + className?: string; +} + +const MarkdownPostEdit: React.FC = ({ + owner: ownerUsername, + timeline: timelineName, + onPosted, + onClose, + onPostError, + className, +}) => { + const { t } = useTranslation(); + + const [canLeave, setCanLeave] = React.useState(true); + + const [process, setProcess] = React.useState(false); + + const { controller, switchDialog } = useDialog({ + "leave-confirm": ( + + ), + }); + + const [text, _setText] = React.useState(""); + const [images, _setImages] = React.useState<{ file: File; url: string }[]>( + [], + ); + const [previewHtml, _setPreviewHtml] = React.useState(""); + + const _builder = React.useRef(null); + + const getBuilder = (): TimelinePostBuilder => { + if (_builder.current == null) { + const builder = new TimelinePostBuilder(() => { + setCanLeave(builder.isEmpty); + _setText(builder.text); + _setImages(builder.images); + _setPreviewHtml(builder.renderHtml()); + }); + _builder.current = builder; + } + return _builder.current; + }; + + const canSend = text.length > 0; + + React.useEffect(() => { + return () => { + getBuilder().dispose(); + }; + }, []); + + React.useEffect(() => { + window.onbeforeunload = (): unknown => { + if (!canLeave) { + return t("timeline.confirmLeave"); + } + }; + + return () => { + window.onbeforeunload = null; + }; + }, [canLeave, t]); + + const send = async (): Promise => { + setProcess(true); + try { + const dataList = await getBuilder().build(); + const post = await getHttpTimelineClient().postPost( + ownerUsername, + timelineName, + { + dataList, + }, + ); + onPosted(post); + onClose(); + } catch (e) { + setProcess(false); + onPostError(); + } + }; + + return ( + <> + + ) : ( +
+ { + if (canLeave) { + onClose(); + } else { + switchDialog("leave-confirm"); + } + }} + /> + {canSend && ( + void send()} /> + )} +
+ ) + } + pages={[ + { + name: "text", + text: "edit", + page: ( +