feat: revamped GamePlay UI — single-column layout, claim modal, activity slide-in#4
Merged
Conversation
…ings - Add full UserSocket + GameChannel design with all 8 event types - Fix OAuth callback — must return token not session cookie - Correct TypeScript types to match actual server shapes (Ticket, Board, NumberPickedEvent, prizes) - Add missing channel events: BogeyEvent, ChatEvent, PlayerJoinedEvent, PlayerLeftEvent, StrikeResultEvent, ClaimRejectionEvent, GameJoinReply - Add chatStore and ActivityFeed.vue for chat/activity feed (was missing) - Add prize_progress to gameStore for host leaderboard - Add reconnect strategy to useChannel - Add package.json with phoenix resolved from deps/ - Add full vite.config.ts with WS proxy - Add missing API endpoints: GET /api/games, POST /api/games/:code/clone - Note tailwind.config.js content paths must be updated - Add injectable socket factory to useChannel for testability - Add full testing strategy (ExUnit + Vitest + Playwright) - Phase execution: backend Channel layer first, then frontend - Expand success criteria from 6 to 10 items
23 tasks covering Phase 1 (backend: UserSocket, GameChannel, OAuth fix, missing API endpoints) and Phase 2 (frontend: types, stores, composables, all UI/game components, all 6 pages, tests, cleanup).
Adds MothWeb.GameChannel handling join/initial state reply, full PubSub-to-channel event translation for all server broadcasts, and inbound handlers for strike/claim/ reaction/chat. Also adds MothWeb.ChannelCase test support module and the GameChannel join test (happy path + game_not_found).
- Wrap String.to_existing_atom in valid_prizes allowlist check to prevent ArgumentError crash on unknown prize strings; reply with invalid_prize rejection - Rename strike test to reflect lobby-state-always-rejected behavior - Tighten chat/reaction assertions to verify user_id is forwarded - Tighten claim assertion to require bogey + bogeys_remaining specifically
Implements usePresenceStore, useGameStore, and useChatStore with full event handler coverage. Includes vitest coverage for game store logic.
…ables Implements Task 14. useChannel wraps Phoenix socket/channel lifecycle, wires all game/chat/presence store handlers, and exposes connect/disconnect for manual lifecycle control and testing.
Add explicit GameJoinReply type on join callback, add socket.onError handler, null out socket/channel on disconnect, clear reaction listeners on disconnect, fix useAutoScroll flush to post, and improve useConfetti catch logging.
Creates app.ts entry point, Vue Router with hash history and requiresAuth guards, App.vue root component, 6 stub page components, and shims-vue.d.ts for .vue module resolution.
…oast, InputField, SegmentedControl, BottomSheet, ConnectionStatus)
…Feed, ReactionOverlay)
- Add ticket_owners and player_ticket_counts fields to defstruct - claim_prize/3 → claim_prize/4 (ticket_id param added) - Add set_ticket_count/4 client API function - Join handler now generates a full strip of 6 tickets, stores all by UUID in state.tickets, tracks ownership via ticket_owners, and returns only the active default_count tickets to the caller - Rejoin path returns currently active tickets from ticket_owners
…ization - GamePlay.vue: support multiple tickets — dynamic ticketRefs array, claimPrize helper auto-selects eligible ticket, v-for over myTickets in both lobby and running states, onStrikeConfirmed iterates all refs - game_channel_test: add ticket_id to claim push to match new handler - game_controller: serialize Ticket structs via Ticket.to_map in join response so MapSet in numbers field doesn't break JSON encoding
- app.css: updated design tokens and animation utilities - UI components (Button, Card, Badge, Avatar): visual refresh with consistent theming, shadows, and hover states - Game components (Board, TicketGrid, CountdownRing, ActivityFeed): improved layout, animations, and readability - Home.vue: full redesign with public game browser, better lobby UX - auth_controller.ex: minor response/redirect polish
…vents - game_channel: trim my_tickets to active count from player_ticket_counts instead of returning the full 6-ticket strip to the player - game_channel: include ticket_count in players list on join reply so host dashboard highlights the correct count button on load - game_channel: look up user name and forward ticket_count in player_joined push (was sending name: nil before) - server: include ticket_count in :player_joined broadcast payload - stores/game.ts: propagate ticket_count into player on onPlayerJoined - types/channel.ts: add ticket_count to PlayerJoinedEvent
Tighten the feasibility floor so columns with remaining numbers always assign at least 1 to the current row, preventing degenerate tickets where a column is skipped entirely in a row despite having numbers left.
Introduce PrizeProgress { struck, required } interface to replace the
opaque Record<string, number> shape. Update GameJoinReply.prize_progress
to use Record<string, Record<string, PrizeProgress>> throughout.
Mirror backend's compute_prize_progress in the game store so prize progress indicators update instantly on each strike without waiting for a server round-trip. Computes early_five, line, and full_house progress per ticket from myStruck + settings.enabled_prizes.
Add a close button (emits 'close') for use in the slide-in overlay. Restyle filter tabs to a pill-group with separator and consistent hover/active states matching the revamped UI palette.
… claim Self-contained component rendering all enabled prizes with claim buttons, won/claimed badges, and a ticket-picker modal for multi-ticket players. Emits claim(prize, ticketId) and handles single-ticket fast-path inline.
…y slide-in - Collapse two-column layout to single centered column (max-w-3xl) - Move claim button inline per-ticket; replace prize sidebar with a modal that lists unclaimed prizes and closes on selection or backdrop click - Replace desktop-pinned activity feed with a slide-in overlay triggered from the floating action bar (💬 button); overlay dismisses via close button or backdrop tap - Auto-strike toggle moved to first ticket header only - Add slide-right CSS transition for the activity panel
hashd
added a commit
that referenced
this pull request
Apr 13, 2026
…validation - Filter struck set per-ticket in prize claims to prevent cross-ticket fraud (#1) - Filter prize progress per-ticket to prevent cross-ticket info leak (#9) - Strip join_secret from sanitized state (#2) - Filter REST show endpoint to only return requesting user's data (#3) - Filter player_tickets_updated broadcast to target user only (#4) - Add chat text length limit (500 chars) and validation (#13) - Add reaction emoji allowlist (#14) - Add max players per game limit (100) (#11) - Add input validation for game creation params (#23) - Remove inspect(reason) from error responses (#22) - Store rich metadata in Registry for O(1) game listing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PrizeProgress { struck, required }type and a client-sidemyPrizeProgresscomputed that mirrors backend logic for instant UI feedback on each strikecloseemit + restyle filter tabs for the overlay contextTest Plan