feat(ads): VK post-move interstitial + retire deprecated hint_balance/paid_account #229

Merged
developer merged 4 commits from feature/ads-interstitial into development 2026-07-10 01:48:11 +00:00
Owner

Interstitial ads (VK)

Post-move fullscreen interstitial, VK-only, client-mirrored gate:

  • Backend adsFor puts the config cooldowns (global 5m / vs_ai 30m / hint 1m) and a suppressed flag on Profile.ads. suppressed is the same no-ads / no_banner gate as the banner, resolved server-side; fail-safe suppressed on a read error.
  • Client maybeShowInterstitial self-gates on a per-kind last-shown time in localStorage, showing a VK interstitial (vkShowInterstitial) after a confirmed play or a hint only — never a pass / exchange / resign. Offline → banner only. The contour VITE_ADS_STUB swaps the "ad fired" toast for the real ad (prod always real).

Retire deprecated hint_balance / paid_account (expand-contract, code only)

The accounts.hint_balance / paid_account columns are no longer read or written by the domain (owner: unused in prod, an MVP leftover). Removed: the Account fields + scan, the dead account.SpendHint, account.GrantHints, and the admin grant-hints action (route + form + DTO). The in-game hint display now comes wholly from the payments hint benefit (HintsAvailable); banner eligibility and account merge no longer read the legacy flags. The columns stay (no migration → an image rollback is DB-safe); a later contract-PR does the DROP.

Tests

  • UI: ads.test.ts (the client-mirrored gate — absent/suppressed/offline/non-VK/per-kind cooldown/vs_ai/stub), codec ads round-trip.
  • Backend: a profile ads-config integration test (cooldowns + suppressed under no-ads / no_banner); a gateway ads transcode test (the encode is not exercised by the mock e2e).
  • Full local suite green: backend unit+integration, gateway/pkg/telegram, UI check/test/build (bundle in budget), codegen fresh, user-card render verified.

Docs

PAYMENTS §10 (+ru) interstitial nuances, the decision amends, backend README, the plan.

## Interstitial ads (VK) Post-move fullscreen interstitial, VK-only, **client-mirrored** gate: - Backend `adsFor` puts the config cooldowns (global 5m / vs_ai 30m / hint 1m) and a `suppressed` flag on `Profile.ads`. `suppressed` is the same no-ads / `no_banner` gate as the banner, resolved server-side; fail-safe suppressed on a read error. - Client `maybeShowInterstitial` self-gates on a per-kind last-shown time in `localStorage`, showing a VK interstitial (`vkShowInterstitial`) after a **confirmed play or a hint only** — never a pass / exchange / resign. Offline → banner only. The contour `VITE_ADS_STUB` swaps the "ad fired" toast for the real ad (prod always real). ## Retire deprecated hint_balance / paid_account (expand-contract, code only) The `accounts.hint_balance` / `paid_account` columns are no longer read or written by the domain (owner: unused in prod, an MVP leftover). Removed: the `Account` fields + scan, the dead `account.SpendHint`, `account.GrantHints`, and the admin **grant-hints** action (route + form + DTO). The in-game hint display now comes wholly from the payments hint benefit (`HintsAvailable`); banner eligibility and account merge no longer read the legacy flags. **The columns stay** (no migration → an image rollback is DB-safe); a later contract-PR does the `DROP`. ## Tests - UI: `ads.test.ts` (the client-mirrored gate — absent/suppressed/offline/non-VK/per-kind cooldown/vs_ai/stub), codec ads round-trip. - Backend: a profile ads-config integration test (cooldowns + suppressed under no-ads / `no_banner`); a gateway ads transcode test (the encode is not exercised by the mock e2e). - Full local suite green: backend unit+integration, gateway/pkg/telegram, UI check/test/build (bundle in budget), codegen fresh, user-card render verified. ## Docs PAYMENTS §10 (+ru) interstitial nuances, the decision amends, backend README, the plan.
developer added 1 commit 2026-07-10 00:48:43 +00:00
feat(ads): VK post-move interstitial + retire deprecated hint_balance/paid_account
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 23s
CI / ui (pull_request) Successful in 1m12s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m53s
13be7c3d9a
Interstitial video after a confirmed play or a hint, VK-only, offline
banner-only. The gate is client-mirrored: the backend puts the config
cooldowns (global 5m / vs_ai 30m / hint 1m) and a suppressed flag (the
no-ads / no_banner gate, same as the banner) on Profile.ads via adsFor;
the client self-gates on a per-kind last-shown time in localStorage, with
the VITE_ADS_STUB contour "ad fired" toast. Never fires after a pass,
exchange or resign. maybeShowInterstitial + vkShowInterstitial; the codec
and gateway transcode carry the ads block.

Also retires the deprecated accounts.hint_balance / paid_account domain
usage (expand-contract, code only — the columns stay for a later DROP so
image rollback stays DB-safe): drop the Account fields and their scan, the
dead account.SpendHint, account.GrantHints and the admin grant-hints
action; the in-game hint display now comes wholly from the payments hint
benefit. Banner eligibility and account merge no longer read the legacy
flags.

Tests: ads.test.ts (the client-mirrored gate), the codec ads round-trip,
the gateway ads transcode test, and a profile ads-config integration test
(cooldowns + suppressed under no-ads / no_banner). Docs: PAYMENTS §10 (+ru)
interstitial, the decision amends, backend README, the plan.
developer added 1 commit 2026-07-10 01:12:32 +00:00
fix(ads): fire the hint interstitial on the confirmed move, not on the hint
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 1m48s
2ce80c241d
Taking a hint fired the post-move interstitial immediately, when the hint's
preview tiles landed — interrupting the turn and reverting the board to the
rack on the ad's close while the hint stayed spent. Move the trigger to the
move confirmation: a hint applied this turn marks it (hintUsedThisTurn), and
the confirmed play fires the hint-kind interstitial (its own cooldown)
instead of the plain move one; the marker clears on any turn boundary
(applyMoveResult) so a hint-then-pass does not leak into the next move.

e2e: taking a hint fires no ad; confirming a move does.
developer added 1 commit 2026-07-10 01:36:20 +00:00
fix(ads): share one interstitial cooldown timer across kinds
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 25s
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 1m53s
b5c8a04f0b
Cooldowns were tracked per kind ({move,hint}), so a hint ad and a move ad
did not see each other: after a hinted move's ad the move timer was still
zero, and the next plain move fired an ad immediately — two ads within a
minute, under the cooldown. Track a single shared last-shown time; the kind
only selects the required gap (hint 1m, move 5m, vs_ai 30m) measured from the
last interstitial of any kind. A hint can still fire on its shorter gap
(D30's "independent" hint cooldown), but never stacks a second ad within a
cooldown.

Tests: a hint uses the shorter shared gap; a move does not stack onto a
just-shown hint ad.
owner approved these changes 2026-07-10 01:43:49 +00:00
Dismissed
developer added 1 commit 2026-07-10 01:43:59 +00:00
test(ads): lock the hint-pushed ad restarting the vs_ai gap (no over-serving)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Successful in 1m11s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m57s
7ce8101cfa
developer dismissed owner's review 2026-07-10 01:43:59 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

owner approved these changes 2026-07-10 01:44:40 +00:00
developer merged commit 00d1bd33e3 into development 2026-07-10 01:48:11 +00:00
developer deleted branch feature/ads-interstitial 2026-07-10 01:48:11 +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#229