From e326506465d26e82f81fc95abc587fe911295ab3 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 8 Aug 2020 19:15:38 +0800 Subject: ... --- Timeline/ClientApp/src/app/data/SyncStatusHub.ts | 19 ++++++ Timeline/ClientApp/src/app/data/user.ts | 76 ++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 Timeline/ClientApp/src/app/data/SyncStatusHub.ts (limited to 'Timeline/ClientApp/src') diff --git a/Timeline/ClientApp/src/app/data/SyncStatusHub.ts b/Timeline/ClientApp/src/app/data/SyncStatusHub.ts new file mode 100644 index 00000000..ed84f056 --- /dev/null +++ b/Timeline/ClientApp/src/app/data/SyncStatusHub.ts @@ -0,0 +1,19 @@ +export class SyncStatusHub { + private map = new Map(); + + get(key: string): boolean { + return this.map.get(key) ?? false; + } + + begin(key: string): void { + this.map.set(key, true); + } + + end(key: string): void { + this.map.set(key, false); + } +} + +export const syncStatusHub = new SyncStatusHub(); + +export default syncStatusHub; diff --git a/Timeline/ClientApp/src/app/data/user.ts b/Timeline/ClientApp/src/app/data/user.ts index 8b299c38..7e9458fe 100644 --- a/Timeline/ClientApp/src/app/data/user.ts +++ b/Timeline/ClientApp/src/app/data/user.ts @@ -6,6 +6,7 @@ import { convertError } from '../utilities/rxjs'; import { pushAlert } from '../common/alert-service'; import { dataStorage } from './common'; +import { syncStatusHub } from './SyncStatusHub'; import { SubscriptionHub, ISubscriptionHub } from './SubscriptionHub'; import { HttpNetworkError, BlobWithEtag, NotModified } from '../http/common'; @@ -18,6 +19,7 @@ import { HttpUserNotExistError, HttpUser, } from '../http/user'; +import { map, filter } from 'rxjs/operators'; export type User = HttpUser; @@ -226,6 +228,80 @@ export function checkLogin(): UserWithToken { export class UserNotExistError extends Error {} export class UserInfoService { + async saveUser(user: HttpUser): Promise { + const syncStatusKey = `user.${user.username}`; + if (syncStatusHub.get(syncStatusKey)) return; + syncStatusHub.begin(syncStatusKey); + await this.doSaveUser(user); + syncStatusHub.end(syncStatusKey); + this._userHub.getLine(user.username)?.next({ user, type: 'synced' }); + } + + private async getCachedUser(username: string): Promise { + const uniqueId = await dataStorage.getItem( + `user.${username}` + ); + if (uniqueId == null) return null; + const user = await dataStorage.getItem(`user.${uniqueId}`); + return user; + } + + private async doSaveUser(user: HttpUser): Promise { + await dataStorage.setItem(`user.${user.username}`, user.uniqueId); + await dataStorage.setItem(`user.${user.uniqueId}`, user); + } + + private async syncUser(username: string): Promise { + const syncStatusKey = `user.${username}`; + if (syncStatusHub.get(syncStatusKey)) return; + syncStatusHub.begin(syncStatusKey); + + try { + const res = await getHttpUserClient().get(username); + await this.doSaveUser(res); + syncStatusHub.end(syncStatusKey); + this._userHub.getLine(username)?.next({ user: res, type: 'synced' }); + } catch (e) { + if (e instanceof HttpUserNotExistError) { + syncStatusHub.end(syncStatusKey); + this._userHub.getLine(username)?.next({ type: 'notexist' }); + } else { + syncStatusHub.end(syncStatusKey); + const line = this._userHub.getLine(username); + if (line != null) { + const cache = await this.getCachedUser(username); + if (cache == null) line.next({ type: 'offline' }); + else line.next({ user: cache, type: 'offline' }); + } + if (!(e instanceof HttpNetworkError)) { + throw e; + } + } + } + } + + private _userHub = new SubscriptionHub< + string, + | { user: User; type: 'cache' | 'synced' | 'offline' } + | { user?: undefined; type: 'notexist' | 'offline' } + >({ + setup: (key, line) => { + void this.getCachedUser(key).then((cache) => { + if (cache != null) { + line.next({ user: cache, type: 'cache' }); + } + return this.syncUser(key); + }); + }, + }); + + getUser$(username: string): Observable { + return this._userHub.getObservable(username).pipe( + map((state) => state?.user), + filter((user): user is User => user != null) + ); + } + private getAvatarKey(username: string): string { return `user.${username}.avatar`; } -- cgit v1.2.3