add catchemall progress bars and hide uncatchable pokemon by default
All checks were successful
Test and deploy staging / staging (push) Successful in 28s

This commit is contained in:
2026-09-23 04:59:22 +00:00
parent eca43c82d0
commit f1cd0ef849
3 changed files with 193 additions and 19 deletions

View File

@@ -0,0 +1,58 @@
import { describe, expect, it } from 'vitest';
import { availabilityValues } from '$lib/availability';
import { summarizeProgress } from './progress';
const catchable = availabilityValues.indexOf('catchable');
const transferOnly = availabilityValues.indexOf('transfer_only');
const pokemon = [
{
id: 1,
entries: [
[11, 1, catchable],
[12, 2, catchable]
]
},
{
id: 2,
entries: [
[21, 1, catchable],
[22, 2, transferOnly]
]
},
{ id: 3, entries: [[31, 2, transferOnly]] }
] as const;
describe('Catchemall progress', () => {
it('counts unique Pokémon and completed games only from selected catchable entries', () => {
expect(summarizeProgress(pokemon, new Set([1, 2, 3]), new Set([11, 12, 31]))).toEqual({
catchableEntries: 3,
caughtEntries: 2,
catchablePokemon: 2,
seenPokemon: 1,
completedPokemon: 1,
catchableGames: 2,
completedGames: 1
});
});
it('recomputes all denominators when the selected games change', () => {
expect(summarizeProgress(pokemon, new Set([2]), new Set([11, 12, 21]))).toEqual({
catchableEntries: 1,
caughtEntries: 1,
catchablePokemon: 1,
seenPokemon: 1,
completedPokemon: 1,
catchableGames: 1,
completedGames: 1
});
expect(summarizeProgress(pokemon, new Set([3]), new Set([11, 12, 21]))).toEqual({
catchableEntries: 0,
caughtEntries: 0,
catchablePokemon: 0,
seenPokemon: 0,
completedPokemon: 0,
catchableGames: 0,
completedGames: 0
});
});
});

View File

@@ -0,0 +1,53 @@
import { availabilityValues } from '$lib/availability';
type PokemonWithEntries = {
id: number;
entries: readonly (readonly [id: number, gameId: number, availability: number, note?: string])[];
};
export function summarizeProgress(
pokemon: readonly PokemonWithEntries[],
selectedGameIds: ReadonlySet<number>,
caughtIds: ReadonlySet<number>
) {
let catchableEntries = 0;
let caughtEntries = 0;
let catchablePokemon = 0;
let seenPokemon = 0;
let completedPokemon = 0;
const games = new Map<number, { total: number; caught: number }>();
for (const species of pokemon) {
let speciesEntries = 0;
let speciesCaught = 0;
for (const [entryId, gameId, availability] of species.entries) {
if (!selectedGameIds.has(gameId) || availabilityValues[availability] !== 'catchable')
continue;
speciesEntries++;
const game = games.get(gameId) ?? { total: 0, caught: 0 };
game.total++;
if (caughtIds.has(entryId)) {
speciesCaught++;
game.caught++;
}
games.set(gameId, game);
}
if (speciesEntries === 0) continue;
catchableEntries += speciesEntries;
caughtEntries += speciesCaught;
catchablePokemon++;
if (speciesCaught > 0) seenPokemon++;
if (speciesCaught === speciesEntries) completedPokemon++;
}
return {
catchableEntries,
caughtEntries,
catchablePokemon,
seenPokemon,
completedPokemon,
catchableGames: games.size,
completedGames: [...games.values()].filter((game) => game.caught === game.total).length
};
}