feat(ui): per-kind active-game limit lock on the New Game screen #235

Merged
developer merged 2 commits from feature/e8-guest-limits-client into development 2026-07-10 09:11:40 +00:00
Owner

Client half of the active-game limits (follows the backend/admin PR): the per-kind lock on the New Game screen.

Wire (additive, forward-compatible — no contour wipe)

  • Profile.game_limits (GameLimits{vs_ai, random, friends}, the caller's tier resolved server-side) + GameView.kind — appended FBS fields + a new GameLimits table (committed regen). Threaded backend DTO → gateway transcode → client codec. Old clients ignore the new fields; a new client on an old backend degrades to no lock.

Client

  • gamelimits.ts (pure): count active games per kind from the lobby, resolve the tier cap, decide the lock.
  • New Game screen: a start whose kind is at its cap renders as an outline 🔒 button and opens GameLimitModal instead of a game — native (Telegram showPopup) or the in-app Modal elsewhere. A guest sees a sign-in funnel (Отмена / Вход → /settings); a durable account sees a plain "finish a current game first" notice (ОК). The lock lifts via the existing profile refetch after a guest→durable upgrade.

Lobby behaviour change (owner-agreed)

The old at_game_limit New-Game tab disable + notice is removed. That flag (now the random-kind cap) conflicted with the per-kind lock — it hid the New Game screen where the lock lives and wrongly blocked starting an unfulfilled kind. The tab is always enabled; the per-kind start lock is the only gate. The GameList.at_game_limit wire field stays (unused by the client) for a later cleanup.

Tests

  • unit: gamelimits.ts (count/cap/lock), codec kind + game_limits roundtrip, gateway transcode game_limits encode, the native popup builders.
  • e2e (gamelimit.spec.ts, rewritten): a capped start shows 🔒, opens the modal without navigating, OK dismisses, and the lock clears when the profile refetch lifts the cap (chromium + webkit).

Full local suite green: go build/vet/gofmt + backend/gateway tests; svelte-check, vitest (567), pnpm build, Playwright.

After this merges, E8 is complete — next is the prod deploy of E5-E8 (the monetization launch).

Client half of the active-game limits (follows the backend/admin PR): the per-kind lock on the New Game screen. ## Wire (additive, forward-compatible — no contour wipe) - **`Profile.game_limits`** (`GameLimits{vs_ai, random, friends}`, the caller's tier resolved server-side) + **`GameView.kind`** — appended FBS fields + a new `GameLimits` table (committed regen). Threaded backend DTO → gateway transcode → client codec. Old clients ignore the new fields; a new client on an old backend degrades to no lock. ## Client - **`gamelimits.ts`** (pure): count active games per kind from the lobby, resolve the tier cap, decide the lock. - **New Game screen**: a start whose kind is at its cap renders as an **outline 🔒** button and opens **`GameLimitModal`** instead of a game — native (Telegram `showPopup`) or the in-app `Modal` elsewhere. A **guest** sees a sign-in funnel (Отмена / Вход → `/settings`); a **durable** account sees a plain "finish a current game first" notice (ОК). The lock lifts via the existing profile refetch after a guest→durable upgrade. ## Lobby behaviour change (owner-agreed) The old `at_game_limit` **New-Game tab disable + notice is removed**. That flag (now the random-kind cap) conflicted with the per-kind lock — it hid the New Game screen where the lock lives and wrongly blocked starting an unfulfilled kind. The tab is always enabled; the per-kind start lock is the only gate. The `GameList.at_game_limit` wire field stays (unused by the client) for a later cleanup. ## Tests - unit: `gamelimits.ts` (count/cap/lock), codec `kind` + `game_limits` roundtrip, gateway transcode `game_limits` encode, the native popup builders. - e2e (`gamelimit.spec.ts`, rewritten): a capped start shows 🔒, opens the modal without navigating, OK dismisses, and the lock clears when the profile refetch lifts the cap (chromium + webkit). Full local suite green: `go build`/`vet`/`gofmt` + backend/gateway tests; `svelte-check`, `vitest` (567), `pnpm build`, Playwright. After this merges, E8 is complete — next is the prod deploy of E5-E8 (the monetization launch).
developer added 1 commit 2026-07-10 08:10:04 +00:00
feat(ui): per-kind active-game limit lock on the New Game screen
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 28s
CI / ui (pull_request) Successful in 1m11s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m46s
3306a016a0
Carry the caller's per-tier active-game caps and each game's kind on the
wire (Profile.game_limits + GameView.kind, additive FBS + gateway transcode
+ client codec, committed regen). The New Game screen counts the player's
active games per kind from the lobby and locks a capped start: an outline
button with a lock that opens a funnel modal instead of a game -- a sign-in
prompt for a guest, a "finish a current game first" notice for a signed-in
account (native Telegram popup, in-app modal elsewhere). The lock lifts via
the existing profile refetch after a guest->durable upgrade.

Remove the lobby's old at_game_limit New-Game tab disable + notice: the flag
(now the random-kind cap) conflicted with the per-kind lock -- it hid the
screen where the lock lives and wrongly blocked an unfulfilled kind. The New
Game tab is always enabled; the per-kind start lock is the only gate. The
at_game_limit wire field stays (unused by the client) for a later cleanup.

Tests: client lock logic + codec kind/game_limits roundtrip + gateway
transcode encode + native popup builders (unit); a mock e2e for the lock
badge and the modal.
developer added 1 commit 2026-07-10 08:55:51 +00:00
fix(games): carry game kind on live events + fix the New Game lock funnel
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m56s
e40adfb0c7
Address review of the active-game limit lock:

- Live-event GameViews (opponent_moved, match_found, game_started, ...) did
  not carry game_kind, so a lobby patch from an event zeroed a game's kind
  and the client's per-kind count under-counted — a capped kind read as
  free (a disabled start instead of the lock, and a wrong per-kind result).
  Thread Kind through notify.GameSummary → the event GameView.

- New Game screen refreshes the lobby games on mount so the per-kind count
  reflects the current set, not a stale cached snapshot.

- The guest funnel's login button routes to the profile screen (the account
  controls), not settings.

- Copy: the guest prompt is "sign in to use all the game's features"; the
  durable notice is "finish your active games to start a new one".

Regression tests: the notify opponent-moved payload carries kind; the client
lock locks only the reached kind (vs_ai at cap leaves random open).
owner approved these changes 2026-07-10 09:11:01 +00:00
developer merged commit 0c9678c42b into development 2026-07-10 09:11:40 +00:00
developer deleted branch feature/e8-guest-limits-client 2026-07-10 09:11:40 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#235