Files
scrabble-game/ui/src/lib/nativedialogs.ts
T
Ilia Denisov 3306a016a0
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
feat(ui): per-kind active-game limit lock on the New Game screen
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.
2026-07-10 10:09:45 +02:00

40 lines
2.2 KiB
TypeScript

// Pure builder for the Telegram native popup (showPopup) used by the deep-link info modals
// (StaleInviteModal / WelcomeRedeemModal) inside the Mini App. Kept free of the SDK and the DOM so
// it unit-tests in the node environment; the showPopup transport wrapper lives in telegram.ts and
// the wiring (native inside Telegram, the in-app Modal elsewhere) in the modal components.
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
* in-app Modal), plus an "open bot" button when a username is known and a closing OK button. With
* no username it is the message (token removed) and OK only.
*/
export function botInfoPopup(title: string, message: string, username: string, okText: string): TelegramPopupParams {
const handle = username ? `@${username}` : '';
const text = (username ? message.replace('{bot}', handle) : message.replace('{bot}', '').replace(' ', ' ')).trim();
const buttons = username
? [{ id: BOT_BUTTON_ID, text: handle }, { id: 'ok', text: okText }]
: [{ id: 'ok', text: okText }];
return { title, message: text, buttons };
}