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
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:
+13
-1
@@ -980,7 +980,19 @@ edge-pause, scroll speed, and the fade-out → gap → fade-in transition) are o
|
||||
eligibility inputs (grants hints, grants/revokes `no_banner`; a future payment flow sets
|
||||
`paid_account`), the backend emits a `notify` **`banner`** sub-kind (a payload-free re-poll signal),
|
||||
and the open client re-fetches `profile.get` to show or hide the banner in place. Operator *content*
|
||||
edits take effect on the next `profile.get` (open/reconnect/foreground), not mid-session. The same
|
||||
edits take effect on the next `profile.get` (open/reconnect/foreground), not mid-session. A
|
||||
non-default campaign may also carry an optional **colour override** (background, text, link) in two
|
||||
sets — one for every theme, one for the dark theme only — plus an **urgent** flag. The colours ride
|
||||
the same block (six optional strings appended to each `BannerCampaign` on the wire — trailing,
|
||||
backward-compatible); the client resolves the cascade (dark ← dark ?? all, light ← all) and derives
|
||||
the strip's border from the background in JS (no CSS `color-mix`, for the old-WebView floor).
|
||||
**Urgent is resolved entirely server-side, with no wire field**: while any enabled, in-window urgent
|
||||
campaign exists, `computeActiveSet` returns *only* the urgent campaigns (preempting the timed set and
|
||||
the default remainder) and `bannerFor` **skips the eligibility gate** for that feed, so an urgent
|
||||
notice reaches every viewer — paid, hint-holding or `no_banner` included. There is no instant
|
||||
broadcast on an urgent toggle (the notify path is per-user); an urgent campaign appears on each
|
||||
viewer's next `profile.get`. The default campaign stays plain (no colours, never urgent), enforced by
|
||||
both the service and a DB CHECK. The same
|
||||
mechanism carries a **`profile`** sub-kind — a payload-free re-fetch signal emitted when a viewer's
|
||||
own account changed out of band (an email confirmed through the one-tap deeplink opened in another
|
||||
browser), so an open in-app session reflects it at once. The live stream is single-shot with no
|
||||
|
||||
Reference in New Issue
Block a user