aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src
diff options
context:
space:
mode:
Diffstat (limited to 'FrontEnd/src')
-rw-r--r--FrontEnd/src/app/services/DataHub2.ts60
-rw-r--r--FrontEnd/src/app/services/timeline.ts80
-rw-r--r--FrontEnd/src/app/services/user.ts14
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx132
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx86
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx23
-rw-r--r--FrontEnd/src/app/views/user/index.tsx27
7 files changed, 180 insertions, 242 deletions
diff --git a/FrontEnd/src/app/services/DataHub2.ts b/FrontEnd/src/app/services/DataHub2.ts
index 50ae919b..f0fb724b 100644
--- a/FrontEnd/src/app/services/DataHub2.ts
+++ b/FrontEnd/src/app/services/DataHub2.ts
@@ -32,6 +32,8 @@ export class DataLine2<TData> {
private _current: DataAndStatus<TData> | null = null;
private _observers: Subscriber<DataAndStatus<TData>>[] = [];
+ private _syncPromise: Promise<void> | null = null;
+
get currentData(): DataAndStatus<TData> | null {
return this._current;
}
@@ -50,7 +52,7 @@ export class DataLine2<TData> {
}
subscribe(subsriber: Subscriber<DataAndStatus<TData>>): void {
- this.sync(); // TODO: Should I sync at this point or let the user sync explicitly.
+ void this.sync(); // TODO: Should I sync at this point or let the user sync explicitly.
this._observers.push(subsriber);
const { currentData } = this;
if (currentData != null) {
@@ -76,36 +78,44 @@ export class DataLine2<TData> {
});
}
- sync(): void {
- const { currentData } = this;
- if (currentData != null && currentData.status === "syncing") return;
- this.next({ data: currentData?.data ?? null, status: "syncing" });
- void this.config.getSavedData().then((savedData) => {
- if (currentData == null && savedData != null) {
- this.next({ data: savedData, status: "syncing" });
- }
- return this.config.fetchData(savedData).then((data) => {
- if (data == null) {
- this.next({
- data: savedData,
- status: "offline",
- });
- } else {
- return this.config.saveData(data).then(() => {
- this.next({ data: data, status: "synced" });
- });
- }
- });
+ private syncWithAction(action: () => Promise<void>): Promise<void> {
+ if (this._syncPromise != null) return this._syncPromise;
+ this._syncPromise = action().then(() => {
+ this._syncPromise = null;
});
+ return this._syncPromise;
+ }
+
+ sync(): Promise<void> {
+ return this.syncWithAction(this.doSync.bind(this));
}
- save(data: TData): void {
+ private async doSync(): Promise<void> {
const { currentData } = this;
- if (currentData != null && currentData.status === "syncing") return;
this.next({ data: currentData?.data ?? null, status: "syncing" });
- void this.config.saveData(data).then(() => {
+ const savedData = await this.config.getSavedData();
+ if (currentData == null && savedData != null) {
+ this.next({ data: savedData, status: "syncing" });
+ }
+ const data = await this.config.fetchData(savedData);
+ if (data == null) {
+ this.next({
+ data: savedData,
+ status: "offline",
+ });
+ } else {
+ await this.config.saveData(data);
this.next({ data: data, status: "synced" });
- });
+ }
+ }
+
+ save(data: TData): Promise<void> {
+ return this.syncWithAction(this.doSave.bind(this, data));
+ }
+
+ private async doSave(data: TData): Promise<void> {
+ await this.config.saveData(data);
+ this.next({ data: data, status: "synced" });
}
getSavedData(): Promise<TData | null> {
diff --git a/FrontEnd/src/app/services/timeline.ts b/FrontEnd/src/app/services/timeline.ts
index 8bc1d40b..46671ea1 100644
--- a/FrontEnd/src/app/services/timeline.ts
+++ b/FrontEnd/src/app/services/timeline.ts
@@ -26,6 +26,8 @@ export type { TimelineVisibility } from "@/http/timeline";
import { dataStorage } from "./common";
import { userInfoService, AuthUser } from "./user";
import { DataAndStatus, DataHub2 } from "./DataHub2";
+import { getHttpBookmarkClient } from "@/http/bookmark";
+import { getHttpHighlightClient } from "@/http/highlight";
export type TimelineInfo = HttpTimelineInfo;
export type TimelineChangePropertyRequest = HttpTimelinePatchRequest;
@@ -104,8 +106,9 @@ export class TimelineService {
saveData: async (timelineName, data) => {
if (data === "notexist") return;
- userInfoService.saveUser(data.owner);
- userInfoService.saveUsers(data.members);
+ // TODO: Avoid save same user.
+ void userInfoService.saveUser(data.owner);
+ void userInfoService.saveUsers(data.members);
await dataStorage.setItem<TimelineData>(
this.generateTimelineDataStorageKey(timelineName),
@@ -157,8 +160,8 @@ export class TimelineService {
},
});
- syncTimeline(timelineName: string): void {
- this.timelineHub.getLine(timelineName).sync();
+ syncTimeline(timelineName: string): Promise<void> {
+ return this.timelineHub.getLine(timelineName).sync();
}
createTimeline(timelineName: string): Observable<TimelineInfo> {
@@ -174,15 +177,12 @@ export class TimelineService {
changeTimelineProperty(
timelineName: string,
req: TimelineChangePropertyRequest
- ): Observable<TimelineInfo> {
- return from(
- getHttpTimelineClient()
- .patchTimeline(timelineName, req)
- .then((timeline) => {
- void this.syncTimeline(timelineName);
- return timeline;
- })
- );
+ ): Promise<void> {
+ return getHttpTimelineClient()
+ .patchTimeline(timelineName, req)
+ .then(() => {
+ void this.syncTimeline(timelineName);
+ });
}
deleteTimeline(timelineName: string): Observable<unknown> {
@@ -222,7 +222,7 @@ export class TimelineService {
};
data.posts.forEach((p) => {
- userInfoService.saveUser(p.author);
+ void userInfoService.saveUser(p.author);
});
await dataStorage.setItem<TimelinePostsData>(
@@ -342,31 +342,27 @@ export class TimelineService {
},
});
- syncPosts(timelineName: string): void {
- this.postsHub.getLine(timelineName).sync();
+ syncPosts(timelineName: string): Promise<void> {
+ return this.postsHub.getLine(timelineName).sync();
}
createPost(
timelineName: string,
request: TimelineCreatePostRequest
- ): Observable<unknown> {
- return from(
- getHttpTimelineClient()
- .postPost(timelineName, request)
- .then(() => {
- this.syncPosts(timelineName);
- })
- );
+ ): Promise<void> {
+ return getHttpTimelineClient()
+ .postPost(timelineName, request)
+ .then(() => {
+ void this.syncPosts(timelineName);
+ });
}
- deletePost(timelineName: string, postId: number): Observable<unknown> {
- return from(
- getHttpTimelineClient()
- .deletePost(timelineName, postId)
- .then(() => {
- this.syncPosts(timelineName);
- })
- );
+ deletePost(timelineName: string, postId: number): Promise<void> {
+ return getHttpTimelineClient()
+ .deletePost(timelineName, postId)
+ .then(() => {
+ void this.syncPosts(timelineName);
+ });
}
isMemberOf(username: string, timeline: TimelineInfo): boolean {
@@ -435,6 +431,26 @@ export class TimelineService {
user.username === post.author.username)
);
}
+
+ setHighlight(timelineName: string, highlight: boolean): Promise<void> {
+ const client = getHttpHighlightClient();
+ const promise = highlight
+ ? client.put(timelineName)
+ : client.delete(timelineName);
+ return promise.then(() => {
+ void timelineService.syncTimeline(timelineName);
+ });
+ }
+
+ setBookmark(timelineName: string, bookmark: boolean): Promise<void> {
+ const client = getHttpBookmarkClient();
+ const promise = bookmark
+ ? client.put(timelineName)
+ : client.delete(timelineName);
+ return promise.then(() => {
+ void timelineService.syncTimeline(timelineName);
+ });
+ }
}
export const timelineService = new TimelineService();
diff --git a/FrontEnd/src/app/services/user.ts b/FrontEnd/src/app/services/user.ts
index 5c4e3ae0..611a86ae 100644
--- a/FrontEnd/src/app/services/user.ts
+++ b/FrontEnd/src/app/services/user.ts
@@ -248,12 +248,12 @@ export function checkLogin(): AuthUser {
export class UserNotExistError extends Error {}
export class UserInfoService {
- saveUser(user: HttpUser): void {
- this.userHub.getLine(user.username).save(user);
+ saveUser(user: HttpUser): Promise<void> {
+ return this.userHub.getLine(user.username).save(user);
}
- saveUsers(users: HttpUser[]): void {
- return users.forEach((user) => this.saveUser(user));
+ saveUsers(users: HttpUser[]): Promise<void> {
+ return Promise.all(users.map((user) => this.saveUser(user))).then();
}
async getCachedUser(username: string): Promise<HttpUser | null> {
@@ -351,15 +351,13 @@ export class UserInfoService {
async setAvatar(username: string, blob: Blob): Promise<void> {
const etag = await getHttpUserClient().putAvatar(username, blob);
- this.avatarHub.getLine(username).save({ data: blob, etag });
+ await this.avatarHub.getLine(username).save({ data: blob, etag });
}
async setNickname(username: string, nickname: string): Promise<void> {
return getHttpUserClient()
.patch(username, { nickname })
- .then((user) => {
- this.saveUser(user);
- });
+ .then((user) => this.saveUser(user));
}
}
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
index f8b2b38b..fc4c52ec 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
@@ -5,15 +5,15 @@ import { UiLogicError } from "@/common";
import { pushAlert } from "@/services/alert";
import { useUser } from "@/services/user";
import { timelineService, usePosts, useTimeline } from "@/services/timeline";
-import { getHttpBookmarkClient } from "@/http/bookmark";
-import { getHttpHighlightClient } from "@/http/highlight";
+import { mergeDataStatus } from "@/services/DataHub2";
import { TimelineMemberDialog } from "./TimelineMember";
import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog";
-import { TimelinePageTemplateUIProps } from "./TimelinePageTemplateUI";
-import { TimelinePostSendCallback } from "./TimelinePostEdit";
+import {
+ TimelinePageTemplateData,
+ TimelinePageTemplateUIProps,
+} from "./TimelinePageTemplateUI";
import { TimelinePostInfoEx } from "./Timeline";
-import { mergeDataStatus } from "@/services/DataHub2";
export interface TimelinePageTemplateProps<TManageItem> {
name: string;
@@ -39,28 +39,37 @@ export default function TimelinePageTemplate<TManageItem>(
null
);
+ const [scrollBottomKey, setScrollBottomKey] = React.useState<number>(0);
+
+ React.useEffect(() => {
+ if (scrollBottomKey > 0) {
+ window.scrollTo(0, document.body.scrollHeight);
+ }
+ }, [scrollBottomKey]);
+
const timelineAndStatus = useTimeline(name);
const postsAndState = usePosts(name);
- const onPost: TimelinePostSendCallback = React.useCallback(
- (req) => {
- return service.createPost(name, req).toPromise().then();
- },
- [service, name]
- );
+ const [
+ scrollToBottomNextSyncKey,
+ setScrollToBottomNextSyncKey,
+ ] = React.useState<number>(0);
- const onManageProp = props.onManage;
+ const scrollToBottomNextSync = (): void => {
+ setScrollToBottomNextSyncKey((old) => old + 1);
+ };
- const onManage = React.useCallback(
- (item: "property" | TManageItem) => {
- if (item === "property") {
- setDialog(item);
- } else {
- onManageProp(item);
+ React.useEffect(() => {
+ let subscribe = true;
+ void timelineService.syncPosts(name).then(() => {
+ if (subscribe) {
+ setScrollBottomKey((old) => old + 1);
}
- },
- [onManageProp]
- );
+ });
+ return () => {
+ subscribe = false;
+ };
+ }, [name, scrollToBottomNextSyncKey]);
const data = ((): TimelinePageTemplateUIProps<TManageItem>["data"] => {
const { status, data: timeline } = timelineAndStatus;
@@ -84,13 +93,11 @@ export default function TimelinePageTemplate<TManageItem>(
...post,
onDelete: service.hasModifyPostPermission(user, timeline, post)
? () => {
- service.deletePost(name, post.id).subscribe({
- error: () => {
- pushAlert({
- type: "danger",
- message: t("timeline.deletePostFailed"),
- });
- },
+ service.deletePost(name, post.id).catch(() => {
+ pushAlert({
+ type: "danger",
+ message: t("timeline.deletePostFailed"),
+ });
});
}
: undefined,
@@ -98,62 +105,55 @@ export default function TimelinePageTemplate<TManageItem>(
}
})();
- const operations = {
- onPost: service.hasPostPermission(user, timeline) ? onPost : undefined,
+ const operations: TimelinePageTemplateData<TManageItem>["operations"] = {
+ onPost: service.hasPostPermission(user, timeline)
+ ? (req) =>
+ service.createPost(name, req).then(() => scrollToBottomNextSync())
+ : undefined,
onManage: service.hasManagePermission(user, timeline)
- ? onManage
+ ? (item) => {
+ if (item === "property") {
+ setDialog(item);
+ } else {
+ props.onManage(item);
+ }
+ }
: undefined,
onMember: () => setDialog("member"),
onBookmark:
user != null
? () => {
- const { isBookmark } = timeline;
- const client = getHttpBookmarkClient();
- const promise = isBookmark
- ? client.delete(name)
- : client.put(name);
- promise.then(
- () => {
- void timelineService.syncTimeline(name);
- },
- () => {
+ service
+ .setBookmark(timeline.name, !timeline.isBookmark)
+ .catch(() => {
pushAlert({
message: {
type: "i18n",
- key: isBookmark
+ key: timeline.isBookmark
? "timeline.removeBookmarkFail"
: "timeline.addBookmarkFail",
},
type: "danger",
});
- }
- );
+ });
}
: undefined,
onHighlight:
user != null && user.hasHighlightTimelineAdministrationPermission
? () => {
- const { isHighlight } = timeline;
- const client = getHttpHighlightClient();
- const promise = isHighlight
- ? client.delete(name)
- : client.put(name);
- promise.then(
- () => {
- void timelineService.syncTimeline(name);
- },
- () => {
+ service
+ .setHighlight(timeline.name, !timeline.isHighlight)
+ .catch(() => {
pushAlert({
message: {
type: "i18n",
- key: isHighlight
+ key: timeline.isHighlight
? "timeline.removeHighlightFail"
: "timeline.addHighlightFail",
},
type: "danger",
});
- }
- );
+ });
}
: undefined,
};
@@ -162,13 +162,9 @@ export default function TimelinePageTemplate<TManageItem>(
}
})();
- const closeDialog = React.useCallback((): void => {
- setDialog(null);
- }, []);
-
- let dialogElement: React.ReactElement | undefined;
-
const timeline = timelineAndStatus?.data;
+ let dialogElement: React.ReactElement | undefined;
+ const closeDialog = (): void => setDialog(null);
if (dialog === "property") {
if (timeline == null || timeline === "notexist") {
@@ -181,14 +177,8 @@ export default function TimelinePageTemplate<TManageItem>(
<TimelinePropertyChangeDialog
open
close={closeDialog}
- oldInfo={{
- title: timeline.title,
- visibility: timeline.visibility,
- description: timeline.description,
- }}
- onProcess={(req) => {
- return service.changeTimelineProperty(name, req).toPromise().then();
- }}
+ timeline={timeline}
+ onProcess={(req) => service.changeTimelineProperty(name, req)}
/>
);
} else if (dialog === "member") {
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
index 41246175..0d0951ee 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
@@ -1,10 +1,9 @@
import React from "react";
import { useTranslation } from "react-i18next";
-import { fromEvent } from "rxjs";
import { Spinner } from "react-bootstrap";
import { getAlertHost } from "@/services/alert";
-import { useEventEmiiter, I18nText, convertI18nText } from "@/common";
+import { I18nText, convertI18nText } from "@/common";
import { TimelineInfo } from "@/services/timeline";
import Timeline, { TimelinePostInfoEx } from "./Timeline";
@@ -25,20 +24,20 @@ export interface TimelineCardComponentProps<TManageItems> {
className?: string;
}
+export interface TimelinePageTemplateData<TManageItems> {
+ timeline: TimelineInfo;
+ posts?: TimelinePostInfoEx[] | "forbid";
+ operations: {
+ onManage?: (item: TManageItems | "property") => void;
+ onMember: () => void;
+ onBookmark?: () => void;
+ onHighlight?: () => void;
+ onPost?: TimelinePostSendCallback;
+ };
+}
+
export interface TimelinePageTemplateUIProps<TManageItems> {
- data?:
- | {
- timeline: TimelineInfo;
- posts?: TimelinePostInfoEx[] | "forbid";
- operations: {
- onManage?: (item: TManageItems | "property") => void;
- onMember: () => void;
- onBookmark?: () => void;
- onHighlight?: () => void;
- onPost?: TimelinePostSendCallback;
- };
- }
- | I18nText;
+ data?: TimelinePageTemplateData<TManageItems> | I18nText;
syncStatus: TimelineSyncStatus;
CardComponent: React.ComponentType<TimelineCardComponentProps<TManageItems>>;
}
@@ -69,60 +68,9 @@ export default function TimelinePageTemplateUI<TManageItems>(
const timelineRef = React.useRef<HTMLDivElement | null>(null);
- const [getResizeEvent, triggerResizeEvent] = useEventEmiiter();
-
const timelineName: string | null =
typeof data === "object" && "timeline" in data ? data.timeline.name : null;
- React.useEffect(() => {
- const { current: timelineElement } = timelineRef;
- if (timelineElement != null) {
- let loadingScrollToBottom = true;
- let pinBottom = false;
-
- const isAtBottom = (): boolean =>
- window.innerHeight + window.scrollY + 10 >= document.body.scrollHeight;
-
- const disableLoadingScrollToBottom = (): void => {
- loadingScrollToBottom = false;
- if (isAtBottom()) pinBottom = true;
- };
-
- const checkAndScrollToBottom = (): void => {
- if (loadingScrollToBottom || pinBottom) {
- window.scrollTo(0, document.body.scrollHeight);
- }
- };
-
- const subscriptions = [
- fromEvent(timelineElement, "wheel").subscribe(
- disableLoadingScrollToBottom
- ),
- fromEvent(timelineElement, "pointerdown").subscribe(
- disableLoadingScrollToBottom
- ),
- fromEvent(timelineElement, "keydown").subscribe(
- disableLoadingScrollToBottom
- ),
- fromEvent(window, "scroll").subscribe(() => {
- if (loadingScrollToBottom) return;
-
- if (isAtBottom()) {
- pinBottom = true;
- } else {
- pinBottom = false;
- }
- }),
- fromEvent(window, "resize").subscribe(checkAndScrollToBottom),
- getResizeEvent().subscribe(checkAndScrollToBottom),
- ];
-
- return () => {
- subscriptions.forEach((s) => s.unsubscribe());
- };
- }
- }, [getResizeEvent, triggerResizeEvent, timelineName]);
-
const cardCollapseLocalStorageKey =
timelineName != null ? `timeline.${timelineName}.cardCollapse` : null;
@@ -173,11 +121,7 @@ export default function TimelinePageTemplateUI<TManageItems>(
className="timeline-container"
style={{ minHeight: `calc(100vh - ${56 + bottomSpaceHeight}px)` }}
>
- <Timeline
- containerRef={timelineRef}
- posts={posts}
- onResize={triggerResizeEvent}
- />
+ <Timeline containerRef={timelineRef} posts={posts} />
</div>
)
) : (
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
index aae227e6..ab3285f5 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
@@ -4,20 +4,15 @@ import {
TimelineVisibility,
kTimelineVisibilities,
TimelineChangePropertyRequest,
+ TimelineInfo,
} from "@/services/timeline";
import OperationDialog from "../common/OperationDialog";
-export interface TimelinePropertyInfo {
- title: string;
- visibility: TimelineVisibility;
- description: string;
-}
-
export interface TimelinePropertyChangeDialogProps {
open: boolean;
close: () => void;
- oldInfo: TimelinePropertyInfo;
+ timeline: TimelineInfo;
onProcess: (request: TimelineChangePropertyRequest) => Promise<void>;
}
@@ -30,6 +25,8 @@ const labelMap: { [key in TimelineVisibility]: string } = {
const TimelinePropertyChangeDialog: React.FC<TimelinePropertyChangeDialogProps> = (
props
) => {
+ const { timeline } = props;
+
return (
<OperationDialog
title={"timeline.dialogChangeProperty.title"}
@@ -37,7 +34,7 @@ const TimelinePropertyChangeDialog: React.FC<TimelinePropertyChangeDialogProps>
{
type: "text",
label: "timeline.dialogChangeProperty.titleField",
- initValue: props.oldInfo.title,
+ initValue: timeline.title,
},
{
type: "select",
@@ -46,25 +43,25 @@ const TimelinePropertyChangeDialog: React.FC<TimelinePropertyChangeDialogProps>
label: labelMap[v],
value: v,
})),
- initValue: props.oldInfo.visibility,
+ initValue: timeline.visibility,
},
{
type: "text",
label: "timeline.dialogChangeProperty.description",
- initValue: props.oldInfo.description,
+ initValue: timeline.description,
},
]}
open={props.open}
close={props.close}
onProcess={([newTitle, newVisibility, newDescription]) => {
const req: TimelineChangePropertyRequest = {};
- if (newTitle !== props.oldInfo.title) {
+ if (newTitle !== timeline.title) {
req.title = newTitle;
}
- if (newVisibility !== props.oldInfo.visibility) {
+ if (newVisibility !== timeline.visibility) {
req.visibility = newVisibility as TimelineVisibility;
}
- if (newDescription !== props.oldInfo.description) {
+ if (newDescription !== timeline.description) {
req.description = newDescription;
}
return props.onProcess(req);
diff --git a/FrontEnd/src/app/views/user/index.tsx b/FrontEnd/src/app/views/user/index.tsx
index 7c0b1563..bb986178 100644
--- a/FrontEnd/src/app/views/user/index.tsx
+++ b/FrontEnd/src/app/views/user/index.tsx
@@ -1,8 +1,7 @@
import React, { useState } from "react";
import { useParams } from "react-router";
-import { UiLogicError } from "@/common";
-import { useUser, userInfoService } from "@/services/user";
+import { userInfoService } from "@/services/user";
import TimelinePageTemplate from "../timeline-common/TimelinePageTemplate";
@@ -14,54 +13,38 @@ import ChangeAvatarDialog from "./ChangeAvatarDialog";
const UserPage: React.FC = (_) => {
const { username } = useParams<{ username: string }>();
- const user = useUser();
-
const [dialog, setDialog] = useState<null | PersonalTimelineManageItem>(null);
let dialogElement: React.ReactElement | undefined;
- const closeDialogHandler = (): void => {
- setDialog(null);
- };
+ const closeDialog = (): void => setDialog(null);
if (dialog === "nickname") {
- if (user == null) {
- throw new UiLogicError("Change nickname without login.");
- }
-
dialogElement = (
<ChangeNicknameDialog
open
- close={closeDialogHandler}
+ close={closeDialog}
onProcess={(newNickname) =>
userInfoService.setNickname(username, newNickname)
}
/>
);
} else if (dialog === "avatar") {
- if (user == null) {
- throw new UiLogicError("Change avatar without login.");
- }
-
dialogElement = (
<ChangeAvatarDialog
open
- close={closeDialogHandler}
+ close={closeDialog}
process={(file) => userInfoService.setAvatar(username, file)}
/>
);
}
- const onManage = React.useCallback((item: PersonalTimelineManageItem) => {
- setDialog(item);
- }, []);
-
return (
<>
<TimelinePageTemplate
name={`@${username}`}
UiComponent={UserPageUI}
- onManage={onManage}
+ onManage={(item) => setDialog(item)}
notFoundI18nKey="timeline.userNotExist"
/>
{dialogElement}