diff options
Diffstat (limited to 'FrontEnd/src/views')
-rw-r--r-- | FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx | 153 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx | 90 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/CollapseButton.tsx (renamed from FrontEnd/src/views/timeline-common/CollapseButton.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/ConnectionStatusBadge.css (renamed from FrontEnd/src/views/timeline-common/ConnectionStatusBadge.css) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/ConnectionStatusBadge.tsx (renamed from FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/MarkdownPostEdit.css (renamed from FrontEnd/src/views/timeline-common/MarkdownPostEdit.css) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/MarkdownPostEdit.tsx (renamed from FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx) | 12 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/PostPropertyChangeDialog.tsx (renamed from FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx) | 11 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/Timeline.tsx (renamed from FrontEnd/src/views/timeline-common/Timeline.tsx) | 7 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelineCard.tsx | 210 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelineDateLabel.tsx (renamed from FrontEnd/src/views/timeline-common/TimelineDateLabel.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelineEmptyItem.tsx (renamed from FrontEnd/src/views/timeline-common/TimelineEmptyItem.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelineLine.tsx (renamed from FrontEnd/src/views/timeline-common/TimelineLine.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelineLoading.tsx (renamed from FrontEnd/src/views/timeline-common/TimelineLoading.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelineMember.css (renamed from FrontEnd/src/views/timeline-common/TimelineMember.css) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelineMember.tsx (renamed from FrontEnd/src/views/timeline-common/TimelineMember.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePagedPostListView.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostContentView.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostEdit.css (renamed from FrontEnd/src/views/timeline-common/TimelinePostEdit.css) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostEdit.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostEditCard.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePostEditCard.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostEditNoLogin.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePostEditNoLogin.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostListView.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePostListView.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePostView.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePostView.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/TimelinePropertyChangeDialog.tsx (renamed from FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/index.css (renamed from FrontEnd/src/views/timeline-common/index.css) | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/timeline/index.tsx | 80 | ||||
-rw-r--r-- | FrontEnd/src/views/user/UserCard.tsx | 53 | ||||
-rw-r--r-- | FrontEnd/src/views/user/index.css | 0 | ||||
-rw-r--r-- | FrontEnd/src/views/user/index.tsx | 36 |
30 files changed, 254 insertions, 398 deletions
diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx deleted file mode 100644 index eb17a9d0..00000000 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import React from "react"; -import classnames from "classnames"; -import { useTranslation } from "react-i18next"; - -import { getHttpHighlightClient } from "@/http/highlight"; -import { getHttpBookmarkClient } from "@/http/bookmark"; - -import { useUser } from "@/services/user"; -import { pushAlert } from "@/services/alert"; -import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline"; - -import { useIsSmallScreen } from "@/utilities/mediaQuery"; - -import { TimelinePageCardProps } from "./TimelinePageTemplate"; - -import CollapseButton from "./CollapseButton"; -import { TimelineMemberDialog } from "./TimelineMember"; -import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; -import ConnectionStatusBadge from "./ConnectionStatusBadge"; -import { MenuItems } from "../common/menu/Menu"; -import PopupMenu from "../common/menu/PopupMenu"; -import FullPageDialog from "../common/dailog/FullPageDialog"; -import Card from "../common/Card"; - -export interface TimelineCardTemplateProps extends TimelinePageCardProps { - infoArea: React.ReactNode; - manageItems?: MenuItems; - dialog: string | "property" | "member" | null; - setDialog: (dialog: "property" | "member" | null) => void; -} - -const TimelinePageCardTemplate: React.FC<TimelineCardTemplateProps> = ({ - timeline, - collapse, - toggleCollapse, - infoArea, - manageItems, - connectionStatus, - onReload, - className, - dialog, - setDialog, -}) => { - const { t } = useTranslation(); - - const isSmallScreen = useIsSmallScreen(); - - const user = useUser(); - - const content = ( - <> - {infoArea} - <p className="mb-0">{timeline.description}</p> - <small className="mt-1 d-block"> - {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])} - </small> - <div className="mt-2 cru-text-end"> - <i - className={classnames( - timeline.isHighlight ? "bi-star-fill" : "bi-star", - "icon-button cru-color-primary me-3" - )} - onClick={ - user?.hasHighlightTimelineAdministrationPermission - ? () => { - getHttpHighlightClient() - [timeline.isHighlight ? "delete" : "put"](timeline.name) - .then(onReload, () => { - pushAlert({ - message: timeline.isHighlight - ? "timeline.removeHighlightFail" - : "timeline.addHighlightFail", - type: "danger", - }); - }); - } - : undefined - } - /> - {user != null ? ( - <i - className={classnames( - timeline.isBookmark ? "bi-bookmark-fill" : "bi-bookmark", - "icon-button cru-color-primary me-3" - )} - onClick={() => { - getHttpBookmarkClient() - [timeline.isBookmark ? "delete" : "put"](timeline.name) - .then(onReload, () => { - pushAlert({ - message: timeline.isBookmark - ? "timeline.removeBookmarkFail" - : "timeline.addBookmarkFail", - type: "danger", - }); - }); - }} - /> - ) : null} - <i - className={"icon-button bi-people cru-color-primary me-3"} - onClick={() => setDialog("member")} - /> - {manageItems != null ? ( - <PopupMenu items={manageItems} containerClassName="d-inline"> - <i className="icon-button bi-three-dots-vertical cru-color-primary" /> - </PopupMenu> - ) : null} - </div> - </> - ); - - return ( - <> - <Card className={classnames("p-2 cru-clearfix", className)}> - <div - className={classnames( - "cru-float-right d-flex align-items-center", - !collapse && "ms-3" - )} - > - <ConnectionStatusBadge status={connectionStatus} className="me-2" /> - <CollapseButton collapse={collapse} onClick={toggleCollapse} /> - </div> - {isSmallScreen ? ( - <FullPageDialog - onBack={toggleCollapse} - show={!collapse} - contentContainerClassName="p-2" - > - {content} - </FullPageDialog> - ) : ( - <div style={{ display: collapse ? "none" : "inline" }}>{content}</div> - )} - </Card> - <TimelineMemberDialog - timeline={timeline} - onClose={() => setDialog(null)} - open={dialog === "member"} - onChange={onReload} - /> - <TimelinePropertyChangeDialog - timeline={timeline} - close={() => setDialog(null)} - open={dialog === "property"} - onChange={onReload} - /> - </> - ); -}; - -export default TimelinePageCardTemplate; diff --git a/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx deleted file mode 100644 index ea6e8d40..00000000 --- a/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from "react"; -import { HubConnectionState } from "@microsoft/signalr"; - -import { HttpTimelineInfo } from "@/http/timeline"; - -import useReverseScrollPositionRemember from "@/utilities/useReverseScrollPositionRemember"; - -import { generatePalette, setPalette } from "@/palette"; - -import Timeline from "./Timeline"; - -export interface TimelinePageCardProps { - timeline: HttpTimelineInfo; - collapse: boolean; - toggleCollapse: () => void; - connectionStatus: HubConnectionState; - className?: string; - onReload: () => void; -} - -export interface TimelinePageTemplateProps { - timelineName: string; - notFoundI18nKey: string; - reloadKey: number; - onReload: () => void; - CardComponent: React.ComponentType<TimelinePageCardProps>; -} - -const TimelinePageTemplate: React.FC<TimelinePageTemplateProps> = (props) => { - const { timelineName, reloadKey, onReload, CardComponent } = props; - - const [timeline, setTimeline] = React.useState<HttpTimelineInfo | null>(null); - - const [connectionStatus, setConnectionStatus] = - React.useState<HubConnectionState>(HubConnectionState.Connecting); - - useReverseScrollPositionRemember(); - - React.useEffect(() => { - if (timeline != null && timeline.color != null) { - return setPalette(generatePalette({ primary: timeline.color })); - } - }, [timeline]); - - const cardCollapseLocalStorageKey = `timeline.${timelineName}.cardCollapse`; - - const [cardCollapse, setCardCollapse] = React.useState<boolean>(true); - - React.useEffect(() => { - const savedCollapse = window.localStorage.getItem( - cardCollapseLocalStorageKey - ); - setCardCollapse(savedCollapse == null ? true : savedCollapse === "true"); - }, [cardCollapseLocalStorageKey]); - - const toggleCardCollapse = (): void => { - const newState = !cardCollapse; - setCardCollapse(newState); - window.localStorage.setItem( - cardCollapseLocalStorageKey, - newState.toString() - ); - }; - - return ( - <> - {timeline != null ? ( - <CardComponent - className="timeline-template-card" - timeline={timeline} - collapse={cardCollapse} - toggleCollapse={toggleCardCollapse} - onReload={onReload} - connectionStatus={connectionStatus} - /> - ) : null} - <div className="container"> - <Timeline - timelineName={timelineName} - reloadKey={reloadKey} - onReload={onReload} - onTimelineLoaded={(t) => setTimeline(t)} - onConnectionStateChanged={setConnectionStatus} - /> - </div> - </> - ); -}; - -export default TimelinePageTemplate; diff --git a/FrontEnd/src/views/timeline-common/CollapseButton.tsx b/FrontEnd/src/views/timeline/CollapseButton.tsx index 31976228..31976228 100644 --- a/FrontEnd/src/views/timeline-common/CollapseButton.tsx +++ b/FrontEnd/src/views/timeline/CollapseButton.tsx diff --git a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.css b/FrontEnd/src/views/timeline/ConnectionStatusBadge.css index 7fe83b9b..7fe83b9b 100644 --- a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.css +++ b/FrontEnd/src/views/timeline/ConnectionStatusBadge.css diff --git a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx b/FrontEnd/src/views/timeline/ConnectionStatusBadge.tsx index c8478557..c8478557 100644 --- a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx +++ b/FrontEnd/src/views/timeline/ConnectionStatusBadge.tsx diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.css b/FrontEnd/src/views/timeline/MarkdownPostEdit.css index e36be992..e36be992 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.css +++ b/FrontEnd/src/views/timeline/MarkdownPostEdit.css diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline/MarkdownPostEdit.tsx index a3a8f408..35a2bbf5 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/views/timeline/MarkdownPostEdit.tsx @@ -14,6 +14,7 @@ import Spinner from "../common/Spinner"; import "./MarkdownPostEdit.css"; export interface MarkdownPostEditProps { + owner: string; timeline: string; onPosted: (post: HttpTimelinePostInfo) => void; onPostError: () => void; @@ -23,6 +24,7 @@ export interface MarkdownPostEditProps { } const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({ + owner: ownerUsername, timeline: timelineName, onPosted, onClose, @@ -84,9 +86,13 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({ setProcess(true); try { const dataList = await getBuilder().build(); - const post = await getHttpTimelineClient().postPost(timelineName, { - dataList, - }); + const post = await getHttpTimelineClient().postPost( + ownerUsername, + timelineName, + { + dataList, + } + ); onPosted(post); onClose(); } catch (e) { diff --git a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx b/FrontEnd/src/views/timeline/PostPropertyChangeDialog.tsx index c1dd416c..d000093d 100644 --- a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx +++ b/FrontEnd/src/views/timeline/PostPropertyChangeDialog.tsx @@ -25,9 +25,14 @@ function PostPropertyChangeDialog(props: { }, ]} onProcess={([time]) => { - return getHttpTimelineClient().patchPost(post.timelineName, post.id, { - time: time === "" ? undefined : new Date(time).toISOString(), - }); + return getHttpTimelineClient().patchPost( + post.timelineOwnerV2, + post.timelineNameV2, + post.id, + { + time: time === "" ? undefined : new Date(time).toISOString(), + } + ); }} onSuccessAndClose={onSuccess} /> diff --git a/FrontEnd/src/views/timeline-common/Timeline.tsx b/FrontEnd/src/views/timeline/Timeline.tsx index e028dddc..4738c705 100644 --- a/FrontEnd/src/views/timeline-common/Timeline.tsx +++ b/FrontEnd/src/views/timeline/Timeline.tsx @@ -29,6 +29,7 @@ import "./index.css"; export interface TimelineProps { className?: string; style?: React.CSSProperties; + timelineOwner: string; timelineName: string; reloadKey: number; onReload: () => void; @@ -37,7 +38,7 @@ export interface TimelineProps { } const Timeline: React.FC<TimelineProps> = (props) => { - const { timelineName, className, style, reloadKey } = props; + const { timelineOwner, timelineName, className, style, reloadKey } = props; const user = useUser(); @@ -82,8 +83,8 @@ const Timeline: React.FC<TimelineProps> = (props) => { const client = getHttpTimelineClient(); Promise.all([ - client.getTimeline(timelineName), - client.listPost(timelineName), + client.getTimeline(timelineOwner, timelineName), + client.listPost(timelineOwner, timelineName), ]).then( ([t, p]) => { if (subscribe) { diff --git a/FrontEnd/src/views/timeline/TimelineCard.tsx b/FrontEnd/src/views/timeline/TimelineCard.tsx index 339fbfa0..156c581e 100644 --- a/FrontEnd/src/views/timeline/TimelineCard.tsx +++ b/FrontEnd/src/views/timeline/TimelineCard.tsx @@ -1,62 +1,182 @@ import React from "react"; +import { useTranslation } from "react-i18next"; +import classnames from "classnames"; +import { HubConnectionState } from "@microsoft/signalr"; -import { TimelinePageCardProps } from "../timeline-common/TimelinePageTemplate"; -import TimelinePageCardTemplate from "../timeline-common/TimelinePageCardTemplate"; +import { useIsSmallScreen } from "@/utilities/mediaQuery"; +import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline"; +import { useUser } from "@/services/user"; +import { pushAlert } from "@/services/alert"; +import { HttpTimelineInfo } from "@/http/timeline"; +import { getHttpHighlightClient } from "@/http/highlight"; +import { getHttpBookmarkClient } from "@/http/bookmark"; import UserAvatar from "../common/user/UserAvatar"; +import PopupMenu from "../common/menu/PopupMenu"; +import FullPageDialog from "../common/dailog/FullPageDialog"; +import Card from "../common/Card"; import TimelineDeleteDialog from "./TimelineDeleteDialog"; +import ConnectionStatusBadge from "./ConnectionStatusBadge"; +import CollapseButton from "./CollapseButton"; +import { TimelineMemberDialog } from "./TimelineMember"; +import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; + +export interface TimelinePageCardProps { + timeline: HttpTimelineInfo; + collapse: boolean; + toggleCollapse: () => void; + connectionStatus: HubConnectionState; + className?: string; + onReload: () => void; +} const TimelineCard: React.FC<TimelinePageCardProps> = (props) => { - const { timeline } = props; + const { + timeline, + collapse, + toggleCollapse, + connectionStatus, + onReload, + className, + } = props; + + const { t } = useTranslation(); const [dialog, setDialog] = React.useState< "member" | "property" | "delete" | null >(null); + const isSmallScreen = useIsSmallScreen(); + + const user = useUser(); + + const content = ( + <> + <h3 className="cru-color-primary d-inline-block align-middle"> + {timeline.title} + <small className="ms-3 cru-color-secondary">{timeline.name}</small> + </h3> + <div> + <UserAvatar + username={timeline.owner.username} + className="cru-avatar small cru-round me-3" + /> + {timeline.owner.nickname} + <small className="ms-3 cru-color-secondary"> + @{timeline.owner.username} + </small> + </div> + <p className="mb-0">{timeline.description}</p> + <small className="mt-1 d-block"> + {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])} + </small> + <div className="mt-2 cru-text-end"> + <i + className={classnames( + timeline.isHighlight ? "bi-star-fill" : "bi-star", + "icon-button cru-color-primary me-3" + )} + onClick={ + user?.hasHighlightTimelineAdministrationPermission + ? () => { + getHttpHighlightClient() + [timeline.isHighlight ? "delete" : "put"](timeline.name) + .then(onReload, () => { + pushAlert({ + message: timeline.isHighlight + ? "timeline.removeHighlightFail" + : "timeline.addHighlightFail", + type: "danger", + }); + }); + } + : undefined + } + /> + {user != null ? ( + <i + className={classnames( + timeline.isBookmark ? "bi-bookmark-fill" : "bi-bookmark", + "icon-button cru-color-primary me-3" + )} + onClick={() => { + getHttpBookmarkClient() + [timeline.isBookmark ? "delete" : "put"](timeline.name) + .then(onReload, () => { + pushAlert({ + message: timeline.isBookmark + ? "timeline.removeBookmarkFail" + : "timeline.addBookmarkFail", + type: "danger", + }); + }); + }} + /> + ) : null} + <i + className={"icon-button bi-people cru-color-primary me-3"} + onClick={() => setDialog("member")} + /> + {timeline.manageable ? ( + <PopupMenu + items={[ + { + type: "button", + text: "timeline.manageItem.property", + onClick: () => setDialog("property"), + }, + { type: "divider" }, + { + type: "button", + onClick: () => setDialog("delete"), + color: "danger", + text: "timeline.manageItem.delete", + }, + ]} + containerClassName="d-inline" + > + <i className="icon-button bi-three-dots-vertical cru-color-primary" /> + </PopupMenu> + ) : null} + </div> + </> + ); + return ( <> - <TimelinePageCardTemplate - infoArea={ - <> - <h3 className="cru-color-primary d-inline-block align-middle"> - {timeline.title} - <small className="ms-3 cru-color-secondary"> - {timeline.name} - </small> - </h3> - <div> - <UserAvatar - username={timeline.owner.username} - className="cru-avatar small cru-round me-3" - /> - {timeline.owner.nickname} - <small className="ms-3 cru-color-secondary"> - @{timeline.owner.username} - </small> - </div> - </> - } - manageItems={ - timeline.manageable - ? [ - { - type: "button", - text: "timeline.manageItem.property", - onClick: () => setDialog("property"), - }, - { type: "divider" }, - { - type: "button", - onClick: () => setDialog("delete"), - color: "danger", - text: "timeline.manageItem.delete", - }, - ] - : undefined - } - dialog={dialog} - setDialog={setDialog} - {...props} + <Card className={classnames("p-2 cru-clearfix", className)}> + <div + className={classnames( + "cru-float-right d-flex align-items-center", + !collapse && "ms-3" + )} + > + <ConnectionStatusBadge status={connectionStatus} className="me-2" /> + <CollapseButton collapse={collapse} onClick={toggleCollapse} /> + </div> + {isSmallScreen ? ( + <FullPageDialog + onBack={toggleCollapse} + show={!collapse} + contentContainerClassName="p-2" + > + {content} + </FullPageDialog> + ) : ( + <div style={{ display: collapse ? "none" : "inline" }}>{content}</div> + )} + </Card> + <TimelineMemberDialog + timeline={timeline} + onClose={() => setDialog(null)} + open={dialog === "member"} + onChange={onReload} + /> + <TimelinePropertyChangeDialog + timeline={timeline} + close={() => setDialog(null)} + open={dialog === "property"} + onChange={onReload} /> <TimelineDeleteDialog timeline={timeline} diff --git a/FrontEnd/src/views/timeline-common/TimelineDateLabel.tsx b/FrontEnd/src/views/timeline/TimelineDateLabel.tsx index 80968ee2..80968ee2 100644 --- a/FrontEnd/src/views/timeline-common/TimelineDateLabel.tsx +++ b/FrontEnd/src/views/timeline/TimelineDateLabel.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelineEmptyItem.tsx b/FrontEnd/src/views/timeline/TimelineEmptyItem.tsx index 8638ad46..8638ad46 100644 --- a/FrontEnd/src/views/timeline-common/TimelineEmptyItem.tsx +++ b/FrontEnd/src/views/timeline/TimelineEmptyItem.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelineLine.tsx b/FrontEnd/src/views/timeline/TimelineLine.tsx index 0a828b32..0a828b32 100644 --- a/FrontEnd/src/views/timeline-common/TimelineLine.tsx +++ b/FrontEnd/src/views/timeline/TimelineLine.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelineLoading.tsx b/FrontEnd/src/views/timeline/TimelineLoading.tsx index f55482fe..f55482fe 100644 --- a/FrontEnd/src/views/timeline-common/TimelineLoading.tsx +++ b/FrontEnd/src/views/timeline/TimelineLoading.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelineMember.css b/FrontEnd/src/views/timeline/TimelineMember.css index adb78764..adb78764 100644 --- a/FrontEnd/src/views/timeline-common/TimelineMember.css +++ b/FrontEnd/src/views/timeline/TimelineMember.css diff --git a/FrontEnd/src/views/timeline-common/TimelineMember.tsx b/FrontEnd/src/views/timeline/TimelineMember.tsx index 59d4c371..59d4c371 100644 --- a/FrontEnd/src/views/timeline-common/TimelineMember.tsx +++ b/FrontEnd/src/views/timeline/TimelineMember.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx b/FrontEnd/src/views/timeline/TimelinePagedPostListView.tsx index 69a5607c..69a5607c 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx +++ b/FrontEnd/src/views/timeline/TimelinePagedPostListView.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx b/FrontEnd/src/views/timeline/TimelinePostContentView.tsx index 607b72c9..607b72c9 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx +++ b/FrontEnd/src/views/timeline/TimelinePostContentView.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEdit.css b/FrontEnd/src/views/timeline/TimelinePostEdit.css index 4ce98383..4ce98383 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEdit.css +++ b/FrontEnd/src/views/timeline/TimelinePostEdit.css diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/views/timeline/TimelinePostEdit.tsx index cd61b4a7..cd61b4a7 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/views/timeline/TimelinePostEdit.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEditCard.tsx b/FrontEnd/src/views/timeline/TimelinePostEditCard.tsx index a69d413a..a69d413a 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEditCard.tsx +++ b/FrontEnd/src/views/timeline/TimelinePostEditCard.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEditNoLogin.tsx b/FrontEnd/src/views/timeline/TimelinePostEditNoLogin.tsx index 82834e95..82834e95 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEditNoLogin.tsx +++ b/FrontEnd/src/views/timeline/TimelinePostEditNoLogin.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePostListView.tsx b/FrontEnd/src/views/timeline/TimelinePostListView.tsx index f6649e9e..f6649e9e 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostListView.tsx +++ b/FrontEnd/src/views/timeline/TimelinePostListView.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx b/FrontEnd/src/views/timeline/TimelinePostView.tsx index 086176f8..086176f8 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx +++ b/FrontEnd/src/views/timeline/TimelinePostView.tsx diff --git a/FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx b/FrontEnd/src/views/timeline/TimelinePropertyChangeDialog.tsx index cd5c46da..cd5c46da 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx +++ b/FrontEnd/src/views/timeline/TimelinePropertyChangeDialog.tsx diff --git a/FrontEnd/src/views/timeline-common/index.css b/FrontEnd/src/views/timeline/index.css index 6929f9ae..6929f9ae 100644 --- a/FrontEnd/src/views/timeline-common/index.css +++ b/FrontEnd/src/views/timeline/index.css diff --git a/FrontEnd/src/views/timeline/index.tsx b/FrontEnd/src/views/timeline/index.tsx index 02d773dc..4faf8af8 100644 --- a/FrontEnd/src/views/timeline/index.tsx +++ b/FrontEnd/src/views/timeline/index.tsx @@ -1,28 +1,84 @@ import React from "react"; +import { HubConnectionState } from "@microsoft/signalr"; import { useParams } from "react-router-dom"; import { UiLogicError } from "@/common"; +import { HttpTimelineInfo } from "@/http/timeline"; +import useReverseScrollPositionRemember from "@/utilities/useReverseScrollPositionRemember"; +import { generatePalette, setPalette } from "@/palette"; -import TimelinePageTemplate from "../timeline-common/TimelinePageTemplate"; +import Timeline from "./Timeline"; import TimelineCard from "./TimelineCard"; const TimelinePage: React.FC = () => { - const { name } = useParams(); + const { owner: ownerUsername, timeline: timelineNameParam } = useParams(); - if (name == null) { - throw new UiLogicError("No route param 'name'."); - } + if (ownerUsername == null || ownerUsername == "") + throw new UiLogicError("Route param owner is not set."); + + const timelineName = + timelineNameParam == null || timelineNameParam === "" + ? "self" + : timelineNameParam; + + const [timeline, setTimeline] = React.useState<HttpTimelineInfo | null>(null); const [reloadKey, setReloadKey] = React.useState<number>(0); + const reload = (): void => setReloadKey(reloadKey + 1); + + const [connectionStatus, setConnectionStatus] = + React.useState<HubConnectionState>(HubConnectionState.Connecting); + + useReverseScrollPositionRemember(); + + React.useEffect(() => { + if (timeline != null && timeline.color != null) { + return setPalette(generatePalette({ primary: timeline.color })); + } + }, [timeline]); + + const cardCollapseLocalStorageKey = `timeline.${ownerUsername}.${timelineName}.cardCollapse`; + + const [cardCollapse, setCardCollapse] = React.useState<boolean>(true); + + React.useEffect(() => { + const savedCollapse = window.localStorage.getItem( + cardCollapseLocalStorageKey + ); + setCardCollapse(savedCollapse == null ? true : savedCollapse === "true"); + }, [cardCollapseLocalStorageKey]); + + const toggleCardCollapse = (): void => { + const newState = !cardCollapse; + setCardCollapse(newState); + window.localStorage.setItem( + cardCollapseLocalStorageKey, + newState.toString() + ); + }; return ( - <TimelinePageTemplate - timelineName={name} - notFoundI18nKey="timeline.timelineNotExist" - reloadKey={reloadKey} - CardComponent={TimelineCard} - onReload={() => setReloadKey(reloadKey + 1)} - /> + <> + {timeline != null ? ( + <TimelineCard + className="timeline-template-card" + timeline={timeline} + collapse={cardCollapse} + toggleCollapse={toggleCardCollapse} + onReload={reload} + connectionStatus={connectionStatus} + /> + ) : null} + <div className="container"> + <Timeline + timelineName={timelineName} + reloadKey={reloadKey} + onReload={reload} + onTimelineLoaded={(t) => setTimeline(t)} + onConnectionStateChanged={setConnectionStatus} + /> + </div> + </> ); }; diff --git a/FrontEnd/src/views/user/UserCard.tsx b/FrontEnd/src/views/user/UserCard.tsx deleted file mode 100644 index 739d26ee..00000000 --- a/FrontEnd/src/views/user/UserCard.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; - -import TimelinePageCardTemplate from "../timeline-common/TimelinePageCardTemplate"; -import { TimelinePageCardProps } from "../timeline-common/TimelinePageTemplate"; -import UserAvatar from "../common/user/UserAvatar"; - -const UserCard: React.FC<TimelinePageCardProps> = (props) => { - const { timeline } = props; - - const [dialog, setDialog] = React.useState<"member" | "property" | null>( - null - ); - - return ( - <> - <TimelinePageCardTemplate - infoArea={ - <> - <h3 className="cru-color-primary d-inline-block"> - {timeline.title} - <small className="ms-3 cru-color-secondary"> - {timeline.name} - </small> - </h3> - <div> - <UserAvatar - username={timeline.owner.username} - className="cru-avatar small cru-round me-3" - /> - {timeline.owner.nickname} - </div> - </> - } - manageItems={ - timeline.manageable - ? [ - { - type: "button", - text: "timeline.manageItem.property", - onClick: () => setDialog("property"), - }, - ] - : undefined - } - dialog={dialog} - setDialog={setDialog} - {...props} - /> - </> - ); -}; - -export default UserCard; diff --git a/FrontEnd/src/views/user/index.css b/FrontEnd/src/views/user/index.css deleted file mode 100644 index e69de29b..00000000 --- a/FrontEnd/src/views/user/index.css +++ /dev/null diff --git a/FrontEnd/src/views/user/index.tsx b/FrontEnd/src/views/user/index.tsx deleted file mode 100644 index 7913b788..00000000 --- a/FrontEnd/src/views/user/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from "react"; -import { useParams } from "react-router-dom"; - -import TimelinePageTemplate from "../timeline-common/TimelinePageTemplate"; -import UserCard from "./UserCard"; - -import { UiLogicError } from "@/common"; - -import "./index.css"; - -const UserPage: React.FC = () => { - const { username } = useParams(); - - if (username == null) { - throw new UiLogicError("No route param 'username'."); - } - - const [reloadKey, setReloadKey] = React.useState<number>(0); - - let dialogElement: React.ReactElement | undefined; - - return ( - <> - <TimelinePageTemplate - timelineName={`@${username}`} - notFoundI18nKey="timeline.userNotExist" - reloadKey={reloadKey} - onReload={() => setReloadKey(reloadKey + 1)} - CardComponent={UserCard} - /> - {dialogElement} - </> - ); -}; - -export default UserPage; |