From 928ba0ce419bacba113951095278a5138ead34cf Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 10 Apr 2022 16:04:03 +0800 Subject: ... --- FrontEnd/src/App.tsx | 5 +- FrontEnd/src/http/timeline.ts | 136 ++++++++--- .../src/views/timeline-common/CollapseButton.tsx | 23 -- .../timeline-common/ConnectionStatusBadge.css | 36 --- .../timeline-common/ConnectionStatusBadge.tsx | 41 ---- .../src/views/timeline-common/MarkdownPostEdit.css | 21 -- .../src/views/timeline-common/MarkdownPostEdit.tsx | 203 ---------------- .../timeline-common/PostPropertyChangeDialog.tsx | 37 --- FrontEnd/src/views/timeline-common/Timeline.tsx | 165 ------------- .../views/timeline-common/TimelineDateLabel.tsx | 19 -- .../views/timeline-common/TimelineEmptyItem.tsx | 25 -- .../src/views/timeline-common/TimelineLine.tsx | 51 ---- .../src/views/timeline-common/TimelineLoading.tsx | 16 -- .../src/views/timeline-common/TimelineMember.css | 8 - .../src/views/timeline-common/TimelineMember.tsx | 193 --------------- .../timeline-common/TimelinePageCardTemplate.tsx | 153 ------------ .../views/timeline-common/TimelinePageTemplate.tsx | 90 ------- .../timeline-common/TimelinePagedPostListView.tsx | 34 --- .../timeline-common/TimelinePostContentView.tsx | 197 --------------- .../src/views/timeline-common/TimelinePostEdit.css | 20 -- .../src/views/timeline-common/TimelinePostEdit.tsx | 270 --------------------- .../views/timeline-common/TimelinePostEditCard.tsx | 31 --- .../timeline-common/TimelinePostEditNoLogin.tsx | 18 -- .../views/timeline-common/TimelinePostListView.tsx | 75 ------ .../src/views/timeline-common/TimelinePostView.tsx | 159 ------------ .../TimelinePropertyChangeDialog.tsx | 88 ------- FrontEnd/src/views/timeline-common/index.css | 246 ------------------- FrontEnd/src/views/timeline/CollapseButton.tsx | 23 ++ .../src/views/timeline/ConnectionStatusBadge.css | 36 +++ .../src/views/timeline/ConnectionStatusBadge.tsx | 41 ++++ FrontEnd/src/views/timeline/MarkdownPostEdit.css | 21 ++ FrontEnd/src/views/timeline/MarkdownPostEdit.tsx | 209 ++++++++++++++++ .../views/timeline/PostPropertyChangeDialog.tsx | 42 ++++ FrontEnd/src/views/timeline/Timeline.tsx | 166 +++++++++++++ FrontEnd/src/views/timeline/TimelineCard.tsx | 210 ++++++++++++---- FrontEnd/src/views/timeline/TimelineDateLabel.tsx | 19 ++ FrontEnd/src/views/timeline/TimelineEmptyItem.tsx | 25 ++ FrontEnd/src/views/timeline/TimelineLine.tsx | 51 ++++ FrontEnd/src/views/timeline/TimelineLoading.tsx | 16 ++ FrontEnd/src/views/timeline/TimelineMember.css | 8 + FrontEnd/src/views/timeline/TimelineMember.tsx | 193 +++++++++++++++ .../views/timeline/TimelinePagedPostListView.tsx | 34 +++ .../src/views/timeline/TimelinePostContentView.tsx | 197 +++++++++++++++ FrontEnd/src/views/timeline/TimelinePostEdit.css | 20 ++ FrontEnd/src/views/timeline/TimelinePostEdit.tsx | 270 +++++++++++++++++++++ .../src/views/timeline/TimelinePostEditCard.tsx | 31 +++ .../src/views/timeline/TimelinePostEditNoLogin.tsx | 18 ++ .../src/views/timeline/TimelinePostListView.tsx | 75 ++++++ FrontEnd/src/views/timeline/TimelinePostView.tsx | 159 ++++++++++++ .../timeline/TimelinePropertyChangeDialog.tsx | 88 +++++++ FrontEnd/src/views/timeline/index.css | 246 +++++++++++++++++++ FrontEnd/src/views/timeline/index.tsx | 80 +++++- FrontEnd/src/views/user/UserCard.tsx | 53 ---- FrontEnd/src/views/user/index.css | 0 FrontEnd/src/views/user/index.tsx | 36 --- 55 files changed, 2327 insertions(+), 2400 deletions(-) delete mode 100644 FrontEnd/src/views/timeline-common/CollapseButton.tsx delete mode 100644 FrontEnd/src/views/timeline-common/ConnectionStatusBadge.css delete mode 100644 FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx delete mode 100644 FrontEnd/src/views/timeline-common/MarkdownPostEdit.css delete mode 100644 FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx delete mode 100644 FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx delete mode 100644 FrontEnd/src/views/timeline-common/Timeline.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelineDateLabel.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelineEmptyItem.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelineLine.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelineLoading.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelineMember.css delete mode 100644 FrontEnd/src/views/timeline-common/TimelineMember.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostEdit.css delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostEditCard.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostEditNoLogin.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostListView.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostView.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx delete mode 100644 FrontEnd/src/views/timeline-common/index.css create mode 100644 FrontEnd/src/views/timeline/CollapseButton.tsx create mode 100644 FrontEnd/src/views/timeline/ConnectionStatusBadge.css create mode 100644 FrontEnd/src/views/timeline/ConnectionStatusBadge.tsx create mode 100644 FrontEnd/src/views/timeline/MarkdownPostEdit.css create mode 100644 FrontEnd/src/views/timeline/MarkdownPostEdit.tsx create mode 100644 FrontEnd/src/views/timeline/PostPropertyChangeDialog.tsx create mode 100644 FrontEnd/src/views/timeline/Timeline.tsx create mode 100644 FrontEnd/src/views/timeline/TimelineDateLabel.tsx create mode 100644 FrontEnd/src/views/timeline/TimelineEmptyItem.tsx create mode 100644 FrontEnd/src/views/timeline/TimelineLine.tsx create mode 100644 FrontEnd/src/views/timeline/TimelineLoading.tsx create mode 100644 FrontEnd/src/views/timeline/TimelineMember.css create mode 100644 FrontEnd/src/views/timeline/TimelineMember.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePagedPostListView.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePostContentView.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePostEdit.css create mode 100644 FrontEnd/src/views/timeline/TimelinePostEdit.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePostEditCard.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePostEditNoLogin.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePostListView.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePostView.tsx create mode 100644 FrontEnd/src/views/timeline/TimelinePropertyChangeDialog.tsx create mode 100644 FrontEnd/src/views/timeline/index.css delete mode 100644 FrontEnd/src/views/user/UserCard.tsx delete mode 100644 FrontEnd/src/views/user/index.css delete mode 100644 FrontEnd/src/views/user/index.tsx (limited to 'FrontEnd/src') diff --git a/FrontEnd/src/App.tsx b/FrontEnd/src/App.tsx index 69223abb..5e61848a 100644 --- a/FrontEnd/src/App.tsx +++ b/FrontEnd/src/App.tsx @@ -8,7 +8,6 @@ import Home from "./views/home"; import Login from "./views/login"; import Settings from "./views/settings"; import About from "./views/about"; -import User from "./views/user"; import TimelinePage from "./views/timeline"; import Search from "./views/search"; import Admin from "./views/admin"; @@ -35,8 +34,8 @@ function App(): ReactElement | null { } /> } /> } /> - } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/FrontEnd/src/http/timeline.ts b/FrontEnd/src/http/timeline.ts index 9697c1a0..49a7b8f2 100644 --- a/FrontEnd/src/http/timeline.ts +++ b/FrontEnd/src/http/timeline.ts @@ -6,7 +6,7 @@ import { axios, apiBaseUrl, extractResponseData, - convertToIfErrorCodeIs, + convertToIfStatusCodeIs, getHttpToken, } from "./common"; import { HttpUser } from "./user"; @@ -18,7 +18,7 @@ export type TimelineVisibility = typeof kTimelineVisibilities[number]; export interface HttpTimelineInfo { uniqueId: string; title: string; - name: string; + nameV2: string; description: string; owner: HttpUser; visibility: TimelineVisibility; @@ -54,7 +54,8 @@ export interface HttpTimelinePostInfo { dataList: HttpTimelinePostDataDigest[]; color: string; lastUpdated: string; - timelineName: string; + timelineOwnerV2: string; + timelineNameV2: string; editable: boolean; } @@ -90,28 +91,57 @@ export class HttpTimelineNameConflictError extends Error { export interface IHttpTimelineClient { listTimeline(query: HttpTimelineListQuery): Promise; - getTimeline(timelineName: string): Promise; + getTimeline( + ownerUsername: string, + timelineName: string + ): Promise; postTimeline(req: HttpTimelinePostRequest): Promise; patchTimeline( + ownerUsername: string, timelineName: string, req: HttpTimelinePatchRequest ): Promise; - deleteTimeline(timelineName: string): Promise; - memberPut(timelineName: string, username: string): Promise; - memberDelete(timelineName: string, username: string): Promise; - listPost(timelineName: string): Promise; - generatePostDataUrl(timelineName: string, postId: number): string; - getPostDataAsString(timelineName: string, postId: number): Promise; + deleteTimeline(ownerUsername: string, timelineName: string): Promise; + memberPut( + ownerUsername: string, + timelineName: string, + username: string + ): Promise; + memberDelete( + ownerUsername: string, + timelineName: string, + username: string + ): Promise; + listPost( + ownerUsername: string, + timelineName: string + ): Promise; + generatePostDataUrl( + ownerUsername: string, + timelineName: string, + postId: number + ): string; + getPostDataAsString( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise; postPost( + ownerUsername: string, timelineName: string, req: HttpTimelinePostPostRequest ): Promise; patchPost( + ownerUsername: string, timelineName: string, postId: number, req: HttpTimelinePostPatchRequest ): Promise; - deletePost(timelineName: string, postId: number): Promise; + deletePost( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise; } export class HttpTimelineClient implements IHttpTimelineClient { @@ -123,63 +153,97 @@ export class HttpTimelineClient implements IHttpTimelineClient { .then(extractResponseData); } - getTimeline(timelineName: string): Promise { + getTimeline( + ownerUsername: string, + timelineName: string + ): Promise { return axios - .get(`${apiBaseUrl}/timelines/${timelineName}`) + .get( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}` + ) .then(extractResponseData); } postTimeline(req: HttpTimelinePostRequest): Promise { return axios - .post(`${apiBaseUrl}/timelines`, req) + .post(`${apiBaseUrl}/v2/timelines`, req) .then(extractResponseData) - .catch(convertToIfErrorCodeIs(11040101, HttpTimelineNameConflictError)); + .catch(convertToIfStatusCodeIs(422, HttpTimelineNameConflictError)); } patchTimeline( + ownerUsername: string, timelineName: string, req: HttpTimelinePatchRequest ): Promise { return axios - .patch(`${apiBaseUrl}/timelines/${timelineName}`, req) + .patch( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}`, + req + ) .then(extractResponseData); } - deleteTimeline(timelineName: string): Promise { - return axios.delete(`${apiBaseUrl}/timelines/${timelineName}`).then(); + deleteTimeline(ownerUsername: string, timelineName: string): Promise { + return axios + .delete(`${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}`) + .then(); } - memberPut(timelineName: string, username: string): Promise { + memberPut( + ownerUsername: string, + timelineName: string, + username: string + ): Promise { return axios - .put(`${apiBaseUrl}/timelines/${timelineName}/members/${username}`) + .put( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/members/${username}` + ) .then(); } - memberDelete(timelineName: string, username: string): Promise { + memberDelete( + ownerUsername: string, + timelineName: string, + username: string + ): Promise { return axios - .delete(`${apiBaseUrl}/timelines/${timelineName}/members/${username}`) + .delete( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/members/${username}` + ) .then(); } - listPost(timelineName: string): Promise { + listPost( + ownerUsername: string, + timelineName: string + ): Promise { return axios .get( - `${apiBaseUrl}/timelines/${timelineName}/posts` + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts` ) .then(extractResponseData); } - generatePostDataUrl(timelineName: string, postId: number): string { + generatePostDataUrl( + ownerUsername: string, + timelineName: string, + postId: number + ): string { return applyQueryParameters( - `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}/data`, + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts/${postId}/data`, { token: getHttpToken() } ); } - getPostDataAsString(timelineName: string, postId: number): Promise { + getPostDataAsString( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise { return axios .get( - `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}/data`, + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts/${postId}/data`, { responseType: "text", } @@ -188,33 +252,41 @@ export class HttpTimelineClient implements IHttpTimelineClient { } postPost( + ownerUsername: string, timelineName: string, req: HttpTimelinePostPostRequest ): Promise { return axios .post( - `${apiBaseUrl}/timelines/${timelineName}/posts`, + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts`, req ) .then(extractResponseData); } patchPost( + ownerUsername: string, timelineName: string, postId: number, req: HttpTimelinePostPatchRequest ): Promise { return axios .patch( - `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}`, + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts/${postId}`, req ) .then(extractResponseData); } - deletePost(timelineName: string, postId: number): Promise { + deletePost( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise { return axios - .delete(`${apiBaseUrl}/timelines/${timelineName}/posts/${postId}`) + .delete( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts/${postId}` + ) .then(); } } diff --git a/FrontEnd/src/views/timeline-common/CollapseButton.tsx b/FrontEnd/src/views/timeline-common/CollapseButton.tsx deleted file mode 100644 index 31976228..00000000 --- a/FrontEnd/src/views/timeline-common/CollapseButton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import classnames from "classnames"; - -const CollapseButton: React.FC<{ - collapse: boolean; - onClick: () => void; - className?: string; - style?: React.CSSProperties; -}> = ({ collapse, onClick, className, style }) => { - return ( - - ); -}; - -export default CollapseButton; diff --git a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.css b/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.css deleted file mode 100644 index 7fe83b9b..00000000 --- a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.css +++ /dev/null @@ -1,36 +0,0 @@ -.connection-status-badge { - font-size: 0.8em; - border-radius: 5px; - padding: 0.1em 1em; - background-color: #eaf2ff; -} -.connection-status-badge::before { - width: 10px; - height: 10px; - border-radius: 50%; - display: inline-block; - content: ""; - margin-right: 0.6em; -} -.connection-status-badge.success { - color: #006100; -} -.connection-status-badge.success::before { - background-color: #006100; -} - -.connection-status-badge.warning { - color: #e4a700; -} - -.connection-status-badge.warning::before { - background-color: #e4a700; -} - -.connection-status-badge.danger { - color: #fd1616; -} - -.connection-status-badge.danger::before { - background-color: #fd1616; -} diff --git a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx b/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx deleted file mode 100644 index c8478557..00000000 --- a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from "react"; -import classnames from "classnames"; -import { HubConnectionState } from "@microsoft/signalr"; -import { useTranslation } from "react-i18next"; - -import "./ConnectionStatusBadge.css"; - -export interface ConnectionStatusBadgeProps { - status: HubConnectionState; - className?: string; - style?: React.CSSProperties; -} - -const classNameMap: Record = { - Connected: "success", - Connecting: "warning", - Disconnected: "danger", - Disconnecting: "warning", - Reconnecting: "warning", -}; - -const ConnectionStatusBadge: React.FC = (props) => { - const { status, className, style } = props; - - const { t } = useTranslation(); - - return ( -
- {t(`connectionState.${status}`)} -
- ); -}; - -export default ConnectionStatusBadge; diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.css b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.css deleted file mode 100644 index e36be992..00000000 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.css +++ /dev/null @@ -1,21 +0,0 @@ -.timeline-markdown-post-edit-page { - overflow: auto; - max-height: 300px; -} - -.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/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx deleted file mode 100644 index a3a8f408..00000000 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React from "react"; -import classnames from "classnames"; -import { useTranslation } from "react-i18next"; - -import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; - -import TimelinePostBuilder from "@/services/TimelinePostBuilder"; - -import FlatButton from "../common/button/FlatButton"; -import TabPages from "../common/tab/TabPages"; -import ConfirmDialog from "../common/dailog/ConfirmDialog"; -import Spinner from "../common/Spinner"; - -import "./MarkdownPostEdit.css"; - -export interface MarkdownPostEditProps { - timeline: string; - onPosted: (post: HttpTimelinePostInfo) => void; - onPostError: () => void; - onClose: () => void; - className?: string; - style?: React.CSSProperties; -} - -const MarkdownPostEdit: React.FC = ({ - timeline: timelineName, - onPosted, - onClose, - onPostError, - className, - style, -}) => { - const { t } = useTranslation(); - - const [canLeave, setCanLeave] = React.useState(true); - - const [process, setProcess] = React.useState(false); - - const [showLeaveConfirmDialog, setShowLeaveConfirmDialog] = - React.useState(false); - - 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(timelineName, { - dataList, - }); - onPosted(post); - onClose(); - } catch (e) { - setProcess(false); - onPostError(); - } - }; - - return ( - <> - - ) : ( -
- { - if (canLeave) { - onClose(); - } else { - setShowLeaveConfirmDialog(true); - } - }} - /> - {canSend && ( - void send()} /> - )} -
- ) - } - pages={[ - { - name: "text", - text: "edit", - page: ( -