diff options
author | Sebastian <sebasjm@gmail.com> | 2023-04-14 13:07:23 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-04-14 14:16:24 -0300 |
commit | c3e1a0bb519bf5012781891c15c433841203bce2 (patch) | |
tree | 621348beccb0b38cf8069db1374debc96a076b4a /packages/web-util/src/utils | |
parent | 665adb69f065f5d371c7ce71b0bdd32c23a600a4 (diff) | |
download | wallet-core-c3e1a0bb519bf5012781891c15c433841203bce2.tar.xz |
observable memory impl
Diffstat (limited to 'packages/web-util/src/utils')
-rw-r--r-- | packages/web-util/src/utils/observable.ts | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/packages/web-util/src/utils/observable.ts b/packages/web-util/src/utils/observable.ts new file mode 100644 index 000000000..dfa434635 --- /dev/null +++ b/packages/web-util/src/utils/observable.ts @@ -0,0 +1,181 @@ +export type ObservableMap<K, V> = Map<K, V> & { + onUpdate: (key: string, callback: () => void) => () => void; +}; + +const UPDATE_EVENT_NAME = "update"; + +//FIXME: allow different type for different properties +export function memoryMap<T>(): ObservableMap<string, T> { + const obs = new EventTarget(); + const theMap = new Map<string, T>(); + const theMemoryMap: ObservableMap<string, T> = { + onUpdate: (key, handler) => { + //@ts-ignore + theMemoryMap.size = theMap.length; + obs.addEventListener(`update-${key}`, handler); + obs.addEventListener(`clear`, handler); + return () => { + obs.removeEventListener(`update-${key}`, handler); + obs.removeEventListener(`clear`, handler); + }; + }, + delete: (key: string) => { + const result = theMap.delete(key); + obs.dispatchEvent(new Event(`update-${key}`)); + return result; + }, + set: (key: string, value: T) => { + theMap.set(key, value); + obs.dispatchEvent(new Event(`update-${key}`)); + return theMemoryMap; + }, + clear: () => { + theMap.clear(); + obs.dispatchEvent(new Event(`clear`)); + }, + entries: theMap.entries.bind(theMap), + forEach: theMap.forEach.bind(theMap), + get: theMap.get.bind(theMap), + has: theMap.has.bind(theMap), + keys: theMap.keys.bind(theMap), + size: theMap.size, + values: theMap.values.bind(theMap), + [Symbol.iterator]: theMap[Symbol.iterator], + [Symbol.toStringTag]: "theMemoryMap", + }; + return theMemoryMap; +} + +export function localStorageMap(): ObservableMap<string, string> { + const obs = new EventTarget(); + const theLocalStorageMap: ObservableMap<string, string> = { + onUpdate: (key, handler) => { + //@ts-ignore + theLocalStorageMap.size = localStorage.length; + obs.addEventListener(`update-${key}`, handler); + obs.addEventListener(`clear`, handler); + function handleStorageEvent(ev: StorageEvent) { + if (ev.key === null || ev.key === key) { + handler(); + } + } + window.addEventListener("storage", handleStorageEvent); + return () => { + window.removeEventListener("storage", handleStorageEvent); + obs.removeEventListener(`update-${key}`, handler); + obs.removeEventListener(`clear`, handler); + }; + }, + delete: (key: string) => { + const exists = localStorage.getItem(key) !== null; + localStorage.removeItem(key); + obs.dispatchEvent(new Event(`update-${key}`)); + return exists; + }, + set: (key: string, v: string) => { + localStorage.setItem(key, v); + obs.dispatchEvent(new Event(`update-${key}`)); + return theLocalStorageMap; + }, + clear: () => { + localStorage.clear(); + obs.dispatchEvent(new Event(`clear`)); + }, + entries: (): IterableIterator<[string, string]> => { + let index = 0; + const total = localStorage.length; + return { + next() { + const key = localStorage.key(index); + if (key === null) { + //we are going from 0 until last, this should not happen + throw Error("key cant be null"); + } + const item = localStorage.getItem(key); + if (item === null) { + //the key exist, this should not happen + throw Error("value cant be null"); + } + if (index == total) return { done: true, value: [key, item] }; + index = index + 1; + return { done: false, value: [key, item] }; + }, + [Symbol.iterator]() { + return this; + }, + }; + }, + forEach: (cb) => { + for (let index = 0; index < localStorage.length; index++) { + const key = localStorage.key(index); + if (key === null) { + //we are going from 0 until last, this should not happen + throw Error("key cant be null"); + } + const item = localStorage.getItem(key); + if (item === null) { + //the key exist, this should not happen + throw Error("value cant be null"); + } + cb(key, item, theLocalStorageMap); + } + }, + get: (key: string) => { + const item = localStorage.getItem(key); + if (item === null) return undefined; + return item; + }, + has: (key: string) => { + return localStorage.getItem(key) === null; + }, + keys: () => { + let index = 0; + const total = localStorage.length; + return { + next() { + const key = localStorage.key(index); + if (key === null) { + //we are going from 0 until last, this should not happen + throw Error("key cant be null"); + } + if (index == total) return { done: true, value: key }; + index = index + 1; + return { done: false, value: key }; + }, + [Symbol.iterator]() { + return this; + }, + }; + }, + size: localStorage.length, + values: () => { + let index = 0; + const total = localStorage.length; + return { + next() { + const key = localStorage.key(index); + if (key === null) { + //we are going from 0 until last, this should not happen + throw Error("key cant be null"); + } + const item = localStorage.getItem(key); + if (item === null) { + //the key exist, this should not happen + throw Error("value cant be null"); + } + if (index == total) return { done: true, value: item }; + index = index + 1; + return { done: false, value: item }; + }, + [Symbol.iterator]() { + return this; + }, + }; + }, + [Symbol.iterator]: function (): IterableIterator<[string, string]> { + return theLocalStorageMap.entries(); + }, + [Symbol.toStringTag]: "theLocalStorageMap", + }; + return theLocalStorageMap; +} |