diff options
author | crupest <crupest@outlook.com> | 2021-03-18 21:17:12 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2021-03-18 21:17:12 +0800 |
commit | ac9f1a2d563e3797d7164a442338997ff6ac47d5 (patch) | |
tree | 53d24d18e4cd0e034945273e19db47d8d0cf9915 | |
parent | 2157ccdb22266781de271b302ddd1538498fcdc8 (diff) | |
download | timeline-ac9f1a2d563e3797d7164a442338997ff6ac47d5.tar.gz timeline-ac9f1a2d563e3797d7164a442338997ff6ac47d5.tar.bz2 timeline-ac9f1a2d563e3797d7164a442338997ff6ac47d5.zip |
feat: Prevent leave.
-rw-r--r-- | FrontEnd/src/app/locales/en/translation.json | 1 | ||||
-rw-r--r-- | FrontEnd/src/app/locales/zh/translation.json | 1 | ||||
-rw-r--r-- | FrontEnd/src/app/services/TimelinePostBuilder.ts | 4 | ||||
-rw-r--r-- | FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx | 149 |
4 files changed, 90 insertions, 65 deletions
diff --git a/FrontEnd/src/app/locales/en/translation.json b/FrontEnd/src/app/locales/en/translation.json index 5613668e..8fcd5bcd 100644 --- a/FrontEnd/src/app/locales/en/translation.json +++ b/FrontEnd/src/app/locales/en/translation.json @@ -67,6 +67,7 @@ "send": "Send", "deletePostFailed": "Failed to delete post.", "sendPostFailed": "Failed to send post.", + "confirmLeave": "Are you sure to leave? All content you typed would be lost.", "visibility": { "public": "public to everyone", "register": "only registed people can see", diff --git a/FrontEnd/src/app/locales/zh/translation.json b/FrontEnd/src/app/locales/zh/translation.json index c73f2876..f03c4c03 100644 --- a/FrontEnd/src/app/locales/zh/translation.json +++ b/FrontEnd/src/app/locales/zh/translation.json @@ -67,6 +67,7 @@ "send": "发送", "deletePostFailed": "删除消息失败。", "sendPostFailed": "发送消息失败。", + "confirmLeave":"确定要离开吗?所有输入的内容将会丢失。", "visibility": { "public": "对所有人公开", "register": "仅注册可见", diff --git a/FrontEnd/src/app/services/TimelinePostBuilder.ts b/FrontEnd/src/app/services/TimelinePostBuilder.ts index 92d8484b..40279eca 100644 --- a/FrontEnd/src/app/services/TimelinePostBuilder.ts +++ b/FrontEnd/src/app/services/TimelinePostBuilder.ts @@ -82,6 +82,10 @@ export default class TimelinePostBuilder { return this._images; } + get isEmpty(): boolean { + return this._text.length === 0 && this._images.length === 0; + } + renderHtml(): string { return this._md.render(this._text); } diff --git a/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx index d43077b4..ab6aafea 100644 --- a/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Form } from "react-bootstrap"; import { useTranslation } from "react-i18next"; +import { Prompt } from "react-router"; import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; @@ -26,6 +27,8 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({ }) => { const { t } = useTranslation(); + const [canLeave, setCanLeave] = React.useState<boolean>(true); + const [process, setProcess] = React.useState<boolean>(false); const [text, _setText] = React.useState<string>(""); @@ -39,6 +42,7 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({ const getBuilder = (): TimelinePostBuilder => { if (_builder.current == null) { const builder = new TimelinePostBuilder(() => { + setCanLeave(builder.isEmpty); _setText(builder.text); _setImages(builder.images); _setPreviewHtml(builder.renderHtml()); @@ -54,6 +58,18 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({ }; }, []); + React.useEffect(() => { + window.onbeforeunload = () => { + if (!canLeave) { + return t("timeline.confirmLeave"); + } + }; + + return () => { + window.onbeforeunload = null; + }; + }, [canLeave, t]); + const send = async (): Promise<void> => { setProcess(true); try { @@ -70,73 +86,76 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({ }; return ( - <TabPages - className={className} - style={style} - pageContainerClassName="py-2" - actions={ - <> - <div className="flat-button text-danger mr-2" onClick={onClose}> - {t("operationDialog.cancel")} - </div> - <div className="flat-button text-primary" onClick={send}> - {t("timeline.send")} - </div> - </> - } - pages={[ - { - id: "text", - tabText: "edit", - page: ( - <Form.Control - as="textarea" - value={text} - disabled={process} - onChange={(event) => { - getBuilder().setMarkdownText(event.currentTarget.value); - }} - /> - ), - }, - { - id: "images", - tabText: "image", - page: ( - <div className="timeline-markdown-post-edit-page"> - {images.map((image) => ( - <img - key={image.url} - src={image.url} - className="timeline-markdown-post-edit-image" - /> - ))} - <Form.File - label={t("chooseImage")} - accept="image/*" - onChange={(event: React.ChangeEvent<HTMLInputElement>) => { - const { files } = event.currentTarget; - if (files != null && files.length !== 0) { - getBuilder().appendImage(files[0]); - } - }} + <> + <Prompt when={!canLeave} message={t("timeline.confirmLeave")} /> + <TabPages + className={className} + style={style} + pageContainerClassName="py-2" + actions={ + <> + <div className="flat-button text-danger mr-2" onClick={onClose}> + {t("operationDialog.cancel")} + </div> + <div className="flat-button text-primary" onClick={send}> + {t("timeline.send")} + </div> + </> + } + pages={[ + { + id: "text", + tabText: "edit", + page: ( + <Form.Control + as="textarea" + value={text} disabled={process} + onChange={(event) => { + getBuilder().setMarkdownText(event.currentTarget.value); + }} /> - </div> - ), - }, - { - id: "preview", - tabText: "preview", - page: ( - <div - className="markdown-container timeline-markdown-post-edit-page" - dangerouslySetInnerHTML={{ __html: previewHtml }} - /> - ), - }, - ]} - /> + ), + }, + { + id: "images", + tabText: "image", + page: ( + <div className="timeline-markdown-post-edit-page"> + {images.map((image) => ( + <img + key={image.url} + src={image.url} + className="timeline-markdown-post-edit-image" + /> + ))} + <Form.File + label={t("chooseImage")} + accept="image/*" + onChange={(event: React.ChangeEvent<HTMLInputElement>) => { + const { files } = event.currentTarget; + if (files != null && files.length !== 0) { + getBuilder().appendImage(files[0]); + } + }} + disabled={process} + /> + </div> + ), + }, + { + id: "preview", + tabText: "preview", + page: ( + <div + className="markdown-container timeline-markdown-post-edit-page" + dangerouslySetInnerHTML={{ __html: previewHtml }} + /> + ), + }, + ]} + /> + </> ); }; |