rt-storage
Version:
A real time storage library based on localforage and rxjs.
127 lines (113 loc) • 3.02 kB
text/typescript
import * as uuidv4 from 'uuid/v4';
import * as localForage from 'localforage';
import { Subject } from 'rxjs';
export default class RTStorage {
private _storage: any;
private _localStorage: any;
private _observable: any;
private _name: string;
private _id: string;
private _storageChangedEventKey: string;
private _tabSyncHandler: any;
private _ready: boolean;
constructor({ name, ...option }) {
this._name = name;
this._id = uuidv4();
localForage.config({ name: this._name, ...option });
this._storage = localForage.createInstance({
name: this._name,
});
this._localStorage = localStorage;
this._storageChangedEventKey = `${this._name}_storage_changed`;
this._observable = new Subject();
this._tabSyncHandler = async (event) => {
if (
event.key !== null &&
typeof event.key !== 'undefined' &&
event.key === this._storageChangedEventKey
) {
try {
const { setter, key } = JSON.parse(event.newValue);
if (!setter || setter === this.id) {
return;
}
const value = await this.getItem(key);
this._observable.next({ key, value });
} catch (e) {
/* ignore error */
}
}
};
window.addEventListener('storage', this._tabSyncHandler);
}
async setItem(key, value) {
await this.waitForReady();
await this._storage.setItem(
key,
{ value, setter: this.id },
);
this._observable.next({ key, value });
this._updateStorageChangeKey(key);
}
async getItem(key) {
await this.waitForReady();
const originalData = await this._storage.getItem(key);
try {
const { value } = originalData;
return value;
} catch (error) {
return undefined;
}
}
async removeItem(key) {
await this.waitForReady();
await this._storage.removeItem(key);
this._observable.next({ key, value: undefined });
this._updateStorageChangeKey(key);
}
async keys() {
await this.waitForReady();
const keys = await this._storage.keys();
return keys;
}
_updateStorageChangeKey(key) {
this._localStorage.setItem(
this._storageChangedEventKey,
JSON.stringify({
timestamp: Date.now(),
key,
setter: this.id,
})
);
}
get id() {
return this._id;
}
async waitForReady() {
if (this._ready) {
return;
}
if (typeof this._storage.ready === 'function') {
await this._storage.ready();
}
this._ready = true;
}
get $() {
return this._observable;
}
subscribe(keyOrFunc: Function|string, func?: Function) {
if (typeof keyOrFunc === 'function') {
return this._observable.subscribe(keyOrFunc);
}
return this._observable.subscribe((e) => {
if (e.key === keyOrFunc) {
func(e.value);
}
});
}
destory() {
if (this._tabSyncHandler) {
window.removeEventListener('storage', this._tabSyncHandler);
}
}
}