Trying to impolement InfoDialogSlice and related api endpoints (Code is faulty)

This commit is contained in:
Jason Zhu 2023-05-17 21:50:34 +10:00
parent 8c442946d3
commit 78b7a4a5f4
10 changed files with 189 additions and 132 deletions

View File

@ -1,6 +1,8 @@
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
import { pokeApi } from 'app/services/pokeApi';
import { filterSlice } from 'features/Filters/filterSlice';
import { getIdFromUrl } from 'app/services/pokeApi';
import { configureStore } from '@reduxjs/toolkit';
import { AppStore } from 'app/store';
@ -87,4 +89,18 @@ describe('pokeApi', () => {
});
});
});
describe('test helper functions', () => {
test('getIdFromUrl works correctly for PokemonSpecies', () => {
const url = 'https://pokeapi.co/api/v2/pokemon-species/6/';
const id = getIdFromUrl(url);
expect(id).toBe(6);
});
test('getIdFromUrl works correctly for evolution-chain', () => {
const url = 'https://pokeapi.co/api/v2/evolution-chain/2/';
const id = getIdFromUrl(url);
expect(id).toBe(2);
});
});
});

View File

@ -9,7 +9,10 @@ import {
PokemonResponseData,
EvolutionChainResponseData,
PokemonSpeciesResponseData,
EvolutionChain,
} from 'types/api';
import { InfoDialogComponentProps } from 'components/InfoDialogComponent';
import { EvolutionSpeciesProps } from 'components/EvolutionSpecies';
export interface pokeApiFullListFetchArgs extends FetchArgs {
fetchAllPages?: boolean;
@ -27,6 +30,11 @@ interface PokeAPIFullListResponse {
results: any[];
}
export const getIdFromUrl = (url: string) => {
const urlParts = url.split('/');
return parseInt(urlParts[urlParts.length - 2]);
};
async function fetchAllPages(url: string | null) {
const allResults: any[] = [];
@ -40,6 +48,23 @@ async function fetchAllPages(url: string | null) {
return allResults;
}
export const convertEvolutionChainResponseDataToEvolutionSpeciesProps = (
evo: EvolutionChainResponseData,
): EvolutionSpeciesProps[] => {
const result: EvolutionSpeciesProps[] = [];
// const addEvolutionSpeciesProps = (evo: EvolutionChain, level: number) => {
// result.push({
// name: evo.species.name,
// });
// evo.evolves_to.forEach(evo => {
// addEvolutionSpeciesProps(evo, level + 1);
// });
// };
//
// addEvolutionSpeciesProps(evo.chain, 0);
return result;
};
export const paginationBaseQuery = (baseUrl: string) =>
fetchBaseQuery({ baseUrl });
@ -94,18 +119,40 @@ export const pokeApi = createApi({
};
},
}),
getPokemon: builder.query<PokemonResponseData, number | string>({
query: IdOrName => ({ url: `pokemon/${IdOrName}` }),
getPokemon: builder.query<PokemonResponseData, number>({
query: Id => ({ url: `pokemon/${Id}` }),
}),
getPokemonSpecies: builder.query<
PokemonSpeciesResponseData,
number | string
>({
query: IdOrName => ({ url: `pokemon-species/${IdOrName}` }),
getPokemonSpecies: builder.query<PokemonSpeciesResponseData, number>({
query: Id => ({ url: `pokemon-species/${Id}` }),
}),
getEvolutionChain: builder.query<EvolutionChainResponseData, number>({
query: Id => ({ url: `evolution-chain/${Id}` }),
}),
getPokemonInfo: builder.query<string, number>({
async queryFn(pokemonId, queryApi) {
const pokemon: PokemonResponseData = await queryApi
.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' };
},
}),
}),
});

View File

