Files
website/src/lib/projects.ts

253 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Real projects (tools and experiments). Personal data trackers live in `trackers`.
export const projects = [
{
name: 'vash esports',
subtitle: 'the best way to play osu!',
href: 'https://esports.vash.software',
external: true
},
{
name: 'osu! map finder',
subtitle: 'find osu! maps for Spotify songs/albums/playlists',
href: '/projects/spotify-beatmap-finder',
external: false
},
{
name: 'derby lens (warning: AI slop)',
subtitle: 'estimate odds for horse betting',
href: '/projects/derby',
external: false
},
{
name: 'world opinions (warning: AI slop)',
subtitle: 'ideologies, values and biases across countries',
href: '/projects/world-opinions',
external: false
},
{
name: 'language learner (warning: AI slop)',
subtitle: 'Dutch, Korean and English, from zero to fluent',
href: '/projects/language-learner',
external: false
}
] as const;
type Tracker = {
name: string;
subtitle: string;
href: string;
external: boolean;
/** Sub-trackers linked directly from this item (header flyout, tile chips). */
children?: readonly Tracker[];
/** Icon key in #lib/tracker-icons when it differs from `name` (games). */
icon?: string;
/** Shown on group tiles before the "more" chip (main and actively played games). */
featured?: boolean;
/** Owner-only (needs login): every listing shows a small lock after the name. */
locked?: boolean;
};
// The spotting trackers: pokedexes of things I can spot in the wild. Grouped under one
// "spotting" item in the header, home page and /trackers; each keeps its own URL.
export const spottingTrackers: readonly Tracker[] = [
{
name: 'cars',
subtitle: 'a car pokedex: every brand and model, with Dutch and US data',
href: '/trackers/cars',
external: false
},
{
name: 'trains',
subtitle:
'a train pokedex: steam, diesel and electric locos, high-speed trains, multiple units, metros and trams',
href: '/trackers/trains',
external: false
},
{
name: 'planes',
subtitle: 'a plane pokedex: airliners, helicopters, bizjets, light aircraft and warbirds',
href: '/trackers/planes',
external: false
},
{
name: 'wildlife',
subtitle:
'a wild animal pokedex: everything you can spot in the Netherlands, plus famous animals worldwide',
href: '/trackers/wildlife',
external: false
}
];
// Games I play (plus speedcubing), grouped under "games" like spotting: every game has its
// 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).
const game = (slug: string, name: string, subtitle: string, featured = false): Tracker => ({
name,
subtitle,
href: `/trackers/games/${slug}`,
external: false,
icon: `game-${slug}`,
...(featured ? { featured } : {})
});
export const gameTrackers: readonly Tracker[] = [
game('osu', 'osu!', 'rhythm game, my main focus right now', true),
game('cs2', 'cs2', 'Counter-Strike 2 (CS:GO)', true),
game('pubg', 'pubg', 'PUBG: Battlegrounds', true),
game('r6', 'r6 siege', 'Tom Clancy’s Rainbow Six Siege', true),
game('tarkov', 'tarkov', 'Escape from Tarkov', true),
game('bo2', 'bo2', 'Call of Duty: Black Ops II', true),
game('valorant', 'valorant', 'Riot’s tactical shooter', false),
game('lol', 'league', 'League of Legends', false),
game('overwatch', 'overwatch 2', 'Blizzard’s hero shooter', false),
game('minecraft', 'minecraft', 'blocks, mining and building', false),
game('factorio', 'factorio', 'factory building and automation', false),
game('ksp', 'ksp', 'Kerbal Space Program', false),
game('smash', 'smash bros', 'Super Smash Bros.', false),
game('cubing', 'speedcubing', 'Rubik’s cube solving, not a video game', false),
game('pokemon', 'pokémon', 'catching them all across the games', false),
game('sot', 'sea of thieves', 'pirate sandbox', false)
];
// 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
// in the sitemap; their routes 404 for everyone else. Private trackers, and listed ones that
// still need a login (`locked`, e.g. money), get a lock icon next to their name everywhere.
const allTrackers: readonly (Tracker & { private: boolean })[] = [
{
name: 'tasks',
subtitle: 'to-dos, goals and my TickTick history',
href: '/trackers/tasks',
external: false,
private: true
},
{
name: 'money',
subtitle: 'a manual ledger for accounts, bills and utilities',
href: '/trackers/money',
external: false,
locked: true,
private: false
},
{
name: 'reading',
subtitle: 'books, scientific papers and articles I am reading, have read and want to read',
href: '/trackers/books',
external: false,
private: false
},
{
name: 'spotting',
subtitle: "cars, trains, planes & wildlife I've spotted",
href: '/trackers/spotting',
external: false,
children: spottingTrackers,
private: false
},
{
name: 'music',
subtitle: 'everything I listen to: top artists, tracks, albums and streaks',
href: '/trackers/music',
external: false,
private: false
},
{
name: 'pc',
subtitle: 'my PC setup: every part, my devices, the next build and mouse sensitivities',
href: '/trackers/pc',
external: false,
private: false
},
{
name: 'health',
subtitle: 'body, workouts, sleep, meds and vaccinations',
href: '/trackers/health',
external: false,
private: true
},
{
name: 'groceries',
subtitle: 'supermarket products, favorites and prices per store',
href: '/trackers/groceries',
external: false,
private: true
},
{
name: 'houses',
subtitle: 'rentals around Gouda, applications and alerts',
href: '/trackers/houses',
external: false,
private: true
},
{
name: 'holders',
subtitle: 'who has my address, email, phone and bank details, and a moving checklist',
href: '/trackers/data-holders',
external: false,
private: true
},
{
name: 'energy',
subtitle: 'Dutch electricity mix, utilization and gas storage',
href: '/projects/energy',
external: false,
private: false
},
{
name: 'games',
subtitle: 'every pokemon in every game, and call of duty camo mastery',
href: '/projects/game-tracker',
external: false,
children: gameTrackers,
private: false
},
{
name: 'skins',
subtitle: 'Counter-Strike skin market prices across time',
href: '/projects/cs-prices',
external: false,
private: false
},
{
name: 'pets',
subtitle: 'animaldex: add neighborhood pets to the world map',
href: '/projects/animaldex',
external: false,
private: false
},
{
name: 'lists',
subtitle: 'books, games, places and other things worth keeping',
href: '/lists',
external: false,
private: false
}
];
const strip = ({
name,
subtitle,
href,
external,
children,
locked,
private: hidden
}: Tracker & { private: boolean }): Tracker => ({
name,
subtitle,
href,
external,
...(children ? { children } : {}),
...(locked || hidden ? { locked: true } : {})
});
/** Public trackers (sitemap, logged-out navigation). */
export const trackers: readonly Tracker[] = allTrackers.filter((t) => !t.private).map(strip);
/** Owner-only trackers. */
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;
}