feat(profile): advertise per-variant dictionary versions for offline preload
The Profile now carries dict_versions (game variant -> current dictionary version), populated from the dictionary registry at the profileResponse choke point, so an installed PWA can preload the matching dawg per enabled variant off the existing cold-start profile request instead of adding a round-trip for a rare feature. Wire path: FBS DictVersion table + Profile.dict_versions (additive, backward-compatible trailing field) -> backend dto/registry -> gateway ProfileResp + FBS encoder -> client codec decode into a per-variant map on model.Profile. Empty in a degenerate no-dictionary deployment; the mock serves v1.3.0 for all three variants. Codec decode covered by a bite-tested round-trip unit test.
This commit is contained in:
@@ -42,6 +42,15 @@ type ProfileResp struct {
|
||||
Email string `json:"email"`
|
||||
TelegramLinked bool `json:"telegram_linked"`
|
||||
VkLinked bool `json:"vk_linked"`
|
||||
// DictVersions is the current dictionary version per game variant, forwarded verbatim
|
||||
// into the Profile payload so an offline-capable client preloads the matching dawg.
|
||||
DictVersions []DictVersion `json:"dict_versions,omitempty"`
|
||||
}
|
||||
|
||||
// DictVersion pairs a game variant's stable label with its current dictionary version.
|
||||
type DictVersion struct {
|
||||
Variant string `json:"variant"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// BannerResp is the advertising-banner block of an eligible viewer's profile: the
|
||||
|
||||
@@ -95,6 +95,7 @@ func encodeProfile(p backendclient.ProfileResp) []byte {
|
||||
banner = encodeBanner(b, *p.Banner)
|
||||
}
|
||||
prefs := buildStringVector(b, p.VariantPreferences, fb.ProfileStartVariantPreferencesVector)
|
||||
dictVersions := encodeDictVersions(b, p.DictVersions)
|
||||
fb.ProfileStart(b)
|
||||
fb.ProfileAddUserId(b, uid)
|
||||
fb.ProfileAddDisplayName(b, name)
|
||||
@@ -111,6 +112,9 @@ func encodeProfile(p backendclient.ProfileResp) []byte {
|
||||
fb.ProfileAddEmail(b, email)
|
||||
fb.ProfileAddTelegramLinked(b, p.TelegramLinked)
|
||||
fb.ProfileAddVkLinked(b, p.VkLinked)
|
||||
if dictVersions != 0 {
|
||||
fb.ProfileAddDictVersions(b, dictVersions)
|
||||
}
|
||||
if p.Banner != nil {
|
||||
fb.ProfileAddBanner(b, banner)
|
||||
}
|
||||
@@ -118,6 +122,31 @@ func encodeProfile(p backendclient.ProfileResp) []byte {
|
||||
return b.FinishedBytes()
|
||||
}
|
||||
|
||||
// encodeDictVersions builds the Profile's dict_versions vector — one DictVersion table per
|
||||
// variant/version pair — and returns its offset, or 0 when there are none (the field is then
|
||||
// omitted). Every child table and its strings are created before the vector is opened, per the
|
||||
// FlatBuffers rule against nesting a table under one still being built; the caller invokes it
|
||||
// before ProfileStart for the same reason.
|
||||
func encodeDictVersions(b *flatbuffers.Builder, dvs []backendclient.DictVersion) flatbuffers.UOffsetT {
|
||||
if len(dvs) == 0 {
|
||||
return 0
|
||||
}
|
||||
offsets := make([]flatbuffers.UOffsetT, len(dvs))
|
||||
for i, dv := range dvs {
|
||||
variant := b.CreateString(dv.Variant)
|
||||
version := b.CreateString(dv.Version)
|
||||
fb.DictVersionStart(b)
|
||||
fb.DictVersionAddVariant(b, variant)
|
||||
fb.DictVersionAddVersion(b, version)
|
||||
offsets[i] = fb.DictVersionEnd(b)
|
||||
}
|
||||
fb.ProfileStartDictVersionsVector(b, len(offsets))
|
||||
for i := len(offsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(offsets[i])
|
||||
}
|
||||
return b.EndVector(len(offsets))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
Reference in New Issue
Block a user