feat(banner): per-campaign colour overrides and urgent alerts
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m50s

Non-default campaigns gain an optional colour override (background / text /
link) in two sets — one for every theme, one for the dark theme only — and an
"urgent" flag.

- Colours ride profile.get as six trailing FlatBuffers strings on
  BannerCampaign (backward-compatible). The client resolves the cascade
  (dark <- dark ?? all, light <- all) per rendered theme and derives the strip
  border from the background in JS (no CSS color-mix, for the old Android
  WebView floor); AdBanner applies them as inline vars scoped to the strip.
- Urgent is resolved entirely server-side: while any enabled, in-window urgent
  campaign exists, computeActiveSet returns only the urgent campaigns and
  bannerFor skips the eligibility gate — so a system notice reaches every viewer
  (paid / hint-holding / no_banner included) and preempts the ordinary feed. No
  wire field; it appears on each viewer's next profile.get.
- Admin console (/_gm/banners): native colour pickers + a live light/dark
  preview of the strip, and an urgent toggle. The default campaign stays plain,
  enforced by the service and a DB CHECK.

Migration 00009 is additive (nullable colour columns + a bool default +
all-or-nothing / hex / default-plain CHECKs) — expand-contract, rollback-safe.

Docs: ARCHITECTURE §10, UI_DESIGN, FUNCTIONAL (+ru). Tests: ads unit (urgent
preempt + colour validation), codec + resolver unit, gateway transcode, and
integration (colour round-trip + urgent bypass against real Postgres).
This commit is contained in:
Ilia Denisov
2026-07-05 15:36:35 +02:00
parent ac383880b7
commit 6db9178449
32 changed files with 1297 additions and 152 deletions
+15 -4
View File
@@ -485,8 +485,19 @@ through**, regardless of their interface language. The client rotates the eligib
**fairly** — every campaign gets its weighted share each cycle, evenly spread rather than at random —
and a campaign with several messages shows them in turn.
A non-default campaign can carry its own **colours** — background, text and link — so a sponsored or
operational message stands out from the neutral strip. Colours come in two sets: one that applies on
**every theme**, and an optional second that overrides the **dark theme** only, so a campaign reads
well on both; the strip's border is derived from the background automatically. A campaign can also be
marked **urgent**: an urgent campaign is shown to **everyone** — even paid players, hint holders and
`no_banner` users — and, while it is live, it is the **only** thing the strip shows (ordinary
campaigns and the house default step aside). Urgent is for genuine system notices (maintenance,
outages), not advertising.
Operators manage all of this in the admin console at **`/_gm/banners`**: create, edit, enable/disable
and schedule campaigns, write each campaign's bilingual messages (reorder or remove them), and set
the global display **timings** (how long a message holds, the scroll of an over-long message, and the
fade-out → gap → fade-in transition between messages). The default campaign cannot be deleted and
keeps at least one message.
and schedule campaigns, write each campaign's bilingual messages (reorder or remove them), pick a
non-default campaign's optional override colours (a native colour picker with a live light-and-dark
preview of the strip) and mark it urgent, and set the global display **timings** (how long a message
holds, the scroll of an over-long message, and the fade-out → gap → fade-in transition between
messages). The default campaign cannot be deleted, keeps at least one message, and stays plain (no
colours, never urgent).