Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add highest/lowest mmr matches #712

Merged
merged 6 commits into from
Feb 17, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ export * from './proMatchesActions';
export * from './localizationActions';
export * from './pvgnaActions';
export * from './heroStatsActions';
export * from './publicMatchesActions';
35 changes: 35 additions & 0 deletions src/actions/publicMatchesActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* global API_HOST */
import fetch from 'isomorphic-fetch';
import querystring from 'querystring';

const REQUEST = 'publicMatches/REQUEST';
const OK = 'publicMatches/OK';
const ERROR = 'publicMatches/ERROR';

export const publicMatchesActions = {
REQUEST,
OK,
ERROR,
};

export const getPublicMatchesRequest = () => ({
type: REQUEST,
});

export const getPublicMatchesOk = payload => ({
type: OK,
payload,
});

export const getPublicMatchesError = payload => ({
type: ERROR,
payload,
});

export const getPublicMatches = options => (dispatch) => {
dispatch(getPublicMatchesRequest());
return fetch(`${API_HOST}/api/publicMatches?${querystring.stringify(options)}`)
.then(response => response.json())
.then(json => dispatch(getPublicMatchesOk(json)))
.catch(error => dispatch(getPublicMatchesError(error)));
};
18 changes: 10 additions & 8 deletions src/components/Hero/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ import styles from './Hero.css';

const getSingleHero = heroId => ({ ...heroes[heroId], img: API_HOST + heroes[heroId].img });

const Hero = ({ props }) => (<div className={styles.Header}>
const Hero = ({ props }) => (<div>
<Helmet title={getSingleHero(props.routeParams.heroId).localized_name} />
<Heading
title={getSingleHero(props.routeParams.heroId).localized_name}
className={styles.Heading}
icon=""
/>
<img role="presentation" src={getSingleHero(props.routeParams.heroId).img} className={styles.image} />
<div className={styles.Header}>
<Heading
title={getSingleHero(props.routeParams.heroId).localized_name}
className={styles.Heading}
icon=""
/>
<img role="presentation" src={getSingleHero(props.routeParams.heroId).img} className={styles.image} />
</div>
<div style={{ display: 'flex' }}>
<div style={{ width: '50%', padding: '15px' }}>
<Heading title={strings.tab_rankings} />
<Heading title={strings.tab_rankings} subtitle={strings.rankings_description} />
<Ranking {...props} />
</div>
<div style={{ width: '50%', padding: '15px' }}>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Heroes/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class RequestLayer extends React.Component {
route: '/heroes/public',
}];

const tab = heroTabs.find(tab => tab.key.toLowerCase() === route);
const tab = heroTabs.find(tab => tab.key === route);
const loading = this.props.loading;

return (<div>
Expand All @@ -104,7 +104,7 @@ class RequestLayer extends React.Component {
info={route}
tabs={heroTabs}
/>
{heroTabs && tab.content(processedData, columns[route])}
{tab && tab.content(processedData, columns[route])}
</div>}
</div>);
}
Expand Down
18 changes: 18 additions & 0 deletions src/components/Matches/Matches.css
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
@import "../palette.css";

.badge {
display: inline-block;

& svg {
width: 10px !important;
height: 10px !important;
margin-right: 5px;
}
}

.confirmed {
composes: badge;

& svg {
fill: var(--colorGolden);
}
}
87 changes: 79 additions & 8 deletions src/components/Matches/index.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import React from 'react';
import { connect } from 'react-redux';
import Helmet from 'react-helmet';
import { getProMatches } from 'actions';
import { getProMatches, getPublicMatches } from 'actions';
import strings from 'lang';
import Table, { TableLink } from 'components/Table';
// import Heading from 'components/Heading';
import { transformations } from 'utility';
import subTextStyle from 'components/Visualizations/Table/subText.css';
import { IconRadiant, IconDire } from 'components/Icons';
import { IconRadiant, IconDire, IconTrophy } from 'components/Icons';
import matchStyles from 'components/Match/Match.css';
import Container from 'components/Container';
import Match from 'components/Match';
import TabBar from 'components/TabBar';
import styles from './Matches.css';

