diff options
-rw-r--r-- | BackEnd/Timeline/Models/Http/HttpTimeline.cs | 7 | ||||
-rw-r--r-- | BackEnd/Timeline/Models/Http/HttpTimelinePost.cs | 14 | ||||
-rw-r--r-- | BackEnd/Timeline/Services/Mapper/TimelineMapper.cs | 3 | ||||
-rw-r--r-- | FrontEnd/package.json | 8 | ||||
-rw-r--r-- | FrontEnd/src/App.tsx | 5 | ||||
-rw-r--r-- | FrontEnd/src/http/timeline.ts | 136 | ||||
-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 |
36 files changed, 385 insertions, 440 deletions
diff --git a/BackEnd/Timeline/Models/Http/HttpTimeline.cs b/BackEnd/Timeline/Models/Http/HttpTimeline.cs index e3e46bd5..83398baf 100644 --- a/BackEnd/Timeline/Models/Http/HttpTimeline.cs +++ b/BackEnd/Timeline/Models/Http/HttpTimeline.cs @@ -10,11 +10,12 @@ namespace Timeline.Models.Http {
public HttpTimeline() { }
- public HttpTimeline(string uniqueId, string title, string name, DateTime nameLastModifed, string description, HttpUser owner, TimelineVisibility visibility, List<HttpUser> members, string? color, DateTime createTime, DateTime lastModified, bool isHighlight, bool isBookmark, bool manageable, bool postable, HttpTimelineLinks links)
+ public HttpTimeline(string uniqueId, string title, string name, string nameV2, DateTime nameLastModifed, string description, HttpUser owner, TimelineVisibility visibility, List<HttpUser> members, string? color, DateTime createTime, DateTime lastModified, bool isHighlight, bool isBookmark, bool manageable, bool postable, HttpTimelineLinks links)
{
UniqueId = uniqueId;
Title = title;
Name = name;
+ NameV2 = nameV2;
NameLastModifed = nameLastModifed;
Description = description;
Owner = owner;
@@ -43,6 +44,10 @@ namespace Timeline.Models.Http /// </summary>
public string Name { get; set; } = default!;
/// <summary>
+ /// Name of timeline.
+ /// </summary>
+ public string NameV2 { get; set; } = default!;
+ /// <summary>
/// Last modified time of timeline name.
/// </summary>
public DateTime NameLastModifed { get; set; } = default!;
diff --git a/BackEnd/Timeline/Models/Http/HttpTimelinePost.cs b/BackEnd/Timeline/Models/Http/HttpTimelinePost.cs index 5e069821..5c6a7167 100644 --- a/BackEnd/Timeline/Models/Http/HttpTimelinePost.cs +++ b/BackEnd/Timeline/Models/Http/HttpTimelinePost.cs @@ -11,7 +11,7 @@ namespace Timeline.Models.Http {
public HttpTimelinePost() { }
- public HttpTimelinePost(long id, List<HttpTimelinePostDataDigest> dataList, bool deleted, DateTime time, HttpUser? author, string? color, DateTime lastUpdated, string timelineName, bool editable)
+ public HttpTimelinePost(long id, List<HttpTimelinePostDataDigest> dataList, bool deleted, DateTime time, HttpUser? author, string? color, DateTime lastUpdated, string timelineOwnerV2, string timelineNameV2, string timelineName, bool editable)
{
Id = id;
DataList = dataList;
@@ -20,6 +20,8 @@ namespace Timeline.Models.Http Author = author;
Color = color;
LastUpdated = lastUpdated;
+ TimelineOwnerV2 = timelineOwnerV2;
+ TimelineNameV2 = timelineNameV2;
TimelineName = timelineName;
Editable = editable;
}
@@ -27,7 +29,7 @@ namespace Timeline.Models.Http /// <summary>
/// Post id.
/// </summary>
- public long Id { get; set; }
+ public long Id { get; set; } /// <summary>
/// The data list.
/// </summary>
@@ -54,6 +56,14 @@ namespace Timeline.Models.Http /// Last updated time.
/// </summary>
public DateTime LastUpdated { get; set; } = default!;
+ /// <summary> + /// Timeline owner username. + /// </summary>
+ public string TimelineOwnerV2 { get; set; } = default!;
+ /// <summary>
+ /// Timeline name.
+ /// </summary>
+ public string TimelineNameV2 { get; set; } = default!;
/// <summary>
/// Timeline name.
/// </summary>
diff --git a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs index 2a0ba89f..a59b906c 100644 --- a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs +++ b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs @@ -77,6 +77,7 @@ namespace Timeline.Services.Mapper uniqueId: entity.UniqueId,
title: string.IsNullOrEmpty(entity.Title) ? timelineName : entity.Title,
name: timelineName,
+ nameV2: entity.Name is null ? "self" : entity.Name,
nameLastModifed: entity.NameLastModified,
description: entity.Description ?? "",
owner: await _userMapper.MapAsync(entity.Owner, urlHelper, user),
@@ -137,6 +138,8 @@ namespace Timeline.Services.Mapper color: entity.Color,
deleted: entity.Deleted,
lastUpdated: entity.LastUpdated,
+ timelineOwnerV2: entity.Timeline.Owner.Username, + timelineNameV2: entity.Timeline.Name is null ? "self" : entity.Timeline.Name,
timelineName: CalculateTimelineName(entity.Timeline),
editable: editable
);
diff --git a/FrontEnd/package.json b/FrontEnd/package.json index b1fc3b2f..ffe9a641 100644 --- a/FrontEnd/package.json +++ b/FrontEnd/package.json @@ -20,9 +20,9 @@ "lodash": "^4.17.21",
"moment": "^2.29.2",
"pepjs": "^0.5.3",
- "react": "^18.0.0",
+ "react": "^17.0.0",
"react-color": "^2.19.3",
- "react-dom": "^18.0.0",
+ "react-dom": "^17.0.0",
"react-i18next": "^11.16.2",
"react-popper": "^2.2.5",
"react-responsive": "^8.2.0",
@@ -34,8 +34,8 @@ "workbox-cacheable-response": "^6.5.2",
"workbox-expiration": "^6.5.2",
"workbox-precaching": "^6.5.2",
- "workbox-routing": "^6.5.0",
- "workbox-strategies": "^6.5.1",
+ "workbox-routing": "^6.5.2",
+ "workbox-strategies": "^6.5.2",
"workbox-window": "^6.5.2",
"xregexp": "^5.1.0"
},
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 { <Route path="/login" element={<Login />} /> <Route path="/settings" element={<Settings />} /> <Route path="/about" element={<About />} /> - <Route path="/timelines/:name" element={<TimelinePage />} /> - <Route path="/users/:username" element={<User />} /> + <Route path="/:owner" element={<TimelinePage />} /> + <Route path="/:owner/:timeline" element={<TimelinePage />} /> <Route path="/search" element={<Search />} /> <Route path="/admin" element={<Admin />} /> <Route element={<NoMatch />} /> 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<HttpTimelineInfo[]>; - getTimeline(timelineName: string): Promise<HttpTimelineInfo>; + getTimeline( + ownerUsername: string, + timelineName: string + ): Promise<HttpTimelineInfo>; postTimeline(req: HttpTimelinePostRequest): Promise<HttpTimelineInfo>; patchTimeline( + ownerUsername: string, timelineName: string, req: HttpTimelinePatchRequest ): Promise<HttpTimelineInfo>; - deleteTimeline(timelineName: string): Promise<void>; - memberPut(timelineName: string, username: string): Promise<void>; - memberDelete(timelineName: string, username: string): Promise<void>; - listPost(timelineName: string): Promise<HttpTimelinePostInfo[]>; - generatePostDataUrl(timelineName: string, postId: number): string; - getPostDataAsString(timelineName: string, postId: number): Promise<string>; + deleteTimeline(ownerUsername: string, timelineName: string): Promise<void>; + memberPut( + ownerUsername: string, + timelineName: string, + username: string + ): Promise<void>; + memberDelete( + ownerUsername: string, + timelineName: string, + username: string + ): Promise<void>; + listPost( + ownerUsername: string, + timelineName: string + ): Promise<HttpTimelinePostInfo[]>; + generatePostDataUrl( + ownerUsername: string, + timelineName: string, + postId: number + ): string; + getPostDataAsString( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise<string>; postPost( + ownerUsername: string, timelineName: string, req: HttpTimelinePostPostRequest ): Promise<HttpTimelinePostInfo>; patchPost( + ownerUsername: string, timelineName: string, postId: number, req: HttpTimelinePostPatchRequest ): Promise<HttpTimelinePostInfo>; - deletePost(timelineName: string, postId: number): Promise<void>; + deletePost( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise<void>; } export class HttpTimelineClient implements IHttpTimelineClient { @@ -123,63 +153,97 @@ export class HttpTimelineClient implements IHttpTimelineClient { .then(extractResponseData); } - getTimeline(timelineName: string): Promise<HttpTimelineInfo> { + getTimeline( + ownerUsername: string, + timelineName: string + ): Promise<HttpTimelineInfo> { return axios - .get<HttpTimelineInfo>(`${apiBaseUrl}/timelines/${timelineName}`) + .get<HttpTimelineInfo>( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}` + ) .then(extractResponseData); } postTimeline(req: HttpTimelinePostRequest): Promise<HttpTimelineInfo> { return axios - .post<HttpTimelineInfo>(`${apiBaseUrl}/timelines`, req) + .post<HttpTimelineInfo>(`${apiBaseUrl}/v2/timelines`, req) .then(extractResponseData) - .catch(convertToIfErrorCodeIs(11040101, HttpTimelineNameConflictError)); + .catch(convertToIfStatusCodeIs(422, HttpTimelineNameConflictError)); } patchTimeline( + ownerUsername: string, timelineName: string, req: HttpTimelinePatchRequest ): Promise<HttpTimelineInfo> { return axios - .patch<HttpTimelineInfo>(`${apiBaseUrl}/timelines/${timelineName}`, req) + .patch<HttpTimelineInfo>( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}`, + req + ) .then(extractResponseData); } - deleteTimeline(timelineName: string): Promise<void> { - return axios.delete(`${apiBaseUrl}/timelines/${timelineName}`).then(); + deleteTimeline(ownerUsername: string, timelineName: string): Promise<void> { + return axios + .delete(`${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}`) + .then(); } - memberPut(timelineName: string, username: string): Promise<void> { + memberPut( + ownerUsername: string, + timelineName: string, + username: string + ): Promise<void> { return axios - .put(`${apiBaseUrl}/timelines/${timelineName}/members/${username}`) + .put( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/members/${username}` + ) .then(); } - memberDelete(timelineName: string, username: string): Promise<void> { + memberDelete( + ownerUsername: string, + timelineName: string, + username: string + ): Promise<void> { return axios - .delete(`${apiBaseUrl}/timelines/${timelineName}/members/${username}`) + .delete( + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/members/${username}` + ) .then(); } - listPost(timelineName: string): Promise<HttpTimelinePostInfo[]> { + listPost( + ownerUsername: string, + timelineName: string + ): Promise<HttpTimelinePostInfo[]> { return axios .get<HttpTimelinePostInfo[]>( - `${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<string> { + getPostDataAsString( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise<string> { return axios .get<string>( - `${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<HttpTimelinePostInfo> { return axios .post<HttpTimelinePostInfo>( - `${apiBaseUrl}/timelines/${timelineName}/posts`, + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts`, req ) .then(extractResponseData); } patchPost( + ownerUsername: string, timelineName: string, postId: number, req: HttpTimelinePostPatchRequest ): Promise<HttpTimelinePostInfo> { return axios .patch<HttpTimelinePostInfo>( - `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}`, + `${apiBaseUrl}/v2/timelines/${ownerUsername}/${timelineName}/posts/${postId}`, req ) .then(extractResponseData); } - deletePost(timelineName: string, postId: number): Promise<void> { + deletePost( + ownerUsername: string, + timelineName: string, + postId: number + ): Promise<void> { 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/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; |