diff --git a/docs/DESIGN.md b/docs/DESIGN.md index c58dd35..06cc33d 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -637,7 +637,8 @@ legend on the same baseline as "Next: …". - **Settings.** About, Changelog, Data & sources (list options first), Region & time, Appearance (interface, theme, scheme, urgency colours, default view, game order, timeline design, focus bar, checklist group), Your own events and games, Backup (tiers plus a manual "Your backups" list), and - per-game Setup / Codes / About. + per-game Setup / Codes / About. Switching to another game keeps the open tab (2026-10-20, owner): + Codes in one game opens Codes in the next. - **Redeem codes (2026-10-18, owner).** A game's Codes tab lists the codes still to use first. A code redeemed on every server the reader plays, or past its stated expiry, is greyed and struck through in a "Redeemed or expired" section below, with its Redeemed box still there to undo. A diff --git a/src/client/ReaderApp.tsx b/src/client/ReaderApp.tsx index bbc30ae..30f781a 100644 --- a/src/client/ReaderApp.tsx +++ b/src/client/ReaderApp.tsx @@ -88,7 +88,10 @@ import { type SettingsPage, } from "./components/redesign/SettingsLayout.tsx"; import { ChangelogPage } from "./components/redesign/ChangelogPage.tsx"; -import { GameSettings } from "./components/redesign/GameSettings.tsx"; +import { + GameSettings, + type GameTab, +} from "./components/redesign/GameSettings.tsx"; import { RoutinesPage } from "./components/redesign/RoutinesPage.tsx"; import { DailyPanel } from "./components/redesign/DailyPanel.tsx"; import { RoutineGroup } from "./components/redesign/RoutineGroup.tsx"; @@ -292,7 +295,7 @@ function ReaderContent({ }, [tour]); const touring = tour === "new" || tour === "returning" || tour === "replay"; const tourForced = tour === "new" || tour === "returning"; - const [gameTab, setGameTab] = useState<"setup" | "codes">("setup"); + const [gameTab, setGameTab] = useState("setup"); const [codesGame, setCodesGame] = useState( startRoute?.game, ); @@ -1021,8 +1024,8 @@ function ReaderContent({ setSettingsPage(p); }} onGame={(g) => { + // The tab stays: Codes in one game opens Codes in the next. setCodesGame(g); - setGameTab("setup"); setSettingsPage("games"); }} > @@ -1041,8 +1044,9 @@ function ReaderContent({ /> ) : settingsPage === "games" && codesGame ? ( Promise; +export const GAME_TABS = ["setup", "codes", "about"] as const; +export type GameTab = (typeof GAME_TABS)[number]; const hostOf = (url: string) => { try { return new URL(url).hostname.replace(/^www\./, ""); @@ -42,14 +44,17 @@ export function GameSettings({ onAction, codes = null, initialGame, - initialTab = "setup", + tab: heldTab, + onTab, }: { state: ReaderState; sources: SourceHealth[]; onAction: ActionHandler; codes?: CodesFeed | null; initialGame?: string | undefined; - initialTab?: "setup" | "codes"; + // When the parent holds the tab, switching games keeps the reader on it. + tab?: GameTab; + onTab?: (tab: GameTab) => void; }) { const renderedMeta = useGameMeta(); const games = orderGames( @@ -72,7 +77,9 @@ export function GameSettings({ games[0] ?? "", ); - const [tab, setTab] = useState(initialTab); + const [ownTab, setOwnTab] = useState("setup"); + const tab = heldTab ?? ownTab, + setTab = onTab ?? setOwnTab; const [trackingAccount, setTrackingAccount] = useState(null); const [addingServer, setAddingServer] = useState(false); const [server, setServer] = useState("asia"); @@ -138,7 +145,7 @@ export function GameSettings({ ) : null}
- {["setup", "codes", "about"].map((id) => ( + {GAME_TABS.map((id) => (