Skip to content

Commit

Permalink
Merge pull request #712 from odota/public_matches
Browse files Browse the repository at this point in the history
add highest/lowest mmr matches
  • Loading branch information
howardchung committed Feb 17, 2017
2 parents c5b57b0 + 297c595 commit 2d75a85
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 29 deletions.
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);
}
}
101 changes: 93 additions & 8 deletions src/components/Matches/index.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
/* global API_HOST */
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 heroes from 'dotaconstants/build/heroes.json';
import styles from './Matches.css';

const matchesColumns = [{
displayName: strings.th_match_id,
Expand All @@ -31,33 +35,114 @@ 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,
},
{
displayName: <span className={matchStyles.teamIconContainer} ><IconRadiant className={matchStyles.iconRadiant} />{strings.general_radiant}</span>,
field: 'radiant_team',
displayFn: (row, col, field) => (field || '').split(',').map(heroId =>
<img key={heroId} style={{ width: '50px' }} src={`${API_HOST}${heroes[heroId].img}`} role="presentation" />),
},
{
displayName: <span className={matchStyles.teamIconContainer} ><IconDire className={matchStyles.iconDire} />{strings.general_dire}</span>,
field: 'dire_team',
displayFn: (row, col, field) => (field || '').split(',').map(heroId =>
<img key={heroId} style={{ width: '50px' }} src={`${API_HOST}${heroes[heroId].img}`} role="presentation" />),
},
];

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
14 changes: 7 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 @@ -551,6 +554,7 @@
"th_party_mmr": "Party MMR",
"th_estimated_mmr": "Estimated MMR",
"th_permanent_buffs": "Buffs",
"th_winner": "Winner",

"ward_log_type": "Type",
"ward_log_owner": "Owner",
Expand Down Expand Up @@ -584,7 +588,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 +702,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),
};

0 comments on commit 2d75a85

Please sign in to comment.