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:
@@ -118,6 +118,23 @@ func encodeProfile(p backendclient.ProfileResp) []byte {
|
||||
return b.FinishedBytes()
|
||||
}
|
||||
|
||||
// optString creates a FlatBuffers string for a non-empty value, or 0 to omit the
|
||||
// optional field (the client then reads it as absent).
|
||||
func optString(b *flatbuffers.Builder, s string) flatbuffers.UOffsetT {
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
return b.CreateString(s)
|
||||
}
|
||||
|
||||
// addOptString adds an optional string slot only when it was created (a 0 offset
|
||||
// leaves the field absent). add is the generated BannerCampaignAdd* setter.
|
||||
func addOptString(b *flatbuffers.Builder, add func(*flatbuffers.Builder, flatbuffers.UOffsetT), off flatbuffers.UOffsetT) {
|
||||
if off != 0 {
|
||||
add(b, off)
|
||||
}
|
||||
}
|
||||
|
||||
// encodeBanner builds a BannerInfo table from the resolved banner block and
|
||||
// returns its offset. It is bottom-up: each campaign's messages vector and table
|
||||
// are built first, then the campaigns vector, then the BannerInfo table. The
|
||||
@@ -134,9 +151,23 @@ func encodeBanner(b *flatbuffers.Builder, banner backendclient.BannerResp) flatb
|
||||
b.PrependUOffsetT(msgOffsets[j])
|
||||
}
|
||||
msgs := b.EndVector(len(msgOffsets))
|
||||
// The optional colour strings must be created before the table starts; an
|
||||
// empty colour is omitted (offset 0), so the client reads it as absent.
|
||||
obg := optString(b, c.OverrideBg)
|
||||
ofg := optString(b, c.OverrideFg)
|
||||
olink := optString(b, c.OverrideLink)
|
||||
obgD := optString(b, c.OverrideBgDark)
|
||||
ofgD := optString(b, c.OverrideFgDark)
|
||||
olinkD := optString(b, c.OverrideLinkDark)
|
||||
fb.BannerCampaignStart(b)
|
||||
fb.BannerCampaignAddWeight(b, int32(c.Weight))
|
||||
fb.BannerCampaignAddMessages(b, msgs)
|
||||
addOptString(b, fb.BannerCampaignAddOverrideBg, obg)
|
||||
addOptString(b, fb.BannerCampaignAddOverrideFg, ofg)
|
||||
addOptString(b, fb.BannerCampaignAddOverrideLink, olink)
|
||||
addOptString(b, fb.BannerCampaignAddOverrideBgDark, obgD)
|
||||
addOptString(b, fb.BannerCampaignAddOverrideFgDark, ofgD)
|
||||
addOptString(b, fb.BannerCampaignAddOverrideLinkDark, olinkD)
|
||||
campOffsets[i] = fb.BannerCampaignEnd(b)
|
||||
}
|
||||
fb.BannerInfoStartCampaignsVector(b, len(campOffsets))
|
||||
|
||||
Reference in New Issue
Block a user