307 lines
8.2 KiB
JavaScript
307 lines
8.2 KiB
JavaScript
/**
|
|
* Copyright (c) 2017 Tomek Wójcik <tomek@bthlabs.pl>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
import ClassName from "classnames";
|
|
import React from "react";
|
|
import {connect} from "react-redux";
|
|
import underscore from "underscore";
|
|
|
|
import ChartComponent from "../components/ChartComponent";
|
|
import {cleanup, loadChartData, setMode} from "../actions/playerStats";
|
|
|
|
const WINS_CHART_TITLE_SESSION = "Wins and losses by session";
|
|
const WINS_CHART_TITLE_MAP = "Wins and losses by map";
|
|
const ACCURACY_CHART_TITLE_SESSION = "Average weapon accuracy by session";
|
|
const ACCURACY_CHART_TITLE_MAP = "Average weapon accuracy by map";
|
|
|
|
class ModeLinkComponent extends React.PureComponent {
|
|
constructor (props) {
|
|
super(props);
|
|
this.onClick = this.onClick.bind(this);
|
|
}
|
|
onClick (event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.props.onSwitchMode(this.props.code);
|
|
}
|
|
render () {
|
|
let className = ClassName({
|
|
active: (this.props.mode == this.props.code)
|
|
});
|
|
|
|
return (
|
|
<li className={className}>
|
|
<a onClick={this.onClick} href="#">{this.props.name}</a>
|
|
</li>
|
|
);
|
|
}
|
|
}
|
|
|
|
ModeLinkComponent.propTypes = {
|
|
mode: React.PropTypes.string.isRequired,
|
|
name: React.PropTypes.string.isRequired,
|
|
code: React.PropTypes.string.isRequired,
|
|
onSwitchMode: React.PropTypes.func.isRequired
|
|
};
|
|
|
|
class BaseStatsChartComponent extends React.PureComponent {
|
|
constructor (props) {
|
|
super(props);
|
|
|
|
this.chartConfig = {};
|
|
}
|
|
componentDidMount () {
|
|
this.load();
|
|
}
|
|
componentDidUpdate (prevProps, prevState) {
|
|
if (prevProps.player != this.props.player) {
|
|
this.load(true);
|
|
} else if (prevProps.mode != this.props.mode) {
|
|
this.load();
|
|
}
|
|
}
|
|
kind () {
|
|
throw new Error("Not Implemented");
|
|
}
|
|
load (force) {
|
|
if (underscore.isUndefined(force)) {
|
|
force = false;
|
|
}
|
|
|
|
let hasData = true;
|
|
if (this.props.mode == "session") {
|
|
hasData = hasData && this.props.sessionData.categories.length > 0;
|
|
hasData = hasData && this.props.sessionData.series.length > 0;
|
|
} else {
|
|
hasData = hasData && this.props.mapData.categories.length > 0;
|
|
hasData = hasData && this.props.mapData.series.length > 0;
|
|
}
|
|
|
|
if (force || !hasData) {
|
|
this.props.dispatch(loadChartData(
|
|
this.props.dispatch, this.props.player, this.kind(), this.props.mode
|
|
));
|
|
}
|
|
}
|
|
title () {
|
|
throw new Error("Not Implemented");
|
|
}
|
|
render () {
|
|
let categories = this.props.sessionData.categories;
|
|
let series = this.props.sessionData.series;
|
|
|
|
if (this.props.mode == "map") {
|
|
categories = this.props.mapData.categories;
|
|
series = this.props.mapData.series;
|
|
}
|
|
|
|
return (
|
|
<ChartComponent
|
|
config={this.chartConfig}
|
|
categories={categories}
|
|
series={series}
|
|
title={this.title()} />
|
|
);
|
|
}
|
|
}
|
|
|
|
class WinsChartComponent extends BaseStatsChartComponent {
|
|
constructor (props) {
|
|
super(props);
|
|
|
|
this.chartConfig = {
|
|
"chart": {
|
|
"type": "column"
|
|
},
|
|
"credits": {
|
|
"enabled": false
|
|
},
|
|
"yAxis": {
|
|
"title": {
|
|
"text": "Score"
|
|
}
|
|
},
|
|
"plotOptions": {
|
|
"column": {
|
|
"pointPadding": 0.2,
|
|
"borderWidth": 0,
|
|
"stacking": "normal",
|
|
"dataLabels": {
|
|
"enabled": true,
|
|
"color": "white",
|
|
"style": {
|
|
"textShadow": "0 0 3px black"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"tooltip": {
|
|
"formatter": function () {
|
|
return (
|
|
"<b>" + this.x + "</b><br/>" + this.series.name + ": " +
|
|
this.y + "<br/>" + "Total: " + this.point.stackTotal
|
|
);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
kind () {
|
|
return "wins";
|
|
}
|
|
title () {
|
|
if (this.props.mode == "map") {
|
|
return WINS_CHART_TITLE_MAP;
|
|
}
|
|
|
|
return WINS_CHART_TITLE_SESSION;
|
|
}
|
|
render () {
|
|
return super.render();
|
|
}
|
|
}
|
|
|
|
WinsChartComponent.propTypes = {
|
|
player: React.PropTypes.string.isRequired,
|
|
mode: React.PropTypes.string.isRequired,
|
|
sessionData: React.PropTypes.object.isRequired,
|
|
mapData: React.PropTypes.object.isRequired
|
|
};
|
|
|
|
class AccuracyChartComponent extends BaseStatsChartComponent {
|
|
constructor (props) {
|
|
super(props);
|
|
|
|
this.chartConfig = {
|
|
"credits": {
|
|
"enabled": false
|
|
},
|
|
"tooltip": {
|
|
"valueDecimals": 2,
|
|
"valueSuffix": "%"
|
|
},
|
|
"yAxis": {
|
|
"title": {
|
|
"text": "Avg accuracy [%]"
|
|
}
|
|
}
|
|
};
|
|
}
|
|
kind () {
|
|
return "accuracy";
|
|
}
|
|
title () {
|
|
if (this.props.mode == "map") {
|
|
return ACCURACY_CHART_TITLE_MAP;
|
|
}
|
|
|
|
return ACCURACY_CHART_TITLE_SESSION;
|
|
}
|
|
render () {
|
|
return super.render();
|
|
}
|
|
}
|
|
|
|
AccuracyChartComponent.propTypes = {
|
|
player: React.PropTypes.string.isRequired,
|
|
mode: React.PropTypes.string.isRequired,
|
|
sessionData: React.PropTypes.object.isRequired,
|
|
mapData: React.PropTypes.object.isRequired
|
|
};
|
|
|
|
class PlayerStatsView extends React.PureComponent {
|
|
constructor (props) {
|
|
super(props);
|
|
this.onSwitchMode = this.onSwitchMode.bind(this);
|
|
}
|
|
componentWillReceiveProps (nextProps) {
|
|
if (nextProps.params.player != this.props.params.player) {
|
|
this.props.dispatch(cleanup());
|
|
}
|
|
}
|
|
onSwitchMode (mode) {
|
|
this.props.dispatch(setMode(mode));
|
|
}
|
|
componentWillUnmount () {
|
|
this.props.dispatch(cleanup());
|
|
}
|
|
render () {
|
|
return (
|
|
<div>
|
|
<h2>{this.props.params.player} stats</h2>
|
|
|
|
<ul className="nav nav-tabs">
|
|
<ModeLinkComponent
|
|
mode={this.props.mode}
|
|
name="By session"
|
|
code="session"
|
|
onSwitchMode={this.onSwitchMode} />
|
|
|
|
<ModeLinkComponent
|
|
mode={this.props.mode}
|
|
name="By map"
|
|
code="map"
|
|
onSwitchMode={this.onSwitchMode} />
|
|
</ul>
|
|
|
|
<WinsChartComponent
|
|
player={this.props.params.player}
|
|
mode={this.props.mode}
|
|
sessionData={this.props.sessionWinsChartData}
|
|
mapData={this.props.mapWinsChartData}
|
|
dispatch={this.props.dispatch} />
|
|
|
|
<AccuracyChartComponent
|
|
player={this.props.params.player}
|
|
mode={this.props.mode}
|
|
sessionData={this.props.sessionAccuracyChartData}
|
|
mapData={this.props.mapAccuracyChartData}
|
|
dispatch={this.props.dispatch} />
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
PlayerStatsView.propTypes = {
|
|
mode: React.PropTypes.string.isRequired,
|
|
sessionWinsChartData: React.PropTypes.object.isRequired,
|
|
mapWinsChartData: React.PropTypes.object.isRequired,
|
|
sessionAccuracyChartData: React.PropTypes.object.isRequired,
|
|
mapAccuracyChartData: React.PropTypes.object.isRequired,
|
|
params: React.PropTypes.object.isRequired
|
|
};
|
|
|
|
const mapStateToProps = (state, props) => {
|
|
return {
|
|
mode: state.playerStats.mode,
|
|
sessionWinsChartData: state.playerStats.sessionWinsChartData,
|
|
mapWinsChartData: state.playerStats.mapWinsChartData,
|
|
sessionAccuracyChartData: state.playerStats.sessionAccuracyChartData,
|
|
mapAccuracyChartData: state.playerStats.mapAccuracyChartData,
|
|
params: props.params
|
|
};
|
|
};
|
|
|
|
const reduxContainer = connect(mapStateToProps)(PlayerStatsView);
|
|
|
|
export default reduxContainer;
|