From 45ac91b797ef76d587c876f1b458a39646688626 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 13:54:41 +0200 Subject: [PATCH 01/21] move cachedwritablestore to ui-comp --- packages/webapp/src/lib/index.ts | 1 - tauri-app/src/lib/stores/settings.ts | 2 +- .../lib/storesGeneric/cachedWritableStore.ts | 71 ------------------- .../src/lib/storesGeneric/detailStore.ts | 2 +- .../src/lib/storesGeneric/fetchableStore.ts | 2 +- tauri-app/src/lib/storesGeneric/listStore.ts | 2 +- .../src/lib/storesGeneric/settingStore.ts | 2 +- 7 files changed, 5 insertions(+), 77 deletions(-) delete mode 100644 packages/webapp/src/lib/index.ts delete mode 100644 tauri-app/src/lib/storesGeneric/cachedWritableStore.ts diff --git a/packages/webapp/src/lib/index.ts b/packages/webapp/src/lib/index.ts deleted file mode 100644 index 856f2b6c3..000000000 --- a/packages/webapp/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/tauri-app/src/lib/stores/settings.ts b/tauri-app/src/lib/stores/settings.ts index 48e70d18d..62468f908 100644 --- a/tauri-app/src/lib/stores/settings.ts +++ b/tauri-app/src/lib/stores/settings.ts @@ -2,7 +2,7 @@ import { asyncDerived, derived, get } from '@square/svelte-store'; import { cachedWritableStore, cachedWritableStringOptional, -} from '$lib/storesGeneric/cachedWritableStore'; +} from '@rainlanguage/ui-components'; import find from 'lodash/find'; import * as chains from 'viem/chains'; import { textFileStore } from '$lib/storesGeneric/textFileStore'; diff --git a/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts b/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts deleted file mode 100644 index ea8a32949..000000000 --- a/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { writable } from 'svelte/store'; - -export function cachedWritableStore( - key: string, - defaultValue: T, - serialize: (value: T) => string, - deserialize: (serialized: string) => T, -) { - const getCache = () => { - const cached = localStorage.getItem(key); - return cached !== null ? deserialize(cached) : defaultValue; - }; - const setCache = (value?: T) => { - if (value !== undefined) { - localStorage.setItem(key, serialize(value)); - } else { - localStorage.removeItem(key); - } - }; - - const data = writable(getCache()); - - data.subscribe((value) => { - setCache(value); - }); - - return data; -} - -export const cachedWritableString = (key: string, defaultValue = '') => - cachedWritableStore( - key, - defaultValue, - (v) => v, - (v) => v, - ); -export const cachedWritableInt = (key: string, defaultValue = 0) => - cachedWritableStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v), - ); - -export const cachedWritableOptionalStore = ( - key: string, - defaultValue: T | undefined = undefined, - serialize: (value: T) => string, - deserialize: (serialized: string) => T, -) => - cachedWritableStore( - key, - defaultValue, - (v) => (v ? serialize(v) : ''), - (v) => (v ? deserialize(v) : undefined), - ); - -export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v), - ); -export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v, - (v) => v, - ); diff --git a/tauri-app/src/lib/storesGeneric/detailStore.ts b/tauri-app/src/lib/storesGeneric/detailStore.ts index af70c104b..fff53afd6 100644 --- a/tauri-app/src/lib/storesGeneric/detailStore.ts +++ b/tauri-app/src/lib/storesGeneric/detailStore.ts @@ -1,5 +1,5 @@ import { toasts } from '$lib/stores/toasts'; -import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableStore } from '@rainlanguage/ui-components'; import { derived, writable, diff --git a/tauri-app/src/lib/storesGeneric/fetchableStore.ts b/tauri-app/src/lib/storesGeneric/fetchableStore.ts index 79a8f31e6..74c772a77 100644 --- a/tauri-app/src/lib/storesGeneric/fetchableStore.ts +++ b/tauri-app/src/lib/storesGeneric/fetchableStore.ts @@ -1,5 +1,5 @@ import { derived, writable } from 'svelte/store'; -import { cachedWritableStore } from './cachedWritableStore'; +import { cachedWritableStore } from '@rainlanguage/ui-components'; import { toasts } from '$lib/stores/toasts'; import { reportErrorToSentry } from '$lib/services/sentry'; diff --git a/tauri-app/src/lib/storesGeneric/listStore.ts b/tauri-app/src/lib/storesGeneric/listStore.ts index fce5e42c9..ddf8b8704 100644 --- a/tauri-app/src/lib/storesGeneric/listStore.ts +++ b/tauri-app/src/lib/storesGeneric/listStore.ts @@ -3,7 +3,7 @@ import { toasts } from '../stores/toasts'; import { save } from '@tauri-apps/api/dialog'; import dayjs from 'dayjs'; import { ToastMessageType } from '../types/tauriBindings'; -import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableStore } from '@rainlanguage/ui-components'; import { flatten } from 'lodash'; import { reportErrorToSentry, SentrySeverityLevel } from '$lib/services/sentry'; diff --git a/tauri-app/src/lib/storesGeneric/settingStore.ts b/tauri-app/src/lib/storesGeneric/settingStore.ts index 4b5984835..4f798a284 100644 --- a/tauri-app/src/lib/storesGeneric/settingStore.ts +++ b/tauri-app/src/lib/storesGeneric/settingStore.ts @@ -1,5 +1,5 @@ import { derived } from 'svelte/store'; -import { cachedWritableString } from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableString } from '@rainlanguage/ui-components'; interface ValidatedSetting { value: T; From ada479e8d1a8849f90fc9f31544033085cea79c7 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 13:54:50 +0200 Subject: [PATCH 02/21] index --- packages/ui-components/src/lib/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index db7948888..0ea0a7f91 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -117,6 +117,7 @@ export { lightCodeMirrorTheme, darkCodeMirrorTheme } from './utils/codeMirrorThe // Stores export { default as transactionStore } from './stores/transactionStore'; +export { cachedWritableStore, cachedWritableString, cachedWritableInt, cachedWritableOptionalStore, cachedWritableIntOptional, cachedWritableStringOptional } from './storesGeneric/cachedWritableStore'; // Assets export { default as logoLight } from './assets/logo-light.svg'; From 2422c7b8888f6ebe35aa3659b6db195d75ac2b00 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 14:21:06 +0200 Subject: [PATCH 03/21] hide zero bal vaults --- .../ListViewOrderbookFilters.svelte | 2 +- .../lib/storesGeneric/cachedWritableStore.ts | 71 +++++++++++++++++++ packages/webapp/src/lib/stores/settings.ts | 44 ++++++++++++ .../webapp/src/routes/vaults/+page.svelte | 3 +- 4 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts create mode 100644 packages/webapp/src/lib/stores/settings.ts diff --git a/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte b/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte index 07a45dfc7..7ec68214c 100644 --- a/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte +++ b/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte @@ -35,7 +35,7 @@ No networks added to settings {:else} - {#if $accounts && !Object.values($accounts).length} + {#if ($accounts && !Object.values($accounts).length) || $account}
{#if !$account} diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts new file mode 100644 index 000000000..ea8a32949 --- /dev/null +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -0,0 +1,71 @@ +import { writable } from 'svelte/store'; + +export function cachedWritableStore( + key: string, + defaultValue: T, + serialize: (value: T) => string, + deserialize: (serialized: string) => T, +) { + const getCache = () => { + const cached = localStorage.getItem(key); + return cached !== null ? deserialize(cached) : defaultValue; + }; + const setCache = (value?: T) => { + if (value !== undefined) { + localStorage.setItem(key, serialize(value)); + } else { + localStorage.removeItem(key); + } + }; + + const data = writable(getCache()); + + data.subscribe((value) => { + setCache(value); + }); + + return data; +} + +export const cachedWritableString = (key: string, defaultValue = '') => + cachedWritableStore( + key, + defaultValue, + (v) => v, + (v) => v, + ); +export const cachedWritableInt = (key: string, defaultValue = 0) => + cachedWritableStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v), + ); + +export const cachedWritableOptionalStore = ( + key: string, + defaultValue: T | undefined = undefined, + serialize: (value: T) => string, + deserialize: (serialized: string) => T, +) => + cachedWritableStore( + key, + defaultValue, + (v) => (v ? serialize(v) : ''), + (v) => (v ? deserialize(v) : undefined), + ); + +export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v), + ); +export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v, + (v) => v, + ); diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts new file mode 100644 index 000000000..959415b30 --- /dev/null +++ b/packages/webapp/src/lib/stores/settings.ts @@ -0,0 +1,44 @@ +import { cachedWritableStore, type ConfigSource } from '@rainlanguage/ui-components'; +import { derived } from 'svelte/store'; + +export const settings = cachedWritableStore( + 'settings', + undefined, + (value) => JSON.stringify(value), + (str) => { + try { + return JSON.parse(str) as ConfigSource; + } catch { + return undefined; + } + }, +); + +export const hideZeroBalanceVaults = cachedWritableStore( + 'settings.hideZeroBalanceVaults', + true, // default value is true + (value) => JSON.stringify(value), + (str) => { + try { + return JSON.parse(str) as boolean; + } catch { + return true; + } + }, +); + +export const subgraph = derived(settings, ($settings) => + $settings?.subgraphs !== undefined ? Object.entries($settings.subgraphs) : [], +); +export const activeSubgraphs = cachedWritableStore>( + 'settings.activeSubgraphs', + {}, + JSON.stringify, + (s) => { + try { + return JSON.parse(s); + } catch { + return {}; + } + }, +); diff --git a/packages/webapp/src/routes/vaults/+page.svelte b/packages/webapp/src/routes/vaults/+page.svelte index e61823d8b..48cb33718 100644 --- a/packages/webapp/src/routes/vaults/+page.svelte +++ b/packages/webapp/src/routes/vaults/+page.svelte @@ -4,17 +4,16 @@ import { page } from '$app/stores'; import { connected } from '$lib/stores/wagmi'; import { writable } from 'svelte/store'; + import { hideZeroBalanceVaults, activeSubgraphs } from '$lib/stores/settings'; const { activeOrderbook, subgraphUrl, orderHash, - activeSubgraphs, settings, accounts, activeAccountsItems, activeOrderStatus, - hideZeroBalanceVaults, activeNetworkRef, activeOrderbookRef, activeAccounts, From 157a963bf25fbd757fffa39486eb36fb81dd32ea Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 14:26:26 +0200 Subject: [PATCH 04/21] Move cachedWritableStore to shared lib, test hideZeroBalanceVaults --- packages/ui-components/src/lib/index.ts | 9 +- .../lib/storesGeneric/cachedWritableStore.ts | 108 +++++++++--------- packages/webapp/src/lib/stores/settings.ts | 54 ++------- tauri-app/src/lib/stores/settings.ts | 5 +- 4 files changed, 75 insertions(+), 101 deletions(-) diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index 0ea0a7f91..9b726520a 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -117,7 +117,14 @@ export { lightCodeMirrorTheme, darkCodeMirrorTheme } from './utils/codeMirrorThe // Stores export { default as transactionStore } from './stores/transactionStore'; -export { cachedWritableStore, cachedWritableString, cachedWritableInt, cachedWritableOptionalStore, cachedWritableIntOptional, cachedWritableStringOptional } from './storesGeneric/cachedWritableStore'; +export { + cachedWritableStore, + cachedWritableString, + cachedWritableInt, + cachedWritableOptionalStore, + cachedWritableIntOptional, + cachedWritableStringOptional +} from './storesGeneric/cachedWritableStore'; // Assets export { default as logoLight } from './assets/logo-light.svg'; diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index ea8a32949..fe87c6583 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -1,71 +1,71 @@ import { writable } from 'svelte/store'; export function cachedWritableStore( - key: string, - defaultValue: T, - serialize: (value: T) => string, - deserialize: (serialized: string) => T, + key: string, + defaultValue: T, + serialize: (value: T) => string, + deserialize: (serialized: string) => T ) { - const getCache = () => { - const cached = localStorage.getItem(key); - return cached !== null ? deserialize(cached) : defaultValue; - }; - const setCache = (value?: T) => { - if (value !== undefined) { - localStorage.setItem(key, serialize(value)); - } else { - localStorage.removeItem(key); - } - }; + const getCache = () => { + const cached = localStorage.getItem(key); + return cached !== null ? deserialize(cached) : defaultValue; + }; + const setCache = (value?: T) => { + if (value !== undefined) { + localStorage.setItem(key, serialize(value)); + } else { + localStorage.removeItem(key); + } + }; - const data = writable(getCache()); + const data = writable(getCache()); - data.subscribe((value) => { - setCache(value); - }); + data.subscribe((value) => { + setCache(value); + }); - return data; + return data; } export const cachedWritableString = (key: string, defaultValue = '') => - cachedWritableStore( - key, - defaultValue, - (v) => v, - (v) => v, - ); + cachedWritableStore( + key, + defaultValue, + (v) => v, + (v) => v + ); export const cachedWritableInt = (key: string, defaultValue = 0) => - cachedWritableStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v), - ); + cachedWritableStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v) + ); export const cachedWritableOptionalStore = ( - key: string, - defaultValue: T | undefined = undefined, - serialize: (value: T) => string, - deserialize: (serialized: string) => T, + key: string, + defaultValue: T | undefined = undefined, + serialize: (value: T) => string, + deserialize: (serialized: string) => T ) => - cachedWritableStore( - key, - defaultValue, - (v) => (v ? serialize(v) : ''), - (v) => (v ? deserialize(v) : undefined), - ); + cachedWritableStore( + key, + defaultValue, + (v) => (v ? serialize(v) : ''), + (v) => (v ? deserialize(v) : undefined) + ); export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v), - ); + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v) + ); export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v, - (v) => v, - ); + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v, + (v) => v + ); diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts index 959415b30..0d0bfe463 100644 --- a/packages/webapp/src/lib/stores/settings.ts +++ b/packages/webapp/src/lib/stores/settings.ts @@ -1,44 +1,14 @@ -import { cachedWritableStore, type ConfigSource } from '@rainlanguage/ui-components'; -import { derived } from 'svelte/store'; - -export const settings = cachedWritableStore( - 'settings', - undefined, - (value) => JSON.stringify(value), - (str) => { - try { - return JSON.parse(str) as ConfigSource; - } catch { - return undefined; - } - }, -); +import { cachedWritableStore } from '@rainlanguage/ui-components'; export const hideZeroBalanceVaults = cachedWritableStore( - 'settings.hideZeroBalanceVaults', - true, // default value is true - (value) => JSON.stringify(value), - (str) => { - try { - return JSON.parse(str) as boolean; - } catch { - return true; - } - }, -); - -export const subgraph = derived(settings, ($settings) => - $settings?.subgraphs !== undefined ? Object.entries($settings.subgraphs) : [], -); -export const activeSubgraphs = cachedWritableStore>( - 'settings.activeSubgraphs', - {}, - JSON.stringify, - (s) => { - try { - return JSON.parse(s); - } catch { - return {}; - } - }, -); + 'settings.hideZeroBalanceVaults', + true, // default value is true + (value) => JSON.stringify(value), + (str) => { + try { + return JSON.parse(str) as boolean; + } catch { + return true; + } + } +); \ No newline at end of file diff --git a/tauri-app/src/lib/stores/settings.ts b/tauri-app/src/lib/stores/settings.ts index 62468f908..4b8ea52c8 100644 --- a/tauri-app/src/lib/stores/settings.ts +++ b/tauri-app/src/lib/stores/settings.ts @@ -1,8 +1,5 @@ import { asyncDerived, derived, get } from '@square/svelte-store'; -import { - cachedWritableStore, - cachedWritableStringOptional, -} from '@rainlanguage/ui-components'; +import { cachedWritableStore, cachedWritableStringOptional } from '@rainlanguage/ui-components'; import find from 'lodash/find'; import * as chains from 'viem/chains'; import { textFileStore } from '$lib/storesGeneric/textFileStore'; From acf5f5c40f4d29c084b87d7f9de5d679c6a81cc6 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 14:30:01 +0200 Subject: [PATCH 05/21] add --- .../src/lib/components/ListViewOrderbookFilters.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte b/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte index 7ec68214c..07a45dfc7 100644 --- a/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte +++ b/packages/ui-components/src/lib/components/ListViewOrderbookFilters.svelte @@ -35,7 +35,7 @@ No networks added to settings {:else} - {#if ($accounts && !Object.values($accounts).length) || $account} + {#if $accounts && !Object.values($accounts).length}
{#if !$account} From 887a313383215615405d038b2e12dbbfa878081c Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 14:31:56 +0200 Subject: [PATCH 06/21] go --- packages/webapp/src/routes/vaults/+page.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/webapp/src/routes/vaults/+page.svelte b/packages/webapp/src/routes/vaults/+page.svelte index 48cb33718..866fe53df 100644 --- a/packages/webapp/src/routes/vaults/+page.svelte +++ b/packages/webapp/src/routes/vaults/+page.svelte @@ -4,7 +4,7 @@ import { page } from '$app/stores'; import { connected } from '$lib/stores/wagmi'; import { writable } from 'svelte/store'; - import { hideZeroBalanceVaults, activeSubgraphs } from '$lib/stores/settings'; + import { hideZeroBalanceVaults } from '$lib/stores/settings'; const { activeOrderbook, @@ -18,7 +18,8 @@ activeOrderbookRef, activeAccounts, activeNetworkOrderbooks, - showMyItemsOnly = writable(false) + showMyItemsOnly = writable(false), + activeSubgraphs } = $page.data.stores; export async function resetActiveNetworkRef() { From b4201c62399640ee364325c0cd6d086bc24763ee Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 17:22:29 +0200 Subject: [PATCH 07/21] replace --- tauri-app/src/lib/stores/settings.ts | 2 +- .../lib/storesGeneric/cachedWritableStore.ts | 71 +++++++++++++++++++ tauri-app/src/lib/storesGeneric/listStore.ts | 2 +- .../src/lib/storesGeneric/settingStore.ts | 2 +- 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 tauri-app/src/lib/storesGeneric/cachedWritableStore.ts diff --git a/tauri-app/src/lib/stores/settings.ts b/tauri-app/src/lib/stores/settings.ts index 4b8ea52c8..320328e01 100644 --- a/tauri-app/src/lib/stores/settings.ts +++ b/tauri-app/src/lib/stores/settings.ts @@ -1,5 +1,5 @@ import { asyncDerived, derived, get } from '@square/svelte-store'; -import { cachedWritableStore, cachedWritableStringOptional } from '@rainlanguage/ui-components'; +import { cachedWritableStore, cachedWritableStringOptional } from '$lib/storesGeneric/cachedWritableStore'; import find from 'lodash/find'; import * as chains from 'viem/chains'; import { textFileStore } from '$lib/storesGeneric/textFileStore'; diff --git a/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts b/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts new file mode 100644 index 000000000..fe87c6583 --- /dev/null +++ b/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts @@ -0,0 +1,71 @@ +import { writable } from 'svelte/store'; + +export function cachedWritableStore( + key: string, + defaultValue: T, + serialize: (value: T) => string, + deserialize: (serialized: string) => T +) { + const getCache = () => { + const cached = localStorage.getItem(key); + return cached !== null ? deserialize(cached) : defaultValue; + }; + const setCache = (value?: T) => { + if (value !== undefined) { + localStorage.setItem(key, serialize(value)); + } else { + localStorage.removeItem(key); + } + }; + + const data = writable(getCache()); + + data.subscribe((value) => { + setCache(value); + }); + + return data; +} + +export const cachedWritableString = (key: string, defaultValue = '') => + cachedWritableStore( + key, + defaultValue, + (v) => v, + (v) => v + ); +export const cachedWritableInt = (key: string, defaultValue = 0) => + cachedWritableStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v) + ); + +export const cachedWritableOptionalStore = ( + key: string, + defaultValue: T | undefined = undefined, + serialize: (value: T) => string, + deserialize: (serialized: string) => T +) => + cachedWritableStore( + key, + defaultValue, + (v) => (v ? serialize(v) : ''), + (v) => (v ? deserialize(v) : undefined) + ); + +export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v) + ); +export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v, + (v) => v + ); diff --git a/tauri-app/src/lib/storesGeneric/listStore.ts b/tauri-app/src/lib/storesGeneric/listStore.ts index ddf8b8704..fce5e42c9 100644 --- a/tauri-app/src/lib/storesGeneric/listStore.ts +++ b/tauri-app/src/lib/storesGeneric/listStore.ts @@ -3,7 +3,7 @@ import { toasts } from '../stores/toasts'; import { save } from '@tauri-apps/api/dialog'; import dayjs from 'dayjs'; import { ToastMessageType } from '../types/tauriBindings'; -import { cachedWritableStore } from '@rainlanguage/ui-components'; +import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; import { flatten } from 'lodash'; import { reportErrorToSentry, SentrySeverityLevel } from '$lib/services/sentry'; diff --git a/tauri-app/src/lib/storesGeneric/settingStore.ts b/tauri-app/src/lib/storesGeneric/settingStore.ts index 4f798a284..4b5984835 100644 --- a/tauri-app/src/lib/storesGeneric/settingStore.ts +++ b/tauri-app/src/lib/storesGeneric/settingStore.ts @@ -1,5 +1,5 @@ import { derived } from 'svelte/store'; -import { cachedWritableString } from '@rainlanguage/ui-components'; +import { cachedWritableString } from '$lib/storesGeneric/cachedWritableStore'; interface ValidatedSetting { value: T; From f598c2aec023447ff14c8cb44189f3636705a94d Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 17:23:48 +0200 Subject: [PATCH 08/21] store --- .../lib/storesGeneric/cachedWritableStore.ts | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index fe87c6583..f758ed935 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -25,47 +25,4 @@ export function cachedWritableStore( }); return data; -} - -export const cachedWritableString = (key: string, defaultValue = '') => - cachedWritableStore( - key, - defaultValue, - (v) => v, - (v) => v - ); -export const cachedWritableInt = (key: string, defaultValue = 0) => - cachedWritableStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v) - ); - -export const cachedWritableOptionalStore = ( - key: string, - defaultValue: T | undefined = undefined, - serialize: (value: T) => string, - deserialize: (serialized: string) => T -) => - cachedWritableStore( - key, - defaultValue, - (v) => (v ? serialize(v) : ''), - (v) => (v ? deserialize(v) : undefined) - ); - -export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v) - ); -export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v, - (v) => v - ); +} \ No newline at end of file From a4d0f39765ca41a2b74906394c58da38d582c631 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 17:29:33 +0200 Subject: [PATCH 09/21] add --- packages/ui-components/src/lib/index.ts | 7 +- .../lib/storesGeneric/cachedWritableStore.ts | 2 +- packages/webapp/src/lib/stores/settings.ts | 2 +- tauri-app/src/lib/stores/settings.ts | 5 +- .../lib/storesGeneric/cachedWritableStore.ts | 108 +++++++++--------- 5 files changed, 61 insertions(+), 63 deletions(-) diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index 9b726520a..8ca4f9bea 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -118,12 +118,7 @@ export { lightCodeMirrorTheme, darkCodeMirrorTheme } from './utils/codeMirrorThe // Stores export { default as transactionStore } from './stores/transactionStore'; export { - cachedWritableStore, - cachedWritableString, - cachedWritableInt, - cachedWritableOptionalStore, - cachedWritableIntOptional, - cachedWritableStringOptional + cachedWritableStore } from './storesGeneric/cachedWritableStore'; // Assets diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index f758ed935..8065aff64 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -25,4 +25,4 @@ export function cachedWritableStore( }); return data; -} \ No newline at end of file +} diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts index 0d0bfe463..2c8ec69e3 100644 --- a/packages/webapp/src/lib/stores/settings.ts +++ b/packages/webapp/src/lib/stores/settings.ts @@ -11,4 +11,4 @@ export const hideZeroBalanceVaults = cachedWritableStore( return true; } } -); \ No newline at end of file +); diff --git a/tauri-app/src/lib/stores/settings.ts b/tauri-app/src/lib/stores/settings.ts index 320328e01..48e70d18d 100644 --- a/tauri-app/src/lib/stores/settings.ts +++ b/tauri-app/src/lib/stores/settings.ts @@ -1,5 +1,8 @@ import { asyncDerived, derived, get } from '@square/svelte-store'; -import { cachedWritableStore, cachedWritableStringOptional } from '$lib/storesGeneric/cachedWritableStore'; +import { + cachedWritableStore, + cachedWritableStringOptional, +} from '$lib/storesGeneric/cachedWritableStore'; import find from 'lodash/find'; import * as chains from 'viem/chains'; import { textFileStore } from '$lib/storesGeneric/textFileStore'; diff --git a/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts b/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts index fe87c6583..ea8a32949 100644 --- a/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts +++ b/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts @@ -1,71 +1,71 @@ import { writable } from 'svelte/store'; export function cachedWritableStore( - key: string, - defaultValue: T, - serialize: (value: T) => string, - deserialize: (serialized: string) => T + key: string, + defaultValue: T, + serialize: (value: T) => string, + deserialize: (serialized: string) => T, ) { - const getCache = () => { - const cached = localStorage.getItem(key); - return cached !== null ? deserialize(cached) : defaultValue; - }; - const setCache = (value?: T) => { - if (value !== undefined) { - localStorage.setItem(key, serialize(value)); - } else { - localStorage.removeItem(key); - } - }; + const getCache = () => { + const cached = localStorage.getItem(key); + return cached !== null ? deserialize(cached) : defaultValue; + }; + const setCache = (value?: T) => { + if (value !== undefined) { + localStorage.setItem(key, serialize(value)); + } else { + localStorage.removeItem(key); + } + }; - const data = writable(getCache()); + const data = writable(getCache()); - data.subscribe((value) => { - setCache(value); - }); + data.subscribe((value) => { + setCache(value); + }); - return data; + return data; } export const cachedWritableString = (key: string, defaultValue = '') => - cachedWritableStore( - key, - defaultValue, - (v) => v, - (v) => v - ); + cachedWritableStore( + key, + defaultValue, + (v) => v, + (v) => v, + ); export const cachedWritableInt = (key: string, defaultValue = 0) => - cachedWritableStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v) - ); + cachedWritableStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v), + ); export const cachedWritableOptionalStore = ( - key: string, - defaultValue: T | undefined = undefined, - serialize: (value: T) => string, - deserialize: (serialized: string) => T + key: string, + defaultValue: T | undefined = undefined, + serialize: (value: T) => string, + deserialize: (serialized: string) => T, ) => - cachedWritableStore( - key, - defaultValue, - (v) => (v ? serialize(v) : ''), - (v) => (v ? deserialize(v) : undefined) - ); + cachedWritableStore( + key, + defaultValue, + (v) => (v ? serialize(v) : ''), + (v) => (v ? deserialize(v) : undefined), + ); export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v) - ); + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v), + ); export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v, - (v) => v - ); + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v, + (v) => v, + ); From fce4eed8a1001b6d26c540850246c31599a190ed Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 17:30:14 +0200 Subject: [PATCH 10/21] update --- tauri-app/src/lib/storesGeneric/detailStore.ts | 2 +- tauri-app/src/lib/storesGeneric/fetchableStore.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tauri-app/src/lib/storesGeneric/detailStore.ts b/tauri-app/src/lib/storesGeneric/detailStore.ts index fff53afd6..af70c104b 100644 --- a/tauri-app/src/lib/storesGeneric/detailStore.ts +++ b/tauri-app/src/lib/storesGeneric/detailStore.ts @@ -1,5 +1,5 @@ import { toasts } from '$lib/stores/toasts'; -import { cachedWritableStore } from '@rainlanguage/ui-components'; +import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; import { derived, writable, diff --git a/tauri-app/src/lib/storesGeneric/fetchableStore.ts b/tauri-app/src/lib/storesGeneric/fetchableStore.ts index 74c772a77..e9b2ef5b9 100644 --- a/tauri-app/src/lib/storesGeneric/fetchableStore.ts +++ b/tauri-app/src/lib/storesGeneric/fetchableStore.ts @@ -1,5 +1,5 @@ import { derived, writable } from 'svelte/store'; -import { cachedWritableStore } from '@rainlanguage/ui-components'; +import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; import { toasts } from '$lib/stores/toasts'; import { reportErrorToSentry } from '$lib/services/sentry'; From a98fae7ec6f95a4e3db30bbad7e9c23331d99749 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 17:31:53 +0200 Subject: [PATCH 11/21] format --- packages/ui-components/src/lib/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index 8ca4f9bea..9a83cc0b1 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -117,9 +117,7 @@ export { lightCodeMirrorTheme, darkCodeMirrorTheme } from './utils/codeMirrorThe // Stores export { default as transactionStore } from './stores/transactionStore'; -export { - cachedWritableStore -} from './storesGeneric/cachedWritableStore'; +export { cachedWritableStore } from './storesGeneric/cachedWritableStore'; // Assets export { default as logoLight } from './assets/logo-light.svg'; From f0f6e4e8b39424471d13f2081da2711890cace22 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 17:51:37 +0200 Subject: [PATCH 12/21] do nothing --- .../lib/storesGeneric/cachedWritableStore.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index 8065aff64..8cffdc9d1 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -7,14 +7,22 @@ export function cachedWritableStore( deserialize: (serialized: string) => T ) { const getCache = () => { - const cached = localStorage.getItem(key); - return cached !== null ? deserialize(cached) : defaultValue; + try { + const cached = localStorage.getItem(key); + return cached !== null ? deserialize(cached) : defaultValue; + } catch (error) { + return defaultValue; + } }; const setCache = (value?: T) => { - if (value !== undefined) { - localStorage.setItem(key, serialize(value)); - } else { - localStorage.removeItem(key); + try { + if (value !== undefined) { + localStorage.setItem(key, serialize(value)); + } else { + localStorage.removeItem(key); + } + } catch { + // do nothing } }; From b30e576f8c278816e0ced5e5fdd96b7d99cb1bc1 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 18:03:17 +0200 Subject: [PATCH 13/21] update --- .../ui-components/src/lib/storesGeneric/cachedWritableStore.ts | 3 ++- packages/webapp/src/lib/stores/settings.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index 8cffdc9d1..729101ffe 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -22,7 +22,8 @@ export function cachedWritableStore( localStorage.removeItem(key); } } catch { - // do nothing + // Silently ignore localStorage errors to allow the application to function + // without persistence in environments where localStorage is unavailable } }; diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts index 2c8ec69e3..268f40d85 100644 --- a/packages/webapp/src/lib/stores/settings.ts +++ b/packages/webapp/src/lib/stores/settings.ts @@ -6,7 +6,8 @@ export const hideZeroBalanceVaults = cachedWritableStore( (value) => JSON.stringify(value), (str) => { try { - return JSON.parse(str) as boolean; + const value = JSON.parse(str); + return typeof value === 'boolean' ? value : true; } catch { return true; } From 5101234d56e4a2e83f14c4e8664afc0465fa6e5f Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 18:25:26 +0200 Subject: [PATCH 14/21] rm err --- .../ui-components/src/lib/storesGeneric/cachedWritableStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index 729101ffe..b012e53b5 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -10,7 +10,7 @@ export function cachedWritableStore( try { const cached = localStorage.getItem(key); return cached !== null ? deserialize(cached) : defaultValue; - } catch (error) { + } catch { return defaultValue; } }; From 051934148d1311d5ee133ddcd93e208696a5e743 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 18:48:06 +0200 Subject: [PATCH 15/21] add jsdoc --- .../lib/storesGeneric/cachedWritableStore.ts | 27 +++++++++++++++++++ packages/webapp/src/lib/stores/settings.ts | 8 ++++++ 2 files changed, 35 insertions(+) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index b012e53b5..637632727 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -1,5 +1,32 @@ import { writable } from 'svelte/store'; +/** + * Creates a writable Svelte store that persists its value to localStorage. + * + * @template T - The type of the value stored in the store + * @param {string} key - The localStorage key used to store the value + * @param {T} defaultValue - The default value to use when no value is found in localStorage + * @param {function(T): string} serialize - Function to convert the store value to a string for storage + * @param {function(string): T} deserialize - Function to convert the stored string back to the original type + * @returns {import('svelte/store').Writable} A writable store that automatically syncs with localStorage + * + * @example + * // Create a store for a boolean value + * const darkMode = cachedWritableStore( + * 'darkMode', + * false, + * value => JSON.stringify(value), + * str => JSON.parse(str) + * ); + * + * // Create a store for a complex object + * const userPreferences = cachedWritableStore( + * 'userPrefs', + * { theme: 'light', fontSize: 14 }, + * value => JSON.stringify(value), + * str => JSON.parse(str) + * ); + */ export function cachedWritableStore( key: string, defaultValue: T, diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts index 268f40d85..0889f8ef8 100644 --- a/packages/webapp/src/lib/stores/settings.ts +++ b/packages/webapp/src/lib/stores/settings.ts @@ -1,5 +1,13 @@ import { cachedWritableStore } from '@rainlanguage/ui-components'; +/** + * A persistent store that controls whether vaults with zero balance should be hidden in the UI. + * + * This setting is saved to local storage and persists between sessions. + * + * @default true - Zero balance vaults are hidden by default + * @returns A writable store containing a boolean value + */ export const hideZeroBalanceVaults = cachedWritableStore( 'settings.hideZeroBalanceVaults', true, // default value is true From bb464c715e57ce92dd020eefb5a824074cf19383 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Fri, 25 Apr 2025 19:33:37 +0200 Subject: [PATCH 16/21] format --- .../src/lib/storesGeneric/cachedWritableStore.ts | 6 +++--- packages/webapp/src/lib/stores/settings.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index 637632727..13d97603e 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -2,14 +2,14 @@ import { writable } from 'svelte/store'; /** * Creates a writable Svelte store that persists its value to localStorage. - * + * * @template T - The type of the value stored in the store * @param {string} key - The localStorage key used to store the value * @param {T} defaultValue - The default value to use when no value is found in localStorage * @param {function(T): string} serialize - Function to convert the store value to a string for storage * @param {function(string): T} deserialize - Function to convert the stored string back to the original type * @returns {import('svelte/store').Writable} A writable store that automatically syncs with localStorage - * + * * @example * // Create a store for a boolean value * const darkMode = cachedWritableStore( @@ -18,7 +18,7 @@ import { writable } from 'svelte/store'; * value => JSON.stringify(value), * str => JSON.parse(str) * ); - * + * * // Create a store for a complex object * const userPreferences = cachedWritableStore( * 'userPrefs', diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts index 0889f8ef8..c0aca67e6 100644 --- a/packages/webapp/src/lib/stores/settings.ts +++ b/packages/webapp/src/lib/stores/settings.ts @@ -2,9 +2,9 @@ import { cachedWritableStore } from '@rainlanguage/ui-components'; /** * A persistent store that controls whether vaults with zero balance should be hidden in the UI. - * + * * This setting is saved to local storage and persists between sessions. - * + * * @default true - Zero balance vaults are hidden by default * @returns A writable store containing a boolean value */ From aa225b296cfe662932d0fbc9a5f30c0d02dc6d21 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 28 Apr 2025 12:46:21 +0200 Subject: [PATCH 17/21] remove from taur-app --- packages/ui-components/src/lib/index.ts | 7 +- .../lib/storesGeneric/cachedWritableStore.ts | 67 ++++++++ tauri-app/src/lib/mocks/mockConfigSource.ts | 37 +++++ tauri-app/src/lib/stores/settings.test.ts | 144 ------------------ tauri-app/src/lib/stores/settings.ts | 112 +++++++++++++- .../lib/storesGeneric/cachedWritableStore.ts | 71 --------- .../src/lib/storesGeneric/detailStore.ts | 2 +- .../src/lib/storesGeneric/fetchableStore.ts | 2 +- tauri-app/src/lib/storesGeneric/listStore.ts | 2 +- .../src/lib/storesGeneric/settingStore.ts | 2 +- 10 files changed, 222 insertions(+), 224 deletions(-) create mode 100644 tauri-app/src/lib/mocks/mockConfigSource.ts delete mode 100644 tauri-app/src/lib/stores/settings.test.ts delete mode 100644 tauri-app/src/lib/storesGeneric/cachedWritableStore.ts diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index 53c706942..35344a445 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -117,7 +117,12 @@ export { lightCodeMirrorTheme, darkCodeMirrorTheme } from './utils/codeMirrorThe // Stores export { default as transactionStore } from './stores/transactionStore'; -export { cachedWritableStore } from './storesGeneric/cachedWritableStore'; +export { + cachedWritableStore, + cachedWritableIntOptional, + cachedWritableStringOptional, + cachedWritableString +} from './storesGeneric/cachedWritableStore'; // Assets export { default as logoLight } from './assets/logo-light.svg'; diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index 13d97603e..e645a0efc 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -62,3 +62,70 @@ export function cachedWritableStore( return data; } + +export const cachedWritableString = (key: string, defaultValue = '') => + cachedWritableStore( + key, + defaultValue, + (v) => v, + (v) => v + ); +export const cachedWritableInt = (key: string, defaultValue = 0) => + cachedWritableStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v) + ); +/** + * Creates a writable store that can hold an optional value of type T and persists to localStorage. + * + * @template T - The type of the value stored + * @param {string} key - The localStorage key to use for persistence + * @param {T | undefined} defaultValue - The default value if nothing is found in localStorage + * @param {function} serialize - Function to convert the value to a string for storage + * @param {function} deserialize - Function to convert the stored string back to a value + * @returns A writable store that persists to localStorage and can hold undefined values + */ +export const cachedWritableOptionalStore = ( + key: string, + defaultValue: T | undefined = undefined, + serialize: (value: T) => string, + deserialize: (serialized: string) => T +) => + cachedWritableStore( + key, + defaultValue, + (v) => (v ? serialize(v) : ''), + (v) => (v ? deserialize(v) : undefined) + ); + +/** + * Creates a writable store that can hold an optional number value and persists to localStorage. + * + * @param {string} key - The localStorage key to use for persistence + * @param {number | undefined} defaultValue - The default value if nothing is found in localStorage + * @returns A writable store that persists to localStorage and can hold an optional number + */ +export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v.toString(), + (v) => parseInt(v) + ); + +/** + * Creates a writable store that can hold an optional string value and persists to localStorage. + * + * @param {string} key - The localStorage key to use for persistence + * @param {string | undefined} defaultValue - The default value if nothing is found in localStorage + * @returns A writable store that persists to localStorage and can hold an optional string + */ +export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => + cachedWritableOptionalStore( + key, + defaultValue, + (v) => v, + (v) => v + ); diff --git a/tauri-app/src/lib/mocks/mockConfigSource.ts b/tauri-app/src/lib/mocks/mockConfigSource.ts new file mode 100644 index 000000000..07f3559b8 --- /dev/null +++ b/tauri-app/src/lib/mocks/mockConfigSource.ts @@ -0,0 +1,37 @@ +import type { ConfigSource } from '@rainlanguage/orderbook'; + +export const mockConfigSource: ConfigSource = { + networks: { + mainnet: { + rpc: 'https://mainnet.infura.io/v3/YOUR-PROJECT-ID', + 'chain-id': 1, + label: 'Ethereum Mainnet', + currency: 'ETH', + }, + }, + subgraphs: { + mainnet: 'https://api.thegraph.com/subgraphs/name/mainnet', + }, + orderbooks: { + orderbook1: { + address: '0xOrderbookAddress1', + network: 'mainnet', + subgraph: 'uniswap', + label: 'Orderbook 1', + }, + }, + deployers: { + deployer1: { + address: '0xDeployerAddress1', + network: 'mainnet', + label: 'Deployer 1', + }, + }, + metaboards: { + metaboard1: 'https://example.com/metaboard1', + }, + accounts: { + name_one: 'address_one', + name_two: 'address_two', + }, +}; diff --git a/tauri-app/src/lib/stores/settings.test.ts b/tauri-app/src/lib/stores/settings.test.ts deleted file mode 100644 index 7181176c9..000000000 --- a/tauri-app/src/lib/stores/settings.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { expect, test, beforeEach, describe } from 'vitest'; -import { settings, activeAccountsItems, activeSubgraphs } from './settings'; -import { get } from 'svelte/store'; - -// Import the ConfigSource type -import type { ConfigSource } from '@rainlanguage/orderbook'; - -// Define the mock directly in the tests -const mockConfigSource: ConfigSource = { - networks: { - mainnet: { - rpc: 'https://mainnet.infura.io/v3/YOUR-PROJECT-ID', - 'chain-id': 1, - label: 'Ethereum Mainnet', - currency: 'ETH', - }, - }, - subgraphs: { - mainnet: 'https://api.thegraph.com/subgraphs/name/mainnet', - }, - orderbooks: { - orderbook1: { - address: '0xOrderbookAddress1', - network: 'mainnet', - subgraph: 'uniswap', - label: 'Orderbook 1', - }, - }, - deployers: { - deployer1: { - address: '0xDeployerAddress1', - network: 'mainnet', - label: 'Deployer 1', - }, - }, - metaboards: { - metaboard1: 'https://example.com/metaboard1', - }, - accounts: { - name_one: 'address_one', - name_two: 'address_two', - }, -}; - -describe('Settings active accounts items', () => { - // Reset store values before each test to prevent state leakage - beforeEach(() => { - // Reset all store values - settings.set(undefined); - activeAccountsItems.set({}); - activeSubgraphs.set({}); - - // Then set our initial test values - settings.set(mockConfigSource); - activeAccountsItems.set({ - name_one: 'address_one', - name_two: 'address_two', - }); - activeSubgraphs.set({ - mainnet: 'https://api.thegraph.com/subgraphs/name/mainnet', - }); - - // Verify initial state - expect(get(settings)).toEqual(mockConfigSource); - expect(get(activeAccountsItems)).toEqual({ - name_one: 'address_one', - name_two: 'address_two', - }); - expect(get(activeSubgraphs)).toEqual({ - mainnet: 'https://api.thegraph.com/subgraphs/name/mainnet', - }); - }); - - test('should remove account if that account is removed', () => { - // Test removing an account - const newSettings = { - ...mockConfigSource, - accounts: { - name_one: 'address_one', - }, - }; - - // Update settings - this should trigger the subscription - settings.set(newSettings); - - // Check the expected result - expect(get(activeAccountsItems)).toEqual({ - name_one: 'address_one', - }); - }); - - test('should remove account if the value is different', () => { - const newSettings = { - ...mockConfigSource, - accounts: { - name_one: 'address_one', - name_two: 'new_value', - }, - }; - - settings.set(newSettings); - - expect(get(activeAccountsItems)).toEqual({ - name_one: 'address_one', - }); - }); - - test('should update active subgraphs when subgraph value changes', () => { - const newSettings = { - ...mockConfigSource, - subgraphs: { - mainnet: 'new value', - }, - }; - - settings.set(newSettings); - - expect(get(activeSubgraphs)).toEqual({}); - }); - - test('should update active subgraphs when subgraph removed', () => { - const newSettings = { - ...mockConfigSource, - subgraphs: { - testnet: 'testnet', - }, - }; - - settings.set(newSettings); - - expect(get(activeSubgraphs)).toEqual({}); - }); - - test('should reset active subgraphs when subgraphs are undefined', () => { - const newSettings = { - ...mockConfigSource, - subgraphs: undefined, - }; - - settings.set(newSettings); - - expect(get(activeSubgraphs)).toEqual({}); - }); -}); diff --git a/tauri-app/src/lib/stores/settings.ts b/tauri-app/src/lib/stores/settings.ts index 48e70d18d..2197640d9 100644 --- a/tauri-app/src/lib/stores/settings.ts +++ b/tauri-app/src/lib/stores/settings.ts @@ -1,8 +1,5 @@ import { asyncDerived, derived, get } from '@square/svelte-store'; -import { - cachedWritableStore, - cachedWritableStringOptional, -} from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableStore, cachedWritableStringOptional } from '@rainlanguage/ui-components'; import find from 'lodash/find'; import * as chains from 'viem/chains'; import { textFileStore } from '$lib/storesGeneric/textFileStore'; @@ -13,6 +10,8 @@ import { } from '@rainlanguage/orderbook'; import { getBlockNumberFromRpc } from '$lib/services/chain'; import { pickBy } from 'lodash'; +import { mockConfigSource } from '$lib/mocks/mockConfigSource'; +import { beforeEach, describe } from 'vitest'; // general export const settingsText = cachedWritableStore( @@ -274,3 +273,108 @@ export const orderHash = cachedWritableStore( (value) => value, (str) => str || '', ); + +if (import.meta.vitest) { + const { test, expect } = import.meta.vitest; + + describe('Settings active accounts items', () => { + // Reset store values before each test to prevent state leakage + beforeEach(() => { + // Reset all store values + settings.set(undefined); + activeAccountsItems.set({}); + activeSubgraphs.set({}); + + // Then set our initial test values + settings.set(mockConfigSource); + activeAccountsItems.set({ + name_one: 'address_one', + name_two: 'address_two', + }); + activeSubgraphs.set({ + mainnet: 'https://api.thegraph.com/subgraphs/name/mainnet', + }); + + // Verify initial state + expect(get(settings)).toEqual(mockConfigSource); + expect(get(activeAccountsItems)).toEqual({ + name_one: 'address_one', + name_two: 'address_two', + }); + expect(get(activeSubgraphs)).toEqual({ + mainnet: 'https://api.thegraph.com/subgraphs/name/mainnet', + }); + }); + + test('should remove account if that account is removed', () => { + // Test removing an account + const newSettings = { + ...mockConfigSource, + accounts: { + name_one: 'address_one', + }, + }; + + // Update settings - this should trigger the subscription + settings.set(newSettings); + + // Check the expected result + expect(get(activeAccountsItems)).toEqual({ + name_one: 'address_one', + }); + }); + + test('should remove account if the value is different', () => { + const newSettings = { + ...mockConfigSource, + accounts: { + name_one: 'address_one', + name_two: 'new_value', + }, + }; + + settings.set(newSettings); + + expect(get(activeAccountsItems)).toEqual({ + name_one: 'address_one', + }); + }); + + test('should update active subgraphs when subgraph value changes', () => { + const newSettings = { + ...mockConfigSource, + subgraphs: { + mainnet: 'new value', + }, + }; + + settings.set(newSettings); + + expect(get(activeSubgraphs)).toEqual({}); + }); + + test('should update active subgraphs when subgraph removed', () => { + const newSettings = { + ...mockConfigSource, + subgraphs: { + testnet: 'testnet', + }, + }; + + settings.set(newSettings); + + expect(get(activeSubgraphs)).toEqual({}); + }); + + test('should reset active subgraphs when subgraphs are undefined', () => { + const newSettings = { + ...mockConfigSource, + subgraphs: undefined, + }; + + settings.set(newSettings); + + expect(get(activeSubgraphs)).toEqual({}); + }); + }); +} diff --git a/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts b/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts deleted file mode 100644 index ea8a32949..000000000 --- a/tauri-app/src/lib/storesGeneric/cachedWritableStore.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { writable } from 'svelte/store'; - -export function cachedWritableStore( - key: string, - defaultValue: T, - serialize: (value: T) => string, - deserialize: (serialized: string) => T, -) { - const getCache = () => { - const cached = localStorage.getItem(key); - return cached !== null ? deserialize(cached) : defaultValue; - }; - const setCache = (value?: T) => { - if (value !== undefined) { - localStorage.setItem(key, serialize(value)); - } else { - localStorage.removeItem(key); - } - }; - - const data = writable(getCache()); - - data.subscribe((value) => { - setCache(value); - }); - - return data; -} - -export const cachedWritableString = (key: string, defaultValue = '') => - cachedWritableStore( - key, - defaultValue, - (v) => v, - (v) => v, - ); -export const cachedWritableInt = (key: string, defaultValue = 0) => - cachedWritableStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v), - ); - -export const cachedWritableOptionalStore = ( - key: string, - defaultValue: T | undefined = undefined, - serialize: (value: T) => string, - deserialize: (serialized: string) => T, -) => - cachedWritableStore( - key, - defaultValue, - (v) => (v ? serialize(v) : ''), - (v) => (v ? deserialize(v) : undefined), - ); - -export const cachedWritableIntOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v.toString(), - (v) => parseInt(v), - ); -export const cachedWritableStringOptional = (key: string, defaultValue = undefined) => - cachedWritableOptionalStore( - key, - defaultValue, - (v) => v, - (v) => v, - ); diff --git a/tauri-app/src/lib/storesGeneric/detailStore.ts b/tauri-app/src/lib/storesGeneric/detailStore.ts index af70c104b..fff53afd6 100644 --- a/tauri-app/src/lib/storesGeneric/detailStore.ts +++ b/tauri-app/src/lib/storesGeneric/detailStore.ts @@ -1,5 +1,5 @@ import { toasts } from '$lib/stores/toasts'; -import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableStore } from '@rainlanguage/ui-components'; import { derived, writable, diff --git a/tauri-app/src/lib/storesGeneric/fetchableStore.ts b/tauri-app/src/lib/storesGeneric/fetchableStore.ts index e9b2ef5b9..74c772a77 100644 --- a/tauri-app/src/lib/storesGeneric/fetchableStore.ts +++ b/tauri-app/src/lib/storesGeneric/fetchableStore.ts @@ -1,5 +1,5 @@ import { derived, writable } from 'svelte/store'; -import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableStore } from '@rainlanguage/ui-components'; import { toasts } from '$lib/stores/toasts'; import { reportErrorToSentry } from '$lib/services/sentry'; diff --git a/tauri-app/src/lib/storesGeneric/listStore.ts b/tauri-app/src/lib/storesGeneric/listStore.ts index fce5e42c9..ddf8b8704 100644 --- a/tauri-app/src/lib/storesGeneric/listStore.ts +++ b/tauri-app/src/lib/storesGeneric/listStore.ts @@ -3,7 +3,7 @@ import { toasts } from '../stores/toasts'; import { save } from '@tauri-apps/api/dialog'; import dayjs from 'dayjs'; import { ToastMessageType } from '../types/tauriBindings'; -import { cachedWritableStore } from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableStore } from '@rainlanguage/ui-components'; import { flatten } from 'lodash'; import { reportErrorToSentry, SentrySeverityLevel } from '$lib/services/sentry'; diff --git a/tauri-app/src/lib/storesGeneric/settingStore.ts b/tauri-app/src/lib/storesGeneric/settingStore.ts index 4b5984835..4f798a284 100644 --- a/tauri-app/src/lib/storesGeneric/settingStore.ts +++ b/tauri-app/src/lib/storesGeneric/settingStore.ts @@ -1,5 +1,5 @@ import { derived } from 'svelte/store'; -import { cachedWritableString } from '$lib/storesGeneric/cachedWritableStore'; +import { cachedWritableString } from '@rainlanguage/ui-components'; interface ValidatedSetting { value: T; From 6b44948a7d4f1ac5666f2827a9a0e1388bcb701a Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 28 Apr 2025 13:00:03 +0200 Subject: [PATCH 18/21] cachedWritableOptionalStore --- .../src/lib/storesGeneric/cachedWritableStore.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index e645a0efc..f39bf90cf 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -75,7 +75,7 @@ export const cachedWritableInt = (key: string, defaultValue = 0) => key, defaultValue, (v) => v.toString(), - (v) => parseInt(v) + (v) => Number.parseInt(v) ); /** * Creates a writable store that can hold an optional value of type T and persists to localStorage. @@ -96,8 +96,8 @@ export const cachedWritableOptionalStore = ( cachedWritableStore( key, defaultValue, - (v) => (v ? serialize(v) : ''), - (v) => (v ? deserialize(v) : undefined) + (v) => (v !== undefined ? serialize(v) : ''), + (v) => (v !== '' ? deserialize(v) : undefined) ); /** @@ -112,7 +112,7 @@ export const cachedWritableIntOptional = (key: string, defaultValue = undefined) key, defaultValue, (v) => v.toString(), - (v) => parseInt(v) + (v) => Number.parseInt(v) ); /** From afcbd4b96b02a8c011959fb3851807c6bc3be7c5 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 28 Apr 2025 13:11:05 +0200 Subject: [PATCH 19/21] formatted --- .../ui-components/src/lib/storesGeneric/cachedWritableStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index f39bf90cf..c37c307b3 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -96,7 +96,7 @@ export const cachedWritableOptionalStore = ( cachedWritableStore( key, defaultValue, - (v) => (v !== undefined ? serialize(v) : ''), + (v) => (v !== undefined ? serialize(v) : ''), (v) => (v !== '' ? deserialize(v) : undefined) ); From 6704f25f2be79fbb3dc562aa1106dd4cf6fd97cd Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 28 Apr 2025 13:21:17 +0200 Subject: [PATCH 20/21] no change --- packages/webapp/src/lib/stores/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts index c0aca67e6..0d327a0de 100644 --- a/packages/webapp/src/lib/stores/settings.ts +++ b/packages/webapp/src/lib/stores/settings.ts @@ -20,4 +20,4 @@ export const hideZeroBalanceVaults = cachedWritableStore( return true; } } -); +); \ No newline at end of file From 0926719d7569a091c6dfaafff68cbb985ba608e1 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 28 Apr 2025 13:27:22 +0200 Subject: [PATCH 21/21] type safing --- .../src/lib/storesGeneric/cachedWritableStore.ts | 10 ++++++++-- packages/webapp/src/lib/stores/settings.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts index c37c307b3..760cb7bf4 100644 --- a/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts +++ b/packages/ui-components/src/lib/storesGeneric/cachedWritableStore.ts @@ -75,7 +75,10 @@ export const cachedWritableInt = (key: string, defaultValue = 0) => key, defaultValue, (v) => v.toString(), - (v) => Number.parseInt(v) + (v) => { + const parsed = Number.parseInt(v); + return isNaN(parsed) ? defaultValue : parsed; + } ); /** * Creates a writable store that can hold an optional value of type T and persists to localStorage. @@ -112,7 +115,10 @@ export const cachedWritableIntOptional = (key: string, defaultValue = undefined) key, defaultValue, (v) => v.toString(), - (v) => Number.parseInt(v) + (v) => { + const parsed = Number.parseInt(v); + return isNaN(parsed) ? (defaultValue ?? 0) : parsed; + } ); /** diff --git a/packages/webapp/src/lib/stores/settings.ts b/packages/webapp/src/lib/stores/settings.ts index 0d327a0de..c0aca67e6 100644 --- a/packages/webapp/src/lib/stores/settings.ts +++ b/packages/webapp/src/lib/stores/settings.ts @@ -20,4 +20,4 @@ export const hideZeroBalanceVaults = cachedWritableStore( return true; } } -); \ No newline at end of file +);