aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/app/views
diff options
context:
space:
mode:
Diffstat (limited to 'FrontEnd/src/app/views')
-rw-r--r--FrontEnd/src/app/views/admin/UserAdmin.tsx31
-rw-r--r--FrontEnd/src/app/views/home/BoardWithUser.tsx12
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx20
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelineMember.tsx78
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx238
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx61
-rw-r--r--FrontEnd/src/app/views/timeline/TimelineInfoCard.tsx6
-rw-r--r--FrontEnd/src/app/views/user/UserInfoCard.tsx6
8 files changed, 220 insertions, 232 deletions
diff --git a/FrontEnd/src/app/views/admin/UserAdmin.tsx b/FrontEnd/src/app/views/admin/UserAdmin.tsx
index d66abbec..fbdfd5a3 100644
--- a/FrontEnd/src/app/views/admin/UserAdmin.tsx
+++ b/FrontEnd/src/app/views/admin/UserAdmin.tsx
@@ -62,7 +62,7 @@ const UsernameLabel: React.FC = (props) => {
const UserDeleteDialog: React.FC<
DialogProps<{ username: string }, unknown>
-> = ({ open, close, token, data: { username }, onSuccess }) => {
+> = ({ open, close, data: { username }, onSuccess }) => {
return (
<OperationDialog
open={open}
@@ -74,7 +74,7 @@ const UserDeleteDialog: React.FC<
0<UsernameLabel>{username}</UsernameLabel>2
</Trans>
)}
- onProcess={() => getHttpUserClient().delete(username, token)}
+ onProcess={() => getHttpUserClient().delete(username)}
onSuccessAndClose={onSuccess}
/>
);
@@ -87,7 +87,7 @@ const UserModifyDialog: React.FC<
},
HttpUser
>
-> = ({ open, close, token, data: { oldUser }, onSuccess }) => {
+> = ({ open, close, data: { oldUser }, onSuccess }) => {
return (
<OperationDialog
open={open}
@@ -115,15 +115,11 @@ const UserModifyDialog: React.FC<
] as const
}
onProcess={([username, password, nickname]) =>
- getHttpUserClient().patch(
- oldUser.username,
- {
- username: username !== oldUser.username ? username : undefined,
- password: password !== "" ? password : undefined,
- nickname: nickname !== oldUser.nickname ? nickname : undefined,
- },
- token
- )
+ getHttpUserClient().patch(oldUser.username, {
+ username: username !== oldUser.username ? username : undefined,
+ password: password !== "" ? password : undefined,
+ nickname: nickname !== oldUser.nickname ? nickname : undefined,
+ })
}
onSuccessAndClose={onSuccess}
/>
@@ -138,7 +134,7 @@ const UserPermissionModifyDialog: React.FC<
},
UserPermission[]
>
-> = ({ open, close, token, data: { username, permissions }, onSuccess }) => {
+> = ({ open, close, data: { username, permissions }, onSuccess }) => {
const oldPermissionBoolList: boolean[] = kUserPermissionList.map(
(permission) => permissions.includes(permission)
);
@@ -168,16 +164,11 @@ const UserPermissionModifyDialog: React.FC<
const permission = kUserPermissionList[index];
if (oldValue === newValue) continue;
if (newValue) {
- await getHttpUserClient().putUserPermission(
- username,
- permission,
- token
- );
+ await getHttpUserClient().putUserPermission(username, permission);
} else {
await getHttpUserClient().deleteUserPermission(
username,
- permission,
- token
+ permission
);
}
}
diff --git a/FrontEnd/src/app/views/home/BoardWithUser.tsx b/FrontEnd/src/app/views/home/BoardWithUser.tsx
index 8afe440b..ba22916c 100644
--- a/FrontEnd/src/app/views/home/BoardWithUser.tsx
+++ b/FrontEnd/src/app/views/home/BoardWithUser.tsx
@@ -20,11 +20,11 @@ const BoardWithUser: React.FC<{ user: AuthUser }> = ({ user }) => {
<Col xs="12" md="6">
<TimelineBoard
title={t("home.bookmarkTimeline")}
- load={() => getHttpBookmarkClient().list(user.token)}
+ load={() => getHttpBookmarkClient().list()}
editHandler={{
onDelete: (timeline) => {
return getHttpBookmarkClient()
- .delete(timeline, user.token)
+ .delete(timeline)
.catch((e) => {
pushAlert({
message: {
@@ -39,8 +39,7 @@ const BoardWithUser: React.FC<{ user: AuthUser }> = ({ user }) => {
onMove: (timeline, index, offset) => {
return getHttpBookmarkClient()
.move(
- { timeline, newPosition: index + offset + 1 }, // +1 because backend contract: index starts at 1
- user.token
+ { timeline, newPosition: index + offset + 1 } // +1 because backend contract: index starts at 1
)
.catch((e) => {
pushAlert({
@@ -75,7 +74,7 @@ const BoardWithUser: React.FC<{ user: AuthUser }> = ({ user }) => {
? {
onDelete: (timeline) => {
return getHttpHighlightClient()
- .delete(timeline, user.token)
+ .delete(timeline)
.catch((e) => {
pushAlert({
message: {
@@ -90,8 +89,7 @@ const BoardWithUser: React.FC<{ user: AuthUser }> = ({ user }) => {
onMove: (timeline, index, offset) => {
return getHttpHighlightClient()
.move(
- { timeline, newPosition: index + offset + 1 }, // +1 because backend contract: index starts at 1
- user.token
+ { timeline, newPosition: index + offset + 1 } // +1 because backend contract: index starts at 1
)
.catch((e) => {
pushAlert({
diff --git a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
index ece1942f..e62f76fa 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
@@ -10,7 +10,11 @@ import SyncStatusBadge from "../timeline-common/SyncStatusBadge";
import CollapseButton from "../timeline-common/CollapseButton";
export interface TimelineCardTemplateProps
- extends Omit<TimelineCardComponentProps<"">, "onManage" | "onMember"> {
+ extends Omit<TimelineCardComponentProps<"">, "operations"> {
+ operations: Pick<
+ TimelineCardComponentProps<"">["operations"],
+ "onHighlight" | "onBookmark"
+ >;
infoArea: React.ReactElement;
manageArea:
| { type: "member"; onMember: () => void }
@@ -33,13 +37,13 @@ function TimelineCardTemplate({
collapse,
infoArea,
manageArea,
- onBookmark,
- onHighlight,
+ operations,
toggleCollapse,
syncStatus,
className,
}: TimelineCardTemplateProps): React.ReactElement | null {
const { t } = useTranslation();
+ const { onBookmark, onHighlight } = operations;
return (
<div className={clsx("cru-card p-2 clearfix", className)}>
@@ -56,13 +60,19 @@ function TimelineCardTemplate({
<div className="text-right mt-2">
{onHighlight != null ? (
<i
- className="bi-star icon-button text-yellow mr-3"
+ className={clsx(
+ timeline.isHighlight ? "bi-star-fill" : "bi-star",
+ "icon-button text-yellow mr-3"
+ )}
onClick={onHighlight}
/>
) : null}
{onBookmark != null ? (
<i
- className="bi-bookmark icon-button text-yellow mr-3"
+ className={clsx(
+ timeline.isBookmark ? "bi-bookmark-fill" : "bi-bookmark",
+ "icon-button text-yellow mr-3"
+ )}
onClick={onBookmark}
/>
) : null}
diff --git a/FrontEnd/src/app/views/timeline-common/TimelineMember.tsx b/FrontEnd/src/app/views/timeline-common/TimelineMember.tsx
index 089d11a0..efa7e971 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelineMember.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelineMember.tsx
@@ -3,6 +3,8 @@ import { useTranslation } from "react-i18next";
import { Container, ListGroup, Modal, Row, Col, Button } from "react-bootstrap";
import { User, useAvatar } from "@/services/user";
+import { TimelineInfo, timelineService } from "@/services/timeline";
+import { getHttpUserClient, HttpUserNotExistError } from "@/http/user";
import SearchInput from "../common/SearchInput";
import BlobImage from "../common/BlobImage";
@@ -52,15 +54,9 @@ const TimelineMemberItem: React.FC<{
);
};
-export interface TimelineMemberCallbacks {
- onCheckUser: (username: string) => Promise<User | null>;
- onAddUser: (user: User) => Promise<void>;
- onRemoveUser: (username: string) => void;
-}
-
export interface TimelineMemberProps {
- members: User[];
- edit: TimelineMemberCallbacks | null | undefined;
+ timeline: TimelineInfo;
+ editable: boolean;
}
const TimelineMember: React.FC<TimelineMemberProps> = (props) => {
@@ -81,7 +77,9 @@ const TimelineMember: React.FC<TimelineMemberProps> = (props) => {
userSearchState.type === "user" ? userSearchState.data.username : undefined
);
- const members = props.members;
+ const { timeline } = props;
+
+ const members = [timeline.owner, ...timeline.members];
return (
<Container className="px-4 py-3">
@@ -91,13 +89,21 @@ const TimelineMember: React.FC<TimelineMemberProps> = (props) => {
key={member.username}
user={member}
owner={index === 0}
- onRemove={props.edit?.onRemoveUser}
+ onRemove={
+ props.editable
+ ? () => {
+ void timelineService.removeMember(
+ timeline.name,
+ member.username
+ );
+ }
+ : undefined
+ }
/>
))}
</ListGroup>
{(() => {
- const edit = props.edit;
- if (edit != null) {
+ if (props.editable) {
return (
<>
<SearchInput
@@ -115,26 +121,34 @@ const TimelineMember: React.FC<TimelineMemberProps> = (props) => {
});
return;
}
-
setUserSearchState({ type: "loading" });
- edit.onCheckUser(userSearchText).then(
- (u) => {
- if (u == null) {
+ getHttpUserClient()
+ .get(userSearchText)
+ .catch((e) => {
+ if (e instanceof HttpUserNotExistError) {
+ return null;
+ } else {
+ throw e;
+ }
+ })
+ .then(
+ (u) => {
+ if (u == null) {
+ setUserSearchState({
+ type: "error",
+ data: "timeline.userNotExist",
+ });
+ } else {
+ setUserSearchState({ type: "user", data: u });
+ }
+ },
+ (e) => {
setUserSearchState({
type: "error",
- data: "timeline.userNotExist",
+ data: `${e as string}`,
});
- } else {
- setUserSearchState({ type: "user", data: u });
}
- },
- (e) => {
- setUserSearchState({
- type: "error",
- data: `${e as string}`,
- });
- }
- );
+ );
}}
/>
{(() => {
@@ -166,10 +180,12 @@ const TimelineMember: React.FC<TimelineMemberProps> = (props) => {
className="align-self-center"
disabled={!addable}
onClick={() => {
- void edit.onAddUser(u).then((_) => {
- setUserSearchText("");
- setUserSearchState({ type: "init" });
- });
+ void timelineService
+ .addMember(timeline.name, u.username)
+ .then(() => {
+ setUserSearchText("");
+ setUserSearchState({ type: "init" });
+ });
}}
>
{t("timeline.member.add")}
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
index 7f5c8206..f8b2b38b 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
@@ -1,16 +1,10 @@
import React from "react";
import { useTranslation } from "react-i18next";
-import { of } from "rxjs";
-import { catchError } from "rxjs/operators";
import { UiLogicError } from "@/common";
import { pushAlert } from "@/services/alert";
-import { useUser, userInfoService, UserNotExistError } from "@/services/user";
-import {
- timelineService,
- usePostList,
- useTimelineInfo,
-} from "@/services/timeline";
+import { useUser } from "@/services/user";
+import { timelineService, usePosts, useTimeline } from "@/services/timeline";
import { getHttpBookmarkClient } from "@/http/bookmark";
import { getHttpHighlightClient } from "@/http/highlight";
@@ -18,8 +12,8 @@ import { TimelineMemberDialog } from "./TimelineMember";
import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog";
import { TimelinePageTemplateUIProps } from "./TimelinePageTemplateUI";
import { TimelinePostSendCallback } from "./TimelinePostEdit";
-import { TimelineSyncStatus } from "./SyncStatusBadge";
import { TimelinePostInfoEx } from "./Timeline";
+import { mergeDataStatus } from "@/services/DataHub2";
export interface TimelinePageTemplateProps<TManageItem> {
name: string;
@@ -45,8 +39,8 @@ export default function TimelinePageTemplate<TManageItem>(
null
);
- const timelineState = useTimelineInfo(name);
- const postListState = usePostList(name);
+ const timelineAndStatus = useTimeline(name);
+ const postsAndState = usePosts(name);
const onPost: TimelinePostSendCallback = React.useCallback(
(req) => {
@@ -68,111 +62,104 @@ export default function TimelinePageTemplate<TManageItem>(
[onManageProp]
);
- const childProps = ((): [
- data: TimelinePageTemplateUIProps<TManageItem>["data"],
- syncStatus: TimelineSyncStatus
- ] => {
- if (timelineState == null) {
- return [undefined, "syncing"];
+ const data = ((): TimelinePageTemplateUIProps<TManageItem>["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 { type, timeline } = timelineState;
- if (timeline == null) {
- if (type === "offline") {
- return [{ type: "custom", value: "Network Error" }, "offline"];
- } else if (type === "synced") {
- return [props.notFoundI18nKey, "synced"];
+ const posts = ((): TimelinePostInfoEx[] | "forbid" | undefined => {
+ const { data: postsInfo } = postsAndState;
+ if (postsInfo === "forbid") {
+ return "forbid";
+ } else if (postsInfo == null || postsInfo === "notexist") {
+ return undefined;
} else {
- return [undefined, "syncing"];
- }
- } else {
- if (postListState != null && postListState.type === "notexist") {
- return [props.notFoundI18nKey, "synced"];
- }
- if (postListState != null && postListState.type === "forbid") {
- return ["timeline.messageCantSee", "synced"];
+ 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 posts:
- | TimelinePostInfoEx[]
- | undefined = postListState?.posts?.map((post) => ({
- ...post,
- onDelete: service.hasModifyPostPermission(user, timeline, post)
+ })();
+
+ const operations = {
+ onPost: service.hasPostPermission(user, timeline) ? onPost : undefined,
+ onManage: service.hasManagePermission(user, timeline)
+ ? onManage
+ : undefined,
+ onMember: () => setDialog("member"),
+ onBookmark:
+ user != null
? () => {
- service.deletePost(name, post.id).subscribe({
- error: () => {
+ 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",
- message: t("timeline.deletePostFailed"),
});
- },
- });
+ }
+ );
}
: undefined,
- }));
-
- const others = {
- onPost: service.hasPostPermission(user, timeline)
- ? onPost
- : undefined,
- onManage: service.hasManagePermission(user, timeline)
- ? onManage
- : undefined,
- onMember: () => setDialog("member"),
- onBookmark:
- user != null
- ? () => {
- void getHttpBookmarkClient()
- .put(name, user.token)
- .then(() => {
- pushAlert({
- message: {
- type: "i18n",
- key: "timeline.addBookmarkSuccess",
- },
- type: "success",
- });
- });
- }
- : undefined,
- onHighlight:
- user != null && user.hasHighlightTimelineAdministrationPermission
- ? () => {
- void getHttpHighlightClient()
- .put(name, user.token)
- .then(() => {
- pushAlert({
- message: {
- type: "i18n",
- key: "timeline.addHighlightSuccess",
- },
- type: "success",
- });
+ 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,
- };
+ }
+ );
+ }
+ : undefined,
+ };
- if (type === "cache") {
- return [{ timeline, posts, ...others }, "syncing"];
- } else if (type === "offline") {
- return [{ timeline, posts, ...others }, "offline"];
- } else {
- if (postListState == null) {
- return [{ timeline, posts, ...others }, "syncing"];
- } else {
- const { type: postListType } = postListState;
- if (postListType === "synced") {
- return [{ timeline, posts, ...others }, "synced"];
- } else if (postListType === "cache") {
- return [{ timeline, posts, ...others }, "syncing"];
- } else if (postListType === "offline") {
- return [{ timeline, posts, ...others }, "offline"];
- }
- }
- }
- }
+ return { timeline, posts, operations };
}
- throw new UiLogicError("Failed to calculate TimelinePageUITemplate props.");
})();
const closeDialog = React.useCallback((): void => {
@@ -181,10 +168,10 @@ export default function TimelinePageTemplate<TManageItem>(
let dialogElement: React.ReactElement | undefined;
- const timeline = timelineState?.timeline;
+ const timeline = timelineAndStatus?.data;
if (dialog === "property") {
- if (timeline == null) {
+ if (timeline == null || timeline === "notexist") {
throw new UiLogicError(
"Timeline is null but attempt to open change property dialog."
);
@@ -205,7 +192,7 @@ export default function TimelinePageTemplate<TManageItem>(
/>
);
} else if (dialog === "member") {
- if (timeline == null) {
+ if (timeline == null || timeline === "notexist") {
throw new UiLogicError(
"Timeline is null but attempt to open change property dialog."
);
@@ -215,33 +202,8 @@ export default function TimelinePageTemplate<TManageItem>(
<TimelineMemberDialog
open
onClose={closeDialog}
- members={[timeline.owner, ...timeline.members]}
- edit={
- service.hasManagePermission(user, timeline)
- ? {
- onCheckUser: (u) => {
- return userInfoService
- .getUserInfo(u)
- .pipe(
- catchError((e) => {
- if (e instanceof UserNotExistError) {
- return of(null);
- } else {
- throw e;
- }
- })
- )
- .toPromise();
- },
- onAddUser: (u) => {
- return service.addMember(name, u.username).toPromise().then();
- },
- onRemoveUser: (u) => {
- service.removeMember(name, u);
- },
- }
- : null
- }
+ timeline={timeline}
+ editable={service.hasManagePermission(user, timeline)}
/>
);
}
@@ -250,7 +212,13 @@ export default function TimelinePageTemplate<TManageItem>(
return (
<>
- <UiComponent data={childProps[0]} syncStatus={childProps[1]} />
+ <UiComponent
+ data={data}
+ syncStatus={mergeDataStatus([
+ timelineAndStatus.status,
+ postsAndState.status,
+ ])}
+ />
{dialogElement}
</>
);
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
index 20ec6e43..41246175 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
@@ -13,26 +13,30 @@ import { TimelineSyncStatus } from "./SyncStatusBadge";
export interface TimelineCardComponentProps<TManageItems> {
timeline: TimelineInfo;
- onManage?: (item: TManageItems | "property") => void;
- onMember: () => void;
- onBookmark?: () => void;
- onHighlight?: () => void;
- className?: string;
- collapse: boolean;
syncStatus: TimelineSyncStatus;
+ operations: {
+ onManage?: (item: TManageItems | "property") => void;
+ onMember: () => void;
+ onBookmark?: () => void;
+ onHighlight?: () => void;
+ };
+ collapse: boolean;
toggleCollapse: () => void;
+ className?: string;
}
export interface TimelinePageTemplateUIProps<TManageItems> {
data?:
| {
timeline: TimelineInfo;
- posts?: TimelinePostInfoEx[];
- onManage?: (item: TManageItems | "property") => void;
- onMember: () => void;
- onBookmark?: () => void;
- onHighlight?: () => void;
- onPost?: TimelinePostSendCallback;
+ posts?: TimelinePostInfoEx[] | "forbid";
+ operations: {
+ onManage?: (item: TManageItems | "property") => void;
+ onMember: () => void;
+ onBookmark?: () => void;
+ onHighlight?: () => void;
+ onPost?: TimelinePostSendCallback;
+ };
}
| I18nText;
syncStatus: TimelineSyncStatus;
@@ -155,32 +159,33 @@ export default function TimelinePageTemplateUI<TManageItems>(
<CardComponent
className="timeline-template-card"
timeline={data.timeline}
- onManage={data.onManage}
- onMember={data.onMember}
- onBookmark={data.onBookmark}
- onHighlight={data.onHighlight}
+ operations={data.operations}
syncStatus={syncStatus}
collapse={cardCollapse}
toggleCollapse={toggleCardCollapse}
/>
) : null}
{posts != null ? (
- <div
- className="timeline-container"
- style={{ minHeight: `calc(100vh - ${56 + bottomSpaceHeight}px)` }}
- >
- <Timeline
- containerRef={timelineRef}
- posts={posts}
- onResize={triggerResizeEvent}
- />
- </div>
+ posts === "forbid" ? (
+ <div>{t("timeline.messageCantSee")}</div>
+ ) : (
+ <div
+ className="timeline-container"
+ style={{ minHeight: `calc(100vh - ${56 + bottomSpaceHeight}px)` }}
+ >
+ <Timeline
+ containerRef={timelineRef}
+ posts={posts}
+ onResize={triggerResizeEvent}
+ />
+ </div>
+ )
) : (
<div className="full-viewport-center-child">
<Spinner variant="primary" animation="grow" />
</div>
)}
- {data != null && data.onPost != null ? (
+ {data != null && data.operations.onPost != null ? (
<>
<div
style={{ height: bottomSpaceHeight }}
@@ -188,7 +193,7 @@ export default function TimelinePageTemplateUI<TManageItems>(
/>
<TimelinePostEdit
className="fixed-bottom"
- onPost={data.onPost}
+ onPost={data.operations.onPost}
onHeightChange={onPostEditHeightChange}
timelineUniqueId={data.timeline.uniqueId}
/>
diff --git a/FrontEnd/src/app/views/timeline/TimelineInfoCard.tsx b/FrontEnd/src/app/views/timeline/TimelineInfoCard.tsx
index f4dbb67d..920f504d 100644
--- a/FrontEnd/src/app/views/timeline/TimelineInfoCard.tsx
+++ b/FrontEnd/src/app/views/timeline/TimelineInfoCard.tsx
@@ -13,8 +13,8 @@ export type OrdinaryTimelineManageItem = "delete";
export type TimelineInfoCardProps = TimelineCardComponentProps<OrdinaryTimelineManageItem>;
const TimelineInfoCard: React.FC<TimelineInfoCardProps> = (props) => {
- const { onMember, onManage, ...otherProps } = props;
- const { timeline } = props;
+ const { timeline, operations } = props;
+ const { onManage, onMember } = operations;
const avatar = useAvatar(timeline?.owner?.username);
@@ -66,7 +66,7 @@ const TimelineInfoCard: React.FC<TimelineInfoCardProps> = (props) => {
};
}
})()}
- {...otherProps}
+ {...props}
/>
);
};
diff --git a/FrontEnd/src/app/views/user/UserInfoCard.tsx b/FrontEnd/src/app/views/user/UserInfoCard.tsx
index f31a939f..01d2c096 100644
--- a/FrontEnd/src/app/views/user/UserInfoCard.tsx
+++ b/FrontEnd/src/app/views/user/UserInfoCard.tsx
@@ -13,8 +13,8 @@ export type PersonalTimelineManageItem = "avatar" | "nickname";
export type UserInfoCardProps = TimelineCardComponentProps<PersonalTimelineManageItem>;
const UserInfoCard: React.FC<UserInfoCardProps> = (props) => {
- const { onMember, onManage, ...otherProps } = props;
- const { timeline } = props;
+ const { timeline, operations } = props;
+ const { onManage, onMember } = operations;
const avatar = useAvatar(timeline?.owner?.username);
@@ -66,7 +66,7 @@ const UserInfoCard: React.FC<UserInfoCardProps> = (props) => {
};
}
})()}
- {...otherProps}
+ {...props}
/>
);
};