From 08138f7c4eddf2a94a49350e31c7cffd97fe3120 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 30 Aug 2026 22:21:39 -0700 Subject: A hat swapped on one device reaches the others in minutes, not by dinnertime: the worn-hat cache revalidates after two minutes instead of six hours (still painted from cache instantly — the refresh rides behind), and learning a hat came OFF now repaints too, where the old check only redrew for fresh hats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr --- src/features/hats.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'src/features') diff --git a/src/features/hats.js b/src/features/hats.js index c0ea3cc..2b4a4d6 100644 --- a/src/features/hats.js +++ b/src/features/hats.js @@ -98,7 +98,11 @@ export function hatsFeature(ctx) { // ---- who wears what: batched lookup + persistent cache ------------------ - const TTL = 6 * 3600_000; + // Serve from cache instantly, but ask the registrar again after a couple + // of minutes — six hours of staleness meant a hat swapped on one device + // kept its old look on every other for the rest of the day. One batched + // request per quiet period is what the /hats endpoint is for. + const REVALIDATE = 2 * 60_000; const worn = new Map(); // pk -> { hat: id|null, t } let loaded = false; const loadStore = () => { @@ -138,9 +142,17 @@ export function hatsFeature(ctx) { const j = await r.json(); if (!r.ok) throw new Error('hats lookup failed'); const now = Date.now(); - for (const pk of pks) worn.set(net + ':' + pk, { hat: (j.hats || {})[pk] || null, t: now }); + let changed = false; + for (const pk of pks) { + const key = net + ':' + pk; + const next = (j.hats || {})[pk] || null; + // a hat coming OFF is a change too — the old check only repainted + // when a fresh entry had a hat, so removals lingered on screen + if ((worn.get(key) || {}).hat !== next) changed = true; + worn.set(key, { hat: next, t: now }); + } saveStore(); - if ([...worn.entries()].some(([pk, v]) => v.hat && v.t === now)) render(); + if (changed) render(); } catch { failedAt = Date.now(); } @@ -151,7 +163,7 @@ export function hatsFeature(ctx) { if (!pk || !/^[0-9a-f]{64}$/.test(pk)) return null; loadStore(); const v = worn.get(hatNet() + ':' + pk); - if (!v || Date.now() - v.t > TTL) queueFetch(pk); + if (!v || Date.now() - v.t > REVALIDATE) queueFetch(pk); return v ? v.hat : null; } -- cgit v1.2.3