Commit Graph

40 Commits

Author SHA1 Message Date
cottongin
59db8f6ed7 feat: poll countdown timer, game-selection sync, source tracking, and multi-admin fixes
Work spanning May 7-10 across multiple sessions:

Poll winner detection + source column (May 7):
- Fix race condition in handleEndPolling where WS voting.ended cleared
  leadingGame before the setTimeout could capture the winner
- Add pollActiveRef guard to prevent late poll.leading messages from
  re-activating an ended poll
- Add 'source' column to session_games (dice/manual/poll) with backward-
  compatible fallback from manually_added flag
- Show indigo "Poll" badge in game lists (Picker, Home, SessionDetail)
- Include source in session export (JSON and text formats)

Multi-admin poll state sync (May 9):
- Enrich poll.start broadcast with pollStartedAt timestamp so all admin
  clients can start their timers from the correct time
- Enrich voting.ended broadcast with winnerGameId/Label/Votes so all
  admins see the winner prompt, not just the one who clicked End Poll
- Add poll.start WS handler in SessionInfo so Admin B sees polls started
  by Admin A without refreshing
- Make handleStartPolling optimistic with rollback on failure

WebSocket keepalive + auto-reconnect (May 9):
- Add 30s ping interval to SessionInfo WS connection (matching server's
  60s timeout) to prevent silent disconnects
- Add auto-reconnect on close with 3s delay
- Proper cleanup of ping interval, reconnect timeout, and onclose handler

Sync selected game across admin clients (May 10):
- New POST/DELETE /sessions/:id/game-selection endpoints with DB
  persistence (pending_game_id, pending_game_source columns)
- Broadcast game.picked/game.dismissed WS events to session subscribers
- handleDismissGame replaces inline setSelectedGame(null) calls
- Restore pending game selection on page load for late-joining admins
- Clear pending selection when game is formally added to session

Poll ending countdown timer (May 10):
- POST /:id/voting/end now accepts optional { delay } (0-300 seconds)
- New POST /:id/voting/cancel-end to abort a scheduled end
- New poll.ending and poll.ending.cancelled WS events
- poll_ending_at column on sessions table for crash recovery
- rescheduleEndingPolls() called on server startup to resume countdowns
- End Poll button opens popover with End Now / 5s / 10s / 30s / custom
- Red "Poll Ending" card with countdown display and Cancel button
- Document new WS events in docs/api/websocket.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 20:33:00 -04:00
cottongin
a1078e0cc7 feat: add alarm sounds and sound controls to countdown timer widget
- Add four synthesized alarm sounds (Digital Beep, Gentle Chime, Urgent
  Alarm, Bell) using Web Audio API oscillators
- Add sound enable/mute toggle button and sound selector dropdown
- Add preview button to test selected alarm before countdown
- Play selected alarm sound when countdown expires (if enabled)
- Default widget mode to 'timer' instead of 'stopwatch'
- Extract shared inputClass constant for timer input styling
- Hide native number input spinners via Tailwind utility classes

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 14:37:00 -04:00
cottongin
4bbc1856f5 chore: bump version to 0.7.7 - Pokémon-Go-To-The-Polls Edition
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-07 20:44:30 -04:00
cottongin
4be520476c feat: add draggable stopwatch widget with lap counter and countdown timer
New general-purpose floating stopwatch accessible from a clock icon in
the nav bar. Supports two modes: stopwatch (count up with laps) and
countdown timer (presets + custom input, red flash on expiry). Desktop
renders as a compact draggable card; mobile docks to the bottom as a
fixed sheet. Timer persists while hidden.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-07 20:44:18 -04:00
cottongin
10c34557c5 feat: add poll control UI to Picker with start/end toggle, leading game indicator, and timer
Adds a three-state poll control card (Start Poll / End Poll / Poll Result)
with an LED-style stopwatch on the End Poll button. Shows the current poll
leader from downstream poll.leading WebSocket messages. On poll end, prompts
the admin to use the winner as the next game choice or ignore it. Restores
poll state from the session on page load for continuity across reloads.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-07 20:44:04 -04:00
cottongin
c5ffe23404 fix: detect game start for Pack 7+ titles that don't use state: "Gameplay"
Pack 7 games (Quiplash 3, Blather Round) transition room state from
"Lobby" to "Logo" instead of "Gameplay". Even newer titles (Doominate,
Big Survey) have no room entity at all — the only signals are room/lock
and room/exit opcodes.

