Skip to content

fix(reactions): recompute mine per-client on socket updates (un-react didn't revert)#748

Merged
lilyshen0722 merged 2 commits into
mainfrom
fix/reaction-mine-socket-recompute
Jul 24, 2026
Merged

fix(reactions): recompute mine per-client on socket updates (un-react didn't revert)#748
lilyshen0722 merged 2 commits into
mainfrom
fix/reaction-mine-socket-recompute

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

Bug (2026-07-24)

Clicking your own reaction a second time didn't remove it, and chips could highlight as "yours" when they weren't.

emitReactionChange computes mine from the reactor's id and broadcasts that single reactions array to the whole pod_<id> room:

io.to(`pod_${podId}`).emit('messageReaction', { messageId, podId, reactions }); // reactions[].mine = the reactor's view

So after account B reacts, account A's client receives B's mine view and adopts it. The bubble uses mine to decide POST-vs-DELETE on click — so A clicking its own reaction can POST (re-add, idempotent → "nothing happened") instead of DELETE. Verified the backend un-react is correct (DELETE removes the caller's reaction, count 2→1); this was purely the client trusting a foreign mine.

Fix

The socket handler recomputes mine for the current client from each reaction's users list (already sent over the wire by reactionAttributionService), falling back to the broadcast value only when users is absent. Extracted as the pure recomputeReactionMine helper.

Test

recomputeReactionMine.test.ts — recomputes true/false from the user list, and falls back to the wire value when users or userId is missing.

This is the last of the reaction-bug layers (❤️ validation #744, membership 403 #746, and now the mine broadcast).

🤖 Generated with Claude Code

https://claude.ai/code/session_01DMeWzgFxsfBcjoLVLewEES

lilyshen0722 and others added 2 commits July 24, 2026 01:35
emitReactionChange computes `mine` for the user who triggered the add/remove
and broadcasts that ONE view to the whole pod room. Every other client then
adopts a wrong `mine` — which flips the add/remove toggle (clicking your own
reaction POSTs/re-adds instead of DELETE/removing it: "un-react didn't revert")
and mis-highlights chips. 2026-07-24: reported after a second account reacted.

Fix: the socket handler recomputes `mine` for THIS client from each reaction's
`users` list (already on the wire), falling back to the broadcast value only
when `users` is absent (older server / Mongo fallback). Extracted as the pure
`recomputeReactionMine` so it's unit-tested.

Backend un-react already works (DELETE removes correctly); this was purely the
client trusting a broadcast `mine` that wasn't its own.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMeWzgFxsfBcjoLVLewEES
useV2PodDetail reads currentUser to recompute reaction `mine` per-client;
useAuth throws without an AuthProvider, so this renderHook-based suite needs
the stub. Only affected suite; other hook consumers already provide/mocked it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMeWzgFxsfBcjoLVLewEES
@lilyshen0722
lilyshen0722 merged commit 5d1bf59 into main Jul 24, 2026
10 checks passed
@lilyshen0722
lilyshen0722 deleted the fix/reaction-mine-socket-recompute branch July 24, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant