aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/app/services/DataHub2.ts
diff options
context:
space:
mode:
Diffstat (limited to 'FrontEnd/src/app/services/DataHub2.ts')
-rw-r--r--FrontEnd/src/app/services/DataHub2.ts191
1 files changed, 0 insertions, 191 deletions
diff --git a/FrontEnd/src/app/services/DataHub2.ts b/FrontEnd/src/app/services/DataHub2.ts
deleted file mode 100644
index f0fb724b..00000000
--- a/FrontEnd/src/app/services/DataHub2.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-import { Observable } from "rxjs";
-
-export type DataStatus = "syncing" | "synced" | "offline";
-
-export function mergeDataStatus(statusList: DataStatus[]): DataStatus {
- if (statusList.includes("offline")) {
- return "offline";
- } else if (statusList.includes("syncing")) {
- return "syncing";
- } else {
- return "synced";
- }
-}
-
-export type Subscriber<TData> = (data: TData) => void;
-
-export interface DataAndStatus<TData> {
- data: TData | null;
- status: DataStatus;
-}
-
-export class DataLine2<TData> {
- constructor(
- private config: {
- saveData: (data: TData) => Promise<void>;
- getSavedData: () => Promise<TData | null>;
- // return null for offline
- fetchData: (savedData: TData | null) => Promise<TData | null>;
- }
- ) {}
-
- private _current: DataAndStatus<TData> | null = null;
- private _observers: Subscriber<DataAndStatus<TData>>[] = [];
-
- private _syncPromise: Promise<void> | null = null;
-
- get currentData(): DataAndStatus<TData> | null {
- return this._current;
- }
-
- get isDestroyable(): boolean {
- const { _observers, currentData } = this;
- return (
- _observers.length === 0 &&
- (currentData == null || currentData.status !== "syncing")
- );
- }
-
- private next(data: DataAndStatus<TData>): void {
- this._current = data;
- this._observers.forEach((o) => o(data));
- }
-
- subscribe(subsriber: Subscriber<DataAndStatus<TData>>): void {
- void this.sync(); // TODO: Should I sync at this point or let the user sync explicitly.
- this._observers.push(subsriber);
- const { currentData } = this;
- if (currentData != null) {
- subsriber(currentData);
- }
- }
-
- unsubscribe(subsriber: Subscriber<DataAndStatus<TData>>): void {
- const index = this._observers.indexOf(subsriber);
- if (index > -1) this._observers.splice(index, 1);
- }
-
- getObservalble(): Observable<DataAndStatus<TData>> {
- return new Observable<DataAndStatus<TData>>((observer) => {
- const f = (data: DataAndStatus<TData>): void => {
- observer.next(data);
- };
- this.subscribe(f);
-
- return () => {
- this.unsubscribe(f);
- };
- });
- }
-
- private syncWithAction(action: () => Promise<void>): Promise<void> {
- if (this._syncPromise != null) return this._syncPromise;
- this._syncPromise = action().then(() => {
- this._syncPromise = null;
- });
- return this._syncPromise;
- }
-
- sync(): Promise<void> {
- return this.syncWithAction(this.doSync.bind(this));
- }
-
- private async doSync(): Promise<void> {
- const { currentData } = this;
- this.next({ data: currentData?.data ?? null, status: "syncing" });
- const savedData = await this.config.getSavedData();
- if (currentData == null && savedData != null) {
- this.next({ data: savedData, status: "syncing" });
- }
- const data = await this.config.fetchData(savedData);
- if (data == null) {
- this.next({
- data: savedData,
- status: "offline",
- });
- } else {
- await this.config.saveData(data);
- this.next({ data: data, status: "synced" });
- }
- }
-
- save(data: TData): Promise<void> {
- return this.syncWithAction(this.doSave.bind(this, data));
- }
-
- private async doSave(data: TData): Promise<void> {
- await this.config.saveData(data);
- this.next({ data: data, status: "synced" });
- }
-
- getSavedData(): Promise<TData | null> {
- return this.config.getSavedData();
- }
-}
-
-export class DataHub2<TKey, TData> {
- private readonly subscriptionLineMap = new Map<string, DataLine2<TData>>();
-
- private keyToString: (key: TKey) => string;
-
- private cleanTimerId = 0;
-
- // setup is called after creating line and if it returns a function as destroyer, then when the line is destroyed the destroyer will be called.
- constructor(
- private config: {
- saveData: (key: TKey, data: TData) => Promise<void>;
- getSavedData: (key: TKey) => Promise<TData | null>;
- fetchData: (key: TKey, savedData: TData | null) => Promise<TData | null>;
- keyToString?: (key: TKey) => string;
- }
- ) {
- this.keyToString =
- config.keyToString ??
- ((value): string => {
- if (typeof value === "string") return value;
- else
- throw new Error(
- "Default keyToString function only pass string value."
- );
- });
- }
-
- private cleanLines(): void {
- const toDelete: string[] = [];
- for (const [key, line] of this.subscriptionLineMap.entries()) {
- if (line.isDestroyable) {
- toDelete.push(key);
- }
- }
-
- if (toDelete.length === 0) return;
-
- for (const key of toDelete) {
- this.subscriptionLineMap.delete(key);
- }
-
- if (this.subscriptionLineMap.size === 0) {
- window.clearInterval(this.cleanTimerId);
- this.cleanTimerId = 0;
- }
- }
-
- private createLine(key: TKey): DataLine2<TData> {
- const keyString = this.keyToString(key);
- const newLine: DataLine2<TData> = new DataLine2<TData>({
- saveData: (data) => this.config.saveData(key, data),
- getSavedData: () => this.config.getSavedData(key),
- fetchData: (savedData) => this.config.fetchData(key, savedData),
- });
- this.subscriptionLineMap.set(keyString, newLine);
- if (this.subscriptionLineMap.size === 1) {
- this.cleanTimerId = window.setInterval(this.cleanLines.bind(this), 20000);
- }
- return newLine;
- }
-
- getLine(key: TKey): DataLine2<TData> {
- const keyString = this.keyToString(key);
- return this.subscriptionLineMap.get(keyString) ?? this.createLine(key);
- }
-}