251 lines
9.5 KiB
TypeScript
251 lines
9.5 KiB
TypeScript
import React from 'react';
|
|
import { ReactCookieProps, withCookies } from 'react-cookie';
|
|
import Media from 'react-media';
|
|
import { connect } from 'react-redux';
|
|
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import { appReducers } from 'app/index';
|
|
|
|
import * as ActionsPokemonApp from 'app/actions';
|
|
import * as ActionsPokemonExplorer from 'app/components/PokemonExplorer/actions';
|
|
import * as ActionsPokemonSelectList from 'app/components/PokemonSelectList/actions';
|
|
import { IPokemonAppDispatch, IRouterProps, Navigation } from 'app/types';
|
|
|
|
import { convertFormParamToPokemonForm, convertIdParamToPokemonId, convertLeagueParamToLeague } from 'app/utils/navigation';
|
|
|
|
import { Footer } from 'app/components/Footer';
|
|
import { Header } from 'app/components/Header';
|
|
import { ConnectedPokemonExplorer } from 'app/components/PokemonExplorer/PokemonExplorer';
|
|
import { PokemonSelectList } from 'app/components/PokemonSelectList/PokemonSelectList';
|
|
import { WelcomeDialog } from 'app/components/WelcomeDialog';
|
|
|
|
import { MAX_MOBILE_WIDTH, MIN_TABLET_WIDTH } from 'common/models/constants';
|
|
import * as PVPogoProtos from 'common/models/PVPogoProtos';
|
|
|
|
import * as styles from 'app/styles/PokemonApp.scss';
|
|
|
|
type PokemonAppProps = ReturnType<typeof appReducers>;
|
|
|
|
interface IConnectedPokemonAppProps extends PokemonAppProps, ReactCookieProps, IPokemonAppDispatch, IRouterProps {}
|
|
|
|
class PokemonApp extends React.Component<IConnectedPokemonAppProps> {
|
|
|
|
public async componentWillMount() {
|
|
const {
|
|
cookies,
|
|
dispatch,
|
|
match,
|
|
} = this.props;
|
|
const {
|
|
id,
|
|
form,
|
|
league,
|
|
} = match.params;
|
|
|
|
await Promise.all([
|
|
dispatch(ActionsPokemonApp.fetchConfig()),
|
|
// TODO: move this action to PokemonApp actions
|
|
dispatch(ActionsPokemonSelectList.fetchPokemonList())
|
|
]);
|
|
dispatch(ActionsPokemonSelectList.setIsLoading(false));
|
|
|
|
const pokemonId = convertIdParamToPokemonId(id);
|
|
const pokemonForm = convertFormParamToPokemonForm(form);
|
|
if (pokemonId !== null && pokemonForm !== null) {
|
|
this.handleActivatePokemon(pokemonId, pokemonForm);
|
|
}
|
|
|
|
const activeLeague = convertLeagueParamToLeague(league);
|
|
if (activeLeague !== null) {
|
|
dispatch(ActionsPokemonExplorer.setActiveLeague(activeLeague));
|
|
}
|
|
|
|
if (pokemonId === null || pokemonForm === null || typeof cookies === 'undefined' || !cookies.get('welcomed')) {
|
|
if (typeof cookies !== 'undefined') {
|
|
cookies.set('welcomed', 1);
|
|
}
|
|
this.handleOpenWelcomeDialog();
|
|
}
|
|
}
|
|
|
|
public render() {
|
|
const {
|
|
isWelcomeShown,
|
|
navigation,
|
|
isInterruption,
|
|
attackTypeEffectiveness,
|
|
combatMoves,
|
|
} = this.props.pokemonAppState;
|
|
const {
|
|
pokemonList,
|
|
pokemonListFiltered,
|
|
filterTerm,
|
|
} = this.props.pokemonSelectListState;
|
|
const {
|
|
leaguePokemon,
|
|
} = this.props.pokemonExplorerState;
|
|
const matchParams = this.props.match.params;
|
|
const activePokemonId = convertIdParamToPokemonId(matchParams.id);
|
|
const activePokemonForm = convertFormParamToPokemonForm(matchParams.form);
|
|
|
|
const isOverlayShown = isWelcomeShown || isInterruption;
|
|
const wrapperCss = classNames(
|
|
styles.wrapper,
|
|
{
|
|
[styles.overlaid]: isOverlayShown,
|
|
}
|
|
);
|
|
|
|
const iconCss = classNames(
|
|
'icon',
|
|
'pixel',
|
|
'sprite',
|
|
);
|
|
|
|
const pokedexCss = classNames(
|
|
iconCss,
|
|
'pokedex',
|
|
{
|
|
active: navigation === 'pokedex',
|
|
}
|
|
);
|
|
const pokedexButtonCss = classNames(
|
|
styles.navigationButton,
|
|
// {
|
|
// [styles.activeNavigationButton]: activeNavigation === 'pokedex',
|
|
// }
|
|
);
|
|
|
|
const hamburgerButtonCss = classNames(
|
|
styles.hamburgerIcon,
|
|
styles.navigationButton,
|
|
// {
|
|
// [styles.activeNavigationButton]: activeNavigation === 'menu',
|
|
// }
|
|
);
|
|
|
|
// react optimization for rendering on pokemon switching
|
|
// https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
|
|
const uniquePokemonId = leaguePokemon !== null ? `${leaguePokemon.id}~${leaguePokemon.form}` : undefined;
|
|
|
|
return (
|
|
<div className={ wrapperCss }>
|
|
<Header
|
|
handleHomeClick={ this.handleOpenWelcomeDialog }
|
|
>
|
|
<PokemonSelectList
|
|
isLoading={ this.props.pokemonSelectListState.isLoading }
|
|
isListOpen={ navigation === 'pokedex' }
|
|
activePokemonId={ activePokemonId }
|
|
activePokemonForm={ activePokemonForm }
|
|
pokemonList={ filterTerm === '' ? pokemonList : pokemonListFiltered }
|
|
filterTerm={ this.props.pokemonSelectListState.filterTerm }
|
|
handleActivatePokemon={ this.handleActivatePokemon }
|
|
handleChangeFilter={ this.handleChangeFilter }
|
|
/>
|
|
<Media query={ { minWidth: MIN_TABLET_WIDTH } }>
|
|
<button className={ pokedexButtonCss } onClick={ this.handlePokedexClick }><i className={ pokedexCss } /></button>
|
|
</Media>
|
|
<Media query={ { maxWidth: MAX_MOBILE_WIDTH } }>
|
|
<button className={ hamburgerButtonCss } onClick={ this.handleMenuClick }>☰</button>
|
|
</Media>
|
|
</Header>
|
|
<ConnectedPokemonExplorer
|
|
key={ uniquePokemonId }
|
|
isMenuOpen={ navigation === 'menu' }
|
|
isOverlaid={ isOverlayShown }
|
|
attackTypeEffectiveness={ attackTypeEffectiveness }
|
|
combatMoves={ combatMoves }
|
|
toggleInterruption={ this.handleToggleInterruption }
|
|
/>
|
|
{ isWelcomeShown &&
|
|
<WelcomeDialog
|
|
handleClose={ this.handleCloseWelcomeDialog }
|
|
/>
|
|
}
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
private readonly handleOpenWelcomeDialog = () => {
|
|
this.props.dispatch(ActionsPokemonApp.setWelcomeShown(true));
|
|
}
|
|
|
|
private readonly handleCloseWelcomeDialog = () => {
|
|
this.props.dispatch(ActionsPokemonApp.setWelcomeShown(false));
|
|
|
|
const matchParams = this.props.match.params;
|
|
const activePokemonId = convertIdParamToPokemonId(matchParams.id);
|
|
const activePokemonForm = convertFormParamToPokemonForm(matchParams.form);
|
|
if (activePokemonId === null || activePokemonForm === null) {
|
|
// this.handleActivatePokemon(1, 0); // set active pokemon to bulbasaur
|
|
this.props.history.replace('/explorer/1/0');
|
|
}
|
|
}
|
|
|
|
private readonly handleInterruption = (isInterruption : boolean, navigation : Navigation) => {
|
|
const { dispatch } = this.props;
|
|
|
|
dispatch(ActionsPokemonApp.setNavigation(isInterruption ? navigation : null));
|
|
dispatch(ActionsPokemonApp.setIsInterruption(isInterruption));
|
|
|
|
if (isInterruption) {
|
|
dispatch(ActionsPokemonExplorer.setCombatMoveSelectorsOpen({
|
|
quickMove: false,
|
|
chargeMove1: false,
|
|
chargeMove2: false,
|
|
}));
|
|
}
|
|
}
|
|
|
|
private readonly handleToggleInterruption = (isInterruption : boolean) => {
|
|
this.props.dispatch(ActionsPokemonApp.setIsInterruption(isInterruption));
|
|
if (!isInterruption) {
|
|
this.props.dispatch(ActionsPokemonApp.setNavigation(null));
|
|
this.handleCloseWelcomeDialog();
|
|
}
|
|
}
|
|
|
|
private readonly handlePokedexClick = () => {
|
|
this.handleInterruption(true, 'pokedex');
|
|
}
|
|
|
|
private readonly handleMenuClick = () => {
|
|
this.handleInterruption(true, 'menu');
|
|
}
|
|
|
|
private readonly handleActivatePokemon = async (pokemonId : PVPogoProtos.PokemonId, form : PVPogoProtos.PokemonForm) => {
|
|
const { dispatch } = this.props;
|
|
|
|
dispatch(ActionsPokemonExplorer.setIsLoading(true));
|
|
try {
|
|
const leaguePokemon = await dispatch(ActionsPokemonApp.fetchPokemonLeagueStats(pokemonId, form));
|
|
dispatch(ActionsPokemonExplorer.reset(leaguePokemon));
|
|
} catch (error) {
|
|
// tslint:disable-next-line:no-console
|
|
console.error(error);
|
|
dispatch(ActionsPokemonExplorer.setLeaguePokemon(null));
|
|
}
|
|
dispatch(ActionsPokemonExplorer.setIsLoading(false));
|
|
this.handleInterruption(false, 'pokedex');
|
|
}
|
|
|
|
private readonly handleChangeFilter = (filterTerm : string) => {
|
|
this.handleInterruption(true, 'pokedex');
|
|
return this.props.dispatch(ActionsPokemonSelectList.filterPokemonList(filterTerm));
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = (state : PokemonAppProps) : PokemonAppProps => {
|
|
return {
|
|
pokemonAppState: state.pokemonAppState,
|
|
pokemonExplorerState: state.pokemonExplorerState,
|
|
pokemonSelectListState: state.pokemonSelectListState,
|
|
};
|
|
};
|
|
|
|
export const ConnectedPokemonApp = withCookies(withRouter(connect(mapStateToProps)(PokemonApp)));
|