From 78cf670939133fc4f656f0f42b21a59431a53842 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 7 Oct 2026 07:24:33 -0700 Subject: Rebuildable caches move from localStorage to IndexedDB (cache-store.js) localStorage is ~5M characters per site whatever the device, and a phone had filled it. Profiles, faceless list, reports, follow packs, profile pages, relay lists, the moderation baseline, community directory, link previews, video sizes/probes and search profiles (~3M on that phone) now live in IndexedDB, which shares the site's disk allowance (GBs). cacheStorage keeps localStorage's string API: reads from memory, coalesced write-behind, flushed on hide. Copies migrate on open and are removed once written. Boot waits for it (max 3s) beside the Ark store; caches read at construction re-merge on ready; an early write can't replace the stored copy. A copy an older build writes to localStorage later moves in on first ask. Wallet wipes reach it; private mode stays on localStorage. Read markers, drafts, follows, note threads and sync outboxes stay in localStorage on purpose (cross-tab consistency, early server-rendered seeding). Also asks navigator.storage.persist(). Co-Authored-By: Claude Opus 5.5 --- src/app.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/app.js') diff --git a/src/app.js b/src/app.js index 8908258..56ab639 100644 --- a/src/app.js +++ b/src/app.js @@ -17,6 +17,7 @@ import { scanQr } from './scan.js'; import { dataSources, getSource, setSource, getNetwork, setNetwork, NETWORKS } from './api.js'; import { STAGING } from './build-flags.js'; import { buildFeatures, loadDeferredFeatures } from './features/index.js'; +import { cacheReady, cacheStore } from './cache-store.js'; import { t, LANGS, getLang, setLang, isRTL, loadLocale } from './i18n.js'; import { rateNow, rateAt, refreshRates, haveRate, getCurrency, setCurrency, currencies, fmtFiat, fmtFiatBare } from './rates.js'; import { @@ -2372,6 +2373,7 @@ function wipeAccountCache(acc) { for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); if (k && bases.some((b) => k.startsWith(b))) keys.push(k); } keys.forEach((k) => localStorage.removeItem(k)); } catch {} + for (const b of bases) { try { cacheStore().removePrefix(b); } catch {} } for (const f of FEATURES) { try { f.wipeCache && f.wipeCache(bases); } catch {} } } @@ -6172,7 +6174,12 @@ loadLocale(getLang()).finally(async () => { refreshRates().then(() => { if (unit === 'fiat') render(); }).catch(() => {}); setInterval(() => { refreshRates().then(() => { if (unit === 'fiat') render(); }).catch(() => {}); }, 10 * 60_000); const storeReady = featureHook('arkStoreReady'); - if (storeReady) await storeReady; + // the IndexedDB caches (profiles, reports, …) open alongside: a wallet's + // caches are read as soon as it opens + await Promise.all([storeReady, cacheReady()].filter(Boolean)).catch(() => {}); + // Ask the browser not to evict this site's storage under disk pressure. + // Chrome answers without a prompt (yes for an installed or busy site). + try { navigator.storage?.persist?.().catch(() => {}); } catch {} _bootDeciding = false; // from here on the screen is a decision, not a guess const opened = restoreAccountsState(); if (!opened) render(); -- cgit v1.2.3