Merge branch 'account' into develop

This commit is contained in:
ntr
2019-08-17 17:52:12 +10:00
57 changed files with 3297 additions and 749 deletions
@@ -0,0 +1,151 @@
const preact = require('preact');
const { Component } = require('preact')
const { connect } = require('preact-redux');
const linkState = require('linkstate').default;
const SpawnButton = require('./spawn.button');
const { postData, errorToast, infoToast } = require('../utils');
const actions = require('../actions');
const addState = connect(
function receiveState(state) {
const {
account,
ping,
ws,
} = state;
function setPassword(current, password) {
postData('/account/password', { current, password })
.then(res => res.json())
.then(data => {
if (!data.success) return errorToast(data.error_message);
infoToast('Password changed. Reloading...')
setTimeout(() => window.location.reload(), 5000);
})
.catch(error => errorToast(error));
}
function logout() {
postData('/account/logout').then(() => window.location.reload(true));
}
function sendConstructSpawn(name) {
return ws.sendMtxConstructSpawn(name);
}
return {
account,
ping,
logout,
setPassword,
sendConstructSpawn,
};
},
);
class AccountStatus extends Component {
constructor(props) {
super(props);
this.state = {
setPassword: { current: '', password: '', confirm: ''},
};
}
render(args) {
const {
account,
ping,
logout,
setPassword,
sendConstructSpawn,
} = args;
if (!account) return null;
const passwordsEqual = () =>
this.state.setPassword.password === this.state.setPassword.confirm;
const setPasswordDisabled = () => {
const { current, password, confirm } = this.state.setPassword;
return !(passwordsEqual() && password && current && confirm);
}
return (
<section class='account'>
<div>
<h1>{account.name}</h1>
<dl>
<dt>Subscription</dt>
<dd>{account.subscribed ? 'some date' : 'unsubscribed'}</dd>
</dl>
<button><a href={`mailto:support@mnml.gg?subject=Account%20Support:%20${account.name}`}> support</a></button>
<button onClick={() => logout()}>Logout</button>
</div>
<div>
<label for="email">Email:</label>
<dl>
<dt>Current Email</dt>
<dd>{account.email ? account.email : 'No email set'}</dd>
<dt>Status</dt>
<dd>{account.email_confirmed ? 'Confirmed' : 'Unconfirmed'}</dd>
</dl>
<input
class="login-input"
type="email"
name="email"
placeholder="new email"
/>
<button>Update</button>
</div>
<div>
<label for="current">Password:</label>
<input
class="login-input"
type="password"
name="current"
value={this.state.setPassword.current}
onInput={linkState(this, 'setPassword.current')}
placeholder="current"
/>
<input
class="login-input"
type="password"
name="new"
value={this.state.setPassword.password}
onInput={linkState(this, 'setPassword.password')}
placeholder="new password"
/>
<input
class="login-input"
type="password"
name="confirm"
value={this.state.setPassword.confirm}
onInput={linkState(this, 'setPassword.confirm')}
placeholder="confirm"
/>
<button
disabled={setPasswordDisabled()}
onClick={() => setPassword(this.state.setPassword.current, this.state.setPassword.password)}>
Set Password
</button>
</div>
<div class="list">
<figure>
<figcaption>spawn new construct</figcaption>
<button onClick={() => sendConstructSpawn()} type="submit">
¤50
</button>
</figure>
</div>
</section>
);
}
}
module.exports = addState(AccountStatus);
+33
View File
@@ -0,0 +1,33 @@
const { connect } = require('preact-redux');
const preact = require('preact');
const Team = require('./team');
const AccountManagement = require('./account.management');
const addState = connect(
function receiveState(state) {
const {
ws,
account,
} = state;
return {
account,
};
},
);
function Account(args) {
const {
account,
} = args;
return (
<main class="menu">
<Team />
<AccountManagement />
</main>
);
}
module.exports = addState(Account);
+19 -70
View File
@@ -1,9 +1,7 @@
const { connect } = require('preact-redux');
const preact = require('preact');
const { Elements, injectStripe } = require('react-stripe-elements');
const { saw } = require('./shapes');
const { postData } = require('./../utils');
const actions = require('../actions');
function pingColour(ping) {
@@ -12,55 +10,6 @@ function pingColour(ping) {
return 'red';
}
function BitsBtn(args) {
const {
stripe,
account,
} = args;
function subscribeClick(e) {
stripe.redirectToCheckout({
items: [{plan: 'plan_FGmRwawcOJJ7Nv', quantity: 1}],
successUrl: 'http://localhost/api/payments/success',
cancelUrl: 'http://localhost/api/payments/cancel',
clientReferenceId: account.id
});
}
function bitsClick(e) {
stripe.redirectToCheckout({
items: [{sku: 'sku_FHUfNEhWQaVDaT', quantity: 1}],
successUrl: 'http://localhost/payments/success',
cancelUrl: 'http://localhost/payments/cancel',
clientReferenceId: account.id
});
}
const subscription = account.subscribed
? <h3 class="account-header">Subscribed</h3>
: <button
onClick={subscribeClick}
class="stripe-btn"
role="link">
Subscribe
</button>;
return (
<div>
<div id="error-message"></div>
{subscription}
<button
onClick={bitsClick}
class="stripe-btn"
role="link">
Get Credits
</button>
</div>
);
}
const StripeBitsBtn = injectStripe(BitsBtn);
const addState = connect(
function receiveState(state) {
const {
@@ -68,23 +17,28 @@ const addState = connect(
ping,
} = state;
function logout() {
postData('/logout').then(() => window.location.reload(true));
}
return {
account,
ping,
logout,
};
},
function receiveDispatch(dispatch) {
function selectConstructs() {
return dispatch(actions.setNav('team'));
function accountPage() {
dispatch(actions.setGame(null));
dispatch(actions.setInstance(null));
dispatch(actions.setCombiner([]));
dispatch(actions.setReclaiming(false));
dispatch(actions.setActiveSkill(null));
dispatch(actions.setActiveConstruct(null));
dispatch(actions.setInfo(null));
dispatch(actions.setItemEquip(null));
dispatch(actions.setItemUnequip([]));
dispatch(actions.setVboxHighlight([]));
return dispatch(actions.setNav('account'));
}
return {
selectConstructs,
accountPage,
};
}
);
@@ -94,25 +48,20 @@ function AccountStatus(args) {
const {
account,
ping,
logout,
selectConstructs,
accountPage,
} = args;
if (!account) return null;
return (
<div class="account">
<div class="account-status">
<div class="account-status">
<div class="account-info">
<h2 class="account-header">{account.name}</h2>
{saw(pingColour(ping))}
<div class="ping-text">{ping}ms</div>
</div>
<h3 class="account-header">{`¤${account.balance}`}</h3>
<Elements>
<StripeBitsBtn account={account} />
</Elements>
<button onClick={() => selectConstructs()}>Constructs </button>
<button onClick={() => logout()}>Logout</button>
<h3 class="account-header credits">{`¤${account.balance}`}</h3>
<button onClick={() => accountPage()}> account</button>
</div>
);
}
+6 -1
View File
@@ -10,12 +10,14 @@ const addState = connect(
function receiveState(state) {
const {
ws,
account,
game,
instance,
nav,
} = state;
return {
account,
game,
instance,
nav,
@@ -26,15 +28,18 @@ const addState = connect(
function Controls(args) {
const {
game,
account,
instance,
nav,
sendGameReady,
} = args;
if (!account) return false;
if (game) return <GameCtrl />;
if (instance) return <InstanceCtrl />;
if (nav === 'play' || !nav) return <PlayCtrl />
if (nav === 'team' || !nav) return <TeamCtrl />
if (nav === 'team' || nav === 'account') return <TeamCtrl />
return false;
}
+40 -1
View File
@@ -20,16 +20,22 @@ const addState = connect(
return ws.sendGameReady(game.id);
}
function getInstanceState() {
return ws.sendInstanceState(game.instance);
}
return {
game,
sendReady,
account,
getInstanceState,
animating,
};
},
function receiveDispatch(dispatch) {
function quit() {
dispatch(actions.setNav('transition'));
dispatch(actions.setGame(null));
dispatch(actions.setInstance(null));
}
@@ -74,6 +80,7 @@ function Controls(args) {
animating,
sendReady,
quit,
getInstanceState,
} = args;
if (!game) return false;
@@ -81,11 +88,43 @@ function Controls(args) {
const opponent = game.players.find(t => t.id !== account.id);
const player = game.players.find(t => t.id === account.id);
function quitClick() {
getInstanceState();
quit();
}
const zero = Date.parse(game.phase_start);
const now = Date.now();
const end = Date.parse(game.phase_end);
const timerPct = game.phase_end
? ((now - zero) / (end - zero) * 100)
: 100;
const displayColour = !game.phase_end
? '#222'
: player.ready
? 'forestgreen'
: timerPct > 80
? 'red'
: 'whitesmoke';
const timerStyles = {
height: `${timerPct > 100 ? 100 : timerPct}%`,
background: displayColour,
};
const timer = (
<div class="timer-container">
<div class="timer" style={timerStyles} >&nbsp;</div>
</div>
);
const readyBtn = <button disabled={animating} class="ready" onClick={() => sendReady()}>Ready</button>;
const quitBtn = <button disabled={animating} onClick={() => quit()}>Back</button>;
const quitBtn = <button disabled={animating} onClick={quitClick}>Back</button>;
return (
<aside class="controls">
{timer}
{scoreboard(game, opponent, true)}
{game.phase === 'Finish' ? quitBtn : readyBtn}
{scoreboard(game, player)}
+27
View File
@@ -66,8 +66,35 @@ function Controls(args) {
const opponent = instance.players.find(t => t.id !== account.id);
const player = instance.players.find(t => t.id === account.id);
const zero = Date.parse(instance.phase_start);
const now = Date.now();
const end = Date.parse(instance.phase_end);
const timerPct = instance.phase_end
? ((now - zero) / (end - zero) * 100)
: 100;
const displayColour = !instance.phase_end
? '#222'
: player.ready
? 'forestgreen'
: timerPct > 80
? 'red'
: 'whitesmoke';
const timerStyles = {
height: `${timerPct > 100 ? 100 : timerPct}%`,
background: displayColour,
};
const timer = (
<div class="timer-container">
<div class="timer" style={timerStyles} >&nbsp;</div>
</div>
);
return (
<aside class="controls">
{timer}
{scoreboard(instance, opponent, true)}
<button class="ready" onClick={() => sendReady()}>Ready</button>
{scoreboard(instance, player)}
+10 -4
View File
@@ -1,8 +1,11 @@
const { connect } = require('preact-redux');
const { Elements } = require('react-stripe-elements');
const preact = require('preact');
const toast = require('izitoast');
const actions = require('./../actions');
const StripeBtns = require('./stripe.buttons');
const addState = connect(
function receiveState(state) {
@@ -63,15 +66,18 @@ function Inventory(args) {
return (
<div class="inventory">
<div>
<h1>¤ {account.balance}</h1>
<h1>Shop</h1>
<Elements>
<StripeBtns account={account} />
</Elements>
<div class='list'>
{shop.owned.map(useMtx)}
{shop.available.map(availableMtx)}
</div>
</div>
<div>
<h1>Shop</h1>
<h1 class="credits">¤ {account.balance}</h1>
<div class='list'>
{shop.available.map(availableMtx)}
{shop.owned.map(useMtx)}
</div>
</div>
</div>
+77 -41
View File
@@ -2,6 +2,7 @@
const preact = require('preact');
const { Component } = require('preact')
const { connect } = require('preact-redux');
const linkState = require('linkstate').default;
const { postData, errorToast } = require('../utils');
@@ -11,10 +12,10 @@ const addState = connect(
ws
} = state;
function submitLogin(name, password) {
postData('/login', { name, password })
postData('/account/login', { name, password })
.then(res => res.json())
.then(data => {
if (!data.success) return errorToast(data.error_message);
if (data.error) return errorToast(data.error);
console.log(data.response);
ws.connect();
})
@@ -22,10 +23,10 @@ const addState = connect(
}
function submitRegister(name, password, code) {
postData('/register', { name, password, code })
postData('/account/register', { name, password, code })
.then(res => res.json())
.then(data => {
if (!data.success) return errorToast(data.error_message);
if (data.error) return errorToast(data.error);
console.log(data.response);
ws.connect();
})
@@ -43,87 +44,122 @@ class Login extends Component {
constructor(props) {
super(props);
this.state = { name: '', password: '', code: ''};
this.state = {
login: { name: '', password: '', code: ''},
register: { name: '', password: '', confirm: '', code: ''},
};
this.nameInput = this.nameInput.bind(this);
this.passwordInput = this.passwordInput.bind(this);
this.codeInput = this.codeInput.bind(this);
this.loginSubmit = this.loginSubmit.bind(this);
this.registerSubmit = this.registerSubmit.bind(this);
}
nameInput(event) {
this.setState({ name: event.target.value });
}
passwordInput(event) {
this.setState({ password: event.target.value });
}
codeInput(event) {
this.setState({ code: event.target.value });
}
loginSubmit(event) {
event.preventDefault();
console.log(this.state);
this.props.submitLogin(this.state.name, this.state.password);
this.setState({ name: '', password: '' });
this.props.submitLogin(this.state.login.name, this.state.login.password);
this.setState({ login: { name: '', password: '' }});
}
registerSubmit(event) {
event.preventDefault();
this.props.submitRegister(this.state.name, this.state.password, this.state.code);
console.log(this.state);
this.setState({ name: '', password: '', code: ''});
this.props.submitRegister(this.state.register.name, this.state.register.password, this.state.register.code);
this.setState({ register: { name: '', password: '', confirm: '', code: ''}});
}
render() {
const registerConfirm = () =>
this.state.register.password === this.state.register.confirm;
const loginDisabled = () => {
const { password, name } = this.state.login;
return !(password && name);
}
const registerDisabled = () => {
const { password, name, code } = this.state.register;
return !(registerConfirm() && password && name && code);
}
return (
<main>
<h1 class="mobile-title" >mnml.gg</h1>
<h1>mnml.gg</h1>
<div class="login">
<div>mnml is an abstract turn based strategy game</div>
<div>free to play</div>
<div>no email required</div>
</div>
<div class="login">
<h2>Login</h2>
<label for="username">Username</label>
<input
class="login-input"
type="email"
placeholder="username"
tabIndex={1}
value={this.state.name}
onInput={this.nameInput}
value={this.state.login.name}
onInput={linkState(this, 'login.name')}
/>
<label for="password">Password</label>
<input
class="login-input"
type="password"
placeholder="password"
tabIndex={2}
value={this.state.password}
onInput={this.passwordInput}
/>
<input
class="login-input"
type="text"
placeholder="code"
tabIndex={3}
value={this.state.code}
onInput={this.codeInput}
value={this.state.login.password}
onInput={linkState(this, 'login.password')}
/>
<button
class="login-btn"
tabIndex={4}
disabled={loginDisabled()}
onClick={this.loginSubmit}>
Login
</button>
</div>
<div class="login">
<h2>Register</h2>
<label for="username">Username</label>
<input
class="login-input"
type="email"
placeholder="username"
value={this.state.register.name}
onInput={linkState(this, 'register.name')}
/>
<label for="password">Password - min 12 chars</label>
<input
class="login-input"
type="password"
placeholder="password"
value={this.state.register.password}
onInput={linkState(this, 'register.password')}
/>
<label for="confirm">Confirm Password</label>
<input
class={`${registerConfirm() ? '' : 'red'} login-input`}
type="password"
placeholder="confirm"
value={this.state.register.confirm}
onInput={linkState(this, 'register.confirm')}
/>
<label for="code">Access Code</label>
<input
class="login-input"
type="text"
placeholder="code"
value={this.state.register.code}
onInput={linkState(this, 'register.code')}
/>
<button
class="login-btn"
tabIndex={5}
disabled={registerDisabled()}
onClick={this.registerSubmit}>
Register
</button>
<button
class="login-btn"
onClick={() => document.location.assign('https://discord.gg/YJJgurM')}>
Discord + Invites
Discord + Codes
</button>
</div>
</main>
+2
View File
@@ -7,6 +7,7 @@ const Game = require('./game');
const Instance = require('./instance.component');
const Team = require('./team');
const Play = require('./play');
const Account = require('./account.page');
const addState = connect(
state => {
@@ -38,6 +39,7 @@ function Main(props) {
if (nav === 'transition') return false;
if (nav === 'play') return <Play />;
if (nav === 'team') return <Team />;
if (nav === 'account') return <Account />;
return (
<Play />
+3
View File
@@ -69,6 +69,8 @@ function Nav(args) {
hideNav,
} = args;
if (!account) return false;
function navTo(p) {
return setNav(p);
}
@@ -90,6 +92,7 @@ function Nav(args) {
const canJoin = team.some(c => !c);
return (
<nav onClick={hideNav} >
<h1 class="header-title">mnml.gg</h1>
+2 -1
View File
@@ -27,7 +27,8 @@ function JoinButtons(args) {
} = args;
return (
<aside>
<aside class='play-ctrl'>
<div class="timer-container"></div>
<button
class='pvp ready'
onClick={() => sendInstanceQueue()}
+3 -3
View File
@@ -97,7 +97,7 @@ function Play(args) {
if (mtxActive === 'Rename') return setConstructRename(construct.id);
return sendConstructAvatarReroll(construct.id);
}}
class="menu-construct" >
class="construct">
<ConstructAvatar construct={construct} />
{constructName}
{confirm}
@@ -107,8 +107,8 @@ function Play(args) {
});
return (
<main class="play">
<div class="team">
<main class="menu">
<div class="team top">
{constructPanels}
</div>
<Inventory />
+54
View File
@@ -0,0 +1,54 @@
const preact = require('preact');
const { injectStripe } = require('react-stripe-elements');
function BitsBtn(args) {
const {
stripe,
account,
} = args;
function subscribeClick() {
stripe.redirectToCheckout({
items: [{ plan: 'plan_FGmRwawcOJJ7Nv', quantity: 1 }],
successUrl: 'http://localhost',
cancelUrl: 'http://localhost',
clientReferenceId: account.id,
});
}
function bitsClick() {
stripe.redirectToCheckout({
items: [{ sku: 'sku_FHUfNEhWQaVDaT', quantity: 1 }],
successUrl: 'http://localhost',
cancelUrl: 'http://localhost',
clientReferenceId: account.id,
});
}
const subscription = account.subscribed
? <button
class="stripe-btn"
disabled>
Subscribed
</button>
: <button
onClick={subscribeClick}
class="stripe-btn"
role="link">
Subscribe
</button>;
return (
<div class='list'>
{subscription}
<button
onClick={bitsClick}
class="stripe-btn"
role="link">
Get Credits
</button>
<div id="error-message"></div>
</div>
);
}
module.exports = injectStripe(BitsBtn);
+1
View File
@@ -29,6 +29,7 @@ function TeamCtrl(args) {
return (
<aside>
<div class="timer-container"></div>
<button
class='ready'
disabled={teamSelect.some(c => !c)}
+4 -20
View File
@@ -1,11 +1,8 @@
const preact = require('preact');
const { connect } = require('preact-redux');
const range = require('lodash/range');
const actions = require('./../actions');
const { COLOURS } = require('./../utils');
const { stringSort } = require('./../utils');
const SpawnButton = require('./spawn.button');
const { ConstructAvatar } = require('./construct');
const addState = connect(
@@ -19,7 +16,6 @@ const addState = connect(
return {
constructs,
teamSelect,
sendConstructSpawn,
};
},
function receiveDispatch(dispatch) {
@@ -31,7 +27,6 @@ const addState = connect(
setTeam,
};
}
);
function Team(args) {
@@ -39,7 +34,6 @@ function Team(args) {
constructs,
teamSelect,
setTeam,
sendConstructSpawn,
} = args;
if (!constructs) return <div></div>;
@@ -70,29 +64,19 @@ function Team(args) {
return (
<div
key={construct.id}
class="menu-construct"
class="construct team-select"
style={ { 'border-color': borderColour || 'whitesmoke' } }
onClick={() => selectConstruct(construct.id)} >
<div class="controls">
<h2>
{construct.name}
</h2>
</div>
<ConstructAvatar construct={construct} />
<h2>{construct.name}</h2>
</div>
);
});
const spawnButtonsNum = (3 - constructs.length % 3);
const spawnButtons = range(spawnButtonsNum)
.map(i => <SpawnButton key={constructs.length + i} spawn={() => sendConstructSpawn()} />);
return (
<main class="team">
<section class="team top">
{constructPanels}
{spawnButtons}
</main>
</section>
);
}