/** * Copyright (c) 2017 Tomek Wójcik * * 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 (
  • {this.props.name}
  • ); } } 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 ( ); } } 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 ( "" + this.x + "
    " + this.series.name + ": " + this.y + "
    " + "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 (

    {this.props.params.player} stats

    ); } } 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;