aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/pages/timeline/TimelineInfoCard.tsx
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-09-20 20:26:42 +0800
committerGitHub <noreply@github.com>2023-09-20 20:26:42 +0800
commitf836d77e73f3ea0af45c5f71dae7268143d6d86f (patch)
tree573cfafd972106d69bef0d41ff5f270ec3c43ec2 /FrontEnd/src/pages/timeline/TimelineInfoCard.tsx
parent4a069bf1268f393d5467166356f691eb89963152 (diff)
parent901fe3d7c032d284da5c9bce24c4aaee9054c7ac (diff)
downloadtimeline-f836d77e73f3ea0af45c5f71dae7268143d6d86f.tar.gz
timeline-f836d77e73f3ea0af45c5f71dae7268143d6d86f.tar.bz2
timeline-f836d77e73f3ea0af45c5f71dae7268143d6d86f.zip
Merge pull request #1395 from crupest/dev
Refector 2023 v0.1
Diffstat (limited to 'FrontEnd/src/pages/timeline/TimelineInfoCard.tsx')
-rw-r--r--FrontEnd/src/pages/timeline/TimelineInfoCard.tsx208
1 files changed, 208 insertions, 0 deletions
diff --git a/FrontEnd/src/pages/timeline/TimelineInfoCard.tsx b/FrontEnd/src/pages/timeline/TimelineInfoCard.tsx
new file mode 100644
index 00000000..2bc40877
--- /dev/null
+++ b/FrontEnd/src/pages/timeline/TimelineInfoCard.tsx
@@ -0,0 +1,208 @@
+import { useState } from "react";
+import { HubConnectionState } from "@microsoft/signalr";
+
+import { useUser } from "~src/services/user";
+
+import { HttpTimelineInfo } from "~src/http/timeline";
+import { getHttpBookmarkClient } from "~src/http/bookmark";
+
+import { pushAlert } from "~src/components/alert";
+import { useMobile } from "~src/components/hooks";
+import { IconButton } from "~src/components/button";
+import {
+ Dialog,
+ FullPageDialog,
+ DialogProvider,
+ useDialog,
+} from "~src/components/dialog";
+import UserAvatar from "~src/components/user/UserAvatar";
+import PopupMenu from "~src/components/menu/PopupMenu";
+import Card from "~src/components/Card";
+
+import TimelineDeleteDialog from "./TimelineDeleteDialog";
+import ConnectionStatusBadge from "./ConnectionStatusBadge";
+import TimelineMember from "./TimelineMember";
+import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog";
+
+import "./TimelineInfoCard.css";
+
+function CollapseButton({
+ collapse,
+ onClick,
+ className,
+}: {
+ collapse: boolean;
+ onClick: () => void;
+ className?: string;
+}) {
+ return (
+ <IconButton
+ color="primary"
+ icon={collapse ? "info-circle" : "x-circle"}
+ onClick={onClick}
+ className={className}
+ />
+ );
+}
+
+interface TimelineInfoCardProps {
+ timeline: HttpTimelineInfo;
+ connectionStatus: HubConnectionState;
+ onReload: () => void;
+}
+
+function TimelineInfoContent({
+ timeline,
+ onReload,
+}: Omit<TimelineInfoCardProps, "connectionStatus">) {
+ const user = useUser();
+
+ const { controller, createDialogSwitch } = useDialog({
+ member: (
+ <Dialog>
+ <TimelineMember timeline={timeline} onChange={onReload} />
+ </Dialog>
+ ),
+ property: (
+ <TimelinePropertyChangeDialog timeline={timeline} onChange={onReload} />
+ ),
+ delete: <TimelineDeleteDialog timeline={timeline} />,
+ });
+
+ return (
+ <div>
+ <h3 className="timeline-card-title">
+ {timeline.title}
+ <small className="timeline-card-title-name">{timeline.nameV2}</small>
+ </h3>
+ <div className="timeline-card-user">
+ <UserAvatar
+ username={timeline.owner.username}
+ className="timeline-card-user-avatar"
+ />
+ <span className="timeline-card-user-nickname">
+ {timeline.owner.nickname}
+ </span>
+ <small className="timeline-card-user-username">
+ @{timeline.owner.username}
+ </small>
+ </div>
+ <p className="timeline-card-description">{timeline.description}</p>
+ <div className="timeline-card-buttons">
+ {user && (
+ <IconButton
+ icon={timeline.isBookmark ? "bookmark-fill" : "bookmark"}
+ color="primary"
+ className="timeline-card-button"
+ onClick={() => {
+ getHttpBookmarkClient()
+ [timeline.isBookmark ? "delete" : "post"](
+ user.username,
+ timeline.owner.username,
+ timeline.nameV2,
+ )
+ .then(onReload, () => {
+ pushAlert({
+ message: timeline.isBookmark
+ ? "timeline.removeBookmarkFail"
+ : "timeline.addBookmarkFail",
+ color: "danger",
+ });
+ });
+ }}
+ />
+ )}
+ <IconButton
+ icon="people"
+ color="primary"
+ className="timeline-card-button"
+ onClick={createDialogSwitch("member")}
+ />
+ {timeline.manageable && (
+ <PopupMenu
+ items={[
+ {
+ type: "button",
+ text: "timeline.manageItem.property",
+ onClick: createDialogSwitch("property"),
+ },
+ { type: "divider" },
+ {
+ type: "button",
+ onClick: createDialogSwitch("delete"),
+ color: "danger",
+ text: "timeline.manageItem.delete",
+ },
+ ]}
+ containerClassName="d-inline"
+ >
+ <IconButton
+ color="primary"
+ className="timeline-card-button"
+ icon="three-dots-vertical"
+ />
+ </PopupMenu>
+ )}
+ </div>
+ <DialogProvider controller={controller} />
+ </div>
+ );
+}
+
+export default function TimelineInfoCard(props: TimelineInfoCardProps) {
+ const { timeline, connectionStatus, onReload } = props;
+
+ const [collapse, setCollapse] = useState(true);
+
+ const isMobile = useMobile((mobile) => {
+ if (!mobile) {
+ switchDialog(null);
+ } else {
+ setCollapse(true);
+ }
+ });
+
+ const { controller, switchDialog } = useDialog(
+ {
+ "full-page": (
+ <FullPageDialog>
+ <TimelineInfoContent timeline={timeline} onReload={onReload} />
+ </FullPageDialog>
+ ),
+ },
+ {
+ onClose: {
+ "full-page": () => {
+ setCollapse(true);
+ },
+ },
+ },
+ );
+
+ return (
+ <Card
+ color="secondary"
+ className={`timeline-card timeline-card-${
+ collapse ? "collapse" : "expand"
+ }`}
+ >
+ <div className="timeline-card-top-right-area">
+ <ConnectionStatusBadge status={connectionStatus} />
+ <CollapseButton
+ collapse={collapse}
+ onClick={() => {
+ const open = collapse;
+ setCollapse(!open);
+ if (isMobile && open) {
+ switchDialog("full-page");
+ }
+ }}
+ />
+ </div>
+ {!collapse && !isMobile && (
+ <TimelineInfoContent timeline={timeline} onReload={onReload} />
+ )}
+ <DialogProvider controller={controller} />
+ </Card>
+ );
+}