aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-05-16 17:50:12 +0800
committercrupest <crupest@outlook.com>2021-05-16 17:50:12 +0800
commite3ee59e8101a786325c00e92a230e1de8069584e (patch)
treefe060dcff29223130a18b8c9301b2326befc64b5 /FrontEnd/src
parentce2856956990c5d2f6f26228ca9ef004afc15e47 (diff)
downloadtimeline-e3ee59e8101a786325c00e92a230e1de8069584e.tar.gz
timeline-e3ee59e8101a786325c00e92a230e1de8069584e.tar.bz2
timeline-e3ee59e8101a786325c00e92a230e1de8069584e.zip
feat: Listen to post update.
Diffstat (limited to 'FrontEnd/src')
-rw-r--r--FrontEnd/src/app/http/common.ts9
-rw-r--r--FrontEnd/src/app/services/timeline.ts51
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx12
3 files changed, 69 insertions, 3 deletions
diff --git a/FrontEnd/src/app/http/common.ts b/FrontEnd/src/app/http/common.ts
index 78ba3cda..e1672985 100644
--- a/FrontEnd/src/app/http/common.ts
+++ b/FrontEnd/src/app/http/common.ts
@@ -1,5 +1,6 @@
import rawAxios, { AxiosError, AxiosResponse } from "axios";
import { Base64 } from "js-base64";
+import { BehaviorSubject, Observable } from "rxjs";
export const apiBaseUrl = "/api";
@@ -44,14 +45,14 @@ axios.interceptors.response.use(undefined, convertToNetworkError);
axios.interceptors.response.use(undefined, convertToForbiddenError);
axios.interceptors.response.use(undefined, convertToNotFoundError);
-let _token: string | null = null;
+const tokenSubject = new BehaviorSubject<string | null>(null);
export function getHttpToken(): string | null {
- return _token;
+ return tokenSubject.value;
}
export function setHttpToken(token: string | null): void {
- _token = token;
+ tokenSubject.next(token);
if (token == null) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -62,6 +63,8 @@ export function setHttpToken(token: string | null): void {
}
}
+export const token$: Observable<string | null> = tokenSubject.asObservable();
+
export function base64(blob: Blob | string): Promise<string> {
if (typeof blob === "string") {
return Promise.resolve(Base64.encode(blob));
diff --git a/FrontEnd/src/app/services/timeline.ts b/FrontEnd/src/app/services/timeline.ts
index a24ec8eb..c49ba654 100644
--- a/FrontEnd/src/app/services/timeline.ts
+++ b/FrontEnd/src/app/services/timeline.ts
@@ -1,5 +1,11 @@
import { TimelineVisibility } from "@/http/timeline";
import XRegExp from "xregexp";
+import { Observable } from "rxjs";
+import { HubConnection, HubConnectionBuilder } from "@microsoft/signalr";
+
+import { UiLogicError } from "@/common";
+
+import { token$ } from "@/http/common";
const timelineNameReg = XRegExp("^[-_\\p{L}]*$", "u");
@@ -15,3 +21,48 @@ export const timelineVisibilityTooltipTranslationMap: Record<
Register: "timeline.visibilityTooltip.register",
Private: "timeline.visibilityTooltip.private",
};
+
+function generateTimelineHubUrl(token: string | null): string {
+ return `/api/hub/timeline${token == null ? "" : "?token=" + token}`;
+}
+
+function createTimelineHubConnection(token: string | null): HubConnection {
+ return new HubConnectionBuilder()
+ .withUrl(generateTimelineHubUrl(token))
+ .withAutomaticReconnect()
+ .build();
+}
+
+let timelineHubConnection: HubConnection | null = null;
+
+token$.subscribe((token) => {
+ if (timelineHubConnection != null) {
+ void timelineHubConnection.stop();
+ }
+ timelineHubConnection = createTimelineHubConnection(token);
+ void timelineHubConnection.start();
+});
+
+export function getTimelinePostUpdate(
+ timelineName: string
+): Observable<string> {
+ return new Observable((subscriber) => {
+ if (timelineHubConnection == null)
+ throw new UiLogicError("Connection is null.");
+
+ const connection = timelineHubConnection;
+
+ const handler = (tn: string): void => {
+ if (timelineName === tn) {
+ subscriber.next(tn);
+ }
+ };
+ connection.on("OnTimelinePostChanged", handler);
+ void connection.invoke("SubscribeTimelinePostChange", timelineName);
+
+ return () => {
+ void connection.invoke("UnsubscribeTimelinePostChange", timelineName);
+ connection.off("OnTimelinePostChanged", handler);
+ };
+ });
+}
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
index 81a3c179..4c0cc8e3 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
@@ -12,6 +12,7 @@ import TimelinePostEdit from "./TimelinePostEdit";
import useReverseScrollPositionRemember from "@/utilities/useReverseScrollPositionRemember";
import { generatePalette, setPalette } from "@/palette";
+import { getTimelinePostUpdate as getTimelinePostUpdate$ } from "@/services/timeline";
export interface TimelinePageCardProps {
timeline: HttpTimelineInfo;
@@ -91,6 +92,17 @@ const TimelinePageTemplate: React.FC<TimelinePageTemplateProps> = (props) => {
setTimelineReloadKey((old) => old + 1);
};
+ React.useEffect(() => {
+ const timelinePostUpdate$ = getTimelinePostUpdate$(timelineName);
+ const subscription = timelinePostUpdate$.subscribe(() => {
+ setTimelineReloadKey((old) => old + 1);
+ });
+
+ return () => {
+ subscription.unsubscribe();
+ };
+ }, [timelineName]);
+
const onPostEditHeightChange = React.useCallback((height: number): void => {
setBottomSpaceHeight(height);
if (height === 0) {