diff --git a/app/components/ConsoleView/ConsoleView.js b/app/components/ConsoleView/ConsoleView.js index e45eacb8..4f3c8e14 100644 --- a/app/components/ConsoleView/ConsoleView.js +++ b/app/components/ConsoleView/ConsoleView.js @@ -8,7 +8,7 @@ import {sendCommandToServer} from '../../utils/sendCommandToServer'; import {log} from '../../utils/loggerUtils'; const welcomeMessage = `MisRCON-by @Csprance -v0.0.1 - Babycakes +v0.0.2 - Macadocious Type help for more options. -------------------------- diff --git a/app/components/InfoView/InfoView.js b/app/components/InfoView/InfoView.js index b8c1f4c7..25e39643 100644 --- a/app/components/InfoView/InfoView.js +++ b/app/components/InfoView/InfoView.js @@ -11,7 +11,7 @@ export default class InfoView extends Component { render() { return (
- TODO: Bans TAB + TODO: INFO TAB
); } diff --git a/app/components/PlayersView/PlayerCard.js b/app/components/PlayersView/PlayerCard.js new file mode 100644 index 00000000..fa294070 --- /dev/null +++ b/app/components/PlayersView/PlayerCard.js @@ -0,0 +1,97 @@ +/** + * Name: PlayersCard + * Author: Chrissprance + * Creation Date: 12/9/2016 + * Description: Component used to display player information from the server as well as from the localStorage + */ +import React, { + Component, + PropTypes, +} from 'react'; +import styled from 'styled-components'; +import {Card, CardActions, CardHeader, CardTitle, CardText} from 'material-ui/Card'; +import FlatButton from 'material-ui/FlatButton'; +import TextField from 'material-ui/TextField'; +import store from 'store'; +import axios from 'axios'; + +import Spacer from '../common/Spacer'; +import {darkGrey, white} from '../../styles/colors'; +import {log} from '../../utils/loggerUtils'; +import ExternalLink from '../common/ExternalLink' + + +class PlayersCard extends Component { + constructor(props, context) { + super(props, context); + this.state = { + avatar: 'http://placehold.it/42x42', + notes: store.get(this.props.steam) !== undefined ? store.get(this.props.steam).notes : '' + } + } + + componentWillMount() { + this.getAvatar(); + } + + getAvatar = () => { + axios.get('https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/', { + params: { + key: 'C4E62F89FF5D569A481850BCD3098D52', + steamids: this.props.steam + } + }).then((res) => { + this.setState({ + avatar: res.data.response.players[0].avatar, + }); + }).catch((err) => { + log('error', err); + }); + }; + + updateNotes = (e) => { + this.setState({ + notes: e.target.value, + }); + store.set(this.props.steam, {avatar: this.state.avatar, notes: e.target.value}); + }; + + render() { + const link = String('https://steamrep.com/profiles/' + this.props.steam); + return ( + + + {this.props.steam}} + /> + + + + + + + + + + + ); + } +} + +PlayersCard.propTypes = {}; +PlayersCard.defaultProps = {}; + +const PCard = styled.div` + flex-basis: 20%; + margin: 5px; +`; + + +export default PlayersCard; diff --git a/app/components/PlayersView/PlayersView.js b/app/components/PlayersView/PlayersView.js index cc8492da..d47f8069 100644 --- a/app/components/PlayersView/PlayersView.js +++ b/app/components/PlayersView/PlayersView.js @@ -2,17 +2,148 @@ * Name: PlayersView * Author: Chrissprance * Creation Date: 12/8/2016 - * Description: + * Description: Contains the list of all the players currently on the server */ import React, {Component} from 'react'; +import styled, {keyframes} from 'styled-components'; +import store from 'store'; +import TextField from 'material-ui/TextField'; +import FloatingActionButton from 'material-ui/FloatingActionButton'; +import RefreshIcon from 'material-ui/svg-icons/navigation/refresh'; +import fuzzy from 'fuzzy'; + +import Spacer from '../common/Spacer'; +import {JSONifyStatus} from '../../utils/JSONifyStatus'; +import {sendCommandToServer} from '../../utils/sendCommandToServer'; +import {log} from '../../utils/loggerUtils'; +import {white} from '../../styles/colors'; +import PlayerCard from './PlayerCard'; + export default class PlayersView extends Component { + constructor(props, context) { + super(props, context); + this.state = { + loading: false, + credentials: store.get('userCredentials'), + players: [], + searchString: '' + }; + } + + componentWillMount() { + // Go and grab the player list from the server. + this.getPlayersAndAddToState(); + } + + getPlayersAndAddToState = () => { + this.setState({ + loading: true, + }); + + sendCommandToServer('status', this.state.credentials) + .then((res) => { + if (res !== null) { + this.setState({ + players: JSONifyStatus(res).players, + loading: false, + }); + } + }) + .catch((err) => { + log('error', err); + }); + }; + + + updateSearchString = (e) => { + this.setState({ + searchString: e.target.value + }); + }; + + render() { + const options = { + extract: (el) => { + return el.name + } + }; + const fuzzyList = fuzzy.filter(this.state.searchString, this.state.players, options).map((el) => { + return el.string; + }); + const filterList = this.state.players.filter((player) => { + return fuzzyList.indexOf(player.name) >= 0 + }); return ( -
- TODO: Bans TAB -
+ + + + + + + { (this.state.loading === true ? : ) } + + + + + {filterList.map((player) => { + return ( + + ) + })} + + ); } } + + +const rotate360 = keyframes` + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +`; + +const AnimatedRefresh = styled(RefreshIcon)` + display: inline-block; + animation: ${rotate360} 2s linear infinite; +`; +const Container = styled.div` + width: 100%; + display: flex; + flex-direction: column; +`; + +const SearchBar = styled(TextField)` + width: 40%; +`; + +const Actions = styled.div` + height: 100px; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + padding-bottom: 25px; + flex-shrink: 1; +`; + + +const PlayerList= styled.div` + width: 100%; + display: flex; + flex-flow: row wrap; + padding: 10px; + overflow-y: auto; + align-items: flex-start; + justify-content: center; +`; + + diff --git a/app/components/common/ExternalLink.js b/app/components/common/ExternalLink.js new file mode 100644 index 00000000..848eea4a --- /dev/null +++ b/app/components/common/ExternalLink.js @@ -0,0 +1,33 @@ +/** + * Name: ExternalLink + * Author: Chrissprance + * Creation Date: 12/9/2016 + * Description: Handles sending a user to an external browser link + */ +import {log} from '../../utils/loggerUtils'; +import React, { + PropTypes, +} from 'react'; +import styled from 'styled-components'; + +const ExternalLink = (props) => { + const handleClick = () => { + require('electron').shell.openExternal(props.to); + log(`Navigating to External Link: ${props.to}`); + }; + + return ( + + {props.children} + + ); +}; + +ExternalLink.propTypes = { + to: PropTypes.string.isRequired +}; + +const Pointer = styled.div` + cursor: pointer; +`; +export default ExternalLink; diff --git a/app/components/common/Spacer.js b/app/components/common/Spacer.js new file mode 100644 index 00000000..f5d43799 --- /dev/null +++ b/app/components/common/Spacer.js @@ -0,0 +1,28 @@ +/** + * Name: Spacer + * Author: Chrissprance + * Creation Date: 12/9/2016 + * Description: Spacer Component + */ +import React, { + PropTypes, +} from 'react'; +import styled from 'styled-components'; + +const Spacer = (props) => { + return ( + + + + ); +}; + + +Spacer.propTypes = {}; +Spacer.defaultProps = {}; +const SpacerDiv = styled.div` + flex-grow: 1; +`; + +export default Spacer; + diff --git a/app/containers/HomePage.js b/app/containers/HomePage.js index 0e530cec..e8c0d3d7 100644 --- a/app/containers/HomePage.js +++ b/app/containers/HomePage.js @@ -1,3 +1,5 @@ +// Handle the login + import React, {Component} from 'react'; import store from 'store'; @@ -30,6 +32,7 @@ export default class HomePage extends Component { //noinspection JSMethodCanBeStatic getStoredCredentials() { + // returns false if there are no stored credentials return store.get('userCredentials') !== undefined; } diff --git a/app/package.json b/app/package.json index b826ac8f..f1a48e99 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "MisRCON", "productName": "MisRCON", - "version": "0.0.1", + "version": "0.0.2", "description": "RCON Tool for Miscreated game.", "main": "./main.js", "author": { diff --git a/app/utils/JSONifyStatus.js b/app/utils/JSONifyStatus.js new file mode 100644 index 00000000..b656a362 --- /dev/null +++ b/app/utils/JSONifyStatus.js @@ -0,0 +1,89 @@ +/** + * Name: JSONifyStatus + * Author: Chrissprance + * Creation Date: 12/9/2016 + * Description: takes a string of a status command and JSONifys it. + */ + + +function getServerStatus(str) { + const regex = /-*[\s\S]*- */g; + return regex.exec(str)[0]; +} + + +function getStatusObjectFromString(str) { + const serverNameRE = new RegExp('name: (.*)\r'); + const ipRE = new RegExp('ip: (.*)\r'); + const versionRE = new RegExp('version: (.*)\r'); + const levelRE = new RegExp('level: (.*)\r'); + const gamerulesRE = new RegExp('gamerules: (.*)\r'); + const playersRE = new RegExp('players: (.*)\r'); + + return { + name: serverNameRE.exec(str) !== null ? serverNameRE.exec(str)[1] : '', + ip: ipRE.exec(str) !== null ? ipRE.exec(str)[1] : '', + version: versionRE.exec(str) !== null ? versionRE.exec(str)[1] : '', + level: levelRE.exec(str) !== null ? levelRE.exec(str)[1] : '', + gameRules: gamerulesRE.exec(str) !== null ? gamerulesRE.exec(str)[1] : '', + players: playersRE.exec(str) !== null ? playersRE.exec(str)[1] : '' + }; + +} + + +function getPlayersString(str) { + let pString = /Connection Status:[\s\S]*.*/g; + let newStr = pString.exec(String(str)); + return newStr[0].replace('Connection Status:\r', ''); +} + + +function splitPlayerStringRowsIntoArray(str) { + let stringArray = str.split('\r'); + let playersArray = []; + + const steamIdRE = new RegExp('steam: (.*) name:'); + const nameRE = new RegExp('name: (.*) entID:'); + const entIDRE = new RegExp('entID:(.*) id:'); + const idRE = new RegExp('id:(.*) ip:'); + const ipRE = new RegExp('ip:(.*) ping:'); + const pingRE = new RegExp('ping:(.*) state:'); + const stateRE = new RegExp('state:(.*) profile:'); + const profileRE = new RegExp('profile: (.*)'); + + stringArray.forEach((player) => { + playersArray.push({ + steam: steamIdRE.exec(player) !== null ? steamIdRE.exec(player)[1] : '', + name: nameRE.exec(player) !== null ? nameRE.exec(player)[1] : '', + entID: entIDRE.exec(player) !== null ? entIDRE.exec(player)[1] : '', + id: idRE.exec(player) !== null ? idRE.exec(player)[1] : '', + ip: ipRE.exec(player) !== null ? ipRE.exec(player)[1] : '', + ping: pingRE.exec(player) !== null ? pingRE.exec(player)[1] : '', + state: stateRE.exec(player) !== null ? stateRE.exec(player)[1] : '', + profile: profileRE.exec(player) !== null ? profileRE.exec(player)[1] : '', + }); + }); + + return playersArray.filter((player) => { + return player.steam !== '' + }) +} + + +export function JSONifyStatus(statusString) { + let serverStatusString = getServerStatus(statusString); + let serverStatusObject = getStatusObjectFromString(serverStatusString); + + let playersString = getPlayersString(statusString); + let playersArray = splitPlayerStringRowsIntoArray(playersString); + + + return { + serverStatus: { + serverStatusObject + }, + players: playersArray + } +} + diff --git a/app/utils/loggerUtils.js b/app/utils/loggerUtils.js index 3bddd17e..a89a3d51 100644 --- a/app/utils/loggerUtils.js +++ b/app/utils/loggerUtils.js @@ -12,9 +12,9 @@ import format from 'date-fns/format'; /** * Replaces the time of a date [time, date] * @param: {string} lvl the log level to log at [silly, debug, info, warn, error] defaults to info - * @param: {msg} msg the msg or object to log out + * @param: {msg} msg the msg or object to log out */ -export function log(lvl='info', msg) { +export function log(lvl = 'info', msg) { let logPath = 'misrcon.log'; let logMsg = `[${lvl}] - ${format(Date.now(), 'MM/DD/YY @ HH:mm:ss')} - ${msg}\n`; // some things we don't want to log to a file what are they? @@ -29,7 +29,7 @@ export function log(lvl='info', msg) { } else { // if it's not in the ignore file log list then log it to a file fs.appendFile(logPath, logMsg, (err) => { - console.log(err); + if (err) console.log(err); }); } } diff --git a/package.json b/package.json index 12367e08..49de033e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "MisRCON", "productName": "MisRCON", - "version": "0.0.1", + "version": "0.0.2", "description": "RCON Tool for Miscreated game.", "main": "main.js", "scripts": { @@ -161,6 +161,7 @@ "date-fns": "^1.11.2", "electron-debug": "^1.1.0", "font-awesome": "^4.7.0", + "fuzzy": "^0.1.3", "jquery": "^3.1.1", "json2xml": "^0.1.3", "later": "^1.2.0", diff --git a/webpack.config.development.js b/webpack.config.development.js index 5e56df9c..6e30cbfa 100644 --- a/webpack.config.development.js +++ b/webpack.config.development.js @@ -3,7 +3,7 @@ * Build config for development process that uses Hot-Module-Replacement * https://webpack.github.io/docs/hot-module-replacement-with-webpack.html */ - +import path from 'path'; import webpack from 'webpack'; import validate from 'webpack-validator'; import merge from 'webpack-merge';