@ -1,6 +1,7 @@
import { configureStore } from '@reduxjs/toolkit';
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
import { filterSlice } from 'features/Filters/filterSlice';
import { infoDialogSlice } from 'features/InfoDialog/infoDialogSlice';
import { pokeApi } from './services/pokeApi';
export const store = configureStore({
@ -8,6 +9,7 @@ export const store = configureStore({
// component slices
pokedex: pokedexSlice.reducer,
filter: filterSlice.reducer,
infoDialog: infoDialogSlice.reducer,
// api slices
[pokeApi.reducerPath]: pokeApi.reducer,

View File

@ -1 +1,2 @@
export { default } from './EvolutionSpecies';
export * from './EvolutionSpecies';

View File

@ -36,14 +36,14 @@ export const Duduo: Story = {
evolutionChain: [
{
types: ['normal', 'flying'],
image:
image_url:
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/84.svg',
name: 'Doduo',
},
{
name: 'Dodrio',
types: ['normal', 'flying'],
image:
image_url:
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/85.svg',
},
],

View File

@ -7,7 +7,9 @@ import { findPokeTypeAsset } from 'components/PokemonTypes';
import { colorTypeGradients } from 'components/utils';
import GenderRate from 'components/GenderRate';
import Delayed from 'components/Delayed';
import EvolutionSpecies from 'components/EvolutionSpecies';
import EvolutionSpecies, {
EvolutionSpeciesProps,
} from 'components/EvolutionSpecies';
interface Stat {
stat__name: string;
@ -27,7 +29,7 @@ export interface InfoDialogComponentProps {
description: string;
abilities: string[];
stats: Stat[];
evolutionChain: { types: string[]; image: string; name: string }[];
evolutionChain: EvolutionSpeciesProps[];
}
const InfoDialog = ({
@ -171,7 +173,7 @@ const InfoDialog = ({
>
<EvolutionSpecies
types={evo.types}
image_url={evo.image}
image_url={evo.image_url}
name={evo.name}
/>
{evolutionChain.indexOf(evo) + 1 &&

View File

@ -1 +1,2 @@
export { default } from './InfoDialogComponent';
export type { InfoDialogComponentProps } from './InfoDialogComponent';

View File

@ -1,131 +1,52 @@
import { Dialog, DialogContent, Tooltip, Zoom } from '@mui/material';
import { useEffect } from 'react';
import { findPokeTypeAsset } from 'components/PokemonTypes';
import { colorTypeGradients } from 'components/utils';
import GenderRate from 'components/GenderRate';
import { useAppDispatch, useAppSelector } from 'app/hooks';
import InfoDialogComponent from 'components/InfoDialogComponent';
import {
useGetPokemonQuery,
useGetPokemonSpeciesQuery,
useGetEvolutionChainQuery,
} from 'app/services/pokeApi';
import { setPokemonSpeciesIdToFetch } from './infoDialogSlice';
export interface InfoDialogProps {
open: boolean;
cancel: boolean;
id: number;
name: string;
genere: string;
types: string[];
height: number;
weight: number;
genderRatio: number;
description: string;
abilities: string[];
stats: {
hp: number;
attack: number;
defense: number;
spAttack: number;
spDefense: number;
speed: number;
};
image: string;
pokemonId: string | number;
}
const InfoDialog = (props: InfoDialogProps) => {
let finalColor;
const InfoDialog = ({ pokemonId }: InfoDialogProps) => {
const dispatch = useAppDispatch();
if (props.types.length === 2) {
finalColor = colorTypeGradients(
props.types[0],
props.types[1],
props.types.length,
);
} else {
finalColor = colorTypeGradients(
props.types[0],
props.types[0],
props.types.length,
);
}
const isOpen = useAppSelector(state => state.infoDialog.isOpen);
const skipGetPokemonSpeciesQuery = useAppSelector(
state => state.infoDialog.skipGetPokemonSpeciesQuery,
);
const skipGetEvolutionChainQuery = useAppSelector(
state => state.infoDialog.skipGetEvolutionChainQuery,
);
const selectedPokemonId = useAppSelector(
state => state.infoDialog.selectedPokemonId,
);
return (
<div>
<Dialog
aria-labelledby="customized-dialog-title"
open={props.open}
fullWidth
maxWidth="md"
className="dialog__bg noselect"
>
<DialogContent
style={{
background: `linear-gradient(${finalColor[0]}, ${finalColor[1]})`,
}}
className="dialog__content"
>
<div className="info__container">
<div className="info__container__img">
<div className="pokemon__id">
#{String(props.id).padStart(3, '0')}
</div>
<div className="pokemon__name">{props.name}</div>
<div
className="pokemon__genera"
style={{ background: finalColor[0] }}
>
{props.genere}
</div>
<div>
<img src={props.image} alt="poke-img" />
</div>
<div className="info__container__data__type">
{props.types.map(type => (
<Tooltip
title={type}
key={type}
TransitionComponent={Zoom}
arrow
>
<div className={`poke__type__bg ${type}`}>
<img src={findPokeTypeAsset(type)} alt="poke-type" />
</div>
</Tooltip>
))}
</div>
<div className="dimensions">
<p>
<span
className="info__container__headings"
style={{ fontSize: '20px' }}
>
Height
</span>
</p>
<p>
<span
className="info__container__headings"
style={{ fontSize: '20px' }}
>
Weight
</span>
</p>
</div>
<div className="gender__container">
{props.genderRatio === -1 ? (
'Genderless'
) : (
<GenderRate genderRatio={props.genderRatio} />
)}
</div>
</div>
<div className="info__container__data">
<div className={'right__box'}>
<div>
<div className={'info__container__headings'}>About</div>
<div className={'desc'}>{props.description}</div>
</div>
</div>
</div>
</div>
</DialogContent>
</Dialog>
</div>
<>
<InfoDialogComponent
openDialog={isOpen}
id={selectedPokemonId}
name={}
types={}
genera={}
image={}
height={}
weight={}
genderRatio={}
description={}
abilities={}
stats={}
evolutionChain={}
/>
</>
);
};

View File

@ -0,0 +1,67 @@
import { createSlice } 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 { PokemonResponseData } from '../../types/api';
export type InfoDialogStateProps = {
isOpen: boolean;
skipGetPokemonSpeciesQuery: boolean;
skipGetEvolutionChainQuery: boolean;
selectedPokemonId: number;
pokemonSpeciesIdToFetch: number;
evolutionChainIdToFetch: number;
};
export const initialState: InfoDialogStateProps = {
isOpen: false,
skipGetPokemonSpeciesQuery: false,
skipGetEvolutionChainQuery: false,
selectedPokemonId: 0,
pokemonSpeciesIdToFetch: 0,
evolutionChainIdToFetch: 0,
};
export const infoDialogSlice: Slice<InfoDialogStateProps> = createSlice({
name: 'infoDialog',
initialState,
reducers: {
setIsOpen: (state, action: PayloadAction<boolean>) => {
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;
},
},
});
export const {
setIsOpen,
setSkipGetPokemonSpeciesQuery,
setSkipGetEvolutionChainQuery,
setSelectedPokemonId,
setPokemonSpeciesIdToFetch,
setEvolutionChainIdToFetch,
} = infoDialogSlice.actions;
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);
};

View File

@ -68,7 +68,7 @@ export type PokemonSpeciesResponseData = {
};
};
type EvolutionChain = {
export type EvolutionChain = {
evolves_to: EvolutionChain[];
species: {
name: string;