gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (6e060da23 -> b1a0d034f)


From: gnunet
Subject: [taler-wallet-core] branch master updated (6e060da23 -> b1a0d034f)
Date: Tue, 18 Apr 2023 15:46:33 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository wallet-core.

    from 6e060da23 some changes
     new 6833b2bd7 remove console log
     new b1a0d034f sync with chrome storage

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/web-util/package.json                 |   3 +
 packages/web-util/src/hooks/useLocalStorage.ts |  20 +++-
 packages/web-util/src/utils/observable.ts      | 140 +++++++++++++++++++++----
 packages/web-util/tsconfig.json                |   1 +
 pnpm-lock.yaml                                 |   7 +-
 5 files changed, 145 insertions(+), 26 deletions(-)

diff --git a/packages/web-util/package.json b/packages/web-util/package.json
index 3451bc0ad..f89d0bb73 100644
--- a/packages/web-util/package.json
+++ b/packages/web-util/package.json
@@ -50,5 +50,8 @@
     "tslib": "^2.4.0",
     "typescript": "^4.9.4",
     "ws": "7.4.5"
+  },
+  "dependencies": {
+    "@types/chrome": "0.0.197"
   }
 }
\ No newline at end of file
diff --git a/packages/web-util/src/hooks/useLocalStorage.ts 
b/packages/web-util/src/hooks/useLocalStorage.ts
index 264919d37..495c9b0f8 100644
--- a/packages/web-util/src/hooks/useLocalStorage.ts
+++ b/packages/web-util/src/hooks/useLocalStorage.ts
@@ -20,7 +20,12 @@
  */
 
 import { useEffect, useState } from "preact/hooks";
