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

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.
This commit is contained in:
Ilia Denisov
2026-07-10 10:09:45 +02:00
parent e45167041f
commit 3306a016a0
45 changed files with 811 additions and 130 deletions
+15
View File
@@ -8,6 +8,21 @@ import type { TelegramPopupParams } from './telegram';
/** BOT_BUTTON_ID is the showPopup button id that means "open the bot chat". */
export const BOT_BUTTON_ID = 'bot';
/** LOGIN_BUTTON_ID is the game-limit popup button that opens the sign-in funnel. */
export const LOGIN_BUTTON_ID = 'login';
/** gameLimitGuestPopup builds the native popup that nudges a guest to sign in when a New-Game start
* is capped for guests: a cancel button and a login button (the sign-in funnel). */
export function gameLimitGuestPopup(title: string, message: string, cancelText: string, loginText: string): TelegramPopupParams {
return { title, message, buttons: [{ id: 'cancel', text: cancelText }, { id: LOGIN_BUTTON_ID, text: loginText }] };
}
/** gameLimitDurablePopup builds the native notice shown to a durable account at its per-kind cap:
* an OK button only (finish a current game first). */
export function gameLimitDurablePopup(title: string, message: string, okText: string): TelegramPopupParams {
return { title, message, buttons: [{ id: 'ok', text: okText }] };
}
/**
* botInfoPopup builds the native popup for a deep-link info modal: the message with the `{bot}`
* token replaced by the `@username` as plain text (a native popup has no inline link, unlike the