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:
Ilia Denisov
2026-07-06 10:12:58 +02:00
parent 8349e222fc
commit a692024b4e
14 changed files with 321 additions and 2 deletions
+16
View File
@@ -364,9 +364,25 @@ export function decodeProfile(buf: Uint8Array): Profile {
email: s(p.email()),
telegramLinked: p.telegramLinked(),
vkLinked: p.vkLinked(),
dictVersions: decodeDictVersions(p),
};
}
// decodeDictVersions reads the profile's per-variant current dictionary versions into a
// variant-keyed map, so the offline preloader can look up the version for each enabled
// variant. An entry with an unknown variant or empty version is skipped.
function decodeDictVersions(p: fb.Profile): Partial<Record<Variant, string>> {
const out: Partial<Record<Variant, string>> = {};
for (let i = 0; i < p.dictVersionsLength(); i++) {
const dv = p.dictVersions(i);
if (!dv) continue;
const variant = s(dv.variant()) as Variant;
const version = s(dv.version());
if (variant && version) out[variant] = version;
}
return out;
}
// decodeVariantPreferences reads the Profile.variant_preferences vector (the variants
// the player enabled in Settings).
function decodeVariantPreferences(p: fb.Profile): Variant[] {