Skip to content

feat: revamped GamePlay UI — single-column layout, claim modal, activity slide-in#4

Merged
hashd merged 81 commits into
masterfrom
feat/revamped-ui
Apr 12, 2026
Merged

feat: revamped GamePlay UI — single-column layout, claim modal, activity slide-in#4
hashd merged 81 commits into
masterfrom
feat/revamped-ui

Conversation

@hashd
Copy link
Copy Markdown
Owner

@hashd hashd commented Apr 12, 2026

Summary

  • Backend: Fix ticket generation so every column with remaining numbers always contributes ≥1 to each row (prevents degenerate blank columns)
  • Types/Store: Add PrizeProgress { struck, required } type and a client-side myPrizeProgress computed that mirrors backend logic for instant UI feedback on each strike
  • GamePlay: Collapse to single-column layout (max-w-3xl), move prize claiming to a per-ticket modal with unclaimed-prize list, replace always-on desktop sidebar with a slide-in activity overlay triggered from the floating action bar
  • ActivityFeed: Add close emit + restyle filter tabs for the overlay context
  • TicketPrizeProgress: New self-contained prize list component with multi-ticket claim picker (not yet wired into GamePlay — available for future use)

Test Plan

  • Join a game as a player — ticket renders correctly with no blank columns
  • Strike numbers — prize progress in store updates instantly without server round-trip
  • Tap "Claim" on a ticket (single ticket) — claim modal opens listing unclaimed prizes; selecting one submits the claim
  • Tap "Claim" on a ticket (multi-ticket) — modal shows all unclaimed prizes; claim submits with correct ticket ID
  • Tap 💬 in the floating action bar — activity feed slides in from the right; close button and backdrop tap both dismiss it
  • All 82 backend + 15 frontend tests pass

hashd added 30 commits April 11, 2026 19:23
…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)
hashd added 27 commits April 12, 2026 13:13
- 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 hashd merged commit 2b284ea into master Apr 12, 2026
@hashd hashd deleted the feat/revamped-ui branch April 13, 2026 11:07
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
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