// Pure helpers for the lobby `games` submenu. // // Both the sidebar (`lib/screens/lobby-shell.svelte`) and the bare-`lobby` // resolver (`lib/screens/lobby-screen.svelte`) need to agree on which // sub-screen is the "first available" — the one auto-expanded in the // sidebar and the redirect target when the user lands on the bare lobby // alias. Centralising the predicates here keeps the two surfaces from // drifting apart. // // The functions are intentionally pure: they take a resolved snapshot of // lobby / account state, return the visible sub-screen list (in canonical // order) and the first entry, and never touch stores or globals. Callers // derive the state from `lobbyData` / `account` and the DEV-affordances // flag. export type GamesSubScreen = | "games-active-past" | "games-recruitment" | "games-invitations" | "games-private-games"; /** * LobbyNavState is the resolved snapshot of the lobby state the * games-submenu visibility rules depend on. Build it from the live * stores at the call site; the helpers below stay pure on top. */ export interface LobbyNavState { /** Player has at least one own / past game in `lobby.my-games.list`. */ readonly hasMyGames: boolean; /** Player has at least one pending invite in `lobby.invites.list`. */ readonly hasInvitations: boolean; /** Player is on a paid tier, or the build exposes DEV affordances. */ readonly isPaidOrDev: boolean; } /** * visibleGamesSubScreens returns the games-submenu sub-screens that * are currently visible, in canonical display order: * * 1. `games-active-past` — only when the player has games. * 2. `games-recruitment` — always visible. * 3. `games-invitations` — only when the player has pending invites. * 4. `games-private-games` — only when paid-tier or DEV. * * `games-recruitment` is unconditional, so the returned array is * always non-empty. */ export function visibleGamesSubScreens( state: LobbyNavState, ): readonly GamesSubScreen[] { const out: GamesSubScreen[] = []; if (state.hasMyGames) out.push("games-active-past"); out.push("games-recruitment"); if (state.hasInvitations) out.push("games-invitations"); if (state.isPaidOrDev) out.push("games-private-games"); return out; } /** * firstVisibleGamesScreen returns the first entry from * `visibleGamesSubScreens(state)`. Since `games-recruitment` is * unconditional, the result is always defined. */ export function firstVisibleGamesScreen(state: LobbyNavState): GamesSubScreen { return visibleGamesSubScreens(state)[0]!; }