Massive refactor complete

This commit is contained in:
2019-05-31 13:44:01 +10:00
parent b97d8fc184
commit 4a2bfb3db9
47 changed files with 1223 additions and 991 deletions
+147
View File
@@ -0,0 +1,147 @@
import React from 'react';
import { connect } from 'react-redux';
import queryString from 'query-string';
import PropTypes from 'prop-types';
//panels
import CommonLinks from '../panels/common_links.jsx';
import PagedCombatLog from '../panels/paged_combat_log.jsx';
class CombatLog extends React.Component {
constructor(props) {
super(props);
let params = queryString.parse(props.location.search);
this.state = {
params: params,
start: parseInt(params.log) || 0,
length: parseInt(params.length) || 20,
fetch: null,
warning: ''
};
}
componentDidMount() {
if (!this.props.loggedIn) {
this.props.history.replace('/login');
}
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (JSON.stringify(this.state) !== JSON.stringify(prevState)) {
this.state.fetch();
}
}
render() {
let warningStyle = {
display: this.state.warning.length > 0 ? 'flex' : 'none'
};
let ButtonHeader = this.buttonHeader.bind(this);
return (
<div className='panel'>
<div className='sidePanelPage'>
<div className='sidePanel'>
<CommonLinks />
</div>
<div className='mainPanel'>
<div className='warning' style={warningStyle}>
<p>{this.state.warning}</p>
</div>
<ButtonHeader />
<PagedCombatLog
setWarning={this.setWarning.bind(this)}
username={this.props.username}
start={this.state.start}
length={this.state.length}
getFetch={this.getFetch.bind(this)}
onReceived={this.onReceived.bind(this)}
/>
<ButtonHeader />
</div>
</div>
</div>
);
}
buttonHeader() {
return (
<div className='table'>
<div className='row'>
<button className='col' onClick={ this.decrement.bind(this) }>{'< Back'}</button>
<div className='col' />
<div className='col' />
<button className='col' onClick={ this.increment.bind(this) }>{'Next >'}</button>
</div>
</div>
);
}
increment() {
let start = this.state.start + this.state.length;
this.props.history.push(`${this.props.location.pathname}?log=${start}`);
}
decrement() {
let start = Math.max(0, this.state.start - this.state.length);
//don't decrement too far
if (start === this.state.start) {
return;
}
this.props.history.push(`${this.props.location.pathname}?log=${start}`);
}
//bound callbacks
getFetch(fn) {
this.setState({ fetch: fn });
}
onReceived(data) {
if (data.length === 0) {
let start = Math.max(0, this.state.start - this.state.length);
//don't decrement too far
if (start === this.state.start) {
return;
}
this.props.history.replace(`${this.props.location.pathname}?log=${start}`);
}
}
setWarning(s) {
this.setState({ warning: s });
}
};
CombatLog.propTypes = {
username: PropTypes.string.isRequired
};
const mapStoreToProps = (store) => {
return {
username: store.account.username,
loggedIn: store.account.id !== 0
};
};
const mapDispatchToProps = (dispatch) => {
return {
//
};
};
CombatLog = connect(mapStoreToProps, mapDispatchToProps)(CombatLog);
export default CombatLog;
+20 -21
View File
@@ -1,23 +1,28 @@
import React from 'react';
import { connect } from 'react-redux';
import { withRouter, Link } from 'react-router-dom';
//panels
import CommonLinks from '../panels/common_links.jsx';
import Blurb from '../panels/blurb.jsx';
import NewsPanel from '../panels/news_panel.jsx';
import News from '../panels/news.jsx';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
//
warning: '', //TODO: unified warning?
fetch: null
};
}
//rendering function
componentDidUpdate(prevProps, prevState, snapshot) {
this.state.fetch();
}
render() {
//return the home page
let warningStyle = {
display: this.state.warning.length > 0 ? 'flex' : 'none'
};
return (
<div className='page'>
<div className='sidePanelPage'>
@@ -26,29 +31,23 @@ class Home extends React.Component {
</div>
<div className='mainPanel'>
<div className='warning' style={warningStyle}>
<p>{this.state.warning}</p>
</div>
<h1 className='centered'>About</h1>
<Blurb />
<h1 className='centered'>News</h1>
<NewsPanel />
<News setWarning={this.setWarning.bind(this)} getFetch={ (fn) => this.setState({ fetch: fn }) } />
</div>
</div>
</div>
);
}
}
function mapStoreToProps(store) {
return {
id: store.account.id
setWarning(s) {
this.setState({ warning: s });
}
}
};
function mapDispatchToProps(dispatch) {
return {
//
}
}
Home = connect(mapStoreToProps, mapDispatchToProps)(Home);
export default withRouter(Home);
export default Home;
+8 -4
View File
@@ -1,6 +1,5 @@
import React from 'react';
import queryString from 'query-string';
import { withRouter } from 'react-router-dom';
import CommonLinks from '../panels/common_links.jsx';
import PagedLadder from '../panels/paged_ladder.jsx';
@@ -38,7 +37,12 @@ class Ladder extends React.Component {
<div className='mainPanel'>
<h1 className='centered'>Game Ladder</h1>
<ButtonHeader />
<PagedLadder start={this.state.start} length={this.state.length} getFetch={this.getFetch.bind(this)} onReceived={this.onReceived.bind(this)} />
<PagedLadder
start={this.state.start}
length={this.state.length}
getFetch={this.getFetch.bind(this)}
onReceived={this.onReceived.bind(this)}
/>
<ButtonHeader />
</div>
</div>
@@ -93,6 +97,6 @@ class Ladder extends React.Component {
this.props.history.replace(`${this.props.location.pathname}?rank=${start}`);
}
}
}
};
export default withRouter(Ladder);
export default Ladder;
+1 -1
View File
@@ -15,7 +15,7 @@ class Login extends React.Component {
render() {
return (
<div className='page constrained'>
<LoginPanel onSubmit={() => this.props.history.push('/profile')} />
<LoginPanel onSuccess={(msg) => this.props.history.push('/profile')} />
<Link to='/' className='centered'>Return Home</Link>
</div>
);
+6 -3
View File
@@ -4,13 +4,16 @@ import { withRouter, Link } from 'react-router-dom';
class PageNotFound extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.state = {
//
};
}
render() {
let style = {
justifyContent: 'center'
}
};
return (
<div className='page centered' style={style}>
<h1>Page Not Found</h1>
@@ -18,6 +21,6 @@ class PageNotFound extends React.Component {
</div>
);
}
}
};
export default withRouter(PageNotFound);
+12 -13
View File
@@ -9,13 +9,12 @@ class PasswordChange extends React.Component {
constructor(props) {
super(props);
this.state = {
changeSent: false,
changeMsg: ''
changed: ''
}
}
componentDidMount() {
if (!this.props.id) {
if (!this.props.loggedIn) {
this.props.history.push('/');
}
}
@@ -23,13 +22,13 @@ class PasswordChange extends React.Component {
render() {
let Panel;
if (!this.state.changeSent) {
if (!this.state.changed) {
Panel = () => {
return (<PasswordChangePanel onPasswordChange={(msg) => this.setState({ changeSent: true, changeMsg: msg }) } />);
return (<PasswordChangePanel onSuccess={(msg) => this.setState({ changed: msg }) } />);
}
} else {
Panel = () => {
return (<p>{this.state.changeMsg}</p>);
return (<p>{this.state.changed}</p>);
}
}
@@ -42,17 +41,17 @@ class PasswordChange extends React.Component {
}
};
function mapStoreToProps(store) {
const mapStoreToProps = (store) => {
return {
id: store.account.id
}
}
loggedIn: store.account.id !== 0
};
};
function mapDispatchToProps(dispatch) {
const mapDispatchToProps = (dispatch) => {
return {
//
}
}
};
};
PasswordChange = connect(mapStoreToProps, mapDispatchToProps)(PasswordChange);
+4 -5
View File
@@ -8,21 +8,20 @@ class PasswordRecover extends React.Component {
constructor(props) {
super(props);
this.state = {
recoverSent: false,
recoverMsg: ''
recovered: ''
}
}
render() {
let Panel;
if (!this.state.recoverSent) {
if (!this.state.recovered) {
Panel = () => {
return (<PasswordRecoverPanel onEmailSent={(msg) => this.setState( {recoverSent: true, recoverMsg: msg} )} />);
return (<PasswordRecoverPanel onSuccess={(msg) => this.setState( {recovered: msg} )} />);
}
} else {
Panel = () => {
return (<p>{this.state.recoverMsg}</p>);
return (<p>{this.state.recovered}</p>);
}
}
+3 -5
View File
@@ -1,6 +1,5 @@
import React from 'react';
import { withRouter, Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import queryString from 'query-string';
//panels
@@ -10,8 +9,7 @@ class PasswordReset extends React.Component {
constructor(props) {
super(props);
this.state = {
reset: false,
resetMsg: '',
reset: '',
params: queryString.parse(props.location.search)
}
}
@@ -21,11 +19,11 @@ class PasswordReset extends React.Component {
if (!this.state.reset) {
Panel = () => {
return (<PasswordResetPanel email={this.state.params.email} token={this.state.params.token} onPasswordReset={(msg) => this.setState( {reset: true, resetMsg: msg} )}/>);
return (<PasswordResetPanel email={this.state.params.email} token={this.state.params.token} onSuccess={ (msg) => this.setState({reset: msg}) } />);
}
} else {
Panel = () => {
return (<p>{this.state.resetMsg}</p>);
return (<p>{this.state.reset}</p>);
}
}
+120 -147
View File
@@ -1,49 +1,25 @@
import React from 'react';
import { connect } from 'react-redux';
import { withRouter, Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import queryString from 'query-string';
//actions
import { storeProfile } from '../../actions/profile.js';
//panels
import CommonLinks from '../panels/common_links.jsx';
import AttackButton from '../panels/attack_button.jsx';
import Equipment from '../panels/equipment.jsx';
import CombatLog from '../panels/combat_log.jsx';
class Profile extends React.Component {
constructor(props) {
super(props);
let params = queryString.parse(props.location.search);
this.state = {
params: params,
username: '',
gold: 0,
recruits: 0,
soldiers: 0,
spies: 0,
scientists: 0,
warning: '',
//combat log
start: params.log,
//equipment
fetchStatistics: null,
fetchEquipment: null
params: queryString.parse(props.location.search),
warning: '', //TODO: unified warning?
};
this.sendRequest('/profilerequest', this.state.params.username ? this.state.params.username : this.props.username);
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (JSON.stringify(this.state) !== JSON.stringify(prevState)) {
// if (this.state.fetchStatistics) this.state.fetchStatistics();
// if (this.state.fetchEquipment) this.state.fetchEquipment();
}
this.sendRequest('/profilerequest', {username: this.state.params.username ? this.state.params.username : this.props.account.username});
}
render() {
@@ -54,8 +30,8 @@ class Profile extends React.Component {
//side panel stuff
let SidePanel;
if (this.props.id) {
if (this.props.username === this.state.username) {
if (this.props.account.id) {
if (this.props.account.username === this.props.profile.username) {
SidePanel = this.MyProfileSidePanel.bind(this);
} else {
SidePanel = this.NotMyProfileSidePanel.bind(this);
@@ -67,13 +43,12 @@ class Profile extends React.Component {
//main panel
let MainPanel;
if (this.props.id) {
if (this.props.account.id) {
//logged in
if (this.state.username === this.props.username) {
if (this.props.account.username === this.props.profile.username) {
MainPanel = this.MyProfileMainPanel.bind(this);
} else {
//not logged in
if (this.state.username !== '') {
if (this.props.profile.username) {
MainPanel = this.NotMyProfileMainPanel.bind(this);
} else {
MainPanel = this.ProfileNotFoundMainPanel.bind(this);
@@ -81,7 +56,7 @@ class Profile extends React.Component {
}
} else {
//not logged in
if (this.state.username !== '') {
if (this.props.profile.username) {
MainPanel = this.LoggedOutMainPanel.bind(this);
} else {
MainPanel = this.ProfileNotFoundMainPanel.bind(this);
@@ -107,23 +82,17 @@ class Profile extends React.Component {
}
//gameplay functions
sendRequest(url, username = this.props.username, role = '') { //NOTE: merged all requests here
//request this profile's info, using my credentials
let formData = new FormData();
formData.append('id', this.props.id);
formData.append('token', this.props.token);
formData.append('username', username);
formData.append('role', role);
sendRequest(url, args = {}) { //send a unified request, using my credentials
//build the XHR
let xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let json = JSON.parse(xhr.responseText);
this.storeProfile(
this.props.storeProfile(
json.username,
json.gold,
json.recruits,
@@ -136,27 +105,18 @@ class Profile extends React.Component {
this.setWarning(xhr.responseText);
}
}
}
};
//send
xhr.open('POST', url, true);
xhr.send(formData);
}
storeProfile(username, gold, recruits, soldiers, spies, scientists) {
this.setState({
username: username,
gold: gold,
recruits: recruits,
soldiers: soldiers,
spies: spies,
scientists: scientists
});
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
xhr.send(JSON.stringify({
id: this.props.account.id,
token: this.props.account.token,
...args
}));
}
//panel functions
MyProfileSidePanel() {
//return the side panel
return (
<div className='sidePanel'>
<CommonLinks />
@@ -171,52 +131,50 @@ class Profile extends React.Component {
<div className='table noCollapse'>
<div className='row'>
<p className='col'>Username:</p>
<p className='col'>{this.state.username}</p>
<p className='col'>{this.props.profile.username}</p>
<div className='col'></div>
<div className='col'></div>
</div>
<div className='row'>
<p className='col'>Gold:</p>
<p className='col'>{this.state.gold}</p>
<div className='col' style={{flex: '2 1 1.5%'}}>(+1 gold for each recruit every half hour)</div>
<p className='col'>{this.props.profile.gold}</p>
<p className='col' style={{flex: '2 1 1%'}}>(+1 gold for each recruit every half hour)</p>
</div>
<div className='row'>
<p className='col'>Recruits:</p>
<p className='col'>{this.state.recruits}</p>
<button className='col' style={{flex: '2 1 1.5%'}} onClick={() => this.sendRequest('/recruitrequest')}>Recruit More Units</button>
<p className='col'>{this.props.profile.recruits}</p>
<button className='col' style={{flex: '2 1 1%'}} onClick={ () => this.sendRequest('/recruitrequest') }>Recruit More Units</button>
</div>
<div className='row'>
<p className='col'>Soldiers:</p>
<p className='col'>{this.state.soldiers}</p>
<button className='col' onClick={() => this.sendRequest('/trainrequest', this.props.username, 'soldier')}>Train Soldier (100 gold)</button>
<button className='col' onClick={() => this.sendRequest('/untrainrequest', this.props.username, 'soldier')}>Untrain Soldier</button>
<p className='col'>{this.props.profile.soldiers}</p>
<button className='col' onClick={ () => this.sendRequest('/trainrequest', {role: 'soldier'}) }>Train Soldier (100 gold)</button>
<button className='col' onClick={ () => this.sendRequest('/untrainrequest', {role: 'soldier'}) }>Untrain Soldier</button>
</div>
<div className='row'>
<p className='col'>Spies:</p>
<p className='col'>{this.state.spies}</p>
<button className='col' onClick={() => this.sendRequest('/trainrequest', this.props.username, 'spy')}>Train Spy (200 gold)</button>
<button className='col' onClick={() => this.sendRequest('/untrainrequest', this.props.username, 'spy')}>Untrain Spy</button>
<p className='col'>{this.props.profile.spies}</p>
<button className='col' onClick={ () => this.sendRequest('/trainrequest', {role: 'spy'}) }>Train Spy (200 gold)</button>
<button className='col' onClick={ () => this.sendRequest('/untrainrequest', {role: 'spy'}) }>Untrain Spy</button>
</div>
<div className='row'>
<p className='col'>Scientists:</p>
<p className='col'>{this.state.scientists}</p>
<button className='col' onClick={() => this.sendRequest('/trainrequest', this.props.username, 'scientist')}>Train Scientist (120 gold)</button>
<button className='col' onClick={() => this.sendRequest('/untrainrequest', this.props.username, 'scientist')}>Untrain Scientist</button>
<p className='col'>{this.props.profile.scientists}</p>
<button className='col' onClick={ () => this.sendRequest('/trainrequest', {role: 'scientist'}) }>Train Scientist (120 gold)</button>
<button className='col' onClick={ () => this.sendRequest('/untrainrequest', {role: 'scientist'}) }>Untrain Scientist</button>
</div>
</div>
<br />
<h1 className='centered'>Equipment</h1>
<Equipment username={this.props.username} token={this.props.token} scientists={1} getFetchSattistics={ (fn) => this.setState({ fetchStatistics: fn }) } getFetchEquipment={ (fn) => this.setState({ fetchEquipment: fn}) } />
<br />
<h1 className='centered'>Combat Log</h1>
<CombatLog username={this.props.username} start={this.state.start} length={this.state.length} />
</div>
);
}
@@ -225,7 +183,12 @@ class Profile extends React.Component {
//return the side panel
return (
<div className='sidePanel'>
<CommonLinks onClickProfile={() => {e.preventDefault(); this.sendRequest('/profilerequest', this.props.username); this.setWarning(''); this.props.history.push('/profile');}} />
<CommonLinks onClickProfile={(e) => {
e.preventDefault();
this.sendRequest('/profilerequest', {username: this.props.account.username});
this.setWarning('');
this.props.history.push('/profile');
}} />
</div>
);
}
@@ -233,47 +196,60 @@ class Profile extends React.Component {
NotMyProfileMainPanel() {
return (
<div className='panel'>
<h1 className='centered'>{this.state.username}'s Kingdom</h1>
<h1 className='centered'>{this.props.profile.username}'s Kingdom</h1>
<div className='table noCollapse'>
<div className='row'>
<p className='col'>Username:</p>
<p className='col'>{this.state.username}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.username}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Gold:</p>
<p className='col'>{this.state.gold}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.gold}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Recruits:</p>
<p className='col'>{this.state.recruits}</p>
<AttackButton className='col' style={{flex: '2 1 1.5%'}} setWarning={this.setWarning.bind(this)} attacker={this.props.username} defender={this.state.username} token={this.props.token} />
<p className='col'>{this.props.profile.recruits}</p>
<AttackButton
className='col'
style={{flex: '2 1 1%'}}
setWarning={this.setWarning.bind(this)}
attacker={this.props.account.username}
defender={this.props.profile.username}
token={this.props.account.token}
/>
</div>
<div className='row'>
<p className='col'>Soldiers:</p>
<p className='col'>{this.state.soldiers}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.soldiers}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Spies:</p>
<p className='col'>{this.state.spies}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.spies}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Scientists:</p>
<p className='col'>{this.state.scientists}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.scientists}</p>
<div className='col' />
<div className='col' />
</div>
</div>
</div>
@@ -291,48 +267,54 @@ class Profile extends React.Component {
LoggedOutMainPanel() {
return (
<div className='panel'>
<h1 className='centered'>{this.state.username}'s Kingdom</h1>
<h1 className='centered'>{this.props.profile.username}'s Kingdom</h1>
<div className='table noCollapse'>
<div className='row'>
<p className='col'>Username:</p>
<p className='col'>{this.state.username}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.username}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Gold:</p>
<p className='col'>{this.state.gold}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.gold}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Recruits:</p>
<p className='col'>{this.state.recruits}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.recruits}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Soldiers:</p>
<p className='col'>{this.state.soldiers}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.soldiers}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Spies:</p>
<p className='col'>{this.state.spies}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.spies}</p>
<div className='col' />
<div className='col' />
</div>
<div className='row'>
<p className='col'>Scientists:</p>
<p className='col'>{this.state.scientists}</p>
<div className='col'></div>
<div className='col'></div>
<p className='col'>{this.props.profile.scientists}</p>
<div className='col' />
<div className='col' />
</div>
</div>
</div>
@@ -341,38 +323,29 @@ class Profile extends React.Component {
ProfileNotFoundMainPanel() {
return (
<div className='page' />
<div className='page'>
<p className='centered'>Profile Not Found!</p>
</div>
);
}
setWarning(s) {
this.setState({
warning: s
});
this.setState({ warning: s });
}
}
Profile.propTypes = {
id: PropTypes.number.isRequired,
email: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
token: PropTypes.number.isRequired
};
function mapStoreToProps(store) {
const mapStoreToProps = (store) => {
return {
id: store.account.id,
email: store.account.email,
username: store.account.username,
token: store.account.token
}
}
account: store.account,
profile: store.profile,
};
};
function mapDispatchToProps(dispatch) {
const mapDispatchToProps = (dispatch) => {
return {
//
}
}
storeProfile: (username, gold, recruits, soldiers, spies, scientists) => dispatch(storeProfile(username, gold, recruits, soldiers, spies, scientists))
};
};
Profile = connect(mapStoreToProps, mapDispatchToProps)(Profile);
+4 -5
View File
@@ -8,8 +8,7 @@ class Signup extends React.Component {
constructor(props) {
super(props);
this.state = {
signupSent: false,
signupMsg: ''
signedUp: ''
}
//TODO: referral links
@@ -18,13 +17,13 @@ class Signup extends React.Component {
render() {
let Panel;
if (!this.state.signupSent) {
if (!this.state.signedUp) {
Panel = () => {
return (<SignupPanel onSignup={(msg) => this.setState( {signupSent: true, signupMsg: msg} )} />);
return (<SignupPanel onSuccess={ (msg) => this.setState({signedUp: msg}) } />);
}
} else {
Panel = () => {
return (<p>{this.state.signupMsg}</p>);
return (<p className='centered'>{this.state.signedUp}</p>);
}
}