fix(ui,i18n): E30 review round 3 — the stored language follows the screen, a fresh text starts collapsed, Retry waits again
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { useLayoutEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { api, getActiveAccount, setActiveAccount } from "../lib/api";
|
||||
import { log } from "../lib/log";
|
||||
import { saveLanguage } from "../lib/saveLanguage";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import i18n, { isSupported, setLanguage } from "../i18n";
|
||||
import { LoadingState, StateMessage } from "./ui/QueryState";
|
||||
@@ -38,6 +40,7 @@ export function BootScreen() {
|
||||
|
||||
export default function BootGate({ children }: { children: ReactNode }) {
|
||||
const { t } = useTranslation();
|
||||
const qc = useQueryClient();
|
||||
// First-run install gate: a fresh instance reports configured=false and runs in setup mode, where
|
||||
// every other route is locked — so `me` is held back until the instance is known to be set up
|
||||
// (otherwise it would 503 against the setup-mode lock).
|
||||
@@ -81,7 +84,10 @@ export default function BootGate({ children }: { children: ReactNode }) {
|
||||
// not is corrected here — otherwise the tab would run as the default account on the server while
|
||||
// reading and writing the gone account's local state.
|
||||
// • Adopt the account's language, which follows the account across devices — the local copy only
|
||||
// knows what this browser last showed.
|
||||
// knows what this browser last showed. An account with none stored yet (the setup wizard's admin,
|
||||
// an account older than the stamp sign-up gives) stores the language it is shown in: the server
|
||||
// orders its lists and writes its mail in the stored one, else English, and a Hungarian screen
|
||||
// over English-ordered lists breaks "A→Z in the UI language's alphabet".
|
||||
const [readyFor, setReadyFor] = useState<number | null | undefined>(undefined);
|
||||
// `me` is read for the identity it produced, so it rides a ref: a background refetch of the same
|
||||
// account must not re-run the adoption over a language the user has just changed. That rule used
|
||||
@@ -104,11 +110,21 @@ export default function BootGate({ children }: { children: ReactNode }) {
|
||||
const account = meRef.current;
|
||||
if (account && getActiveAccount() !== account.id) setActiveAccount(account.id);
|
||||
const lang = account?.preferences?.language;
|
||||
// eslint-disable-next-line local/locale-format -- a one-shot read in an effect, compared before adopting the account's language; nothing renders it, so there is no memoized value to go stale.
|
||||
if (typeof lang === "string" && isSupported(lang) && lang !== i18n.language) setLanguage(lang);
|
||||
if (typeof lang === "string" && isSupported(lang)) {
|
||||
// eslint-disable-next-line local/locale-format -- a one-shot read in an effect, compared before adopting the account's language; nothing renders it, so there is no memoized value to go stale.
|
||||
if (lang !== i18n.language) setLanguage(lang);
|
||||
} else if (account) {
|
||||
// eslint-disable-next-line local/locale-format -- a one-shot read of the language the app is about to be shown in, stored once; nothing renders it.
|
||||
const shown = i18n.language;
|
||||
saveLanguage(qc, shown).catch((e: unknown) =>
|
||||
log.warn("boot.language_store_failed", "the shown language could not be stored", {
|
||||
error: String(e),
|
||||
}),
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- the adoption latch. It cannot be derived: the whole point is that the tree mounts only AFTER the pin and the language have been applied, and both are writes to things outside React (sessionStorage, i18n). A layout effect so the frame that first shows the app already has them.
|
||||
setReadyFor(identity);
|
||||
}, [identity, readyFor]);
|
||||
}, [identity, readyFor, qc]);
|
||||
|
||||
// No answer at all (a network or 5xx failure on the very first setup or account probe — past the
|
||||
// one retry `req` already makes): say so, with a retry. An unanswered setup probe is NOT "signed
|
||||
|
||||
Reference in New Issue
Block a user