Bug fixes and dev quality-of-life improvements#3
Merged
Conversation
Full rewrite design covering: context-separated monolith architecture, supervised game engine with crash recovery, player-initiated prize claims with bogey system, magic link + OAuth auth, LiveView web UI, REST API + Channels for native mobile apps, and cluster-ready scaling.
Key changes: - Eliminate per-second timer broadcasts; use client-side countdown - Write-through for critical mutations (prize claims, player joins) - Define connection resilience (host disconnect, player reconnect) - Define Moth.Game.Monitor responsibilities and rest_for_one supervision - Add token lifecycle (expiry, rotation, revocation) - Add chat specification (ephemeral, rate-limited) - Fix deps: keep phoenix_html, defer ueberauth_apple, swap to bandit - Add room code design with enumeration protection - Add concrete test scenarios and load testing plan - Downgrade cluster-readiness claims to honest assessment - Add settings constraints (min interval 10s) - Add edge case table and status enum mapping
Covers: project foundation, migrations, schemas, Board/Ticket/Prize/Code pure functions with TDD, Auth context (magic links, OAuth, tokens), game supervision tree, GameServer lifecycle, crash recovery, Game context API, router/layouts/plugs, LiveViews, API controllers, Channel, and integration tests.
Full redesign spec covering design system, all screens, accessibility, error/loading states, and scoped backend additions. Reviewed by critic, architect, and QA lead agents — all critical and major findings addressed.
Covers: design system foundation, UI component library, JS hooks, backend additions, all screen redesigns, presence, reactions, and accessibility pass.
…config Add CSS custom property token system (light/dark), Tailwind darkMode class config, Inter font via Google Fonts, animation keyframes with reduced-motion support, connection status banner, and page loading bar.
…t, skeleton, etc.
… presence, board sheet, auto-scroll, auto-dismiss
…one, auto-strike cast - Add prize_progress computation in sanitize_state (per-player, per-prize struck/required tuples) - Add server_now to pick broadcast for client clock sync - Add strike_out_async (GenServer.cast) for fire-and-forget auto-strike - Add reaction handler with 1s rate limiting - Add recent_games query (host_id, ordered by inserted_at) - Add clone_game for rematch flow - Fix monotonic time rate-limit default for chat and reactions
Save the requested path in session when require_authenticated_user redirects unauthenticated users. Preserve it across session renewal in log_in_user, and redirect to it after successful login via OAuth or magic link.
…, countdown New MothWeb.Components.Game module with redesigned components: - ticket_grid/1: 3x9 grid with card wrapper, accessibility roles - ticket_cell/1: four visual states (empty, unpicked, picked, struck) - prize_chip/1: available/claimed/disabled states with progress display - number_pill/1: called-number pill with bounce-in animation - board/1: 9x10 board (1-90) with pick highlighting and ticket dot indicator - countdown_ring/1: JS hook wrapper for countdown animation Imported in moth_web.ex html_helpers. Resolved import conflict with old GameComponents in play_live.ex (qualified call for claim_buttons).
Adds the real-time activity feed component used by both PlayLive and HostLive. Uses Phoenix.LiveView streams for efficient DOM updates (capped at 50 entries). Supports five entry types: pick, prize_claimed, bogey, chat, and system. Includes filter tabs (All/Chat/Events), chat input form, AutoScroll hook, and aria-live for accessibility.
… feed + reactions Three-state player view: lobby (waiting room with game code, player count, prize list), running/paused (two-column layout with ticket grid, countdown, prize chips with progress, picked numbers, board with mobile bottom sheet, activity feed, reactions bar), and finished (game over with confetti, prize winners, back-to-home). Uses strike_out_async for auto-strike, proper PubSub event handling with activity feed updates.
…d, activity log Rewrite host_live.ex with three distinct states: - Lobby: game code hero card with copy hook, live player count, player list (capped at 20 + overflow), settings summary, and start button - Running/Paused: three-column command center with board + countdown (left), game controls + player leaderboard with near-prize alerts + prize tracker (center), and activity feed (right); responsive single-column on mobile - Game Over: winners summary, game stats, play-again (clone) and back-home Adds full PubSub handler set (pick, status, prize_claimed, bogey, chat, reaction, player_joined, player_left) with activity feed integration.
Expand MothWeb.Presence with track_player/3, update_status/4, and list_players/1 helpers. Integrate presence tracking in both PlayLive and HostLive mounts, handle presence_diff broadcasts by re-listing, and wire up away/online events from the Presence JS hook.
…cleanup old components - Delete old game_components.ex (replaced by Components.Game) - Improve ticket_cell aria-labels: "Number N, not called" / "called, not struck" / "struck" / "empty cell" - Add aria-pressed on struck cells - Add role="button" and aria-label on prize_chip (claim/won states) - Add aria-labelledby on modal pointing to title element - Change toast role to "status" with aria-live="polite" - Add aria-label on badge for live/paused/finished variants - Add animate-shake and animate-strike-ripple to reduced-motion overrides
Phoenix.Tracker.track/4 expects a PID as the first argument, not a LiveView socket struct. Pass self() to get the LiveView process PID.
Replace {expr} with <%= expr %> for text content in templates.
LiveView 0.20.x only supports {} in tag attributes, not text nodes.
Also consolidate duplicate ActivityFeed live_component instances
(desktop + mobile) into a single instance to avoid duplicate ID errors.
Auto-logs in a dev user (dev@localhost) when dev_routes is enabled, skipping the OAuth flow during local development.
non_empty/1 helper treats "" as nil so an empty name field still produces "Untitled Game". Also wraps the name input_field in a form so phx-change fires correctly on debounce.
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
Phoenix.Tracker.track/4requires a PID — was passing a LiveView socket, fixed withself(){expr}in text nodes renders as literal text in LiveView 0.20.x — converted to<%= expr %>across host, player, and activity feed viewsActivityFeedwas mounted twice (desktop + mobile) with the same ID — consolidated to one instance with responsive CSSnon_empty/1guardinput_fieldin a<form>sophx-changefires correctly on debouncedev@localhostwhendev_routes: true, bypassing OAuth during local dev.elixir_lsto.gitignore, reorganized docs underdocs/Test plan
{expr}strings