Skip to content

Fix leaderboard live updates across connection and bingo flows - #122

Merged
rxmox merged 1 commit into
mainfrom
leaderboard-pusher-fixes
Apr 30, 2026
Merged

Fix leaderboard live updates across connection and bingo flows#122
rxmox merged 1 commit into
mainfrom
leaderboard-pusher-fixes

Conversation

@rxmox

Copy link
Copy Markdown
Collaborator

Summary

The leaderboard was not updating live for several state changes. After tracing the Pusher path end-to-end, three classes of bugs were fixed plus the missing mobile wiring for bingo line completion.

Bugs fixed

  • Empty Pusher payloads on connection create (by-emails) and delete. Both endpoints called pusher.trigger(..., \"leaderboard-updated\", {}). The web client's handler checks payload.participantId and payload.connectionsCount, both undefined on an empty payload, and falls into its drop branch — so the leaderboard silently ignored the event. Now both endpoints emit a full payload per affected participant.
  • Frontend sort missed tiebreakers.Leaderboard.tsx sorted only by connectionsCount, while the backend GET sorts by (completed, linesCompleted desc, connectionsCount desc). After live updates, the order would diverge from a refresh. Frontend sort now mirrors the backend.
  • Bingo line completion never reached the leaderboard. No client called PUT /leaderboard/score, so linesCompleted was stuck at 0 in practice. Mobile's handleAssign now fires the score update (with the blackout completed flag) after each assignment.

Refactor

Extracted emitLeaderboardUpdate(eventId, participantId) helper. It recomputes the participant's connectionsCount, emits the full payload to event-{eventId}, and logs Pusher errors instead of throwing — so a flaky Pusher call can't 500 the originating request. Replaced four inline emit blocks with helper calls.

Files

  • `shatter-backend/src/utils/leaderboard_pusher.ts` (new)
  • `shatter-backend/src/controllers/leaderboard_controller.ts` — uses helper
  • `shatter-backend/src/controllers/participant_connections_controller.ts` — three call sites (create, by-emails, delete) all emit full payloads
  • `shatter-web/src/components/Leaderboard.tsx` — sort tiebreakers
  • `shatter-mobile/src/api/games/game.api.tsx` — `UpdateLeaderboardScoreApi`
  • `shatter-mobile/src/components/games/NameBingo.tsx` — fire-and-forget score update inside `handleAssign`

Verification

Backend was exercised end-to-end against a local stack:

  • REST: full lifecycle (signup → create event → join → connect → score → delete → by-emails reconnect) returned correct shapes and counts; the GET sort matched expectations.
  • Pusher delivery: a node subscriber on `event-{eventId}` confirmed every mutation produced exactly the right number of `leaderboard-updated` events with full payloads. Connection create/delete fire two events (one per participant); score updates fire one.
  • Frontend logic: a headless port of the React component (initial fetch + filter, Pusher merge, sort) was driven through the same mutation sequence; every state snapshot matched expected, including the `/by-emails` path that previously dropped events and the `completed` tiebreaker putting Alice above Bob despite a lower `connectionsCount`.
  • Builds: `shatter-backend` (`tsc`) and `shatter-web` (`tsc -b && vite build`) both clean. Mobile typecheck shows no new errors in touched files (pre-existing expo-router route literal warnings remain unchanged).

Test plan

  • Open the host web dashboard for a test event in a browser; have two clients (mobile or curl) join.
  • Trigger a connection create — confirm both rows appear live with "1 link" each, no refresh.
  • On mobile, mark squares to complete a line — confirm the leaderboard shows "1 line" live and re-sorts.
  • Fill the entire grid (blackout) — confirm "completed" checkmark and top-of-board sort.
  • Delete the connection from the host side — confirm both rows decrement to 0 links live.

- Extract emitLeaderboardUpdate helper that recomputes a participant's
connectionsCount and emits a full Pusher payload, replacing duplicated
inline blocks in updateScore and createParticipantConnection.
- Fix createParticipantConnectionByEmails and deleteParticipantConnection,
which previously emitted empty {} payloads that the web client silently
dropped, causing the leaderboard to miss link/unlink events.
- Mirror backend sort tiebreakers in the web Leaderboard component
(completed, then linesCompleted, then connectionsCount).
- Wire mobile NameBingo handleAssign to PUT /leaderboard/score after each
square assignment so linesCompleted and the blackout completed flag
propagate to the live leaderboard.
@rxmox
rxmox requested a review from lnqminh3003April 30, 2026 21:53
@rxmox
rxmox merged commit 1d46083 into mainApr 30, 2026
4 checks passed
@rxmox
rxmox deleted the leaderboard-pusher-fixes branch May 1, 2026 07:05
Sign up for freeto 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

@rxmox