From d071a09604599831d370b06563144cf4ba028902 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 6 Jul 2026 11:12:31 -0700 Subject: Gift cross-device sync, snapshot size cap, reliable SW updates, gift-detail layout - Gifts now sync across devices: gift records + reserved/reclaimed coin sets ride the encrypted snapshot as a merge-safe extension (like ark), and every gift state change (create/reclaim/revoke) triggers a saveCache so it actually publishes. A gift made on one device now shows on the others. - Cap the published snapshot at ~40KB JSON (relay rejects >64KB events, nip44 expands ~1.5x): trim oldest on-chain txs when oversized; another device backfills via rescan. Typical wallets untouched. - Service worker: register with updateViaCache:'none' so a new build is detected immediately (not held by the 4h HTTP cache), and reload once on controllerchange so the fresh app runs without a manual second refresh. - Gift detail: View/Reclaim share a row, Revoke on its own full-width line so 'Revoke now' stops wrapping. Co-Authored-By: Claude Fable 5 --- src/features/sync.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'src/features/sync.js') diff --git a/src/features/sync.js b/src/features/sync.js index 1f3414d..715c789 100644 --- a/src/features/sync.js +++ b/src/features/sync.js @@ -6,6 +6,24 @@ import { NostrSync, getSyncConfig, setSyncConfig, npubOf, syncDtag, deviceDtag, isOurDtag } from '../nostr.js'; import { t } from '../i18n.js'; +// Keep a published snapshot under the relay's event-size cap. relay.coinos.io +// rejects events over 64KB and nip44 expands the plaintext ~1.5x, so the JSON +// must stay under ~40KB. The on-chain tx history is the main unbounded growth; +// drop the oldest (this.txs is newest-first) — another device backfills the +// rest with a rescan. Only trims oversized snapshots, so typical wallets are +// untouched. +const RELAY_JSON_BUDGET = 40000; +function trimForRelay(snap) { + if (JSON.stringify(snap).length <= RELAY_JSON_BUDGET) return snap; + const s = { ...snap }; + let txs = (s.txs || []).slice(); + while (txs.length > 25 && JSON.stringify(s).length > RELAY_JSON_BUDGET) { + txs = txs.slice(0, Math.max(25, txs.length - 25)); + s.txs = txs; + } + return s; +} + export function installSyncWallet(wallet) { if (wallet.syncFromNostr) return; // already installed wallet.nostr = new NostrSync(); @@ -112,7 +130,7 @@ export function installSyncWallet(wallet) { // snapshot's network even if the wallet switches networks before it fires const dtag = deviceDtag(wallet.netName); clearTimeout(wallet._nostrPubTimer); - wallet._nostrPubTimer = setTimeout(() => wallet.nostr.publish(snap, dtag), 2500); + wallet._nostrPubTimer = setTimeout(() => wallet.nostr.publish(trimForRelay(snap), dtag), 2500); }); wallet.registerRealtimeHook({ stop: () => clearTimeout(wallet._nostrPubTimer) }); } -- cgit v1.2.3