aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-30 22:21:39 -0700
committerAdam Soltys <[email protected]>2026-08-30 22:21:39 -0700
commit08138f7c4eddf2a94a49350e31c7cffd97fe3120 (patch)
treefb901786789bf0d9c493ff28d8bb63bb8517823d /src/features
parentee1c8bb8ae6fa44f5d09949ca30edc039a5a39a5 (diff)
downloadcoinosv3-08138f7c4eddf2a94a49350e31c7cffd97fe3120.tar.gz
coinosv3-08138f7c4eddf2a94a49350e31c7cffd97fe3120.zip
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
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
Diffstat (limited to 'src/features')
-rw-r--r--src/features/hats.js20
1 files changed, 16 insertions, 4 deletions
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;
}