From 3d2698df763910405b11a8bddcac4c9ae5841583 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 19 Jan 2021 16:20:42 +0800 Subject: ... --- .../src/app/views/timeline-common/Timeline.tsx | 81 +++++++++++--------- .../views/timeline-common/TimelinePageTemplate.tsx | 65 ++++++++-------- .../timeline-common/TimelinePageTemplateUI.tsx | 88 ++++++++++++---------- 3 files changed, 127 insertions(+), 107 deletions(-) (limited to 'FrontEnd/src') diff --git a/FrontEnd/src/app/views/timeline-common/Timeline.tsx b/FrontEnd/src/app/views/timeline-common/Timeline.tsx index ab658b89..2446c0dd 100644 --- a/FrontEnd/src/app/views/timeline-common/Timeline.tsx +++ b/FrontEnd/src/app/views/timeline-common/Timeline.tsx @@ -1,11 +1,16 @@ import React from "react"; import clsx from "clsx"; -import { TimelinePostInfo } from "@/services/timeline"; +import { + TimelineInfo, + TimelinePostInfo, + timelineService, +} from "@/services/timeline"; import TimelineItem from "./TimelineItem"; import TimelineTop from "./TimelineTop"; import TimelineDateItem from "./TimelineDateItem"; +import { useUser } from "@/services/user"; function dateEqual(left: Date, right: Date): boolean { return ( @@ -15,30 +20,27 @@ function dateEqual(left: Date, right: Date): boolean { ); } -export interface TimelinePostInfoEx extends TimelinePostInfo { - onDelete?: () => void; -} - -export type TimelineDeleteCallback = (index: number, id: number) => void; - export interface TimelineProps { className?: string; style?: React.CSSProperties; - posts: TimelinePostInfoEx[]; - containerRef?: React.Ref; + timeline: TimelineInfo; + posts: TimelinePostInfo[]; + onDelete: (post: TimelinePostInfo) => void; } const Timeline: React.FC = (props) => { - const { posts } = props; + const { timeline, posts } = props; + + const user = useUser(); const [showMoreIndex, setShowMoreIndex] = React.useState(-1); const groupedPosts = React.useMemo< - { date: Date; posts: (TimelinePostInfoEx & { index: number })[] }[] + { date: Date; posts: (TimelinePostInfo & { index: number })[] }[] >(() => { const result: { date: Date; - posts: (TimelinePostInfoEx & { index: number })[]; + posts: (TimelinePostInfo & { index: number })[]; }[] = []; let index = 0; for (const post of posts) { @@ -59,36 +61,41 @@ const Timeline: React.FC = (props) => { }, [posts]); return ( -
+
{groupedPosts.map((group) => { return ( <> - {group.posts.map((post) => ( - - setShowMoreIndex((old) => - old === post.index ? -1 : post.index - ), - onDelete: post.onDelete, - } - : undefined - } - onClick={() => setShowMoreIndex(-1)} - /> - ))} + {group.posts.map((post) => { + const deletable = timelineService.hasModifyPostPermission( + user, + timeline, + post + ); + return ( + + setShowMoreIndex((old) => + old === post.index ? -1 : post.index + ), + onDelete: () => { + props.onDelete(post); + }, + } + : undefined + } + onClick={() => setShowMoreIndex(-1)} + /> + ); + })} ); })} diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx index fc4c52ec..da020be4 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx @@ -4,16 +4,20 @@ import { useTranslation } from "react-i18next"; import { UiLogicError } from "@/common"; import { pushAlert } from "@/services/alert"; import { useUser } from "@/services/user"; -import { timelineService, usePosts, useTimeline } from "@/services/timeline"; +import { + TimelinePostInfo, + timelineService, + usePosts, + useTimeline, +} from "@/services/timeline"; import { mergeDataStatus } from "@/services/DataHub2"; import { TimelineMemberDialog } from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; import { - TimelinePageTemplateData, + TimelinePageTemplateUIOperations, TimelinePageTemplateUIProps, } from "./TimelinePageTemplateUI"; -import { TimelinePostInfoEx } from "./Timeline"; export interface TimelinePageTemplateProps { name: string; @@ -71,41 +75,26 @@ export default function TimelinePageTemplate( }; }, [name, scrollToBottomNextSyncKey]); - const data = ((): TimelinePageTemplateUIProps["data"] => { + const uiTimelineProp = ((): TimelinePageTemplateUIProps["timeline"] => { const { status, data: timeline } = timelineAndStatus; if (timeline == null) { if (status === "offline") { - return { type: "custom", value: "Network Error" }; + return "offline"; } else { return undefined; } } else if (timeline === "notexist") { - return props.notFoundI18nKey; + return "notexist"; } else { - const posts = ((): TimelinePostInfoEx[] | "forbid" | undefined => { - const { data: postsInfo } = postsAndState; - if (postsInfo === "forbid") { - return "forbid"; - } else if (postsInfo == null || postsInfo === "notexist") { - return undefined; - } else { - return postsInfo.posts.map((post) => ({ - ...post, - onDelete: service.hasModifyPostPermission(user, timeline, post) - ? () => { - service.deletePost(name, post.id).catch(() => { - pushAlert({ - type: "danger", - message: t("timeline.deletePostFailed"), - }); - }); - } - : undefined, - })); - } - })(); - - const operations: TimelinePageTemplateData["operations"] = { + const operations: TimelinePageTemplateUIOperations = { + onDeletePost: (post) => { + service.deletePost(name, post.id).catch(() => { + pushAlert({ + type: "danger", + message: t("timeline.deletePostFailed"), + }); + }); + }, onPost: service.hasPostPermission(user, timeline) ? (req) => service.createPost(name, req).then(() => scrollToBottomNextSync()) @@ -158,7 +147,18 @@ export default function TimelinePageTemplate( : undefined, }; - return { timeline, posts, operations }; + const posts = ((): TimelinePostInfo[] | "forbid" | undefined => { + const { data: postsInfo } = postsAndState; + if (postsInfo === "forbid") { + return "forbid"; + } else if (postsInfo == null || postsInfo === "notexist") { + return undefined; + } else { + return postsInfo.posts; + } + })(); + + return { ...timeline, operations, posts }; } })(); @@ -203,11 +203,12 @@ export default function TimelinePageTemplate( return ( <> {dialogElement} diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx index 0d0951ee..815906d3 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx @@ -3,10 +3,9 @@ import { useTranslation } from "react-i18next"; import { Spinner } from "react-bootstrap"; import { getAlertHost } from "@/services/alert"; -import { I18nText, convertI18nText } from "@/common"; -import { TimelineInfo } from "@/services/timeline"; +import { TimelineInfo, TimelinePostInfo } from "@/services/timeline"; -import Timeline, { TimelinePostInfoEx } from "./Timeline"; +import Timeline from "./Timeline"; import TimelinePostEdit, { TimelinePostSendCallback } from "./TimelinePostEdit"; import { TimelineSyncStatus } from "./SyncStatusBadge"; @@ -24,28 +23,32 @@ export interface TimelineCardComponentProps { className?: string; } -export interface TimelinePageTemplateData { - timeline: TimelineInfo; - posts?: TimelinePostInfoEx[] | "forbid"; - operations: { - onManage?: (item: TManageItems | "property") => void; - onMember: () => void; - onBookmark?: () => void; - onHighlight?: () => void; - onPost?: TimelinePostSendCallback; - }; +export interface TimelinePageTemplateUIOperations { + onDeletePost: (post: TimelinePostInfo) => void; + onManage?: (item: TManageItems | "property") => void; + onMember: () => void; + onBookmark?: () => void; + onHighlight?: () => void; + onPost?: TimelinePostSendCallback; } export interface TimelinePageTemplateUIProps { - data?: TimelinePageTemplateData | I18nText; + timeline?: + | (TimelineInfo & { + operations: TimelinePageTemplateUIOperations; + posts?: TimelinePostInfo[] | "forbid"; + }) + | "notexist" + | "offline"; syncStatus: TimelineSyncStatus; + notExistMessageI18nKey: string; CardComponent: React.ComponentType>; } export default function TimelinePageTemplateUI( props: TimelinePageTemplateUIProps ): React.ReactElement | null { - const { data, syncStatus, CardComponent } = props; + const { timeline, syncStatus, CardComponent } = props; const { t } = useTranslation(); @@ -66,10 +69,7 @@ export default function TimelinePageTemplateUI( } }, []); - const timelineRef = React.useRef(null); - - const timelineName: string | null = - typeof data === "object" && "timeline" in data ? data.timeline.name : null; + const timelineName = typeof timeline === "object" ? timeline.name : null; const cardCollapseLocalStorageKey = timelineName != null ? `timeline.${timelineName}.cardCollapse` : null; @@ -96,32 +96,44 @@ export default function TimelinePageTemplateUI( let body: React.ReactElement; - if (data != null && (typeof data === "string" || "type" in data)) { - body =

{convertI18nText(data, t)}

; + if (timeline == null) { + body = ( +
+ +
+ ); + } else if (timeline === "offline") { + // TODO: i18n + body =

Offline!

; + } else if (timeline === "notexist") { + body =

{t(props.notExistMessageI18nKey)}

; } else { - const posts = data?.posts; - + const { operations, posts } = timeline; body = ( <> - {data != null ? ( - - ) : null} + {posts != null ? ( posts === "forbid" ? (
{t("timeline.messageCantSee")}
) : (
- +
) ) : ( @@ -129,7 +141,7 @@ export default function TimelinePageTemplateUI(
)} - {data != null && data.operations.onPost != null ? ( + {operations.onPost != null ? ( <>
( /> ) : null} -- cgit v1.2.3