Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { GetNextFiltered, GetPrevFiltered } from "./controllers/pokemonControlle

export default function App() {
const [currentId, setCurrentId] = useState(1);
const { favoritePokemons, isFavorite, toggleFavorite, isLoadingFavorites } =
const { favoriteIds, favoritePokemons, isFavorite, toggleFavorite, isLoadingFavorites } =
useFavorites();
const { filters, setFilter, resetFilters } = useFilters();

Expand All @@ -23,14 +23,14 @@ export default function App() {
});

const handlePrevious = async () => {
const prevPokemon = await GetPrevFiltered(currentId, filters);
const prevPokemon = await GetPrevFiltered(currentId, filters, favoriteIds);
if (prevPokemon) {
setCurrentId(prevPokemon.id);
}
};

const handleNext = async () => {
const nextPokemon = await GetNextFiltered(currentId, filters);
const nextPokemon = await GetNextFiltered(currentId, filters, favoriteIds);
if (nextPokemon) {
setCurrentId(nextPokemon.id);
}
Expand Down
9 changes: 7 additions & 2 deletions src/components/PokemonList/PokemonList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ export function PokemonList({

const loading = !onlyFavorites && isPending;

// Extract IDs from favoritePokemons so matchesFilter can accurately check favorite status
const favoriteIds = favoritePokemons.map((pokemon) => pokemon.id);

const pokemonList = onlyFavorites
? favoritePokemons.filter((pokemon) => matchesFilter(pokemon, filters))
? favoritePokemons.filter((pokemon) =>
matchesFilter(pokemon, filters, favoriteIds),
)
: fetchedList;

const ordered = [...pokemonList].sort((a, b) =>
Expand Down Expand Up @@ -139,4 +144,4 @@ async function buildPokemonList(
}

return pokemonList;
}
}
50 changes: 35 additions & 15 deletions src/controllers/pokemonController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ function isInRange(val: number, rangeStr?: string): boolean {
export function matchesFilter(
pokemon: PokemonData,
rules: FilterRules,
favoriteIds: number[] = [],
): boolean {

if (rules.onlyFavorites && !favoriteIds.includes(pokemon.id)) return false;
if (rules.type && rules.type.length > 0) {
const hasType = pokemon.types.some((t) =>
rules.type?.includes(t.type.name),
Expand Down Expand Up @@ -92,15 +95,16 @@ export async function GetMany(ids: number[]): Promise<PokemonData[]> {
export async function GetPrevFiltered(
currentId: number,
rules: FilterRules,
favoriteIds: number[] = [],
): Promise<PokemonData | null> {
const candidates = await candidateIds(rules);
const candidates = await candidateIds(rules, favoriteIds);

if (candidates) {
for (let i = candidates.length - 1; i >= 0; i--) {
const id = candidates[i];
if (id >= currentId) continue;
const pokemon = await fetchSafe(id);
if (pokemon && matchesFilter(pokemon, rules)) return pokemon;
if (pokemon && matchesFilter(pokemon, rules, favoriteIds)) return pokemon;
}
return null;
}
Expand All @@ -109,7 +113,7 @@ export async function GetPrevFiltered(

while (prevId >= 1) {
const pokemon = await fetchSafe(prevId);
if (pokemon && matchesFilter(pokemon, rules)) {
if (pokemon && matchesFilter(pokemon, rules, favoriteIds)) {
return pokemon;
}
prevId--;
Expand All @@ -120,14 +124,15 @@ export async function GetPrevFiltered(
export async function GetNextFiltered(
currentId: number,
rules: FilterRules,
favoriteIds: number[] = [],
): Promise<PokemonData | null> {
const candidates = await candidateIds(rules);
const candidates = await candidateIds(rules, favoriteIds);

if (candidates) {
for (const id of candidates) {
if (id <= currentId) continue;
const pokemon = await fetchSafe(id);
if (pokemon && matchesFilter(pokemon, rules)) return pokemon;
if (pokemon && matchesFilter(pokemon, rules, favoriteIds)) return pokemon;
}
return null;
}
Expand All @@ -136,29 +141,44 @@ export async function GetNextFiltered(

while (nextId <= MAX_POKEMON_ID) {
const pokemon = await fetchSafe(nextId);
if (pokemon && matchesFilter(pokemon, rules)) {
if (pokemon && matchesFilter(pokemon, rules, favoriteIds)) {
return pokemon;
}
nextId++;
}
return null;
}

// The ids worth looking at for these rules, or null when every id qualifies.
// A type rule narrows it to the members of those types, which turns a walk
// through the whole pokedex into a walk through a short list
// Returns a sorted, deduplicated list of candidate IDs filtered by type and/or favorites, or null if no candidate filters are active.
export async function candidateIds(
rules: FilterRules,
favoriteIds: number[] = [],
): Promise<number[] | null> {
if (!rules.type || rules.type.length === 0) {
const hasTypeFilter = Array.isArray(rules.type) && rules.type.length > 0;

if (!rules.onlyFavorites && !hasTypeFilter) {
return null;
}

const lists = await Promise.all(rules.type.map(idsForType));
let typeIdsSet: Set<number> | null = null;
if (hasTypeFilter) {
const lists = await Promise.all(rules.type!.map(idsForType));
// matchesFilter keeps a pokemon that has any of the chosen types, so the
// lists are merged rather than intersected
typeIdsSet = new Set(lists.flat());
}

if (rules.onlyFavorites) {
const uniqueFavorites = Array.from(new Set(favoriteIds));

// matchesFilter keeps a pokemon that has any of the chosen types, so the
// lists are merged rather than intersected
return [...new Set(lists.flat())].sort((a, b) => a - b);
const result = typeIdsSet
? uniqueFavorites.filter((id) => typeIdsSet!.has(id))
: uniqueFavorites;

return result.sort((a, b) => a - b);
}

return Array.from(typeIdsSet!).sort((a, b) => a - b);
}

// The members of a type never change during a session, so the id list is
Expand All @@ -181,4 +201,4 @@ async function idsForType(type: string): Promise<number[]> {
} catch {
return [];
}
}
}
1 change: 1 addition & 0 deletions src/controllers/useFavorites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export function useFavorites() {
);

return {
favoriteIds,
favoritePokemons,
isLoadingFavorites: favoriteIds.length > 0 && isPending,
isFavorite,
Expand Down