aboutsummaryrefslogtreecommitdiff
path: root/Timeline/ClientApp
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-08-08 21:04:00 +0800
committercrupest <crupest@outlook.com>2020-08-08 21:04:00 +0800
commitbfcd27afb82d44505a2b7b0bfd1335ea2d8205b7 (patch)
tree4fffd94a3241800e88a76cb82527baaf98045f3d /Timeline/ClientApp
parentb4950b4b9d5fbfe0dbef2b1706bf322f737b877e (diff)
downloadtimeline-bfcd27afb82d44505a2b7b0bfd1335ea2d8205b7.tar.gz
timeline-bfcd27afb82d44505a2b7b0bfd1335ea2d8205b7.tar.bz2
timeline-bfcd27afb82d44505a2b7b0bfd1335ea2d8205b7.zip
...
Diffstat (limited to 'Timeline/ClientApp')
-rw-r--r--Timeline/ClientApp/src/app/data/timeline.ts107
1 files changed, 103 insertions, 4 deletions
diff --git a/Timeline/ClientApp/src/app/data/timeline.ts b/Timeline/ClientApp/src/app/data/timeline.ts
index 477d410b..7ef7a8bb 100644
--- a/Timeline/ClientApp/src/app/data/timeline.ts
+++ b/Timeline/ClientApp/src/app/data/timeline.ts
@@ -1,7 +1,7 @@
import React from 'react';
import XRegExp from 'xregexp';
import { Observable, from, combineLatest } from 'rxjs';
-import { map, switchMap } from 'rxjs/operators';
+import { map, switchMap, filter } from 'rxjs/operators';
import { convertError } from '../utilities/rxjs';
@@ -111,6 +111,10 @@ type TimelineData = Omit<HttpTimelineInfo, 'owner' | 'members'> & {
members: string[];
};
+type TimelinePostData = Omit<HttpTimelinePostInfo, 'author'> & {
+ author: string;
+};
+
export class TimelineService {
private getCachedTimeline(
timelineName: string
@@ -261,9 +265,7 @@ export class TimelineService {
getHttpTimelineClient()
.memberPut(timelineName, username, user.token)
.then(() => {
- userInfoService.getUserInfo(username).subscribe(() => {
- void this.syncTimeline(timelineName);
- });
+ void this.syncTimeline(timelineName);
})
);
}
@@ -501,6 +503,103 @@ export class TimelineService {
return this._postsSubscriptionHub;
}
+ private getCachedPostData(
+ timelineName: string,
+ postId: number
+ ): Promise<Blob | null> {
+ return dataStorage
+ .getItem<BlobWithEtag | null>(
+ `timeline.${timelineName}.post.${postId}.data`
+ )
+ .then((data) => data?.data ?? null);
+ }
+
+ private async syncPostData(
+ timelineName: string,
+ postId: number
+ ): Promise<void> {
+ const syncStatusKey = `user.timeline.${timelineName}.post.data.${postId}`;
+ if (syncStatusHub.get(syncStatusKey)) return;
+ syncStatusHub.begin(syncStatusKey);
+
+ const dataKey = `timeline.${timelineName}.post.${postId}.data`;
+
+ const cache = await dataStorage.getItem<BlobWithEtag | null>(dataKey);
+ if (cache == null) {
+ try {
+ const data = await getHttpTimelineClient().getPostData(
+ timelineName,
+ postId
+ );
+ await dataStorage.setItem<BlobWithEtag>(dataKey, data);
+ syncStatusHub.end(syncStatusKey);
+ this._postDataHub
+ .getLine({ timelineName, postId })
+ ?.next({ data: data.data, type: 'synced' });
+ } catch (e) {
+ syncStatusHub.end(syncStatusKey);
+ this._postDataHub
+ .getLine({ timelineName, postId })
+ ?.next({ type: 'offline' });
+ if (!(e instanceof HttpNetworkError)) {
+ throw e;
+ }
+ }
+ } else {
+ try {
+ const res = await getHttpTimelineClient().getPostData(
+ timelineName,
+ postId,
+ cache.etag
+ );
+ if (res instanceof NotModified) {
+ syncStatusHub.end(syncStatusKey);
+ this._postDataHub
+ .getLine({ timelineName, postId })
+ ?.next({ data: cache.data, type: 'synced' });
+ } else {
+ const avatar = res;
+ await dataStorage.setItem<BlobWithEtag>(dataKey, avatar);
+ syncStatusHub.end(syncStatusKey);
+ this._postDataHub
+ .getLine({ timelineName, postId })
+ ?.next({ data: avatar.data, type: 'synced' });
+ }
+ } catch (e) {
+ syncStatusHub.end(syncStatusKey);
+ this._postDataHub
+ .getLine({ timelineName, postId })
+ ?.next({ data: cache.data, type: 'offline' });
+ if (!(e instanceof HttpNetworkError)) {
+ throw e;
+ }
+ }
+ }
+ }
+
+ private _postDataHub = new SubscriptionHub<
+ { timelineName: string; postId: number },
+ | { data: Blob; type: 'cache' | 'synced' | 'offline' }
+ | { data?: undefined; type: 'notexist' | 'offline' }
+ >({
+ keyToString: (key) => `${key.timelineName}.${key.postId}`,
+ setup: (key, line) => {
+ void this.getCachedPostData(key.timelineName, key.postId).then((data) => {
+ if (data != null) {
+ line.next({ data: data, type: 'cache' });
+ }
+ return this.syncPostData(key.timelineName, key.postId);
+ });
+ },
+ });
+
+ getPostData$(timelineName: string, postId: number): Observable<Blob> {
+ return this._postDataHub.getObservable({ timelineName, postId }).pipe(
+ map((state) => state.data),
+ filter((blob): blob is Blob => blob != null)
+ );
+ }
+
createPost(
timelineName: string,
request: TimelineCreatePostRequest