- parseRoomEntity: detect game started as any non-Lobby state
- handleMessage: add room/lock and room/exit opcode handling
- handleRoomLock: emit game.started as fallback for room-entity-less games
- handleRoomExit: emit game.ended on explicit room close
- Tests: 6 new tests covering Logo state, room/lock, room/exit

Verified against live rooms: quiplash3, blanky-blank, you-ruined-it,
bigsurvey.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-03 00:23:32 -04:00
cottongin
91b7de3bb7 style: remove footer top margin, match presence bar bottom padding to 1rem, stack sections vertically
Made-with: Cursor
2026-04-05 05:24:17 -04:00
cottongin
ea23b66cbf feat: move presence bar to sticky bottom-right widget
Reposition the presence bar from above main content to a sticky
bottom-right indicator that docks above the footer on scroll.
Sized to fit content rather than spanning full width.

Made-with: Cursor
2026-04-05 04:49:17 -04:00
cottongin
ea6e8db90b feat: ticker symbol voting for live votes
- Add ticker column to games table with migration
- Bootstrap tickers from tickers.json config on startup
- POST /api/votes/live accepts optional ticker field for direct game
  lookup (bypasses timestamp-interval matching)
- Ticker votes work for any game, not just session games
- Update API docs and add e2e tests for ticker voting
- Version bump to 0.6.5

Made-with: Cursor
2026-04-05 04:47:06 -04:00
cottongin
b2bb2989e9 feat: role-aware presence bar, WebSocket logging fixes
- findAdminByKey returns role from admins.json (defaults to 'admin')
- JWT includes config-defined role instead of hardcoded 'admin'
- PresenceBar split into "who's here?" (page admins) and "connected"
  (bot/utility services with icon+color badges)
- Bot/utility roles appear in presence on all pages when connected
- usePresence hook uses refs to avoid WS reconnect on navigation
- WS auth log prints admin name instead of generic 'admin'
- WS connection log reads X-Forwarded-For for real client IP
- AuthContext stores adminRole from login response
- Uncomment admins.json Docker volume mount, add SELinux :z flags

Made-with: Cursor
2026-04-05 04:46:56 -04:00
cottongin
850fed5a87 chore: update README.md with new features and changes, version bump. 2026-03-23 12:04:03 -04:00
cottongin
0833cf6167 feat: add Prev/Next pagination bar to session history
Made-with: Cursor
2026-03-23 11:35:42 -04:00
cottongin
bfabf390b4 feat: render sessions grouped by day with styled header bars
Made-with: Cursor
2026-03-23 11:34:41 -04:00
cottongin
ad8efc0fbf feat: add pagination state and offset to session API calls
Made-with: Cursor
2026-03-23 11:32:59 -04:00
cottongin
db369a807e feat: add getLocalDateKey, formatDayHeader, formatTimeOnly date helpers
Made-with: Cursor
2026-03-23 11:31:35 -04:00
cottongin
85c06ff258 fix: session count label distinguishes visible vs total
Show "X visible (Y total)" when the history list is filtered or limited,
and "X sessions total" only when every session is actually displayed.

