Trying to implement InfoDialogSlice and related api endpoints (app is running but infodialog is not showing)
parent
281eafc863
commit
8607a8f1ad
|
@ -59,6 +59,24 @@ describe('pokeApi', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('test getPokemonSpecies query', () => {
|
||||||
|
test('visit https://pokeapi.co/api/v2/pokemon-species/6/', async () => {
|
||||||
|
await store.dispatch(
|
||||||
|
pokeApi.endpoints.getPokemonSpeciesFromUrl.initiate(
|
||||||
|
'https://pokeapi.co/api/v2/pokemon-species/6/',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const pokemonSpecies =
|
||||||
|
pokeApi.endpoints.getPokemonSpeciesFromUrl.select(
|
||||||
|
'https://pokeapi.co/api/v2/pokemon-species/6/',
|
||||||
|
)(store.getState()).data as PokemonSpeciesResponseData;
|
||||||
|
expect(pokemonSpecies?.evolution_chain.url).toBe(
|
||||||
|
'https://pokeapi.co/api/v2/evolution-chain/2/',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('test getTypeList query', () => {
|
describe('test getTypeList query', () => {
|
||||||
test('visit https://pokeapi.co/api/v2/type should return correct data in list', async () => {
|
test('visit https://pokeapi.co/api/v2/type should return correct data in list', async () => {
|
||||||
await store.dispatch(pokeApi.endpoints.getTypeList.initiate());
|
await store.dispatch(pokeApi.endpoints.getTypeList.initiate());
|
||||||
|
@ -88,6 +106,30 @@ describe('pokeApi', () => {
|
||||||
).toBe('https://pokeapi.co/api/v2/pokemon-species/6/');
|
).toBe('https://pokeapi.co/api/v2/pokemon-species/6/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('test getEvolutionChainFromUrl query', () => {
|
||||||
|
test('visit https://pokeapi.co/api/v2/evolution-chain/2/', async () => {
|
||||||
|
await store.dispatch(
|
||||||
|
pokeApi.endpoints.getEvolutionChainFromUrl.initiate(
|
||||||
|
'https://pokeapi.co/api/v2/evolution-chain/2/',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const evolutionChainData =
|
||||||
|
pokeApi.endpoints.getEvolutionChainFromUrl.select(
|
||||||
|
'https://pokeapi.co/api/v2/evolution-chain/2/',
|
||||||
|
)(store.getState()).data as EvolutionChainResponseData;
|
||||||
|
expect(evolutionChainData?.chain.species.url).toBe(
|
||||||
|
'https://pokeapi.co/api/v2/pokemon-species/4/',
|
||||||
|
);
|
||||||
|
expect(evolutionChainData?.chain.evolves_to[0].species.url).toBe(
|
||||||
|
'https://pokeapi.co/api/v2/pokemon-species/5/',
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
evolutionChainData?.chain.evolves_to[0].evolves_to[0].species.url,
|
||||||
|
).toBe('https://pokeapi.co/api/v2/pokemon-species/6/');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('test helper functions', () => {
|
describe('test helper functions', () => {
|
||||||
|
|
|
@ -119,40 +119,25 @@ export const pokeApi = createApi({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
getPokemon: builder.query<PokemonResponseData, number>({
|
getPokemon: builder.query<PokemonResponseData, string | number>({
|
||||||
query: Id => ({ url: `pokemon/${Id}` }),
|
query: IdOrName => ({ url: `pokemon/${IdOrName}` }),
|
||||||
}),
|
}),
|
||||||
getPokemonSpecies: builder.query<PokemonSpeciesResponseData, number>({
|
getPokemonSpecies: builder.query<PokemonSpeciesResponseData, number>({
|
||||||
query: Id => ({ url: `pokemon-species/${Id}` }),
|
query: Id => ({ url: `pokemon-species/${Id}` }),
|
||||||
}),
|
}),
|
||||||
|
getPokemonSpeciesFromUrl: builder.query<PokemonSpeciesResponseData, string>(
|
||||||
|
{
|
||||||
|
query: url => ({ url: `pokemon-species/${getIdFromUrl(url)}` }),
|
||||||
|
},
|
||||||
|
),
|
||||||
getEvolutionChain: builder.query<EvolutionChainResponseData, number>({
|
getEvolutionChain: builder.query<EvolutionChainResponseData, number>({
|
||||||
query: Id => ({ url: `evolution-chain/${Id}` }),
|
query: Id => ({ url: `evolution-chain/${Id}` }),
|
||||||
}),
|
}),
|
||||||
getPokemonInfo: builder.query<string, number>({
|
getEvolutionChainFromUrl: builder.query<EvolutionChainResponseData, string>(
|
||||||
async queryFn(pokemonId, queryApi) {
|
{
|
||||||
const pokemon: PokemonResponseData = await queryApi
|
query: url => ({ url: `evolution-chain/${getIdFromUrl(url)}` }),
|
||||||
.dispatch(pokeApi.endpoints.getPokemon.initiate(pokemonId))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
const pokemonSpecies: PokemonSpeciesResponseData = await queryApi
|
|
||||||
.dispatch(
|
|
||||||
pokeApi.endpoints.getPokemonSpecies.initiate(
|
|
||||||
getIdFromUrl(pokemon.species.url),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
const evolutionChain: EvolutionChainResponseData = await queryApi
|
|
||||||
.dispatch(
|
|
||||||
pokeApi.endpoints.getEvolutionChain.initiate(
|
|
||||||
getIdFromUrl(pokemonSpecies.evolution_chain.url),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
return { data: 'test' };
|
|
||||||
},
|
},
|
||||||
}),
|
),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -160,5 +145,7 @@ export const {
|
||||||
useGetTypeListQuery,
|
useGetTypeListQuery,
|
||||||
useGetPokemonQuery,
|
useGetPokemonQuery,
|
||||||
useGetPokemonSpeciesQuery,
|
useGetPokemonSpeciesQuery,
|
||||||
|
useGetPokemonSpeciesFromUrlQuery,
|
||||||
useGetEvolutionChainQuery,
|
useGetEvolutionChainQuery,
|
||||||
|
useGetEvolutionChainFromUrlQuery,
|
||||||
} = pokeApi;
|
} = pokeApi;
|
||||||
|
|
|
@ -11,7 +11,7 @@ import EvolutionSpecies, {
|
||||||
EvolutionSpeciesProps,
|
EvolutionSpeciesProps,
|
||||||
} from 'components/EvolutionSpecies';
|
} from 'components/EvolutionSpecies';
|
||||||
|
|
||||||
interface Stat {
|
export interface Stat {
|
||||||
stat__name: string;
|
stat__name: string;
|
||||||
stat__value: number;
|
stat__value: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export { default } from './InfoDialogComponent';
|
export { default } from './InfoDialogComponent';
|
||||||
export type { InfoDialogComponentProps } from './InfoDialogComponent';
|
export * from './InfoDialogComponent';
|
||||||
|
|
|
@ -13,11 +13,21 @@ export interface PokemonCardProps {
|
||||||
types: string[];
|
types: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PokemonCardPropsActionable = PokemonCardProps & {
|
||||||
|
onClickAction: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
export function formatNumber(num: number) {
|
export function formatNumber(num: number) {
|
||||||
return '#' + num.toString().padStart(3, '0');
|
return '#' + num.toString().padStart(3, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
const PokemonCard = ({ id, name, image, types }: PokemonCardProps) => {
|
const PokemonCard = ({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
image,
|
||||||
|
types,
|
||||||
|
onClickAction,
|
||||||
|
}: PokemonCardPropsActionable) => {
|
||||||
const finalColor = colorTypeGradients(types);
|
const finalColor = colorTypeGradients(types);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -38,6 +48,7 @@ const PokemonCard = ({ id, name, image, types }: PokemonCardProps) => {
|
||||||
height="1em"
|
height="1em"
|
||||||
width="1em"
|
width="1em"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
onClick={onClickAction}
|
||||||
>
|
>
|
||||||
<path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path>
|
<path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -19,32 +19,26 @@ const InfoDialog = ({ pokemonId }: InfoDialogProps) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const isOpen = useAppSelector(state => state.infoDialog.isOpen);
|
const isOpen = useAppSelector(state => state.infoDialog.isOpen);
|
||||||
const skipGetPokemonSpeciesQuery = useAppSelector(
|
const selectedInfoDialogDetails = useAppSelector(
|
||||||
state => state.infoDialog.skipGetPokemonSpeciesQuery,
|
state => state.infoDialog.InfoDialogDetails,
|
||||||
);
|
|
||||||
const skipGetEvolutionChainQuery = useAppSelector(
|
|
||||||
state => state.infoDialog.skipGetEvolutionChainQuery,
|
|
||||||
);
|
|
||||||
const selectedPokemonId = useAppSelector(
|
|
||||||
state => state.infoDialog.selectedPokemonId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<InfoDialogComponent
|
<InfoDialogComponent
|
||||||
openDialog={isOpen}
|
openDialog={isOpen}
|
||||||
id={selectedPokemonId}
|
id={selectedInfoDialogDetails.id}
|
||||||
name={}
|
name={selectedInfoDialogDetails.name}
|
||||||
types={}
|
types={selectedInfoDialogDetails.types}
|
||||||
genera={}
|
genera={selectedInfoDialogDetails.genera}
|
||||||
image={}
|
image={selectedInfoDialogDetails.image}
|
||||||
height={}
|
height={selectedInfoDialogDetails.height}
|
||||||
weight={}
|
weight={selectedInfoDialogDetails.weight}
|
||||||
genderRatio={}
|
genderRatio={selectedInfoDialogDetails.genderRatio}
|
||||||
description={}
|
description={selectedInfoDialogDetails.description}
|
||||||
abilities={}
|
abilities={selectedInfoDialogDetails.abilities}
|
||||||
stats={}
|
stats={selectedInfoDialogDetails.stats}
|
||||||
evolutionChain={}
|
evolutionChain={selectedInfoDialogDetails.evolutionChain}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
import {
|
||||||
|
constructEvolutionChainFromResponse,
|
||||||
|
findEnglishGenera,
|
||||||
|
findFirstEnglishFlavorText,
|
||||||
|
} from './infoDialogSlice';
|
||||||
|
|
||||||
|
const bulbasaurEvolutionChainResponseData = {
|
||||||
|
chain: {
|
||||||
|
evolves_to: [
|
||||||
|
{
|
||||||
|
species: {
|
||||||
|
name: 'ivysaur',
|
||||||
|
url: 'https://pokeapi.co/api/v2/pokemon-species/2/',
|
||||||
|
},
|
||||||
|
evolves_to: [
|
||||||
|
{
|
||||||
|
species: {
|
||||||
|
name: 'venusaur',
|
||||||
|
url: 'https://pokeapi.co/api/v2/pokemon-species/3/',
|
||||||
|
},
|
||||||
|
evolves_to: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
species: {
|
||||||
|
name: 'bulbasaur',
|
||||||
|
url: 'https://pokeapi.co/api/v2/pokemon-species/1/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const charmanderEvolutionChainResponseData = {
|
||||||
|
chain: {
|
||||||
|
evolves_to: [
|
||||||
|
{
|
||||||
|
species: {
|
||||||
|
name: 'charmeleon',
|
||||||
|
url: 'https://pokeapi.co/api/v2/pokemon-species/5/',
|
||||||
|
},
|
||||||
|
evolves_to: [
|
||||||
|
{
|
||||||
|
species: {
|
||||||
|
name: 'charizard',
|
||||||
|
url: 'https://pokeapi.co/api/v2/pokemon-species/6/',
|
||||||
|
},
|
||||||
|
evolves_to: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
species: {
|
||||||
|
name: 'charmander',
|
||||||
|
url: 'https://pokeapi.co/api/v2/pokemon-species/4/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const bulbasaurGenera = {
|
||||||
|
genera: [
|
||||||
|
{
|
||||||
|
genus: 'たねポケモン',
|
||||||
|
language: {
|
||||||
|
name: 'ja-Hrkt',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/1/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genus: '씨앗포켓몬',
|
||||||
|
language: {
|
||||||
|
name: 'ko',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/3/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genus: 'Seed Pokémon',
|
||||||
|
language: {
|
||||||
|
name: 'en',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/9/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genus: 'たねポケモン',
|
||||||
|
language: {
|
||||||
|
name: 'ja',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/11/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genus: '种子宝可梦',
|
||||||
|
language: {
|
||||||
|
name: 'zh-Hans',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/12/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const flavor_text_entries = [
|
||||||
|
{
|
||||||
|
flavor_text:
|
||||||
|
'A strange seed was\nplanted on its\nback at birth.\fThe plant sprouts\nand grows with\nthis POKéMON.',
|
||||||
|
language: {
|
||||||
|
name: 'en',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/9/',
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
name: 'red',
|
||||||
|
url: 'https://pokeapi.co/api/v2/version/1/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flavor_text:
|
||||||
|
'A strange seed was\nplanted on its\nback at birth.\fThe plant sprouts\nand grows with\nthis POKéMON.',
|
||||||
|
language: {
|
||||||
|
name: 'en',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/9/',
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
name: 'blue',
|
||||||
|
url: 'https://pokeapi.co/api/v2/version/2/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flavor_text:
|
||||||
|
'It can go for days\nwithout eating a\nsingle morsel.\fIn the bulb on\nits back, it\nstores energy.',
|
||||||
|
language: {
|
||||||
|
name: 'en',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/9/',
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
name: 'yellow',
|
||||||
|
url: 'https://pokeapi.co/api/v2/version/3/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flavor_text:
|
||||||
|
'うまれたときから せなかに\nふしぎな タネが うえてあって\nからだと ともに そだつという。',
|
||||||
|
language: {
|
||||||
|
name: 'ja-Hrkt',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/1/',
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
name: 'x',
|
||||||
|
url: 'https://pokeapi.co/api/v2/version/23/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flavor_text:
|
||||||
|
'태어났을 때부터 등에\n이상한 씨앗이 심어져 있으며\n몸과 함께 자란다고 한다.',
|
||||||
|
language: {
|
||||||
|
name: 'ko',
|
||||||
|
url: 'https://pokeapi.co/api/v2/language/3/',
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
name: 'x',
|
||||||
|
url: 'https://pokeapi.co/api/v2/version/23/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('test infoDialogSlice', () => {
|
||||||
|
describe('test utility functions', () => {
|
||||||
|
test('constructEvolutionChainFromResponse works correctly for bulbasaur', () => {
|
||||||
|
const evolutionChain = constructEvolutionChainFromResponse(
|
||||||
|
bulbasaurEvolutionChainResponseData,
|
||||||
|
);
|
||||||
|
expect(evolutionChain.length).toBe(3);
|
||||||
|
expect(evolutionChain[0]).toBe('bulbasaur');
|
||||||
|
expect(evolutionChain[1]).toBe('ivysaur');
|
||||||
|
expect(evolutionChain[2]).toBe('venusaur');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('constructEvolutionChainFromResponse works correctly for charmander', () => {
|
||||||
|
const evolutionChain = constructEvolutionChainFromResponse(
|
||||||
|
charmanderEvolutionChainResponseData,
|
||||||
|
);
|
||||||
|
expect(evolutionChain.length).toBe(3);
|
||||||
|
expect(evolutionChain[0]).toBe('charmander');
|
||||||
|
expect(evolutionChain[1]).toBe('charmeleon');
|
||||||
|
expect(evolutionChain[2]).toBe('charizard');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('findEnglishGenera works correctly for bulbasaur', () => {
|
||||||
|
const englishGenera = findEnglishGenera(bulbasaurGenera.genera);
|
||||||
|
expect(englishGenera?.genus).toBe('Seed Pokémon');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('findEnglishFlavorTextForRed works correctly for species 1', () => {
|
||||||
|
const englishFlavorText = findFirstEnglishFlavorText(flavor_text_entries);
|
||||||
|
expect(englishFlavorText).toBe(
|
||||||
|
'A strange seed was\nplanted on its\nback at birth.\fThe plant sprouts\nand grows with\nthis POKéMON.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,28 +1,167 @@
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||||
import type { Slice, PayloadAction } from '@reduxjs/toolkit';
|
import type { Slice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { buildDevCheckHandler } from '@reduxjs/toolkit/dist/query/core/buildMiddleware/devMiddleware';
|
|
||||||
import { useAppSelector } from '../../app/hooks';
|
import { pokeApi } from 'app/services/pokeApi';
|
||||||
import { pokeApi } from '../../app/services/pokeApi';
|
import { EvolutionSpeciesProps } from 'components/EvolutionSpecies';
|
||||||
import { PokemonResponseData } from '../../types/api';
|
import { Stat } from 'components/InfoDialogComponent';
|
||||||
|
import {
|
||||||
|
EvolutionChain,
|
||||||
|
EvolutionChainResponseData,
|
||||||
|
FlavorTextEntry,
|
||||||
|
generaItem,
|
||||||
|
PokemonResponseData,
|
||||||
|
PokemonSpeciesResponseData,
|
||||||
|
} from 'types/api';
|
||||||
|
|
||||||
|
type InfoDiaglogDetails = {
|
||||||
|
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: InfoDiaglogDetails = {
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
genera: '',
|
||||||
|
image: '',
|
||||||
|
types: [],
|
||||||
|
height: 0,
|
||||||
|
weight: 0,
|
||||||
|
genderRatio: 0,
|
||||||
|
description: '',
|
||||||
|
abilities: [],
|
||||||
|
stats: [],
|
||||||
|
evolutionChain: [],
|
||||||
|
};
|
||||||
|
|
||||||
export type InfoDialogStateProps = {
|
export type InfoDialogStateProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
skipGetPokemonSpeciesQuery: boolean;
|
InfoDialogDetails: InfoDiaglogDetails;
|
||||||
skipGetEvolutionChainQuery: boolean;
|
|
||||||
selectedPokemonId: number;
|
|
||||||
pokemonSpeciesIdToFetch: number;
|
|
||||||
evolutionChainIdToFetch: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialState: InfoDialogStateProps = {
|
export const initialState: InfoDialogStateProps = {
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
skipGetPokemonSpeciesQuery: false,
|
InfoDialogDetails: initialInfoDialogDetails,
|
||||||
skipGetEvolutionChainQuery: false,
|
|
||||||
selectedPokemonId: 0,
|
|
||||||
pokemonSpeciesIdToFetch: 0,
|
|
||||||
evolutionChainIdToFetch: 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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[],
|
||||||
|
): InfoDiaglogDetails => {
|
||||||
|
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 - 1,
|
||||||
|
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 (pokemonId: number, thunkAPI) => {
|
||||||
|
const { data: selectedPokemon } = await pokeApi.useGetPokemonQuery(
|
||||||
|
pokemonId,
|
||||||
|
);
|
||||||
|
if (selectedPokemon && selectedPokemon.species) {
|
||||||
|
const { data: selectedPokemonSpecies } =
|
||||||
|
await pokeApi.useGetPokemonSpeciesFromUrlQuery(
|
||||||
|
selectedPokemon.species.url,
|
||||||
|
);
|
||||||
|
if (selectedPokemonSpecies && selectedPokemonSpecies.evolution_chain) {
|
||||||
|
const { data: selectedPokemonEvolutionChain } =
|
||||||
|
await pokeApi.useGetEvolutionChainFromUrlQuery(
|
||||||
|
selectedPokemonSpecies.evolution_chain.url,
|
||||||
|
);
|
||||||
|
if (selectedPokemonEvolutionChain) {
|
||||||
|
const evolutionChainNames = constructEvolutionChainFromResponse(
|
||||||
|
selectedPokemonEvolutionChain,
|
||||||
|
);
|
||||||
|
// for each name in evolutionChain, fetch the pokemon
|
||||||
|
const evolutionChain: EvolutionSpeciesProps[] = [];
|
||||||
|
evolutionChainNames.map(async name => {
|
||||||
|
const { data: evolutionChainPokemon } =
|
||||||
|
await pokeApi.useGetPokemonQuery(name);
|
||||||
|
if (evolutionChainPokemon) {
|
||||||
|
evolutionChain.push({
|
||||||
|
types: evolutionChainPokemon.types.map(type => type.type.name),
|
||||||
|
name: evolutionChainPokemon.name,
|
||||||
|
image_url:
|
||||||
|
evolutionChainPokemon.sprites.other.dream_world.front_default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedPokemonInfo = constructPokemonInfoFromResponses(
|
||||||
|
selectedPokemon,
|
||||||
|
selectedPokemonSpecies,
|
||||||
|
evolutionChain,
|
||||||
|
);
|
||||||
|
|
||||||
|
return selectedPokemonInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const infoDialogSlice: Slice<InfoDialogStateProps> = createSlice({
|
export const infoDialogSlice: Slice<InfoDialogStateProps> = createSlice({
|
||||||
name: 'infoDialog',
|
name: 'infoDialog',
|
||||||
initialState,
|
initialState,
|
||||||
|
@ -30,38 +169,17 @@ export const infoDialogSlice: Slice<InfoDialogStateProps> = createSlice({
|
||||||
setIsOpen: (state, action: PayloadAction<boolean>) => {
|
setIsOpen: (state, action: PayloadAction<boolean>) => {
|
||||||
state.isOpen = action.payload;
|
state.isOpen = action.payload;
|
||||||
},
|
},
|
||||||
setSkipGetPokemonSpeciesQuery: (state, action: PayloadAction<boolean>) => {
|
|
||||||
state.skipGetPokemonSpeciesQuery = action.payload;
|
|
||||||
},
|
|
||||||
setSkipGetEvolutionChainQuery: (state, action: PayloadAction<boolean>) => {
|
|
||||||
state.skipGetEvolutionChainQuery = action.payload;
|
|
||||||
},
|
|
||||||
setSelectedPokemonId: (state, action: PayloadAction<number>) => {
|
|
||||||
state.selectedPokemonId = action.payload;
|
|
||||||
},
|
|
||||||
setPokemonSpeciesIdToFetch: (state, action: PayloadAction<number>) => {
|
|
||||||
state.pokemonSpeciesIdToFetch = action.payload;
|
|
||||||
},
|
|
||||||
setEvolutionChainIdToFetch: (state, action: PayloadAction<number>) => {
|
|
||||||
state.evolutionChainIdToFetch = action.payload;
|
|
||||||
},
|
},
|
||||||
|
extraReducers: builder => {
|
||||||
|
builder.addCase(fetchSelectedPokemonInfo.fulfilled, (state, action) => {
|
||||||
|
if (action.payload) {
|
||||||
|
state.InfoDialogDetails = action.payload;
|
||||||
|
state.isOpen = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const { setIsOpen } = infoDialogSlice.actions;
|
||||||
setIsOpen,
|
|
||||||
setSkipGetPokemonSpeciesQuery,
|
|
||||||
setSkipGetEvolutionChainQuery,
|
|
||||||
setSelectedPokemonId,
|
|
||||||
setPokemonSpeciesIdToFetch,
|
|
||||||
setEvolutionChainIdToFetch,
|
|
||||||
} = infoDialogSlice.actions;
|
|
||||||
|
|
||||||
export default infoDialogSlice.reducer;
|
export default infoDialogSlice.reducer;
|
||||||
|
|
||||||
const fetchSelectedPokemonInfo = () => async (dispatch: any, getState: any) => {
|
|
||||||
dispatch(setIsOpen(true));
|
|
||||||
const selectedPokemonId = getState().InfoDialog.selectedPokemonId;
|
|
||||||
const { data: selectedPokemon } =
|
|
||||||
pokeApi.useGetPokemonQuery(selectedPokemonId);
|
|
||||||
};
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Loading from 'components/Loading';
|
||||||
|
|
||||||
import { useAppSelector, useAppDispatch } from 'app/hooks';
|
import { useAppSelector, useAppDispatch } from 'app/hooks';
|
||||||
import { fetchPokemonsInTheRegion } from './pokedexSlice';
|
import { fetchPokemonsInTheRegion } from './pokedexSlice';
|
||||||
|
import { fetchSelectedPokemonInfo } from '../InfoDialog/infoDialogSlice';
|
||||||
|
|
||||||
export const filterPokemonCardsByType = (
|
export const filterPokemonCardsByType = (
|
||||||
pokemonList: PokemonCardProps[],
|
pokemonList: PokemonCardProps[],
|
||||||
|
@ -90,6 +91,9 @@ const Pokedex = ({
|
||||||
name={pokemonCard.name}
|
name={pokemonCard.name}
|
||||||
image={pokemonCard.image}
|
image={pokemonCard.image}
|
||||||
types={pokemonCard.types}
|
types={pokemonCard.types}
|
||||||
|
onClickAction={() =>
|
||||||
|
dispatch(fetchSelectedPokemonInfo(pokemonCard.id))
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -61,11 +61,25 @@ export interface PokemonResponseData {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type generaItem = {
|
||||||
|
genus: string;
|
||||||
|
language: nameUrlPair;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FlavorTextEntry = {
|
||||||
|
flavor_text: string;
|
||||||
|
language: nameUrlPair;
|
||||||
|
version: nameUrlPair;
|
||||||
|
};
|
||||||
|
|
||||||
export type PokemonSpeciesResponseData = {
|
export type PokemonSpeciesResponseData = {
|
||||||
// many fields are ignored
|
// many fields are ignored
|
||||||
|
genera: generaItem[];
|
||||||
evolution_chain: {
|
evolution_chain: {
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
gender_rate: number;
|
||||||
|
flavor_text_entries: FlavorTextEntry[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EvolutionChain = {
|
export type EvolutionChain = {
|
||||||
|
|
Loading…
Reference in New Issue