aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-01-09 01:07:35 +0800
committercrupest <crupest@outlook.com>2021-01-09 01:07:35 +0800
commit8edf6d566cecd94d251a4e29ae8c35b77f88d6db (patch)
tree0e4371f4a40fabaf5c87baca491d2631665cd2ae /FrontEnd/src
parent777efa6e0405f4e871de4da21b939e30ed07f754 (diff)
downloadtimeline-8edf6d566cecd94d251a4e29ae8c35b77f88d6db.tar.gz
timeline-8edf6d566cecd94d251a4e29ae8c35b77f88d6db.tar.bz2
timeline-8edf6d566cecd94d251a4e29ae8c35b77f88d6db.zip
...
Diffstat (limited to 'FrontEnd/src')
-rw-r--r--FrontEnd/src/app/http/bookmark.ts27
-rw-r--r--FrontEnd/src/app/http/common.ts19
-rw-r--r--FrontEnd/src/app/http/highlight.ts21
-rw-r--r--FrontEnd/src/app/http/timeline.ts122
-rw-r--r--FrontEnd/src/app/http/token.ts2
-rw-r--r--FrontEnd/src/app/http/user.ts59
-rw-r--r--FrontEnd/src/app/services/timeline.ts62
-rw-r--r--FrontEnd/src/app/services/user.ts30
-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.tsx10
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx62
12 files changed, 210 insertions, 247 deletions
diff --git a/FrontEnd/src/app/http/bookmark.ts b/FrontEnd/src/app/http/bookmark.ts
index 68de4d73..15e55d98 100644
--- a/FrontEnd/src/app/http/bookmark.ts
+++ b/FrontEnd/src/app/http/bookmark.ts
@@ -1,6 +1,5 @@
-import axios from "axios";
-
import {
+ axios,
apiBaseUrl,
convertToNetworkError,
extractResponseData,
@@ -18,38 +17,38 @@ export interface HttpHighlightMoveRequest {
}
export interface IHttpBookmarkClient {
- list(token: string): Promise<HttpTimelineInfo[]>;
- put(timeline: string, token: string): Promise<void>;
- delete(timeline: string, token: string): Promise<void>;
- move(req: HttpHighlightMoveRequest, token: string): Promise<void>;
+ list(): Promise<HttpTimelineInfo[]>;
+ put(timeline: string): Promise<void>;
+ delete(timeline: string): Promise<void>;
+ move(req: HttpHighlightMoveRequest): Promise<void>;
}
export class HttpHighlightClient implements IHttpBookmarkClient {
- list(token: string): Promise<HttpTimelineInfo[]> {
+ list(): Promise<HttpTimelineInfo[]> {
return axios
- .get<RawHttpTimelineInfo[]>(`${apiBaseUrl}/bookmarks?token=${token}`)
+ .get<RawHttpTimelineInfo[]>(`${apiBaseUrl}/bookmarks`)
.then(extractResponseData)
.then((list) => list.map(processRawTimelineInfo))
.catch(convertToNetworkError);
}
- put(timeline: string, token: string): Promise<void> {
+ put(timeline: string): Promise<void> {
return axios
- .put(`${apiBaseUrl}/bookmarks/${timeline}?token=${token}`)
+ .put(`${apiBaseUrl}/bookmarks/${timeline}`)
.catch(convertToNetworkError)
.then();
}
- delete(timeline: string, token: string): Promise<void> {
+ delete(timeline: string): Promise<void> {
return axios
- .delete(`${apiBaseUrl}/bookmarks/${timeline}?token=${token}`)
+ .delete(`${apiBaseUrl}/bookmarks/${timeline}`)
.catch(convertToNetworkError)
.then();
}
- move(req: HttpHighlightMoveRequest, token: string): Promise<void> {
+ move(req: HttpHighlightMoveRequest): Promise<void> {
return axios
- .post(`${apiBaseUrl}/bookmarkop/move?token=${token}`, req)
+ .post(`${apiBaseUrl}/bookmarkop/move`, req)
.catch(convertToNetworkError)
.then();
}
diff --git a/FrontEnd/src/app/http/common.ts b/FrontEnd/src/app/http/common.ts
index 54203d1a..2dd3677b 100644
--- a/FrontEnd/src/app/http/common.ts
+++ b/FrontEnd/src/app/http/common.ts
@@ -1,7 +1,24 @@
-import { AxiosError, AxiosResponse } from "axios";
+import rawAxios, { AxiosError, AxiosResponse } from "axios";
+import { BehaviorSubject } from "rxjs";
export const apiBaseUrl = "/api";
+export const axios = rawAxios.create();
+
+export const tokenSubject: BehaviorSubject<string | null> = new BehaviorSubject<
+ string | null
+>(null);
+
+tokenSubject.subscribe((token) => {
+ if (token == null) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ delete axios.defaults.headers.common["Authorization"];
+ } else {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
+ }
+});
+
export function base64(blob: Blob): Promise<string> {
return new Promise<string>((resolve) => {
const reader = new FileReader();
diff --git a/FrontEnd/src/app/http/highlight.ts b/FrontEnd/src/app/http/highlight.ts
index 1f226c19..851d52ce 100644
--- a/FrontEnd/src/app/http/highlight.ts
+++ b/FrontEnd/src/app/http/highlight.ts
@@ -1,6 +1,5 @@
-import axios from "axios";
-
import {
+ axios,
apiBaseUrl,
convertToNetworkError,
extractResponseData,
@@ -19,9 +18,9 @@ export interface HttpHighlightMoveRequest {
export interface IHttpHighlightClient {
list(): Promise<HttpTimelineInfo[]>;
- put(timeline: string, token: string): Promise<void>;
- delete(timeline: string, token: string): Promise<void>;
- move(req: HttpHighlightMoveRequest, token: string): Promise<void>;
+ put(timeline: string): Promise<void>;
+ delete(timeline: string): Promise<void>;
+ move(req: HttpHighlightMoveRequest): Promise<void>;
}
export class HttpHighlightClient implements IHttpHighlightClient {
@@ -33,23 +32,23 @@ export class HttpHighlightClient implements IHttpHighlightClient {
.catch(convertToNetworkError);
}
- put(timeline: string, token: string): Promise<void> {
+ put(timeline: string): Promise<void> {
return axios
- .put(`${apiBaseUrl}/highlights/${timeline}?token=${token}`)
+ .put(`${apiBaseUrl}/highlights/${timeline}`)
.catch(convertToNetworkError)
.then();
}
- delete(timeline: string, token: string): Promise<void> {
+ delete(timeline: string): Promise<void> {
return axios
- .delete(`${apiBaseUrl}/highlights/${timeline}?token=${token}`)
+ .delete(`${apiBaseUrl}/highlights/${timeline}`)
.catch(convertToNetworkError)
.then();
}
- move(req: HttpHighlightMoveRequest, token: string): Promise<void> {
+ move(req: HttpHighlightMoveRequest): Promise<void> {
return axios
- .post(`${apiBaseUrl}/highlightop/move?token=${token}`, req)
+ .post(`${apiBaseUrl}/highlightop/move`, req)
.catch(convertToNetworkError)
.then();
}
diff --git a/FrontEnd/src/app/http/timeline.ts b/FrontEnd/src/app/http/timeline.ts
index 6be0a183..ed02a65b 100644
--- a/FrontEnd/src/app/http/timeline.ts
+++ b/FrontEnd/src/app/http/timeline.ts
@@ -1,8 +1,9 @@
-import axios, { AxiosError } from "axios";
+import { AxiosError } from "axios";
import { updateQueryString, applyQueryParameters } from "../utilities/url";
import {
+ axios,
apiBaseUrl,
extractResponseData,
convertToNetworkError,
@@ -30,6 +31,8 @@ export interface HttpTimelineInfo {
visibility: TimelineVisibility;
lastModified: Date;
members: HttpUser[];
+ isHighlight: boolean;
+ isBookmark: boolean;
}
export interface HttpTimelineListQuery {
@@ -130,6 +133,8 @@ export interface RawHttpTimelineInfo {
visibility: TimelineVisibility;
lastModified: string;
members: HttpUser[];
+ isHighlight: boolean;
+ isBookmark: boolean;
}
interface RawTimelinePostTextContent {
@@ -229,33 +234,17 @@ export interface IHttpTimelineClient {
ifModifiedSince: Date;
}
): Promise<HttpTimelineInfo | NotModified>;
- postTimeline(
- req: HttpTimelinePostRequest,
- token: string
- ): Promise<HttpTimelineInfo>;
+ postTimeline(req: HttpTimelinePostRequest): Promise<HttpTimelineInfo>;
patchTimeline(
timelineName: string,
- req: HttpTimelinePatchRequest,
- token: string
+ req: HttpTimelinePatchRequest
): Promise<HttpTimelineInfo>;
- deleteTimeline(timelineName: string, token: string): Promise<void>;
- memberPut(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void>;
- memberDelete(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void>;
+ deleteTimeline(timelineName: string): Promise<void>;
+ memberPut(timelineName: string, username: string): Promise<void>;
+ memberDelete(timelineName: string, username: string): Promise<void>;
+ listPost(timelineName: string): Promise<HttpTimelinePostInfo[]>;
listPost(
timelineName: string,
- token?: string
- ): Promise<HttpTimelinePostInfo[]>;
- listPost(
- timelineName: string,
- token: string | undefined,
query: {
modifiedSince?: Date;
includeDeleted?: false;
@@ -263,33 +252,22 @@ export interface IHttpTimelineClient {
): Promise<HttpTimelinePostInfo[]>;
listPost(
timelineName: string,
- token: string | undefined,
query: {
modifiedSince?: Date;
includeDeleted: true;
}
): Promise<HttpTimelineGenericPostInfo[]>;
+ getPostData(timelineName: string, postId: number): Promise<BlobWithEtag>;
getPostData(
timelineName: string,
postId: number,
- token?: string
- ): Promise<BlobWithEtag>;
- getPostData(
- timelineName: string,
- postId: number,
- token: string | undefined,
etag: string
): Promise<BlobWithEtag | NotModified>;
postPost(
timelineName: string,
- req: HttpTimelinePostPostRequest,
- token: string
+ req: HttpTimelinePostPostRequest
): Promise<HttpTimelinePostInfo>;
- deletePost(
- timelineName: string,
- postId: number,
- token: string
- ): Promise<void>;
+ deletePost(timelineName: string, postId: number): Promise<void>;
}
export class HttpTimelineClient implements IHttpTimelineClient {
@@ -339,12 +317,9 @@ export class HttpTimelineClient implements IHttpTimelineClient {
.catch(convertToNetworkError);
}
- postTimeline(
- req: HttpTimelinePostRequest,
- token: string
- ): Promise<HttpTimelineInfo> {
+ postTimeline(req: HttpTimelinePostRequest): Promise<HttpTimelineInfo> {
return axios
- .post<RawHttpTimelineInfo>(`${apiBaseUrl}/timelines?token=${token}`, req)
+ .post<RawHttpTimelineInfo>(`${apiBaseUrl}/timelines`, req)
.then(extractResponseData)
.then(processRawTimelineInfo)
.catch(convertToIfErrorCodeIs(11040101, HttpTimelineNameConflictError))
@@ -353,12 +328,11 @@ export class HttpTimelineClient implements IHttpTimelineClient {
patchTimeline(
timelineName: string,
- req: HttpTimelinePatchRequest,
- token: string
+ req: HttpTimelinePatchRequest
): Promise<HttpTimelineInfo> {
return axios
.patch<RawHttpTimelineInfo>(
- `${apiBaseUrl}/timelines/${timelineName}?token=${token}`,
+ `${apiBaseUrl}/timelines/${timelineName}`,
req
)
.then(extractResponseData)
@@ -366,46 +340,30 @@ export class HttpTimelineClient implements IHttpTimelineClient {
.catch(convertToNetworkError);
}
- deleteTimeline(timelineName: string, token: string): Promise<void> {
+ deleteTimeline(timelineName: string): Promise<void> {
return axios
- .delete(`${apiBaseUrl}/timelines/${timelineName}?token=${token}`)
+ .delete(`${apiBaseUrl}/timelines/${timelineName}`)
.catch(convertToNetworkError)
.then();
}
- memberPut(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void> {
+ memberPut(timelineName: string, username: string): Promise<void> {
return axios
- .put(
- `${apiBaseUrl}/timelines/${timelineName}/members/${username}?token=${token}`
- )
+ .put(`${apiBaseUrl}/timelines/${timelineName}/members/${username}`)
.catch(convertToNetworkError)
.then();
}
- memberDelete(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void> {
+ memberDelete(timelineName: string, username: string): Promise<void> {
return axios
- .delete(
- `${apiBaseUrl}/timelines/${timelineName}/members/${username}?token=${token}`
- )
+ .delete(`${apiBaseUrl}/timelines/${timelineName}/members/${username}`)
.catch(convertToNetworkError)
.then();
}
+ listPost(timelineName: string): Promise<HttpTimelinePostInfo[]>;
listPost(
timelineName: string,
- token?: string
- ): Promise<HttpTimelinePostInfo[]>;
- listPost(
- timelineName: string,
- token: string | undefined,
query: {
modifiedSince?: Date;
includeDeleted?: false;
@@ -413,7 +371,6 @@ export class HttpTimelineClient implements IHttpTimelineClient {
): Promise<HttpTimelinePostInfo[]>;
listPost(
timelineName: string,
- token: string | undefined,
query: {
modifiedSince?: Date;
includeDeleted: true;
@@ -421,14 +378,12 @@ export class HttpTimelineClient implements IHttpTimelineClient {
): Promise<HttpTimelineGenericPostInfo[]>;
listPost(
timelineName: string,
- token?: string,
query?: {
modifiedSince?: Date;
includeDeleted?: boolean;
}
): Promise<HttpTimelineGenericPostInfo[]> {
let url = `${apiBaseUrl}/timelines/${timelineName}/posts`;
- url = updateQueryString("token", token, url);
if (query != null) {
if (query.modifiedSince != null) {
url = updateQueryString(
@@ -457,15 +412,10 @@ export class HttpTimelineClient implements IHttpTimelineClient {
);
}
+ getPostData(timelineName: string, postId: number): Promise<BlobWithEtag>;
getPostData(
timelineName: string,
postId: number,
- token: string
- ): Promise<BlobWithEtag>;
- getPostData(
- timelineName: string,
- postId: number,
- token?: string,
etag?: string
): Promise<BlobWithEtag | NotModified> {
const headers =
@@ -475,8 +425,7 @@ export class HttpTimelineClient implements IHttpTimelineClient {
}
: undefined;
- let url = `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}/data`;
- url = updateQueryString("token", token, url);
+ const url = `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}/data`;
return axios
.get(url, {
@@ -491,8 +440,7 @@ export class HttpTimelineClient implements IHttpTimelineClient {
async postPost(
timelineName: string,
- req: HttpTimelinePostPostRequest,
- token: string
+ req: HttpTimelinePostPostRequest
): Promise<HttpTimelinePostInfo> {
let content: RawTimelinePostPostRequestContent;
if (req.content.type === "image") {
@@ -512,7 +460,7 @@ export class HttpTimelineClient implements IHttpTimelineClient {
}
return await axios
.post<RawTimelinePostInfo>(
- `${apiBaseUrl}/timelines/${timelineName}/posts?token=${token}`,
+ `${apiBaseUrl}/timelines/${timelineName}/posts`,
rawReq
)
.then(extractResponseData)
@@ -520,15 +468,9 @@ export class HttpTimelineClient implements IHttpTimelineClient {
.then((rawPost) => processRawTimelinePostInfo(rawPost));
}
- deletePost(
- timelineName: string,
- postId: number,
- token: string
- ): Promise<void> {
+ deletePost(timelineName: string, postId: number): Promise<void> {
return axios
- .delete(
- `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}?token=${token}`
- )
+ .delete(`${apiBaseUrl}/timelines/${timelineName}/posts/${postId}`)
.catch(convertToNetworkError)
.then();
}
diff --git a/FrontEnd/src/app/http/token.ts b/FrontEnd/src/app/http/token.ts
index ae0cf3f6..c0644515 100644
--- a/FrontEnd/src/app/http/token.ts
+++ b/FrontEnd/src/app/http/token.ts
@@ -1,3 +1,5 @@
+// Don't use axios in common because it will contains
+// authorization header, which shouldn't be used in token apis.
import axios, { AxiosError } from "axios";
import {
diff --git a/FrontEnd/src/app/http/user.ts b/FrontEnd/src/app/http/user.ts
index 929956d0..8345880e 100644
--- a/FrontEnd/src/app/http/user.ts
+++ b/FrontEnd/src/app/http/user.ts
@@ -1,6 +1,7 @@
-import axios, { AxiosError } from "axios";
+import { AxiosError } from "axios";
import {
+ axios,
apiBaseUrl,
convertToNetworkError,
extractResponseData,
@@ -62,28 +63,22 @@ export class HttpChangePasswordBadCredentialError extends Error {
export interface IHttpUserClient {
list(): Promise<HttpUser[]>;
get(username: string): Promise<HttpUser>;
- patch(
- username: string,
- req: HttpUserPatchRequest,
- token: string
- ): Promise<HttpUser>;
- delete(username: string, token: string): Promise<void>;
+ patch(username: string, req: HttpUserPatchRequest): Promise<HttpUser>;
+ delete(username: string): Promise<void>;
getAvatar(username: string): Promise<BlobWithEtag>;
getAvatar(
username: string,
etag: string
): Promise<BlobWithEtag | NotModified>;
- putAvatar(username: string, data: Blob, token: string): Promise<void>;
- changePassword(req: HttpChangePasswordRequest, token: string): Promise<void>;
+ putAvatar(username: string, data: Blob): Promise<void>;
+ changePassword(req: HttpChangePasswordRequest): Promise<void>;
putUserPermission(
username: string,
- permission: UserPermission,
- token: string
+ permission: UserPermission
): Promise<void>;
deleteUserPermission(
username: string,
- permission: UserPermission,
- token: string
+ permission: UserPermission
): Promise<void>;
createUser(req: HttpCreateUserRequest, token: string): Promise<HttpUser>;
@@ -105,20 +100,16 @@ export class HttpUserClient implements IHttpUserClient {
.catch(convertToNetworkError);
}
- patch(
- username: string,
- req: HttpUserPatchRequest,
- token: string
- ): Promise<HttpUser> {
+ patch(username: string, req: HttpUserPatchRequest): Promise<HttpUser> {
return axios
- .patch<HttpUser>(`${apiBaseUrl}/users/${username}?token=${token}`, req)
+ .patch<HttpUser>(`${apiBaseUrl}/users/${username}`, req)
.then(extractResponseData)
.catch(convertToNetworkError);
}
- delete(username: string, token: string): Promise<void> {
+ delete(username: string): Promise<void> {
return axios
- .delete(`${apiBaseUrl}/users/${username}?token=${token}`)
+ .delete(`${apiBaseUrl}/users/${username}`)
.catch(convertToNetworkError)
.then();
}
@@ -146,9 +137,9 @@ export class HttpUserClient implements IHttpUserClient {
.catch(convertToNetworkError);
}
- putAvatar(username: string, data: Blob, token: string): Promise<void> {
+ putAvatar(username: string, data: Blob): Promise<void> {
return axios
- .put(`${apiBaseUrl}/users/${username}/avatar?token=${token}`, data, {
+ .put(`${apiBaseUrl}/users/${username}/avatar`, data, {
headers: {
"Content-Type": data.type,
},
@@ -157,9 +148,9 @@ export class HttpUserClient implements IHttpUserClient {
.then();
}
- changePassword(req: HttpChangePasswordRequest, token: string): Promise<void> {
+ changePassword(req: HttpChangePasswordRequest): Promise<void> {
return axios
- .post(`${apiBaseUrl}/userop/changepassword?token=${token}`, req)
+ .post(`${apiBaseUrl}/userop/changepassword`, req)
.catch(
convertToIfErrorCodeIs(11020201, HttpChangePasswordBadCredentialError)
)
@@ -169,33 +160,27 @@ export class HttpUserClient implements IHttpUserClient {
putUserPermission(
username: string,
- permission: UserPermission,
- token: string
+ permission: UserPermission
): Promise<void> {
return axios
- .put(
- `${apiBaseUrl}/users/${username}/permissions/${permission}?token=${token}`
- )
+ .put(`${apiBaseUrl}/users/${username}/permissions/${permission}`)
.catch(convertToNetworkError)
.then();
}
deleteUserPermission(
username: string,
- permission: UserPermission,
- token: string
+ permission: UserPermission
): Promise<void> {
return axios
- .delete(
- `${apiBaseUrl}/users/${username}/permissions/${permission}?token=${token}`
- )
+ .delete(`${apiBaseUrl}/users/${username}/permissions/${permission}`)
.catch(convertToNetworkError)
.then();
}
- createUser(req: HttpCreateUserRequest, token: string): Promise<HttpUser> {
+ createUser(req: HttpCreateUserRequest): Promise<HttpUser> {
return axios
- .post<HttpUser>(`${apiBaseUrl}/userop/createuser?token=${token}`, req)
+ .post<HttpUser>(`${apiBaseUrl}/userop/createuser`, req)
.then(extractResponseData)
.catch(convertToNetworkError)
.then();
diff --git a/FrontEnd/src/app/services/timeline.ts b/FrontEnd/src/app/services/timeline.ts
index c58516fc..3b9a9072 100644
--- a/FrontEnd/src/app/services/timeline.ts
+++ b/FrontEnd/src/app/services/timeline.ts
@@ -28,13 +28,7 @@ export type { TimelineVisibility } from "@/http/timeline";
import { dataStorage, throwIfNotNetworkError, BlobOrStatus } from "./common";
import { DataHub, WithSyncStatus } from "./DataHub";
-import {
- checkLogin,
- userService,
- userInfoService,
- User,
- AuthUser,
-} from "./user";
+import { userInfoService, User, AuthUser } from "./user";
export type TimelineInfo = HttpTimelineInfo;
export type TimelineChangePropertyRequest = HttpTimelinePatchRequest;
@@ -227,14 +221,10 @@ export class TimelineService {
}
createTimeline(timelineName: string): Observable<TimelineInfo> {
- const user = checkLogin();
return from(
- getHttpTimelineClient().postTimeline(
- {
- name: timelineName,
- },
- user.token
- )
+ getHttpTimelineClient().postTimeline({
+ name: timelineName,
+ })
).pipe(
convertError(HttpTimelineNameConflictError, TimelineNameConflictError)
);
@@ -244,10 +234,9 @@ export class TimelineService {
timelineName: string,
req: TimelineChangePropertyRequest
): Observable<TimelineInfo> {
- const user = checkLogin();
return from(
getHttpTimelineClient()
- .patchTimeline(timelineName, req, user.token)
+ .patchTimeline(timelineName, req)
.then((timeline) => {
void this.syncTimeline(timelineName);
return timeline;
@@ -256,17 +245,13 @@ export class TimelineService {
}
deleteTimeline(timelineName: string): Observable<unknown> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient().deleteTimeline(timelineName, user.token)
- );
+ return from(getHttpTimelineClient().deleteTimeline(timelineName));
}
addMember(timelineName: string, username: string): Observable<unknown> {
- const user = checkLogin();
return from(
getHttpTimelineClient()
- .memberPut(timelineName, username, user.token)
+ .memberPut(timelineName, username)
.then(() => {
void this.syncTimeline(timelineName);
})
@@ -274,10 +259,9 @@ export class TimelineService {
}
removeMember(timelineName: string, username: string): Observable<unknown> {
- const user = checkLogin();
return from(
getHttpTimelineClient()
- .memberDelete(timelineName, username, user.token)
+ .memberDelete(timelineName, username)
.then(() => {
void this.syncTimeline(timelineName);
})
@@ -344,10 +328,7 @@ export class TimelineService {
try {
if (lastUpdatedTime == null) {
- const httpPosts = await getHttpTimelineClient().listPost(
- key,
- userService.currentUser?.token
- );
+ const httpPosts = await getHttpTimelineClient().listPost(key);
userInfoService.saveUsers(
uniqBy(
@@ -362,14 +343,10 @@ export class TimelineService {
line.next({ type: "synced", posts });
} else {
- const httpPosts = await getHttpTimelineClient().listPost(
- key,
- userService.currentUser?.token,
- {
- modifiedSince: lastUpdatedTime,
- includeDeleted: true,
- }
- );
+ const httpPosts = await getHttpTimelineClient().listPost(key, {
+ modifiedSince: lastUpdatedTime,
+ includeDeleted: true,
+ });
const deletedIds = httpPosts
.filter((p) => p.deleted)
@@ -582,10 +559,9 @@ export class TimelineService {
timelineName: string,
request: TimelineCreatePostRequest
): Observable<unknown> {
- const user = checkLogin();
return from(
getHttpTimelineClient()
- .postPost(timelineName, request, user.token)
+ .postPost(timelineName, request)
.then(() => {
void this.syncPosts(timelineName);
})
@@ -593,10 +569,9 @@ export class TimelineService {
}
deletePost(timelineName: string, postId: number): Observable<unknown> {
- const user = checkLogin();
return from(
getHttpTimelineClient()
- .deletePost(timelineName, postId, user.token)
+ .deletePost(timelineName, postId)
.then(() => {
void this.syncPosts(timelineName);
})
@@ -681,7 +656,10 @@ export function validateTimelineName(name: string): boolean {
export function useTimelineInfo(
timelineName: string
-): TimelineWithSyncStatus | undefined {
+): [
+ TimelineWithSyncStatus | undefined,
+ React.Dispatch<React.SetStateAction<TimelineWithSyncStatus | undefined>>
+] {
const [state, setState] = React.useState<TimelineWithSyncStatus | undefined>(
undefined
);
@@ -695,7 +673,7 @@ export function useTimelineInfo(
subscription.unsubscribe();
};
}, [timelineName]);
- return state;
+ return [state, setState];
}
export function usePostList(
diff --git a/FrontEnd/src/app/services/user.ts b/FrontEnd/src/app/services/user.ts
index 7a60b474..c3343493 100644
--- a/FrontEnd/src/app/services/user.ts
+++ b/FrontEnd/src/app/services/user.ts
@@ -5,7 +5,12 @@ import { map, filter } from "rxjs/operators";
import { UiLogicError } from "@/common";
import { convertError } from "@/utilities/rxjs";
-import { HttpNetworkError, BlobWithEtag, NotModified } from "@/http/common";
+import {
+ HttpNetworkError,
+ BlobWithEtag,
+ NotModified,
+ tokenSubject,
+} from "@/http/common";
import {
getHttpTokenClient,
HttpCreateTokenBadCredentialError,
@@ -61,6 +66,12 @@ export class BadCredentialError {
const USER_STORAGE_KEY = "currentuser";
export class UserService {
+ constructor() {
+ this.userSubject.subscribe((u) => {
+ tokenSubject.next(u?.token ?? null);
+ });
+ }
+
private userSubject = new BehaviorSubject<AuthUser | null | undefined>(
undefined
);
@@ -167,13 +178,10 @@ export class UserService {
throw new UiLogicError("Not login or checked now, can't log out.");
}
const $ = from(
- getHttpUserClient().changePassword(
- {
- oldPassword,
- newPassword,
- },
- this.currentUser.token
- )
+ getHttpUserClient().changePassword({
+ oldPassword,
+ newPassword,
+ })
);
$.subscribe(() => {
void this.logout();
@@ -378,15 +386,13 @@ export class UserInfoService {
}
async setAvatar(username: string, blob: Blob): Promise<void> {
- const user = checkLogin();
- await getHttpUserClient().putAvatar(username, blob, user.token);
+ await getHttpUserClient().putAvatar(username, blob);
this._avatarHub.getLine(username)?.next({ data: blob, type: "synced" });
}
async setNickname(username: string, nickname: string): Promise<void> {
- const user = checkLogin();
return getHttpUserClient()
- .patch(username, { nickname }, user.token)
+ .patch(username, { nickname })
.then((user) => {
this.saveUser(user);
});
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..b2b349bc 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
@@ -56,13 +56,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/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
index 7f5c8206..caced3b7 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
@@ -45,7 +45,7 @@ export default function TimelinePageTemplate<TManageItem>(
null
);
- const timelineState = useTimelineInfo(name);
+ const [timelineState, setTimelineState] = useTimelineInfo(name);
const postListState = usePostList(name);
const onPost: TimelinePostSendCallback = React.useCallback(
@@ -121,24 +121,64 @@ export default function TimelinePageTemplate<TManageItem>(
onBookmark:
user != null
? () => {
- void getHttpBookmarkClient()
- .put(name, user.token)
- .then(() => {
- pushAlert({
- message: {
- type: "i18n",
- key: "timeline.addBookmarkSuccess",
+ if (timeline.isBookmark) {
+ setTimelineState({
+ ...timelineState,
+ timeline: {
+ ...timeline,
+ isBookmark: false,
+ },
+ });
+ void getHttpBookmarkClient()
+ .delete(name)
+ .then(
+ () => {
+ void timelineService.syncTimeline(name);
},
- type: "success",
- });
+ () => {
+ pushAlert({
+ message: {
+ type: "i18n",
+ key: "timeline.removeBookmarkFail",
+ },
+ type: "danger",
+ });
+ setTimelineState(timelineState);
+ }
+ );
+ } else {
+ setTimelineState({
+ ...timelineState,
+ timeline: {
+ ...timeline,
+ isBookmark: true,
+ },
});
+ void getHttpBookmarkClient()
+ .put(name)
+ .then(
+ () => {
+ void timelineService.syncTimeline(name);
+ },
+ () => {
+ pushAlert({
+ message: {
+ type: "i18n",
+ key: "timeline.addBookmarkFail",
+ },
+ type: "danger",
+ });
+ setTimelineState(timelineState);
+ }
+ );
+ }
}
: undefined,
onHighlight:
user != null && user.hasHighlightTimelineAdministrationPermission
? () => {
void getHttpHighlightClient()
- .put(name, user.token)
+ .put(name)
.then(() => {
pushAlert({
message: {