Made-with: Cursor
2026-03-23 10:41:38 -04:00
cottongin
3b18034d11 tweak: language for admin presence bar 2026-03-23 10:21:03 -04:00
cottongin
3da97a39ad chore: version bump 2026-03-23 10:03:24 -04:00
cottongin
95e7402d81 feat: PresenceBar component shows who is watching each page
Made-with: Cursor
2026-03-23 09:57:31 -04:00
cottongin
f0b614e28a feat: usePresence hook for WebSocket-based page presence
Made-with: Cursor
2026-03-23 09:57:22 -04:00
cottongin
a4d74baf51 feat: per-admin localStorage namespacing with migration
Made-with: Cursor
2026-03-23 09:42:50 -04:00
cottongin
512b36da51 fix: long-press select deselection bug, swap sun emoji for dice, bump version to 0.6.2
Made-with: Cursor
2026-03-23 02:48:56 -04:00
cottongin
d613d4e507 feat: rewrite History page with controls bar, multi-select, Sunday badge, and update SessionDetail with archive support
Made-with: Cursor
2026-03-23 02:21:37 -04:00
cottongin
35474e5df4 feat: add archived column to sessions table and isSunday helper
Made-with: Cursor
2026-03-23 01:40:07 -04:00
cottongin
a68a617508 feat: simplify History page to session list with notes preview and navigation
Made-with: Cursor
2026-03-23 00:17:32 -04:00
cottongin
0ee97b35c5 feat: add SessionDetail page with notes view/edit and route
Made-with: Cursor
2026-03-23 00:16:45 -04:00
cottongin
7ce5251543 chore: add react-markdown and @tailwindcss/typography dependencies
Made-with: Cursor
2026-03-23 00:14:31 -04:00
cottongin
171303a6f9 fix: enforce single playing game and clean up stale shard monitors
Mark games as 'played' when shard detects game.ended or room_closed.
Stop old shard monitors before demoting previous playing games on new
game add or status change. Sync frontend playingGame state with the
games list on every refresh to prevent stale UI. Use terminate() for
probe connections to prevent shard connection leaks.

Made-with: Cursor
2026-03-20 23:34:22 -04:00
cottongin
34637d6d2c feat: add periodic game.status broadcast and live status REST endpoint
Add 20-second game.status WebSocket heartbeat from active shard monitors
containing full game state, and GET /status-live REST endpoint for on-demand
polling. Fix missing token destructuring in SessionInfo causing crash.
Relax frontend polling from 3s to 60s since WebSocket events now cover
real-time updates. Bump version to 0.6.0.

Made-with: Cursor
2026-03-20 21:05:19 -04:00
cottongin
65036a4e1b fix: Picker WebSocket auth, event handling, and status display
- Authenticate with JWT before subscribing to session events
- Use message.type instead of message.event (matches server format)
- Handle all new shard monitor events (room.connected, lobby.*,
  game.started, game.ended, room.disconnected)
- Replace never-set 'waiting' status with 'monitoring'
- Show monitoring indicator with live player count

Made-with: Cursor
2026-03-20 11:47:19 -04:00
cottongin
5cf5901001 tweaks 2025-11-03 18:38:27 -05:00
cottongin
f52754ac87 done 2025-11-03 17:56:15 -05:00
cottongin
2a75237e90 IDK, it's working and we're moving on 2025-11-02 16:06:31 -05:00
cottongin
6308d99d33 first release! 0.3.6 2025-10-30 19:27:23 -04:00
cottongin
47db3890e2 pretty much ready to 'ship'.3 2025-10-30 17:52:44 -04:00
cottongin
1a74b4d777 pretty much ready to 'ship'.2 2025-10-30 17:34:44 -04:00
cottongin
8f3a12ad76 pretty much ready to 'ship' 2025-10-30 17:18:30 -04:00
cottongin
7bb3aabd72 wow, chrome-extension MUCH improved - websockets 2025-10-30 15:17:15 -04:00
cottongin
db2a8abe66 we're about to port the chrome-extension. everything else mostly works 2025-10-30 13:27:55 -04:00
cottongin
2db707961c initial commit 2025-10-30 04:27:43 -04:00