-import { localStorageMap, memoryMap } from "../utils/observable.js";
+import {
+  ObservableMap,
+  browserStorageMap,
+  localStorageMap,
+  memoryMap,
+} from "../utils/observable.js";
 
 export interface LocalStorageState {
   value?: string;
@@ -29,8 +34,18 @@ export interface LocalStorageState {
 }
 
 const supportLocalStorage = typeof window !== "undefined";
+const supportBrowserStorage =
+  typeof chrome !== "undefined" && typeof chrome.storage !== "undefined";
 
-const storage = supportLocalStorage ? localStorageMap() : memoryMap<string>();
+const storage: ObservableMap<string, string> = (function buildStorage() {
+  if (supportBrowserStorage) {
+    return browserStorageMap(memoryMap<string>());
+  } else if (supportLocalStorage) {
+    return localStorageMap();
+  } else {
+    return memoryMap<string>();
+  }
+})();
 
 export function useLocalStorage(
   key: string,
@@ -50,7 +65,6 @@ export function useLocalStorage(
   useEffect(() => {
     return storage.onUpdate(key, () => {
       const newValue = storage.get(key);
-      console.log("new value", key, newValue);
       setStoredValue(newValue ?? initialValue);
     });
   }, []);
diff --git a/packages/web-util/src/utils/observable.ts 
b/packages/web-util/src/utils/observable.ts
index dfa434635..01e655eaa 100644
--- a/packages/web-util/src/utils/observable.ts
+++ b/packages/web-util/src/utils/observable.ts
@@ -1,17 +1,25 @@
+import { isArrayBufferView } from "util/types";
+
 export type ObservableMap<K, V> = Map<K, V> & {
+  onAnyUpdate: (callback: () => void) => () => void;
   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> {
+export function memoryMap<T>(
+  backend: Map<string, T> = new Map<string, T>(),
+): ObservableMap<string, T> {
   const obs = new EventTarget();
-  const theMap = new Map<string, T>();
   const theMemoryMap: ObservableMap<string, T> = {
+    onAnyUpdate: (handler) => {
+      obs.addEventListener(`update`, handler);
+      obs.addEventListener(`clear`, handler);
+      return () => {
+        obs.removeEventListener(`update`, handler);
+        obs.removeEventListener(`clear`, handler);
+      };
+    },
     onUpdate: (key, handler) => {
-      //@ts-ignore
-      theMemoryMap.size = theMap.length;
       obs.addEventListener(`update-${key}`, handler);
       obs.addEventListener(`clear`, handler);
       return () => {
@@ -20,38 +28,56 @@ export function memoryMap<T>(): ObservableMap<string, T> {
       };
     },
     delete: (key: string) => {
-      const result = theMap.delete(key);
+      const result = backend.delete(key);
+      //@ts-ignore
+      theMemoryMap.size = backend.length;
       obs.dispatchEvent(new Event(`update-${key}`));
+      obs.dispatchEvent(new Event(`update`));
       return result;
     },
     set: (key: string, value: T) => {
-      theMap.set(key, value);
+      backend.set(key, value);
+      //@ts-ignore
+      theMemoryMap.size = backend.length;
       obs.dispatchEvent(new Event(`update-${key}`));
+      obs.dispatchEvent(new Event(`update`));
       return theMemoryMap;
     },
     clear: () => {
-      theMap.clear();
+      backend.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],
+    entries: backend.entries.bind(backend),
+    forEach: backend.forEach.bind(backend),
+    get: backend.get.bind(backend),
+    has: backend.has.bind(backend),
+    keys: backend.keys.bind(backend),
+    size: backend.size,
+    values: backend.values.bind(backend),
+    [Symbol.iterator]: backend[Symbol.iterator],
     [Symbol.toStringTag]: "theMemoryMap",
   };
   return theMemoryMap;
 }
 
+//FIXME: change this implementation to match the
+// browser storage. instead of creating a sync implementation
+// of observable map it should reuse the memoryMap and
+// sync the state with local storage
 export function localStorageMap(): ObservableMap<string, string> {
   const obs = new EventTarget();
   const theLocalStorageMap: ObservableMap<string, string> = {
+    onAnyUpdate: (handler) => {
+      obs.addEventListener(`update`, handler);
+      obs.addEventListener(`clear`, handler);
+      window.addEventListener("storage", handler);
+      return () => {
+        window.removeEventListener("storage", handler);
+        obs.removeEventListener(`update`, handler);
+        obs.removeEventListener(`clear`, handler);
+      };
+    },
     onUpdate: (key, handler) => {
-      //@ts-ignore
-      theLocalStorageMap.size = localStorage.length;
       obs.addEventListener(`update-${key}`, handler);
       obs.addEventListener(`clear`, handler);
       function handleStorageEvent(ev: StorageEvent) {
@@ -69,12 +95,18 @@ export function localStorageMap(): ObservableMap<string, 
string> {
     delete: (key: string) => {
       const exists = localStorage.getItem(key) !== null;
       localStorage.removeItem(key);
+      //@ts-ignore
+      theLocalStorageMap.size = localStorage.length;
       obs.dispatchEvent(new Event(`update-${key}`));
+      obs.dispatchEvent(new Event(`update`));
       return exists;
     },
     set: (key: string, v: string) => {
       localStorage.setItem(key, v);
+      //@ts-ignore
+      theLocalStorageMap.size = localStorage.length;
       obs.dispatchEvent(new Event(`update-${key}`));
+      obs.dispatchEvent(new Event(`update`));
       return theLocalStorageMap;
     },
     clear: () => {
@@ -179,3 +211,73 @@ export function localStorageMap(): ObservableMap<string, 
string> {
   };
   return theLocalStorageMap;
 }
+
+const isFirefox =
+  typeof (window as any) !== "undefined" &&
+  typeof (window as any)["InstallTrigger"] !== "undefined";
+
+async function getAllContent() {
+  //Firefox and Chrome has different storage api
+  if (isFirefox) {
+    // @ts-ignore
+    return browser.storage.local.get();
+  } else {
+    return chrome.storage.local.get();
+  }
+}
+
+async function updateContent(obj: Record<string, any>) {
+  if (isFirefox) {
+    // @ts-ignore
+    return browser.storage.local.set(obj);
+  } else {
+    return chrome.storage.local.set(obj);
+  }
+}
+type Changes = { [key: string]: { oldValue?: any; newValue?: any } };
+function onBrowserStorageUpdate(cb: (changes: Changes) => void): void {
+  if (isFirefox) {
+    // @ts-ignore
+    browser.storage.local.onChanged.addListener(cb);
+  } else {
+    chrome.storage.local.onChanged.addListener(cb);
+  }
+}
+
+export function browserStorageMap(
+  backend: ObservableMap<string, string>,
+): ObservableMap<string, string> {
+  getAllContent().then((content) => {
+    Object.entries(content ?? {}).forEach(([k, v]) => {
+      backend.set(k, v as string);
+    });
+  });
+
+  backend.onAnyUpdate(async () => {
+    const result: Record<string, string> = {};
+    for (const [key, value] of backend.entries()) {
+      result[key] = value;
+    }
+    await updateContent(result);
+  });
+
+  onBrowserStorageUpdate((changes) => {
+    //another chrome instance made the change
+    const changedItems = Object.keys(changes);
+    if (changedItems.length === 0) {
+      backend.clear();
+    } else {
+      for (const key of changedItems) {
+        if (!changes[key].newValue) {
+          backend.delete(key);
+        } else {
+          if (changes[key].newValue !== changes[key].oldValue) {
+            backend.set(key, changes[key].newValue);
+          }
+        }
+      }
+    }
+  });
+
+  return backend;
+}
diff --git a/packages/web-util/tsconfig.json b/packages/web-util/tsconfig.json
index 3965f537d..e2ac248aa 100644
--- a/packages/web-util/tsconfig.json
+++ b/packages/web-util/tsconfig.json
@@ -9,6 +9,7 @@
     "moduleResolution": "Node16",
     "sourceMap": true,
     "lib": [
+      "DOM",
       "es6"
     ],
     "outDir": "lib",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 56f678e46..c7b2df4bf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -560,6 +560,7 @@ importers:
   packages/web-util:
     specifiers:
       '@gnu-taler/taler-util': workspace:*
+      '@types/chrome': 0.0.197
       '@types/express': ^4.17.14
       '@types/node': ^18.11.17
       '@types/web': ^0.0.82
@@ -576,6 +577,8 @@ importers:
       tslib: ^2.4.0
       typescript: ^4.9.4
       ws: 7.4.5
+    dependencies:
+      '@types/chrome': 0.0.197
     devDependencies:
       '@gnu-taler/taler-util': link:../taler-util
       '@types/express': 4.17.14
@@ -3775,7 +3778,6 @@ packages:
     dependencies:
       '@types/filesystem': 0.0.32
       '@types/har-format': 1.2.9
-    dev: true
 
   /@types/connect-history-api-fallback/1.3.5:
     resolution: {integrity: 
sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==}
@@ -3815,15 +3817,12 @@ packages:
     resolution: {integrity: 
sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==}
     dependencies:
       '@types/filewriter': 0.0.29
-    dev: true
 
   /@types/filewriter/0.0.29:
     resolution: {integrity: 
sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==}
-    dev: true
 
   /@types/har-format/1.2.9:
     resolution: {integrity: 
sha512-rffW6MhQ9yoa75bdNi+rjZBAvu2HhehWJXlhuWXnWdENeuKe82wUgAwxYOb7KRKKmxYN+D/iRKd2NDQMLqlUmg==}
-    dev: true
 
   /@types/history/4.7.11:
     resolution: {integrity: 
sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]