Promote development → master: banner tip set + banner/push language fix #114
@@ -158,7 +158,12 @@ arrive from a platform rather than completing a mandatory registration).
|
||||
rendered in the recipient's **interface language** (`preferred_language`, en/ru), not in
|
||||
any bot-scoped language, and the friend-invite **share link** (and its caption) point at
|
||||
that one bot. First Telegram contact seeds the new account's `preferred_language` from the
|
||||
launch `language_code` (§4); the interface language is otherwise edited in Settings.
|
||||
launch `language_code` (§4), but the **interface language follows the device** — the system
|
||||
guess, or an explicit Settings choice saved locally — and the bot never dictates the UI.
|
||||
`preferred_language` is then **reconciled to the active interface locale on every session
|
||||
adopt** (not only on a Settings change; a no-op for guests and when already equal), so the
|
||||
server-rendered language surfaces — this push and the ad banner — always match the UI rather
|
||||
than stranding a user who never opened Settings on the creation-time seed.
|
||||
- **Variant preferences (New Game gating).** Which variants a player may be matched into is a
|
||||
per-user **profile** setting — `variant_preferences`, a set of `engine.Variant` labels
|
||||
(`scrabble_en`, `scrabble_ru`, `erudit_ru`) edited on the Settings/Profile screen. New
|
||||
@@ -640,7 +645,7 @@ in either direction (the enqueue excludes the caller's `BlockedWith` set);
|
||||
**floats games with any unread entry to the top** of the your-turn and opponent-turn
|
||||
sections (the finished section keeps its activity order). On each clear the publish-to-read
|
||||
latency is recorded; the read time itself is not retained.
|
||||
- **Profile**: `preferred_language` (en/ru, edited in Settings), display name, email
|
||||
- **Profile**: `preferred_language` (en/ru; tracks the interface language — §4), display name, email
|
||||
(confirm-code binding, see §4), **timezone**, the daily **away window**, the
|
||||
**variant preferences** (`variant_preferences`, the matchable-variant set that gates New
|
||||
Game — §3, defaulting to Erudit only, at least one enforced) and the
|
||||
|
||||
+17
-18
@@ -68,7 +68,6 @@ export const app = $state<{
|
||||
locale: Locale;
|
||||
reduceMotion: boolean;
|
||||
boardLabels: BoardLabelMode;
|
||||
localeLocked: boolean;
|
||||
/** Pending incoming friend requests, for the lobby ⚙️ badge and the Settings Friends tab. */
|
||||
notifications: number;
|
||||
/** Per-game flag: the player has at least one unread chat entry (message or nudge) in that
|
||||
@@ -109,7 +108,6 @@ export const app = $state<{
|
||||
locale: 'en',
|
||||
reduceMotion: false,
|
||||
boardLabels: 'beginner',
|
||||
localeLocked: false,
|
||||
notifications: 0,
|
||||
chatUnread: {},
|
||||
messageUnread: {},
|
||||
@@ -451,18 +449,20 @@ async function adoptSession(s: Session): Promise<void> {
|
||||
await saveSession(s);
|
||||
try {
|
||||
app.profile = await gateway.profileGet();
|
||||
// The live interface language follows the device — the explicit local choice (locked, saved
|
||||
// in prefs) or the system guess made at bootstrap — and is no longer overridden from the
|
||||
// account here. preferred_language stays the user's saved choice (written from Settings,
|
||||
// and used for out-of-app push routing), but the Telegram bot a user signs in through must
|
||||
// not dictate the UI: a ru-bot launch on an English system stays English.
|
||||
// The live interface language follows the device — the explicit local choice (saved in
|
||||
// prefs) or the system guess made at bootstrap — and is no longer overridden from the
|
||||
// account here: the Telegram bot a user signs in through must not dictate the UI, so a
|
||||
// ru-bot launch on an English system stays English.
|
||||
//
|
||||
// But the banner and out-of-app push routing ARE resolved from preferred_language, so an
|
||||
// explicit device choice the account has not recorded yet (picked while a guest, or
|
||||
// differing from the Telegram system-language seed) would otherwise leave them in the wrong
|
||||
// language until the next Settings change. Reconcile the account to the saved local choice
|
||||
// here; persistLanguageToServer no-ops for guests and when already equal.
|
||||
if (app.localeLocked) void persistLanguageToServer(app.locale);
|
||||
// The banner and out-of-app push are resolved server-side from preferred_language, so it
|
||||
// must track whatever language the UI actually shows — the explicit choice AND the system
|
||||
// guess. Reconcile it to the active locale on every adopt, not only after an explicit
|
||||
// Settings choice: a user who never opened Settings would otherwise be stuck on the
|
||||
// creation-time seed — e.g. an English banner under a Russian UI. This keeps every
|
||||
// server-rendered, language-dependent surface (banner, out-of-app push) aligned with the
|
||||
// interface, not just one. persistLanguageToServer self-gates (a no-op for guests and when
|
||||
// already equal), so there is no write in the steady state.
|
||||
void persistLanguageToServer(app.locale);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
}
|
||||
@@ -483,9 +483,10 @@ export async function applyLinkResult(r: LinkResult): Promise<void> {
|
||||
return;
|
||||
}
|
||||
app.profile = await gateway.profileGet();
|
||||
// A guest who chose a language and then linked in place now has a durable account: push the
|
||||
// saved choice so the banner + push routing follow it (see adoptSession).
|
||||
if (app.localeLocked) void persistLanguageToServer(app.locale);
|
||||
// A guest who linked in place now has a durable account: push the active interface language
|
||||
// so the banner + push routing follow it (see adoptSession — reconciled regardless of an
|
||||
// explicit Settings choice).
|
||||
void persistLanguageToServer(app.locale);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,7 +539,6 @@ export async function bootstrap(): Promise<void> {
|
||||
applyReduceMotion(app.reduceMotion);
|
||||
if (prefs.locale) {
|
||||
app.locale = prefs.locale;
|
||||
app.localeLocked = true;
|
||||
setLocale(prefs.locale);
|
||||
} else {
|
||||
const guess = localeFrom(typeof navigator !== 'undefined' ? navigator.language : 'en');
|
||||
@@ -754,7 +754,6 @@ export function setTheme(theme: ThemePref): void {
|
||||
|
||||
export function setLocalePref(locale: Locale): void {
|
||||
app.locale = locale;
|
||||
app.localeLocked = true;
|
||||
setLocale(locale);
|
||||
persistPrefs();
|
||||
void persistLanguageToServer(locale);
|
||||
|
||||
Reference in New Issue
Block a user