206 lines
5.9 KiB
TypeScript
206 lines
5.9 KiB
TypeScript
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
|
import type { Slice, PayloadAction } from '@reduxjs/toolkit';
|
|
|
|
import { pokeRestApi } from 'app/services/pokeRestApi';
|
|
import { EvolutionSpeciesProps } from 'components/EvolutionSpecies';
|
|
import { Stat } from 'components/InfoDialogComponent';
|
|
import {
|
|
EvolutionChain,
|
|
EvolutionChainResponseData,
|
|
FlavorTextEntry,
|
|
generaItem,
|
|
PokemonResponseData,
|
|
PokemonSpeciesResponseData,
|
|
} from 'types/api';
|
|
|
|
export type InfoDialogDetails = {
|
|
id: number;
|
|
name: string;
|
|
genera: string;
|
|
image: string;
|
|
types: string[];
|
|
height: number;
|
|
weight: number;
|
|
genderRatio: number;
|
|
description: string;
|
|
abilities: string[];
|
|
stats: Stat[];
|
|
evolutionChain: EvolutionSpeciesProps[];
|
|
};
|
|
|
|
const initialInfoDialogDetails: InfoDialogDetails = {
|
|
id: 0,
|
|
name: '',
|
|
genera: '',
|
|
image: '',
|
|
types: [],
|
|
height: 0,
|
|
weight: 0,
|
|
genderRatio: 0,
|
|
description: '',
|
|
abilities: [],
|
|
stats: [],
|
|
evolutionChain: [],
|
|
};
|
|
|
|
export type InfoDialogStateProps = {
|
|
isOpen: boolean;
|
|
InfoDialogDetails: InfoDialogDetails;
|
|
};
|
|
|
|
export const initialState: InfoDialogStateProps = {
|
|
isOpen: false,
|
|
InfoDialogDetails: initialInfoDialogDetails,
|
|
};
|
|
|
|
// create a function named constructNameOfEvolutionChainFromResponse to
|
|
// iterate though EvolutionChainResponseData recursively and add name to result
|
|
export const constructEvolutionChainFromResponse = (
|
|
response: EvolutionChainResponseData,
|
|
) => {
|
|
const result: string[] = [];
|
|
const addEvolutionSpeciesProps = (evo: EvolutionChain) => {
|
|
result.push(evo.species.name);
|
|
evo.evolves_to.forEach(evo => {
|
|
addEvolutionSpeciesProps(evo);
|
|
});
|
|
};
|
|
|
|
addEvolutionSpeciesProps(response.chain);
|
|
return result;
|
|
};
|
|
|
|
export const findEnglishGenera = (generaItem: generaItem[]) =>
|
|
generaItem.find(genera => genera.language.name === 'en');
|
|
|
|
export const findFirstEnglishFlavorText = (
|
|
flavorTextEntries: FlavorTextEntry[],
|
|
): string => {
|
|
const englishFlavorTextItems = flavorTextEntries.filter(
|
|
flavorText => flavorText.language.name === 'en',
|
|
);
|
|
if (englishFlavorTextItems) {
|
|
return englishFlavorTextItems[0].flavor_text;
|
|
} else {
|
|
return 'Error';
|
|
}
|
|
};
|
|
|
|
export const constructPokemonInfoFromResponses = (
|
|
fetchedPokemon: PokemonResponseData,
|
|
fetchedPokemonSpecies: PokemonSpeciesResponseData,
|
|
evolutionChain: EvolutionSpeciesProps[],
|
|
): InfoDialogDetails => {
|
|
return {
|
|
id: fetchedPokemon.id,
|
|
name: fetchedPokemon.name,
|
|
genera: findEnglishGenera(fetchedPokemonSpecies.genera)?.genus || '',
|
|
image: fetchedPokemon.sprites.other.dream_world.front_default,
|
|
types: fetchedPokemon.types.map(type => type.type.name),
|
|
height: fetchedPokemon.height,
|
|
weight: fetchedPokemon.weight,
|
|
genderRatio: fetchedPokemonSpecies.gender_rate,
|
|
description: findFirstEnglishFlavorText(
|
|
fetchedPokemonSpecies.flavor_text_entries,
|
|
),
|
|
abilities: fetchedPokemon.abilities.map(ability => ability.ability.name),
|
|
stats: fetchedPokemon.stats.map(stat => ({
|
|
stat__name: stat.stat.name,
|
|
stat__value: stat.base_stat,
|
|
})),
|
|
evolutionChain: evolutionChain,
|
|
};
|
|
};
|
|
|
|
export const fetchSelectedPokemonInfo = createAsyncThunk(
|
|
'infoDialog/fetchSelectedPokemonInfo',
|
|
async (pokemonIdOrName: number | string, thunkAPI) => {
|
|
const dispatch = thunkAPI.dispatch;
|
|
|
|
try {
|
|
const selectedPokemon = await dispatch(
|
|
pokeRestApi.endpoints.getPokemon.initiate(pokemonIdOrName),
|
|
);
|
|
|
|
if (selectedPokemon.data) {
|
|
const selectedPokemonSpecies = await dispatch(
|
|
pokeRestApi.endpoints.getPokemonSpeciesFromUrl.initiate(
|
|
selectedPokemon.data.species.url,
|
|
),
|
|
);
|
|
|
|
if (selectedPokemonSpecies.data) {
|
|
const selectedPokemonEvolutionChain = await dispatch(
|
|
pokeRestApi.endpoints.getEvolutionChainFromUrl.initiate(
|
|
selectedPokemonSpecies.data.evolution_chain.url,
|
|
),
|
|
);
|
|
|
|
if (selectedPokemonEvolutionChain.data) {
|
|
const evolutionChainName = constructEvolutionChainFromResponse(
|
|
selectedPokemonEvolutionChain.data,
|
|
);
|
|
|
|
// for each name in evolutionChain, fetch the pokemon
|
|
const evolutionChain: EvolutionSpeciesProps[] = [];
|
|
await Promise.all(
|
|
evolutionChainName.map(async name => {
|
|
const evolutionChainPokemon = await dispatch(
|
|
pokeRestApi.endpoints.getPokemon.initiate(name),
|
|
);
|
|
if (evolutionChainPokemon.data) {
|
|
evolutionChain.push({
|
|
types: evolutionChainPokemon.data.types.map(
|
|
type => type.type.name,
|
|
),
|
|
name: evolutionChainPokemon.data.name,
|
|
image_url:
|
|
evolutionChainPokemon.data.sprites.other.dream_world
|
|
.front_default,
|
|
});
|
|
}
|
|
}),
|
|
);
|
|
|
|
const selectedPokemonInfo = constructPokemonInfoFromResponses(
|
|
selectedPokemon.data,
|
|
selectedPokemonSpecies.data,
|
|
evolutionChain,
|
|
);
|
|
return selectedPokemonInfo;
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
return thunkAPI.rejectWithValue(err);
|
|
}
|
|
|
|
return null;
|
|
},
|
|
);
|
|
|
|
export const infoDialogSlice: Slice<InfoDialogStateProps> = createSlice({
|
|
name: 'infoDialog',
|
|
initialState,
|
|
reducers: {
|
|
setIsOpen: (state, action: PayloadAction<boolean>) => {
|
|
state.isOpen = action.payload;
|
|
},
|
|
setCloseDialog: state => {
|
|
state.isOpen = false;
|
|
},
|
|
},
|
|
extraReducers: builder => {
|
|
builder.addCase(fetchSelectedPokemonInfo.fulfilled, (state, action) => {
|
|
if (action.payload) {
|
|
state.InfoDialogDetails = action.payload;
|
|
state.isOpen = true;
|
|
}
|
|
});
|
|
},
|
|
});
|
|
|
|
export const { setIsOpen, setCloseDialog } = infoDialogSlice.actions;
|
|
|
|
export default infoDialogSlice.reducer;
|