From 5343a28e6c9df9dcc800c4ad136ecc23755971c8 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Sat, 26 Sep 2026 00:09:35 -0700 Subject: [PATCH] Declare every persisted local store with synced in its owner module and await their ready promises at boot --- .agents/skills/flotilla-architecture/SKILL.md | 3 +- .agents/skills/flotilla-state/SKILL.md | 29 +++++++----- src/app/components/ChatMenu.svelte | 2 +- .../components/NewNotificationSound.svelte | 3 +- src/app/components/SpaceMenuActions.svelte | 3 +- src/app/components/SpaceMenuHeader.svelte | 3 +- src/app/device.ts | 5 +-- src/app/healthChecks.ts | 6 ++- src/app/lightning.ts | 9 ++-- src/app/notifications.ts | 19 ++++---- src/app/push/adapters/common.ts | 27 ++++++++++-- src/app/push/adapters/web.ts | 3 +- src/app/push/index.ts | 6 +-- src/app/reminders.ts | 3 +- src/app/settings.ts | 13 ------ src/app/storage.ts | 10 ++++- src/app/sync.ts | 12 ++--- src/routes/+layout.svelte | 44 ++++--------------- src/routes/chat/[chat]/+page.svelte | 3 +- src/routes/settings/alerts/+page.svelte | 3 +- 20 files changed, 103 insertions(+), 103 deletions(-) diff --git a/.agents/skills/flotilla-architecture/SKILL.md b/.agents/skills/flotilla-architecture/SKILL.md index b4c0679c..7277760f 100644 --- a/.agents/skills/flotilla-architecture/SKILL.md +++ b/.agents/skills/flotilla-architecture/SKILL.md @@ -156,7 +156,8 @@ size from `localStorage` before first paint. Then, in order: 1. `restoreSession()` restores the saved session, if there is one, which builds a user-scoped `App` through `login`. -2. The device, wallet and notification stores sync to `kv`/`ss`. +2. It awaits `.ready` on the `synced` stores boot reads (device, wallet, push and notification + settings, `shouldUnwrap`, `needsKeyBackup`). 3. It waits for storage, then `setupDeepLinks()` listens for warm-start links and handles a cold-start one. 4. Each long-running subscription goes onto one `unsubscribers` list: `setupHistory`, diff --git a/.agents/skills/flotilla-state/SKILL.md b/.agents/skills/flotilla-state/SKILL.md index 19bc34d6..55969e4b 100644 --- a/.agents/skills/flotilla-state/SKILL.md +++ b/.agents/skills/flotilla-state/SKILL.md @@ -275,18 +275,27 @@ To persist another kind, add it to `kinds` in `storage.ts`. To persist a new map add a `TABLES` entry and an `init*` method shaped like `initHandles`: load the rows, subscribe to `onItem`, and batch the writes. -### kv, ss and the two ways to bind them +### kv, ss and synced stores `kv` wraps Capacitor `Preferences` and `ss` wraps `SecureStorage`. Both are exported from `storage.ts`, queue their writes, and JSON-encode values. Neither is namespaced per user, so anything in them outlives a login and is cleared only by logout. Secrets go in `ss`. -- `synced({key, storage, defaultValue})` creates a store that persists itself. It emits the - default first, and the stored value arrives later (`.ready`). `theme` and `flTheme` - (`theme.ts`), `checked` (`notifications.ts`) and `shouldUnwrap` (`sync.ts`) use it. -- `sync({key, store, storage})` binds a store that already exists. The root layout awaits it - for `device`, `wallet`, `notificationSettings`, `pushState` and `needsKeyBackup` before first - render, so boot code sees the restored values. It also binds `shouldUnwrap`, which `synced` already persists. +Each persisted store is declared in its owner module with `synced({key, storage, defaultValue})`, +or `syncedWithGetter` from `storage.ts` when callers need `.get()` (`withGetter`'s type drops +`.ready`). The store emits the default first and loads the stored value in the background, so +`.ready` resolves once it has arrived: + +- `theme`, `flTheme` (`theme.ts`), `checked` (`notifications.ts`), `shouldUnwrap` (`sync.ts`), + `device` (`device.ts`), `needsKeyBackup` (`healthChecks.ts`) in `kv` +- `pushState` (key `notificationState`) and `notificationSettings` in `kv`, both in + `push/adapters/common.ts` and re-exported from `@app/push` +- `wallet` (`lightning.ts`) in `ss` + +The root layout awaits `.ready` for the stores boot code reads (`device`, `shouldUnwrap`, +`wallet`, `notificationSettings`, `pushState`, `needsKeyBackup`) before `Push.sync()` and +background sync start. A new store that boot reads joins that list. Never change a key, since +it is where existing installs keep their value. Raw `localStorage` holds only `theme`, `fl-theme` and `font-size`. The root layout mirrors them there to apply them synchronously before `kv` loads, which avoids a flash of the wrong theme. @@ -305,7 +314,7 @@ Settings are an encrypted app-data event with d-tag `flotilla/settings`, read th A preference that should follow the user across devices goes in `SettingsValues` and `defaultSettings`. One that belongs to a device goes in a `kv` store, as push, sound and badge do -in `notificationSettings`. Per-space alert preferences are published (`alerts`); the device's +in `notificationSettings` (`@app/push`). Per-space alert preferences are published (`alerts`); the device's push permission is not. `checked`, the read markers behind badges, lives in `kv`, and `syncCheckedRemote` mirrors it to @@ -413,8 +422,8 @@ NIP-86 calls (`relayManagement.get().forUrl(url)`) are not thunks. They return - **UI signals.** `toast` in `toast.ts`, and `relaysPendingTrust` in `policies.ts`. - **A controller per flow.** `Nip46Controller` (`nip46.ts`) is a class a component instantiates. It holds the writables and actions for a multi-step flow. -- **Module-owned values.** `wallet` in `lightning.ts` is a `withGetter(writable(...))` that the - root layout persists. +- **Module-owned values.** `wallet` in `lightning.ts` persists itself to `ss` with + `syncedWithGetter`. ## Runes and stores diff --git a/src/app/components/ChatMenu.svelte b/src/app/components/ChatMenu.svelte index 5881cf72..c28b1452 100644 --- a/src/app/components/ChatMenu.svelte +++ b/src/app/components/ChatMenu.svelte @@ -11,7 +11,7 @@ import {messagingRelayLists, user} from "@app/core" import {pushModal} from "@app/modal" import {setChecked} from "@app/notifications" - import {notificationSettings} from "@app/settings" + import {notificationSettings} from "@app/push" type Props = { onClick: () => void diff --git a/src/app/components/NewNotificationSound.svelte b/src/app/components/NewNotificationSound.svelte index 8075e24b..5f84b6f7 100644 --- a/src/app/components/NewNotificationSound.svelte +++ b/src/app/components/NewNotificationSound.svelte @@ -1,8 +1,7 @@