From dd44dc2bd3dada15d9b0b676cc5a5b1ef0686559 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 30 Jul 2020 23:35:27 +0800 Subject: Save post data locally. --- Timeline/ClientApp/src/app/data/timeline.ts | 100 ++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 12 deletions(-) (limited to 'Timeline/ClientApp/src') diff --git a/Timeline/ClientApp/src/app/data/timeline.ts b/Timeline/ClientApp/src/app/data/timeline.ts index 04b63de2..50d45aa5 100644 --- a/Timeline/ClientApp/src/app/data/timeline.ts +++ b/Timeline/ClientApp/src/app/data/timeline.ts @@ -32,6 +32,7 @@ import { HttpTimelineNameConflictError, HttpTimelineGenericPostInfo, } from '../http/timeline'; +import { BlobWithEtag, NotModified } from '../http/common'; export type TimelineInfo = HttpTimelineInfo; export type TimelineChangePropertyRequest = HttpTimelinePatchRequest; @@ -158,6 +159,7 @@ export class TimelineService { // post list storage structure: // each timeline has a PostListInfo saved with key created by getPostListInfoKey // each post of a timeline has a HttpTimelinePostInfo with key created by getPostKey + // each post with data has BlobWithEtag with key created by getPostDataKey private getPostListInfoKey(timelineUniqueId: string): string { return `timeline.${timelineUniqueId}.postListInfo`; @@ -167,6 +169,10 @@ export class TimelineService { return `timeline.${timelineUniqueId}.post.${id}`; } + private getPostDataKey(timelineUniqueId: string, id: number): string { + return `timeline.${timelineUniqueId}.post.${id}.data`; + } + private async getCachedPostList( timelineName: string ): Promise { @@ -260,6 +266,9 @@ export class TimelineService { await dataStorage.removeItem( this.getPostKey(timeline.uniqueId, post.id) ); + await dataStorage.removeItem( + this.getPostDataKey(timeline.uniqueId, post.id) + ); } else { await dataStorage.setItem( this.getPostKey(timeline.uniqueId, post.id), @@ -323,6 +332,75 @@ export class TimelineService { return this._postListSubscriptionHub; } + private async getCachePostData( + timelineName: string, + postId: number + ): Promise { + const timeline = await this.getTimeline(timelineName).toPromise(); + const cache = await dataStorage.getItem( + this.getPostDataKey(timeline.uniqueId, postId) + ); + if (cache == null) { + return null; + } else { + return cache.data; + } + } + + private async syncCachePostData( + timelineName: string, + postId: number + ): Promise { + const timeline = await this.getTimeline(timelineName).toPromise(); + const dataKey = this.getPostDataKey(timeline.uniqueId, postId); + const cache = await dataStorage.getItem(dataKey); + + if (cache == null) { + const dataWithEtag = await getHttpTimelineClient().getPostData( + timelineName, + postId, + userService.currentUser?.token + ); + await dataStorage.setItem(dataKey, dataWithEtag); + this._postDataSubscriptionHub.update( + { + postId, + timelineName, + }, + () => + Promise.resolve({ + blob: dataWithEtag.data, + url: URL.createObjectURL(dataWithEtag.data), + }) + ); + return dataWithEtag.data; + } else { + const res = await getHttpTimelineClient().getPostData( + timelineName, + postId, + userService.currentUser?.token, + cache.etag + ); + if (res instanceof NotModified) { + return cache.data; + } else { + await dataStorage.setItem(dataKey, res); + this._postDataSubscriptionHub.update( + { + postId, + timelineName, + }, + () => + Promise.resolve({ + blob: res.data, + url: URL.createObjectURL(res.data), + }) + ); + return res.data; + } + } + } + private _postDataSubscriptionHub = new SubscriptionHub< PostKey, BlobWithUrl | null @@ -330,18 +408,16 @@ export class TimelineService { (key) => `${key.timelineName}/${key.postId}`, () => null, async (key) => { - const blob = ( - await getHttpTimelineClient().getPostData( - key.timelineName, - key.postId, - userService.currentUser?.token - ) - ).data; - const url = URL.createObjectURL(blob); - return { - blob, - url, - }; + const blob = await this.getCachePostData(key.timelineName, key.postId); + const result = + blob == null + ? null + : { + blob, + url: URL.createObjectURL(blob), + }; + void this.syncCachePostData(key.timelineName, key.postId); + return result; }, (_key, data) => { if (data != null) URL.revokeObjectURL(data.url); -- cgit v1.2.3