games: active vs non-active games (games.library.active, owner toggles on the dashboard and each game ⋯ menu; nothing deleted). Header flyout and mobile menu list every game split into active / non-active; /trackers and home tiles show active chips and open both sections on hover; /start shows active with a non-active toggle. Games dashboard moved from /projects/game-tracker to /trackers/games (dashboard of active games only; old URL and /credits 301); deep trackers stay put
All checks were successful
Deploy production / deploy (push) Successful in 1m13s
All checks were successful
Deploy production / deploy (push) Successful in 1m13s
This commit is contained in:
@@ -43,6 +43,8 @@ type Tracker = {
|
||||
icon?: string;
|
||||
/** Shown on group tiles before the "more" chip (main and actively played games). */
|
||||
featured?: boolean;
|
||||
/** A game I don't play now (games.library.active = false): listed under "non-active". */
|
||||
inactive?: boolean;
|
||||
/** Owner-only (needs login): every listing shows a small lock after the name. */
|
||||
locked?: boolean;
|
||||
};
|
||||
@@ -82,6 +84,9 @@ export const spottingTrackers: readonly Tracker[] = [
|
||||
// own page at /trackers/games/<slug>, one click away from the header flyout and the chips.
|
||||
// `featured` games (my main game and the ones I actively play) show first on tiles; the
|
||||
// rest sit behind a "more" chip. Data per game lives in games.library (same slugs).
|
||||
// Which games are active (the ones I play now) is my pick in games.library.active, toggled on
|
||||
// /trackers/games; sortedGames() puts them first, makes them the tile chips and flags the
|
||||
// rest `inactive`, so the header, /start and the tiles show "active" and "non-active" parts.
|
||||
// Call of Duty is one entry per title and Pokémon one per generation (remakes such as
|
||||
// HeartGold & SoulSilver and spin-offs separate); full names, no abbreviations. The old
|
||||
// /trackers/games/bo2 and /trackers/games/pokemon URLs redirect (see [slug]/+page.server.ts).
|
||||
@@ -185,6 +190,66 @@ export const gameTrackers: readonly Tracker[] = [
|
||||
game('pokemon-legends-z-a', 'pokémon legends: z-a', 'generation 9, Nintendo Switch')
|
||||
];
|
||||
|
||||
/** games.library slug of a game tracker entry (`/trackers/games/<slug>`). */
|
||||
export const gameSlug = (t: { href: string }) => t.href.replace('/trackers/games/', '');
|
||||
|
||||
/** Every game, active ones first (games.library.active, my pick), non-active ones flagged
|
||||
* `inactive`; `inactiveGames` comes from the root layout data. */
|
||||
export function sortedGames(inactiveGames: readonly string[] = []): readonly Tracker[] {
|
||||
if (!inactiveGames.length) return gameTrackers;
|
||||
const off = new Set(inactiveGames);
|
||||
const flagged = gameTrackers.map((g) =>
|
||||
off.has(gameSlug(g)) ? { ...g, inactive: true, featured: false } : { ...g, featured: true }
|
||||
);
|
||||
return [...flagged.filter((g) => !g.inactive), ...flagged.filter((g) => g.inactive)];
|
||||
}
|
||||
/** Only the games I play now. */
|
||||
export const activeGames = (inactiveGames: readonly string[] = []) =>
|
||||
sortedGames(inactiveGames).filter((g) => !g.inactive);
|
||||
|
||||
// Which games.library entry each Pokémon journal game (ref.pokemon_games.slug) belongs to,
|
||||
// so hiding a generation also hides its games on the hub. Unlisted games always show.
|
||||
const POKEMON_ENTRY_GAMES: Record<string, readonly string[]> = {
|
||||
'pokemon-red-blue-yellow': ['red', 'blue', 'yellow', 'red-japan', 'blue-japan', 'green-japan'],
|
||||
'pokemon-gold-silver-crystal': ['gold', 'silver', 'crystal'],
|
||||
'pokemon-ruby-sapphire-emerald': ['ruby', 'sapphire', 'emerald'],
|
||||
'pokemon-colosseum': ['colosseum'],
|
||||
'pokemon-xd': ['xd'],
|
||||
'pokemon-firered-leafgreen': ['firered', 'leafgreen'],
|
||||
'pokemon-diamond-pearl-platinum': ['diamond', 'pearl', 'platinum'],
|
||||
'pokemon-heartgold-soulsilver': ['heartgold', 'soulsilver'],
|
||||
'pokemon-black-white': ['black', 'white', 'black-2', 'white-2'],
|
||||
'pokemon-x-y': ['x', 'y'],
|
||||
'pokemon-omega-ruby-alpha-sapphire': ['omega-ruby', 'alpha-sapphire'],
|
||||
'pokemon-sun-moon': ['sun', 'moon', 'ultra-sun', 'ultra-moon'],
|
||||
'pokemon-lets-go': ['lets-go-pikachu', 'lets-go-eevee'],
|
||||
'pokemon-sword-shield': [
|
||||
'sword',
|
||||
'shield',
|
||||
'the-isle-of-armor-sword',
|
||||
'the-isle-of-armor-shield',
|
||||
'the-crown-tundra-sword',
|
||||
'the-crown-tundra-shield'
|
||||
],
|
||||
'pokemon-brilliant-diamond-shining-pearl': ['brilliant-diamond', 'shining-pearl'],
|
||||
'pokemon-legends-arceus': ['legends-arceus'],
|
||||
'pokemon-scarlet-violet': [
|
||||
'scarlet',
|
||||
'violet',
|
||||
'the-teal-mask-scarlet',
|
||||
'the-teal-mask-violet',
|
||||
'the-indigo-disk-scarlet',
|
||||
'the-indigo-disk-violet'
|
||||
],
|
||||
'pokemon-legends-z-a': ['legends-za', 'mega-dimension']
|
||||
};
|
||||
const POKEMON_GAME_ENTRY = new Map(
|
||||
Object.entries(POKEMON_ENTRY_GAMES).flatMap(([entry, games]) => games.map((g) => [g, entry]))
|
||||
);
|
||||
/** games.library entry for a Pokémon journal game slug, if it has one. */
|
||||
export const pokemonEntryFor = (slug: string | null | undefined) =>
|
||||
slug ? POKEMON_GAME_ENTRY.get(slug) : undefined;
|
||||
|
||||
// Trackers, in display order. Names are short single nouns. Most keep their existing
|
||||
// /projects/... URLs (no moves, so no redirects needed). `private` trackers are shown in the
|
||||
// header, home page and /trackers only to the logged-in owner (layout data `isOwner`), never
|
||||
@@ -273,7 +338,7 @@ const allTrackers: readonly (Tracker & { private: boolean })[] = [
|
||||
{
|
||||
name: 'games',
|
||||
subtitle: 'every pokemon in every game, and call of duty camo mastery',
|
||||
href: '/projects/game-tracker',
|
||||
href: '/trackers/games',
|
||||
external: false,
|
||||
children: gameTrackers,
|
||||
private: false
|
||||
@@ -323,6 +388,14 @@ export const trackers: readonly Tracker[] = allTrackers.filter((t) => !t.private
|
||||
export const privateTrackers: readonly Tracker[] = allTrackers.filter((t) => t.private).map(strip);
|
||||
const ownerTrackers: readonly Tracker[] = allTrackers.map(strip);
|
||||
|
||||
export function visibleTrackers(isOwner: boolean): readonly Tracker[] {
|
||||
return isOwner ? ownerTrackers : trackers;
|
||||
/** Trackers for the header, home, /trackers, /start and error pages; with `inactiveGames` (root
|
||||
* layout data) the games group lists active games first and flags the rest `inactive`. */
|
||||
export function visibleTrackers(
|
||||
isOwner: boolean,
|
||||
inactiveGames: readonly string[] = []
|
||||
): readonly Tracker[] {
|
||||
const list = isOwner ? ownerTrackers : trackers;
|
||||
if (!inactiveGames.length) return list;
|
||||
const games = sortedGames(inactiveGames);
|
||||
return list.map((t) => (t.children === gameTrackers ? { ...t, children: games } : t));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user