import React from "react"; 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 { getHttpBookmarkClient } from "@/http/bookmark"; import { getHttpHighlightClient } from "@/http/highlight"; import { TimelineMemberDialog } from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; import { TimelinePageTemplateData, TimelinePageTemplateUIProps, } from "./TimelinePageTemplateUI"; import { TimelinePostInfoEx } from "./Timeline"; import { mergeDataStatus } from "@/services/DataHub2"; export interface TimelinePageTemplateProps { name: string; onManage: (item: TManageItem) => void; UiComponent: React.ComponentType< Omit, "CardComponent"> >; notFoundI18nKey: string; } export default function TimelinePageTemplate( props: TimelinePageTemplateProps ): React.ReactElement | null { const { t } = useTranslation(); const { name } = props; const service = timelineService; const user = useUser(); const [dialog, setDialog] = React.useState( null ); const timelineAndStatus = useTimeline(name); const postsAndState = usePosts(name); const data = ((): TimelinePageTemplateUIProps["data"] => { const { status, data: timeline } = timelineAndStatus; if (timeline == null) { if (status === "offline") { return { type: "custom", value: "Network Error" }; } else { return undefined; } } else if (timeline === "notexist") { return props.notFoundI18nKey; } 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).subscribe({ error: () => { pushAlert({ type: "danger", message: t("timeline.deletePostFailed"), }); }, }); } : undefined, })); } })(); const operations: TimelinePageTemplateData["operations"] = { onPost: service.hasPostPermission(user, timeline) ? (req) => { return service.createPost(name, req).toPromise().then(); } : undefined, onManage: service.hasManagePermission(user, timeline) ? (item) => { if (item === "property") { setDialog(item); } else { props.onManage(item); } } : undefined, onMember: () => setDialog("member"), onBookmark: user != null ? () => { const { isBookmark } = timeline; const client = getHttpBookmarkClient(); const promise = isBookmark ? client.delete(name) : client.put(name); promise.then( () => { void timelineService.syncTimeline(name); }, () => { pushAlert({ message: { type: "i18n", key: isBookmark ? "timeline.removeBookmarkFail" : "timeline.addBookmarkFail", }, type: "danger", }); } ); } : undefined, onHighlight: user != null && user.hasHighlightTimelineAdministrationPermission ? () => { const { isHighlight } = timeline; const client = getHttpHighlightClient(); const promise = isHighlight ? client.delete(name) : client.put(name); promise.then( () => { void timelineService.syncTimeline(name); }, () => { pushAlert({ message: { type: "i18n", key: isHighlight ? "timeline.removeHighlightFail" : "timeline.addHighlightFail", }, type: "danger", }); } ); } : undefined, }; return { timeline, posts, operations }; } })(); const closeDialog = React.useCallback((): void => { setDialog(null); }, []); let dialogElement: React.ReactElement | undefined; const timeline = timelineAndStatus?.data; if (dialog === "property") { if (timeline == null || timeline === "notexist") { throw new UiLogicError( "Timeline is null but attempt to open change property dialog." ); } dialogElement = ( service.changeTimelineProperty(name, req)} /> ); } else if (dialog === "member") { if (timeline == null || timeline === "notexist") { throw new UiLogicError( "Timeline is null but attempt to open change property dialog." ); } dialogElement = ( ); } const { UiComponent } = props; return ( <> {dialogElement} ); }