diff --git a/src/ts/app/PokemonApp.tsx b/src/ts/app/PokemonApp.tsx index ffe91f4..69547a7 100644 --- a/src/ts/app/PokemonApp.tsx +++ b/src/ts/app/PokemonApp.tsx @@ -1,13 +1,14 @@ -import * as React from 'react'; +import React from 'react'; import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; import { appReducers } from './index'; -import * as ActionsPokemonApp from './actions'; +import * as ActionsPokemonExplorer from './components/PokemonExplorer/actions'; +import * as ActionsPokemonSelectList from './components/PokemonSelectList/actions'; import { ThunkDispatchPokemonSelectList } from './types'; -import { PokemonSelectList } from './components/PokemonSelectList'; +import { PokemonExplorer } from './components/PokemonExplorer/PokemonExplorer'; +import { PokemonSelectList } from './components/PokemonSelectList/PokemonSelectList'; type PokemonAppProps = ReturnType; @@ -21,28 +22,58 @@ class PokemonApp extends React.Component { } public componentWillMount() { - this.props.dispatch(ActionsPokemonApp.fetchPokemonList()); + this.props.dispatch(ActionsPokemonSelectList.fetchPokemonList()) + .then(() => this.props.dispatch(ActionsPokemonSelectList.setIsLoading(false))); } public render() { - const { pokemonSelectListState } = this.props; + const { + activePokemonIndex, + pokemonList, + } = this.props.pokemonSelectListState; + const { + leaguePokemon, + } = this.props.pokemonExplorerState; + return ( - +
+ + { leaguePokemon !== null && + + } +
); } private readonly onActivatePokemon = (pokemonIndex : number) => { - this.props.dispatch(ActionsPokemonApp.setActivePokemonIndex(pokemonIndex)); - this.props.dispatch(ActionsPokemonApp.fetchPokemonLeagueStats(this.props.pokemonSelectListState.pokemonList[pokemonIndex].id)); + const { dispatch, pokemonSelectListState } = this.props; + const pokemonId = pokemonSelectListState.pokemonList[pokemonIndex].id; + + dispatch(ActionsPokemonSelectList.fetchPokemonLeagueStats(pokemonId)) + .then((leaguePokemon) => { + dispatch(ActionsPokemonSelectList.setActivePokemonIndex(pokemonIndex)); + dispatch(ActionsPokemonExplorer.setLeaguePokemon(leaguePokemon)); + }) + .catch((error) => { + // tslint:disable-next-line:no-console + console.error(error); + dispatch(ActionsPokemonExplorer.setLeaguePokemon(null)); + }) + .then(() => dispatch(ActionsPokemonExplorer.setIsLoading(false))); } } const mapStateToProps = (state : ReturnType) : PokemonAppProps => { return { + pokemonExplorerState: state.pokemonExplorerState, pokemonSelectListState: state.pokemonSelectListState, }; }; diff --git a/src/ts/app/actions.js b/src/ts/app/actions.js deleted file mode 100644 index 95a291d..0000000 --- a/src/ts/app/actions.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -exports.__esModule = true; -var typesafe_actions_1 = require("typesafe-actions"); -var types_1 = require("./types"); -exports.setPokemonList = function (pokemonList) { return typesafe_actions_1.action(types_1.PokemonSelectListActionTypes.SET_POKEMON_LIST, { pokemonList: pokemonList }); }; -exports.setActivePokemonIndex = function (activePokemonIndex) { return typesafe_actions_1.action(types_1.PokemonSelectListActionTypes.SET_ACTIVE_POKEMON_INDEX, { activePokemonIndex: activePokemonIndex }); }; -exports.setPokemonLeagueStats = function (pokemonId, pokemonLeagueStats) { return typesafe_actions_1.action(types_1.PokemonSelectListActionTypes.SET_POKEMON_LEAGUE_STATS, { pokemonId: pokemonId, pokemonLeagueStats: pokemonLeagueStats }); }; -exports.fetchPokemonList = function () { - return function (dispatch, getState, extraArguments) { - return new Promise(function (resolve, reject) { - extraArguments.services.pokemonService.getPokemonList() - .then(function (pokemonList) { - dispatch(exports.setPokemonList(pokemonList)); - resolve(); - }); - }); - }; -}; -exports.fetchPokemonLeagueStats = function (pokemonId) { - return function (dispatch, getState, extraArguments) { - return new Promise(function (resolve, reject) { - extraArguments.services.pokemonService.getPokemonLeagueStats(pokemonId) - .then(function (pokemonLeagueStats) { - dispatch(exports.setPokemonLeagueStats(pokemonId, pokemonLeagueStats)); - resolve(); - })["catch"](function () { - reject(); - }); - }); - }; -}; diff --git a/src/ts/app/actions.ts b/src/ts/app/actions.ts deleted file mode 100644 index 4672ebe..0000000 --- a/src/ts/app/actions.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { action } from 'typesafe-actions'; - -import { ILeaguePokemon, IPokemon } from 'app/models/Pokemon'; - -import { PokemonSelectListActionTypes, ThunkResult } from './types'; - -export const setPokemonList = (pokemonList : Array) => action(PokemonSelectListActionTypes.SET_POKEMON_LIST, { pokemonList }); - -export const setActivePokemonIndex = (activePokemonIndex : number) => action(PokemonSelectListActionTypes.SET_ACTIVE_POKEMON_INDEX, { activePokemonIndex }); - -export const setPokemonLeagueStats = (pokemonId : string, pokemonLeagueStats : ILeaguePokemon) => action(PokemonSelectListActionTypes.SET_POKEMON_LEAGUE_STATS, { pokemonId, pokemonLeagueStats }); - -export const fetchPokemonList = ( -) : ThunkResult> => { - return (dispatch, getState, extraArguments) => { - return new Promise((resolve, reject) => { - extraArguments.services.pokemonService.getPokemonList() - .then((pokemonList) => { - dispatch(setPokemonList(pokemonList)); - resolve(); - }); - }); - }; -}; - -export const fetchPokemonLeagueStats = ( - pokemonId : string -) : ThunkResult> => { - return (dispatch, getState, extraArguments) => { - return new Promise((resolve, reject) => { - extraArguments.services.pokemonService.getPokemonLeagueStats(pokemonId) - .then((pokemonLeagueStats) => { - dispatch(setPokemonLeagueStats(pokemonId, pokemonLeagueStats)); - resolve(); - }) - .catch(() => { - reject(); - }); - }); - }; -}; diff --git a/src/ts/app/components/PokemonExplorer.tsx b/src/ts/app/components/PokemonExplorer.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/ts/app/components/PokemonExplorer/PokemonExplorer.tsx b/src/ts/app/components/PokemonExplorer/PokemonExplorer.tsx new file mode 100644 index 0000000..949c6d8 --- /dev/null +++ b/src/ts/app/components/PokemonExplorer/PokemonExplorer.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import { ILeaguePokemon } from 'app/models/Pokemon'; + +export interface IPokemonSelectListProps { + isLoading : boolean; + leaguePokemon : ILeaguePokemon; +} + +interface IState { +} + +export class PokemonExplorer extends React.Component { + public render() { + return (
{ this.props.leaguePokemon.name }
); + } +} diff --git a/src/ts/app/components/PokemonExplorer/actions.ts b/src/ts/app/components/PokemonExplorer/actions.ts new file mode 100644 index 0000000..3e227e1 --- /dev/null +++ b/src/ts/app/components/PokemonExplorer/actions.ts @@ -0,0 +1,9 @@ +import { action } from 'typesafe-actions'; + +import { PokemonExplorerActionTypes } from './types'; + +import { ILeaguePokemon } from 'app/models/Pokemon'; + +export const setIsLoading = (isLoading : boolean) => action(PokemonExplorerActionTypes.SET_IS_LOADING, { isLoading }); + +export const setLeaguePokemon = (leaguePokemon : ILeaguePokemon | null) => action(PokemonExplorerActionTypes.SET_LEAGUE_POKEMON, { leaguePokemon }); diff --git a/src/ts/app/components/PokemonExplorer/reducers.ts b/src/ts/app/components/PokemonExplorer/reducers.ts new file mode 100644 index 0000000..46755f9 --- /dev/null +++ b/src/ts/app/components/PokemonExplorer/reducers.ts @@ -0,0 +1,39 @@ +import { Reducer } from 'redux'; + +import * as Actions from './actions'; +import { IPokemonExplorerState, PokemonExplorerActionTypes } from './types'; + +export const initialState : IPokemonExplorerState = { + isLoading: false, + leaguePokemon: null, +}; + +const reduceSetIsLoading = ( + state : IPokemonExplorerState, + action : ReturnType +) : IPokemonExplorerState => ({ + ...state, + isLoading: action.payload.isLoading, +}); + +const reduceSetLeaguePokemon = ( + state : IPokemonExplorerState, + action : ReturnType +) : IPokemonExplorerState => ({ + ...state, + leaguePokemon: action.payload.leaguePokemon, +}); + +export const PokemonExplorerReducers : Reducer = ( + state : IPokemonExplorerState = initialState, + action, +) : IPokemonExplorerState => { + switch (action.type) { + case PokemonExplorerActionTypes.SET_IS_LOADING: + return reduceSetIsLoading(state, action as ReturnType); + case PokemonExplorerActionTypes.SET_LEAGUE_POKEMON: + return reduceSetLeaguePokemon(state, action as ReturnType); + default: + return state; + } +}; diff --git a/src/ts/app/components/PokemonExplorer/types.ts b/src/ts/app/components/PokemonExplorer/types.ts new file mode 100644 index 0000000..9c6749c --- /dev/null +++ b/src/ts/app/components/PokemonExplorer/types.ts @@ -0,0 +1,11 @@ +import { ILeaguePokemon } from 'app/models/Pokemon'; + +export interface IPokemonExplorerState { + isLoading : boolean; + leaguePokemon : ILeaguePokemon | null; +} + +export const PokemonExplorerActionTypes = { + SET_IS_LOADING: 'POKEMON_EXPLORER/SET_IS_LOADING', + SET_LEAGUE_POKEMON: 'POKEMON_EXPLORER/SET_LEAGUE_POKEMON', +}; diff --git a/src/ts/app/components/PokemonSelectList.tsx b/src/ts/app/components/PokemonSelectList/PokemonSelectList.tsx similarity index 91% rename from src/ts/app/components/PokemonSelectList.tsx rename to src/ts/app/components/PokemonSelectList/PokemonSelectList.tsx index e73cccc..352bd5e 100644 --- a/src/ts/app/components/PokemonSelectList.tsx +++ b/src/ts/app/components/PokemonSelectList/PokemonSelectList.tsx @@ -7,7 +7,8 @@ import classNames from 'classnames'; import { IPokemon } from 'app/models/Pokemon'; export interface IPokemonSelectListProps { - activePokemonIndex : number; + isLoading : boolean; + activePokemonIndex : number | null; pokemonList : Array; onActivatePokemon : (index : number) => void; @@ -45,9 +46,12 @@ export class PokemonSelectList extends React.Component +
action(PokemonSelectListActionTypes.SET_IS_LOADING, { isLoading }); + +export const setPokemonList = (pokemonList : Array) => action(PokemonSelectListActionTypes.SET_POKEMON_LIST, { pokemonList }); + +export const setActivePokemonIndex = (activePokemonIndex : number | null) => action(PokemonSelectListActionTypes.SET_ACTIVE_POKEMON_INDEX, { activePokemonIndex }); + +export const setPokemonLeagueStats = (pokemonId : string, pokemonLeagueStats : ILeaguePokemon) => action(PokemonSelectListActionTypes.SET_POKEMON_LEAGUE_STATS, { pokemonId, pokemonLeagueStats }); + +export const fetchPokemonList = ( +) : ThunkResult> => { + return async (dispatch, getState, extraArguments) => { + const pokemonList = await extraArguments.services.pokemonService.getPokemonList(); + dispatch(setPokemonList(pokemonList)); + }; +}; + +export const fetchPokemonLeagueStats = ( + pokemonId : string +) : ThunkResult> => { + return async (dispatch, getState, extraArguments) => { + const pokemonLeagueStats = await extraArguments.services.pokemonService.getPokemonLeagueStats(pokemonId); + dispatch(setPokemonLeagueStats(pokemonId, pokemonLeagueStats)); + return pokemonLeagueStats; + }; +}; diff --git a/src/ts/app/reducers.ts b/src/ts/app/components/PokemonSelectList/reducers.ts similarity index 81% rename from src/ts/app/reducers.ts rename to src/ts/app/components/PokemonSelectList/reducers.ts index d7f0954..f821fd4 100644 --- a/src/ts/app/reducers.ts +++ b/src/ts/app/components/PokemonSelectList/reducers.ts @@ -4,12 +4,21 @@ import * as Actions from './actions'; import { IPokemonSelectListState, PokemonSelectListActionTypes } from './types'; export const initialState : IPokemonSelectListState = { - activePokemonIndex: -1, + isLoading: true, + activePokemonIndex: null, pokemonList: [], pokemonListFiltered: [], pokemonLeagueStats: {} }; +const reduceSetIsLoading = ( + state : IPokemonSelectListState, + action : ReturnType +) : IPokemonSelectListState => ({ + ...state, + isLoading: action.payload.isLoading, +}); + const reduceSetPokemonList = ( state : IPokemonSelectListState, action : ReturnType @@ -42,6 +51,8 @@ export const PokemonSelectListReducers : Reducer = ( action, ) : IPokemonSelectListState => { switch (action.type) { + case PokemonSelectListActionTypes.SET_IS_LOADING: + return reduceSetIsLoading(state, action as ReturnType); case PokemonSelectListActionTypes.SET_POKEMON_LIST: return reduceSetPokemonList(state, action as ReturnType); case PokemonSelectListActionTypes.SET_ACTIVE_POKEMON_INDEX: diff --git a/src/ts/app/components/PokemonSelectList/types.ts b/src/ts/app/components/PokemonSelectList/types.ts new file mode 100644 index 0000000..053cf11 --- /dev/null +++ b/src/ts/app/components/PokemonSelectList/types.ts @@ -0,0 +1,16 @@ +import { ILeaguePokemon, IPokemon } from 'app/models/Pokemon'; + +export interface IPokemonSelectListState { + isLoading : boolean; + activePokemonIndex : number | null; + pokemonList : Array; + pokemonListFiltered : Array; + pokemonLeagueStats : { [id : string] : ILeaguePokemon }; +} + +export const PokemonSelectListActionTypes = { + SET_IS_LOADING: 'POKEMON_SELECT_LIST/SET_IS_LOADING', + SET_POKEMON_LIST: 'POKEMON_SELECT_LIST/SET_POKEMON_LIST', + SET_ACTIVE_POKEMON_INDEX: 'POKEMON_SELECT_LIST/SET_ACTIVE_POKEMON_INDEX', + SET_POKEMON_LEAGUE_STATS: 'POKEMON_SELECT_LIST/SET_POKEMON_LEAGUE_STATS', +}; diff --git a/src/ts/app/index.tsx b/src/ts/app/index.tsx index 9c8c072..951f1d1 100644 --- a/src/ts/app/index.tsx +++ b/src/ts/app/index.tsx @@ -4,21 +4,23 @@ import { Provider } from 'react-redux'; import * as Redux from 'redux'; import thunk from 'redux-thunk'; -import { IPokemonSelectListExtraArguments } from 'app/types'; +import { IPokemonAppExtraArguments } from 'app/types'; import { PokemonService } from 'api/PokemonService'; -import { PokemonSelectListReducers } from './reducers'; +import { PokemonExplorerReducers } from './components/PokemonExplorer/reducers'; +import { PokemonSelectListReducers } from './components/PokemonSelectList/reducers'; import { ConnectedPokemonApp } from './PokemonApp'; import 'styles/index.scss'; export const appReducers = Redux.combineReducers({ - pokemonSelectListState: PokemonSelectListReducers + pokemonSelectListState: PokemonSelectListReducers, + pokemonExplorerState: PokemonExplorerReducers, }); -const extraArguments : IPokemonSelectListExtraArguments = { +const extraArguments : IPokemonAppExtraArguments = { services: { pokemonService: new PokemonService() } diff --git a/src/ts/app/reducers.js b/src/ts/app/reducers.js deleted file mode 100644 index c217bd5..0000000 --- a/src/ts/app/reducers.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -exports.__esModule = true; -var types_1 = require("./types"); -exports.initialState = { - activePokemonIndex: -1, - pokemonList: [], - pokemonListFiltered: [], - pokemonLeagueStats: {} -}; -var reduceSetPokemonList = function (state, action) { return (__assign({}, state, { pokemonList: action.payload.pokemonList })); }; -var reduceSetActivePokemonIndex = function (state, action) { return (__assign({}, state, { activePokemonIndex: action.payload.activePokemonIndex })); }; -var reduceSetPokemonLeagueStats = function (state, action) { - var _a; - return (__assign({}, state, { pokemonLeagueStats: __assign({}, state.pokemonLeagueStats, (_a = {}, _a[action.payload.pokemonId] = action.payload.pokemonStats, _a)) })); -}; -exports.PokemonSelectListReducers = function (state, action) { - if (state === void 0) { state = exports.initialState; } - switch (action.type) { - case types_1.PokemonSelectListActionTypes.SET_POKEMON_LIST: - return reduceSetPokemonList(state, action); - case types_1.PokemonSelectListActionTypes.SET_ACTIVE_POKEMON_INDEX: - return reduceSetActivePokemonIndex(state, action); - case types_1.PokemonSelectListActionTypes.SET_POKEMON_LEAGUE_STATS: - return reduceSetPokemonLeagueStats(state, action); - default: - return state; - } -}; diff --git a/src/ts/app/types.js b/src/ts/app/types.js deleted file mode 100644 index 8e0f4ea..0000000 --- a/src/ts/app/types.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -exports.__esModule = true; -exports.PokemonSelectListActionTypes = { - SET_POKEMON_LIST: 'POKEMON_APP/SET_POKEMON_LIST', - SET_ACTIVE_POKEMON_INDEX: 'POKEMON_APP/SET_ACTIVE_POKEMON_INDEX', - SET_POKEMON_LEAGUE_STATS: 'POKEMON_APP/SET_POKEMON_LEAGUE_STATS' -}; diff --git a/src/ts/app/types.ts b/src/ts/app/types.ts index b7ca270..9dd8525 100644 --- a/src/ts/app/types.ts +++ b/src/ts/app/types.ts @@ -3,34 +3,23 @@ import { ThunkAction, ThunkDispatch } from 'redux-thunk'; import { IProviderExtraArguments } from 'common/models/IProviderExtraArguments'; +import { IPokemonExplorerState } from './components/PokemonExplorer/types'; +import { IPokemonSelectListState } from './components/PokemonSelectList/types'; + import { PokemonService } from 'api/PokemonService'; -import { ILeaguePokemon, IPokemon } from 'app/models/Pokemon'; - -export interface IPokemonSelectListState { - activePokemonIndex : number; - pokemonList : Array; - pokemonListFiltered : Array; - pokemonLeagueStats : { [id : string] : ILeaguePokemon }; -} - -export interface IPokemonSelectListStore { +export interface IPokemonAppStore { pokemonSelectListState : IPokemonSelectListState; + pokemonExplorerState : IPokemonExplorerState; } -export interface IPokemonSelectListServices { +export interface IPokemonAppServices { pokemonService : PokemonService; } -export interface IPokemonSelectListExtraArguments extends IProviderExtraArguments { - services : IPokemonSelectListServices; +export interface IPokemonAppExtraArguments extends IProviderExtraArguments { + services : IPokemonAppServices; } -export type ThunkResult = ThunkAction; -export type ThunkDispatchPokemonSelectList = ThunkDispatch; - -export const PokemonSelectListActionTypes = { - SET_POKEMON_LIST: 'POKEMON_APP/SET_POKEMON_LIST', - SET_ACTIVE_POKEMON_INDEX: 'POKEMON_APP/SET_ACTIVE_POKEMON_INDEX', - SET_POKEMON_LEAGUE_STATS: 'POKEMON_APP/SET_POKEMON_LEAGUE_STATS', -}; +export type ThunkResult = ThunkAction; +export type ThunkDispatchPokemonSelectList = ThunkDispatch;