diff options
| author | Adam Soltys <[email protected]> | 2026-09-15 22:39:05 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-15 22:39:05 -0700 |
| commit | 3d706acd1edccb5c4eb0f781b52b3d302bdafa2b (patch) | |
| tree | 3f43b2daf254b87a8f8dec58eeb2481f8b4678d7 /src/public-chat.js | |
| parent | 12e069fc62bff8715de189aedc777b69d1e56377 (diff) | |
| download | coinosv3-3d706acd1edccb5c4eb0f781b52b3d302bdafa2b.tar.gz coinosv3-3d706acd1edccb5c4eb0f781b52b3d302bdafa2b.zip | |
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
Diffstat (limited to 'src/public-chat.js')
| -rw-r--r-- | src/public-chat.js | 11 |
1 files changed, 10 insertions, 1 deletions
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); |
