From 3d706acd1edccb5c4eb0f781b52b3d302bdafa2b Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 15 Sep 2026 22:39:05 -0700 Subject: A changed reaction stays changed: the fold keeps the newest, not the last to arrive. One reaction per author was folded as "last one in wins". A reload's backfill hands reactions back in relay order, so a flame swapped for confetti came back as the flame. Rooms, DMs and the public page now keep the newest by the rumor's own clock, and an optimistic send stamps its time so an older one arriving afterwards stays out. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z --- src/public-chat.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/public-chat.js') diff --git a/src/public-chat.js b/src/public-chat.js index 91ded5b..9cce6f5 100644 --- a/src/public-chat.js +++ b/src/public-chat.js @@ -84,6 +84,7 @@ export function mountPublicChat() { const deletes = new Map(); // rumorId -> Set(author) const reactions = new Map(); // rumorId -> Map(author -> emoji) const reactEmoji = new Map(); // shortcode -> url, from reactions' own emoji tags + const reactAt = new Map(); // `${rumorId}|${author}` -> ms of the reaction that stands const controlEntries = []; let folded = null; // foldControl result: banned set + channel renames let foldT = 0; // fold-once-per-burst debounce, same as the app's rooms @@ -157,7 +158,15 @@ export function mountPublicChat() { } } else if (rumor.kind === 7) { const target2 = tag('e')?.[1]; - if (target2) (reactions.get(target2) || reactions.set(target2, new Map()).get(target2)).set(author, rumor.content); + if (target2) { + // newest by the rumor's clock, not last to arrive (see the app) + const key = target2 + '|' + author; + const at = eventMs(rumor) || rumor.created_at * 1000; + if (at >= (reactAt.get(key) || 0)) { + reactAt.set(key, at); + (reactions.get(target2) || reactions.set(target2, new Map()).get(target2)).set(author, rumor.content); + } + } // a :code: reaction names its picture in its own tag; kept by code so // the chip can show it for (const [code, url] of emojiTagMap(rumor.tags)) reactEmoji.set(code, url); -- cgit v1.2.3