diff --git a/packages/taler-wallet-webextension/src/hooks/useSettings.ts b/packages/taler-wallet-webextension/src/hooks/useSettings.ts
index ebb1e991c..7e7b26a39 100644
--- a/packages/taler-wallet-webextension/src/hooks/useSettings.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useSettings.ts
@@ -14,8 +14,13 @@
GNU Taler; see the file COPYING. If not, see
*/
-import { useLocalStorage } from "@gnu-taler/web-util/browser";
+import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
import { Settings, defaultSettings } from "../platform/api.js";
+import {
+ Codec,
+ buildCodecForObject,
+ codecForBoolean,
+} from "@gnu-taler/taler-util";
function parse_json_or_undefined(str: string | undefined): T | undefined {
if (str === undefined) return undefined;
@@ -26,17 +31,30 @@ function parse_json_or_undefined(str: string | undefined): T | undefined {
}
}
+export const codecForSettings = (): Codec =>
+ buildCodecForObject()
+ .property("walletAllowHttp", codecForBoolean())
+ .property("walletBatchWithdrawal", codecForBoolean())
+ .property("injectTalerSupport", codecForBoolean())
+ .property("advanceMode", codecForBoolean())
+ .property("backup", codecForBoolean())
+ .property("langSelector", codecForBoolean())
+ .property("showJsonOnError", codecForBoolean())
+ .property("extendedAccountTypes", codecForBoolean())
+ .property("deleteActiveTransactions", codecForBoolean())
+ .build("Settings");
+
+const SETTINGS_KEY = buildStorageKey("wallet-settings", codecForSettings());
+
export function useSettings(): [
Readonly,
(key: T, value: Settings[T]) => void,
] {
- const { value, update } = useLocalStorage("wallet-settings");
+ const { value, update } = useLocalStorage(SETTINGS_KEY);
- const parsed: Settings = parse_json_or_undefined(value) ?? defaultSettings;
+ const parsed: Settings = value ?? defaultSettings;
function updateField(k: T, v: Settings[T]) {
- const newValue = { ...parsed, [k]: v };
- const json = JSON.stringify(newValue);
- update(json);
+ update({ ...parsed, [k]: v });
}
return [parsed, updateField];
}
diff --git a/packages/web-util/src/hooks/index.ts b/packages/web-util/src/hooks/index.ts
index ae8872497..a3a2053e6 100644
--- a/packages/web-util/src/hooks/index.ts
+++ b/packages/web-util/src/hooks/index.ts
@@ -1,5 +1,5 @@
export { useLang } from "./useLang.js";
-export { useLocalStorage } from "./useLocalStorage.js";
+export { useLocalStorage, buildStorageKey } from "./useLocalStorage.js";
export { useMemoryStorage } from "./useMemoryStorage.js";
export {
useNotifications,
diff --git a/packages/web-util/src/hooks/useLang.ts b/packages/web-util/src/hooks/useLang.ts
index d64cf6e1a..448cd8aba 100644
--- a/packages/web-util/src/hooks/useLang.ts
+++ b/packages/web-util/src/hooks/useLang.ts
@@ -14,7 +14,11 @@
GNU Anastasis; see the file COPYING. If not, see
*/
-import { LocalStorageState, useLocalStorage } from "./useLocalStorage.js";
+import {
+ StorageState,
+ buildStorageKey,
+ useLocalStorage,
+} from "./useLocalStorage.js";
function getBrowserLang(): string | undefined {
if (typeof window === "undefined") return undefined;
@@ -23,7 +27,9 @@ function getBrowserLang(): string | undefined {
return undefined;
}
-export function useLang(initial?: string): Required {
+const langPreferenceKey = buildStorageKey("lang-preference");
+
+export function useLang(initial?: string): Required {
const defaultValue = (getBrowserLang() || initial || "en").substring(0, 2);
- return useLocalStorage("lang-preference", { defaultValue: defaultValue });
+ return useLocalStorage(langPreferenceKey, defaultValue);
}
diff --git a/packages/web-util/src/hooks/useLocalStorage.ts b/packages/web-util/src/hooks/useLocalStorage.ts
index 55efd01cb..45b7abd3c 100644
--- a/packages/web-util/src/hooks/useLocalStorage.ts
+++ b/packages/web-util/src/hooks/useLocalStorage.ts
@@ -19,7 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { Codec } from "@gnu-taler/taler-util";
+import { Codec, codecForString } from "@gnu-taler/taler-util";
import { useEffect, useState } from "preact/hooks";
import {
ObservableMap,
@@ -28,7 +28,26 @@ import {
memoryMap,
} from "../utils/observable.js";
-export interface LocalStorageState {
+declare const opaque_StorageKey: unique symbol;
+
+export type StorageKey = {
+ id: string;
+ [opaque_StorageKey]: true;
+ codec: Codec;
+};
+
+export function buildStorageKey(
+ name: string,
+ codec?: Codec,
+): StorageKey {
+ return {
+ id: name,
+ codec: codec ?? (codecForString() as Codec),
+ [opaque_StorageKey]: true,
+ };
+}
+
+export interface StorageState {
value?: Type;
update: (s: Type) => void;
reset: () => void;
@@ -50,59 +69,48 @@ const storage: ObservableMap = (function buildStorage() {
//with initial value
export function useLocalStorage(
- key: string,
- options?: {
- defaultValue: Type;
- codec?: Codec;
- },
-): Required>;
+ key: StorageKey,
+ defaultValue: Type,
+): Required>;
//without initial value
export function useLocalStorage(
- key: string,
- options?: {
- codec?: Codec;
- },
-): LocalStorageState;
+ key: StorageKey,
+): StorageState;
// impl
export function useLocalStorage(
- key: string,
- options?: {
- defaultValue?: Type;
- codec?: Codec;
- },
-): LocalStorageState {
+ key: StorageKey,
+ defaultValue?: Type,
+): StorageState {
function convert(updated: string | undefined): Type | undefined {
- if (updated === undefined) return options?.defaultValue; //optional
+ if (updated === undefined) return defaultValue; //optional
try {
- return !options?.codec
- ? (updated as Type)
- : options.codec.decode(JSON.parse(updated));
+ return key.codec.decode(JSON.parse(updated));
} catch (e) {
//decode error
- return options?.defaultValue;
+ return defaultValue;
}
}
const [storedValue, setStoredValue] = useState(
(): Type | undefined => {
- const prev = storage.get(key);
+ const prev = storage.get(key.id);
return convert(prev);
},
);
useEffect(() => {
- return storage.onUpdate(key, () => {
- const newValue = storage.get(key);
+ return storage.onUpdate(key.id, () => {
+ const newValue = storage.get(key.id);
setStoredValue(convert(newValue));
});
}, []);
const setValue = (value?: Type): void => {
if (value === undefined) {
- storage.delete(key);
+ storage.delete(key.id);
} else {
storage.set(
- key,
- options?.codec ? JSON.stringify(value) : (value as string),
+ key.id,
+ key.codec ? JSON.stringify(value) : (value as string),
);
}
};
@@ -111,7 +119,7 @@ export function useLocalStorage(
value: storedValue,
update: setValue,
reset: () => {
- setValue(options?.defaultValue);
+ setValue(defaultValue);
},
};
}
diff --git a/packages/web-util/src/hooks/useMemoryStorage.ts b/packages/web-util/src/hooks/useMemoryStorage.ts
index 7160b035e..d8be24c1e 100644
--- a/packages/web-util/src/hooks/useMemoryStorage.ts
+++ b/packages/web-util/src/hooks/useMemoryStorage.ts
@@ -20,48 +20,44 @@
*/
import { useEffect, useState } from "preact/hooks";
-import {
- ObservableMap,
- browserStorageMap,
- localStorageMap,
- memoryMap,
-} from "../utils/observable.js";
+import { ObservableMap, memoryMap } from "../utils/observable.js";
+import { StorageKey, StorageState } from "./useLocalStorage.js";
-export interface LocalStorageState {
- value?: string;
- update: (s: string) => void;
- reset: () => void;
-}
+const storage: ObservableMap = memoryMap();
-const storage: ObservableMap = memoryMap();
-
-export function useMemoryStorage(
- key: string,
- initialValue: string,
-): Required;
-export function useMemoryStorage(key: string): LocalStorageState;
-export function useMemoryStorage(
- key: string,
- initialValue?: string,
-): LocalStorageState {
- const [storedValue, setStoredValue] = useState(
- (): string | undefined => {
- return storage.get(key) ?? initialValue;
+//with initial value
+export function useMemoryStorage(
+ key: StorageKey,
+ defaultValue: Type,
+): Required>;
+//with initial value
+export function useMemoryStorage(
+ key: StorageKey,
+): StorageState;
+// impl
+export function useMemoryStorage(
+ key: StorageKey,
+ defaultValue?: Type,
+): StorageState {
+ const [storedValue, setStoredValue] = useState(
+ (): Type | undefined => {
+ const prev = storage.get(key.id);
+ return prev;
},
);
useEffect(() => {
- return storage.onUpdate(key, () => {
- const newValue = storage.get(key);
- setStoredValue(newValue ?? initialValue);
+ return storage.onUpdate(key.id, () => {
+ const newValue = storage.get(key.id);
+ setStoredValue(newValue);
});
}, []);
- const setValue = (value?: string): void => {
+ const setValue = (value?: Type): void => {
if (value === undefined) {
- storage.delete(key);
+ storage.delete(key.id);
} else {
- storage.set(key, value);
+ storage.set(key.id, value);
}
};
@@ -69,7 +65,7 @@ export function useMemoryStorage(
value: storedValue,
update: setValue,
reset: () => {
- setValue(initialValue);
+ setValue(defaultValue);
},
};
}