bigbluebutton-Github/bigbluebutton-html5/imports/ui/services/storage/observable.ts

104 lines
2.7 KiB
TypeScript
Raw Normal View History

import { isString, isEqual } from 'radash';
import logger from '/imports/startup/client/logger';
export type StorageData = string | object | boolean | number | null;
/**
* Observable wrapper for browser Storage's
*/
export default class ObservableStorage {
private readonly observers: Record<string, Set<(value: StorageData) => void>>;
private readonly prefix: string;
private readonly storage: Storage;
constructor(storage: Storage, prefix = '') {
this.observers = {};
this.prefix = prefix;
this.storage = storage;
}
public registerObserver(key: string, observer: (value: StorageData) => void) {
const prefixedKey = this.prefixedKey(key);
this.ensureDeps(prefixedKey);
this.observers[prefixedKey].add(observer);
}
public revokeObserver(key: string, observer: (value: StorageData) => void) {
const prefixedKey = this.prefixedKey(key);
this.ensureDeps(prefixedKey);
this.observers[prefixedKey].delete(observer);
}
private ensureDeps(key: string) {
if (!(this.observers[key] instanceof Set)) {
this.observers[key] = new Set();
}
}
private prefixedKey(key: string) {
const unprefixedKey = key.replace(this.prefix, '');
return `${this.prefix}${unprefixedKey}`;
}
public key(n: number) {
return this.storage.key(n);
}
public getItem(key: string): StorageData {
const prefixedKey = this.prefixedKey(key);
let value = this.storage.getItem(prefixedKey);
if (value && isString(value)) {
try {
value = JSON.parse(value);
} catch (e) {
logger.warn(`StorageTracker: parsing ${key} threw an error`, e);
}
}
return value;
}
public setItem(key: string, value: NonNullable<StorageData>) {
const prefixedKey = this.prefixedKey(key);
this.ensureDeps(prefixedKey);
let stringifiedValue: string;
if (isString(value)) {
stringifiedValue = value;
} else {
stringifiedValue = JSON.stringify(value);
}
this.storage.setItem(prefixedKey, stringifiedValue);
this.observers[prefixedKey].forEach((observer) => {
if (typeof observer === 'function') {
observer(value);
}
});
}
public removeItem(key: string) {
const prefixedKey = this.prefixedKey(key);
this.storage.removeItem(prefixedKey);
if (!this.observers[prefixedKey]) return;
this.observers[prefixedKey].forEach((observer) => {
if (typeof observer === 'function') {
observer(null);
}
});
}
public clear() {
Object.keys(this.observers).forEach((key) => {
this.removeItem(key);
});
}
public equals(key: string, value: StorageData) {
return isEqual(this.getItem(key), value);
}
}