const matchesColumns = [{
displayName: strings.th_match_id,
Expand All @@ -31,33 +33,102 @@ const matchesColumns = [{
displayName: <span className={matchStyles.teamIconContainer} ><IconRadiant className={matchStyles.iconRadiant} />{strings.general_radiant}</span>,
field: 'radiant_name',
color: matchStyles.green,
displayFn: (row, col, field) => <div>{row.radiant_win && <span className={styles.confirmed}><IconTrophy /></span>}{field}</div>,
}, {
displayName: <span className={matchStyles.teamIconContainer} ><IconDire className={matchStyles.iconDire} />{strings.general_dire}</span>,
field: 'dire_name',
color: matchStyles.red,
displayFn: (row, col, field) => <div>{!row.radiant_win && <span className={styles.confirmed}><IconTrophy /></span>}{field}</div>,
}];

const publicMatchesColumns = [
{
displayName: strings.th_match_id,
field: 'match_id',
sortFn: true,
displayFn: (row, col, field) => <div>
<TableLink to={`/matches/${field}`}>{field}</TableLink>
<span className={subTextStyle.subText} style={{ display: 'block', marginTop: 1 }}>
{row.avg_mmr} {strings.th_mmr}
</span>
</div>,
}, {
displayName: strings.th_duration,
tooltip: strings.tooltip_duration,
field: 'duration',
sortFn: true,
displayFn: transformations.duration,
},
];

const matchTabs = [{
name: strings.hero_pro_tab,
key: 'pro',
content: props => (<div>
<Table data={props.proData} columns={matchesColumns} />
</div>),
route: '/matches/pro',
}, {
name: strings.matches_highest_mmr,
key: 'highMmr',
content: props => (<div>
<Table data={props.publicData} columns={publicMatchesColumns} />
</div>),
route: '/matches/highMmr',
}, {
name: strings.matches_lowest_mmr,
key: 'lowMmr',
content: props => (<div>
<Table data={props.publicData} columns={publicMatchesColumns} />
</div>),
route: '/matches/lowMmr',
}];

const getData = (props) => {
props.dispatchProMatches();
props.dispatchPublicMatches({ mmr_ascending: props.routeParams.matchId === 'lowMmr' ? '1' : '' });
};

class RequestLayer extends React.Component {
componentDidMount() {
this.props.dispatchProMatches();
getData(this.props);
}

componentWillUpdate(nextProps) {
if (this.props.routeParams.matchId !== nextProps.routeParams.matchId) {
getData(nextProps);
}
}
render() {
const route = this.props.routeParams.matchId || 'pro';

if (Number.isInteger(Number(route))) {
return <Match {...this.props} />;
}

const tab = matchTabs.find(tab => tab.key === route);
return (<div>
<Helmet title={strings.title_matches} />
<Container>
<Table data={this.props.data} columns={matchesColumns} />
</Container>
<div>
<TabBar
info={route}
tabs={matchTabs}
/>
{tab && tab.content(this.props)}
</div>
</div>);
}
}

const mapStateToProps = state => ({
data: state.app.proMatches.list,
proData: state.app.proMatches.list,
publicData: state.app.publicMatches.list,
loading: state.app.proMatches.loading,
});

const mapDispatchToProps = dispatch => ({
dispatchProMatches: () => dispatch(getProMatches()),
dispatchPublicMatches: options => dispatch(getPublicMatches(options)),
});

export default connect(mapStateToProps, mapDispatchToProps)(RequestLayer);
2 changes: 1 addition & 1 deletion src/components/Player/Pages/Rankings/Rankings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import playerRankingsColumns from './playerRankingsColumns';

const Rankings = ({ data, error, loading }) => (
<div>
<Container title={strings.heading_rankings} error={error} loading={loading}>
<Container title={strings.heading_rankings} subtitle={strings.rankings_description} error={error} loading={loading}>
<Table paginated columns={playerRankingsColumns} data={data} />
</Container>
</div>
Expand Down
4 changes: 1 addition & 3 deletions src/components/Router/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
} from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import App from 'components/App';
import Match from 'components/Match';
import Player from 'components/Player';
import Home from 'components/Home';
import Search from 'components/Search';
Expand All @@ -33,8 +32,7 @@ export default () => (
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="matches" component={Matches} />
<Route path="matches/:matchId(/:info)" component={Match} />
<Route path="matches(/:matchId)(/:info)" component={Matches} />
<Route path="players/:playerId(/:info)(/:subInfo)" component={Player} />
<Route path="heroes(/:heroId)(/:info)" component={Heroes} />
<Route path="distributions(/:info)" component={Distributions} />
Expand Down
13 changes: 6 additions & 7 deletions src/lang/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@
"match_first_barracks": "First barracks",
"match_pick": "Pick",
"match_ban": "Ban",

"matches_highest_mmr": "High MMR",
"matches_lowest_mmr": "Low MMR",

"npc_dota_beastmaster_boar_#": "Boar",
"npc_dota_lesser_eidolon": "Eidolon",
Expand Down Expand Up @@ -416,7 +419,7 @@
"tab_wardmap": "Wardmap",
"tab_wordcloud": "Wordcloud",
"tab_mmr": "MMR",
"tab_rankings": "Seasonal Rankings",
"tab_rankings": "Rankings",
"tab_benchmarks": "Benchmarks",
"tab_performances": "Performances",
"tab_damage": "Damage",
Expand Down Expand Up @@ -584,7 +587,7 @@

"title_default": "OpenDota - Dota 2 Statistics",
"title_template": "%s - OpenDota - Dota 2 Statistics",
"title_matches": "Professional Matches",
"title_matches": "Matches",
"title_request": "Request a Parse",
"title_search": "Search",
"title_status": "Status",
Expand Down Expand Up @@ -698,11 +701,7 @@
"xp_reasons_2": "Hero",
"xp_reasons_3": "Roshan",

"subheading_ranking": "Based on wins and average visible MMR in matches played",
"teamfight_participation": "Radiant participation/Dire participation",
"teamfight_score": "Radiant kills/Dire kills",
"teamfight_radiant_gold_adv": "Net gold advantage for Radiant",
"teamfight_radiant_xp_adv": "Net XP advantage for Radiant",
"rankings_description": "Based on visible MMR in matches won. Resets each quarter.",

"vision_expired": "Expired after",
"vision_destroyed": "Destroyed after",
Expand Down
3 changes: 3 additions & 0 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import heroRanking, { getHeroRanking } from 'reducers/heroRanking';
import heroBenchmark, { getHeroBenchmark } from 'reducers/heroBenchmark';
import search from 'reducers/search';
import proPlayers, { getProPlayers } from 'reducers/proPlayers';
import publicMatches, { getPublicMatches } from 'reducers/publicMatches';
import proMatches, { getProMatches } from 'reducers/proMatches';
import gotPlayer, {
player,
Expand Down Expand Up @@ -55,6 +56,7 @@ export {
getLocalization as localization,
pvgnaGuides,
getHeroStats as heroStats,
getPublicMatches as publicMatches,
};

export default combineReducers({
Expand All @@ -73,4 +75,5 @@ export default combineReducers({
localization,
pvgnaGuides,
heroStats,
publicMatches,
});
15 changes: 15 additions & 0 deletions src/reducers/publicMatches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { publicMatchesActions } from 'actions';
import { listData, selectors } from './reducerFactory';

const initialState = {
loaded: false,
error: false,
loading: false,
list: [],
};

export default listData(initialState, publicMatchesActions);

export const getPublicMatches = {
...selectors(state => state.app.publicMatches),
};