consolidate views for auth and anon

This commit is contained in:
ntr 2019-12-24 12:09:30 +10:00
parent 1bbaede2a6
commit 395bf640c9
12 changed files with 238 additions and 84 deletions

View File

@ -283,6 +283,7 @@ header {
} }
border: none; border: none;
border-radius: 0;
} }
} }

View File

@ -1,4 +1,5 @@
export const setAccount = value => ({ type: 'SET_ACCOUNT', value }); export const setAccount = value => ({ type: 'SET_ACCOUNT', value });
export const setAuthenticated = value => ({ type: 'SET_AUTHENTICATED', value });
export const setAnimating = value => ({ type: 'SET_ANIMATING', value }); export const setAnimating = value => ({ type: 'SET_ANIMATING', value });
export const setAnimFocus = value => ({ type: 'SET_ANIM_FOCUS', value }); export const setAnimFocus = value => ({ type: 'SET_ANIM_FOCUS', value });

View File

@ -0,0 +1,138 @@
// const { connect } = require('preact-redux');
const preact = require('preact');
const { connect } = require('preact-redux');
const { errorToast, infoToast } = require('../utils');
const actions = require('./../actions');
const VERSION = process.env.npm_package_version;
const Welcome = require('./welcome');
const addState = connect(
function receiveState(state) {
const {
ws,
account,
instances,
invite,
pvp,
} = state;
function sendInstanceState(id) {
ws.sendInstanceState(id);
}
function sendInstancePractice() {
ws.sendInstancePractice();
}
function sendInstanceQueue() {
ws.sendInstanceQueue();
}
function sendInstanceInvite() {
ws.sendInstanceInvite();
}
function sendInstanceLeave() {
ws.sendInstanceLeave();
}
return {
account,
instances,
invite,
pvp,
sendInstanceState,
sendInstanceQueue,
sendInstancePractice,
sendInstanceInvite,
sendInstanceLeave,
};
},
function receiveDispatch(dispatch) {
function setMtxActive(mtx) {
dispatch(actions.setConstructRename(null));
dispatch(actions.setMtxActive(mtx));
return true;
}
function setNav(place) {
return dispatch(actions.setNav(place));
}
return {
setMtxActive,
setNav,
};
}
);
function Play(args) {
const {
account,
instances,
invite,
pvp,
sendInstanceState,
sendInstanceQueue,
sendInstancePractice,
sendInstanceInvite,
sendInstanceLeave,
setNav,
} = args;
const news = (
<div class="news">
<p> MNML is a turn-based 1v1 strategy game in an abstract setting. </p>
<p>
Build a unique team of 3 constructs from a range of skills and specialisations.<br />
Outplay your opponent in multiple rounds by adapting to an always shifting meta. <br />
Simple rules, complex interactions and unique mechanics.<br />
</p>
</div>
);
const list = () => {
return (
<div>
<div class='list play'>
<figure>
<button
class="ready"
onClick={() => sendInstancePractice()}>
Tutorial
</button>
<figcaption>Learn MNML</figcaption>
</figure>
<figure>
<button
class='discord-btn'
onClick={() => window.open('https://discord.gg/YJJgurM') }>
&nbsp;
</button>
<figcaption>Join the Community</figcaption>
</figure>
</div>
</div>
);
};
return (
<section class="top">
<div class="news">
<h1>Welcome to MNML</h1>
{news}
{list()}
</div>
<Welcome />
</section>
);
}
module.exports = addState(Play);

View File

@ -8,6 +8,7 @@ const addState = connect(
const { const {
ws, ws,
account, account,
authenticated,
nav, nav,
} = state; } = state;
@ -22,6 +23,7 @@ const addState = connect(
return { return {
account, account,
authenticated,
nav, nav,
sendInstanceState, sendInstanceState,
@ -48,6 +50,7 @@ const addState = connect(
function Header(args) { function Header(args) {
const { const {
account, account,
authenticated,
nav, nav,
sendAccountStates, sendAccountStates,
@ -56,6 +59,18 @@ function Header(args) {
if (!account) return false; if (!account) return false;
if (!authenticated) return (
<header>
<div class="options">
<button
onClick={() => navTo('play')}
class='logo login-btn'>
&nbsp;
</button>
</div>
</header>
)
function navTo(p) { function navTo(p) {
return setNav(p); return setNav(p);
} }

View File

@ -4,6 +4,7 @@ const preact = require('preact');
const actions = require('./../actions'); const actions = require('./../actions');
const AccountTop = require('./account.top'); const AccountTop = require('./account.top');
const GuestTop = require('./guest.top');
const Play = require('./play'); const Play = require('./play');
const Shop = require('./shop'); const Shop = require('./shop');
const Reshape = require('./reshape'); const Reshape = require('./reshape');
@ -12,10 +13,12 @@ const addState = connect(
function receiveState(state) { function receiveState(state) {
const { const {
nav, nav,
authenticated,
} = state; } = state;
return { return {
nav, nav,
authenticated,
}; };
}, },
); );
@ -23,8 +26,11 @@ const addState = connect(
function Top(args) { function Top(args) {
const { const {
nav, nav,
authenticated,
} = args; } = args;
if (!authenticated) return <GuestTop />
if (nav === 'account') return <AccountTop />; if (nav === 'account') return <AccountTop />;
if (nav === 'play') return <Play /> if (nav === 'play') return <Play />
if (nav === 'shop') return <Shop /> if (nav === 'shop') return <Shop />

View File

@ -7,7 +7,7 @@ const Help = require('./welcome.help');
// const About = require('./welcome.about'); // const About = require('./welcome.about');
function Welcome() { function Welcome() {
const page = this.state.page || 'register'; const page = this.state.page || 'login';
const pageEl = () => { const pageEl = () => {
if (page === 'login') return <Login />; if (page === 'login') return <Login />;
@ -16,57 +16,32 @@ function Welcome() {
return false; return false;
}; };
const news = ( const form = <div>{pageEl()}</div>;
<div class="news">
<p> Welcome to mnml.</p>
<p> MNML is a turn-based 1v1 strategy game in an abstract setting. </p>
<p>
Build a unique team of 3 constructs from a range of skills and specialisations.<br />
Outplay your opponent in multiple rounds by adapting to an always shifting meta. <br />
Simple rules, complex interactions and unique mechanics.<br />
</p>
<p> Free to play, no pay to win. Register to start playing.<br /></p>
<a href='https://www.youtube.com/watch?v=VtZLlkpJuS8'>Tutorial Playthrough on YouTube</a>
</div>
);
const main = <section>{news}{pageEl()}</section>;
return ( return (
<main class="menu welcome"> <div>
<header> <div class="options">
<div class="options"> <button
<button class={`login-btn ${page === 'login' ? 'highlight' : ''}`}
onClick={() => this.setState({ page: 'login' })} disabled={page === 'login'}
class='logo login-btn'> onClick={() => this.setState({ page: 'login' })}>
&nbsp; Login
</button> </button>
<button <button
class={`login-btn ${page === 'login' ? 'highlight' : ''}`} class={`login-btn ${page === 'register' ? 'highlight' : ''}`}
disabled={page === 'login'} disabled={page === 'register'}
onClick={() => this.setState({ page: 'login' })}> onClick={() => this.setState({ page: 'register' })}>
Login Register
</button> </button>
<button <button
class={`login-btn ${page === 'register' ? 'highlight' : ''}`} class={`login-btn ${page === 'help' ? 'highlight' : ''}`}
disabled={page === 'register'} disabled={page === 'help'}
onClick={() => this.setState({ page: 'register' })}> onClick={() => this.setState({ page: 'help' })}>
Register Help
</button> </button>
<button
class={`login-btn ${page === 'help' ? 'highlight' : ''}`}
disabled={page === 'help'}
onClick={() => this.setState({ page: 'help' })}>
Help
</button>
</div>
</header>
<div class="top">
{main}
</div> </div>
</main> {form}
</div>
); );
} }

View File

@ -27,7 +27,6 @@ function registerEvents(store) {
function clearTutorial() { function clearTutorial() {
store.dispatch(actions.setTutorial(null)); store.dispatch(actions.setTutorial(null));
localStorage.setItem('tutorial-complete', true);
} }
@ -35,7 +34,6 @@ function registerEvents(store) {
store.dispatch(actions.setTutorialGame(null)); store.dispatch(actions.setTutorialGame(null));
} }
function setPing(ping) { function setPing(ping) {
store.dispatch(actions.setPing(ping)); store.dispatch(actions.setPing(ping));
} }
@ -179,18 +177,14 @@ function registerEvents(store) {
const player = v.players.find(p => p.id === account.id); const player = v.players.find(p => p.id === account.id);
store.dispatch(actions.setPlayer(player)); store.dispatch(actions.setPlayer(player));
if (tutorial) tutorialVbox(player, store, tutorial);
if (v.phase === 'Finished') { if (v.phase === 'Finished') {
ws.sendAccountInstances(); ws.sendAccountInstances();
} }
// instance.mobile.less hides info at @media 1000
if (localStorage.getItem('tutorial-complete') || window.innerWidth <= 1100) {
store.dispatch(actions.setTutorial(null));
} else if (v.time_control === 'Practice' && v.rounds.length === 1 && tutorial) {
tutorialVbox(player, store, tutorial);
}
} }
return store.dispatch(actions.setInstance(v)); return store.dispatch(actions.setInstance(v));
} }
@ -214,6 +208,11 @@ function registerEvents(store) {
return true; return true;
} }
function startTutorial() {
store.dispatch(actions.setTutorial(1));
}
window.addEventListener('hashchange', urlHashChange, false); window.addEventListener('hashchange', urlHashChange, false);
return { return {
@ -243,6 +242,8 @@ function registerEvents(store) {
setSubscription, setSubscription,
setWs, setWs,
startTutorial,
urlHashChange, urlHashChange,
notify, notify,

View File

@ -10,6 +10,7 @@ function createReducer(defaultState, actionType) {
/* eslint-disable key-spacing */ /* eslint-disable key-spacing */
module.exports = { module.exports = {
account: createReducer(null, 'SET_ACCOUNT'), account: createReducer(null, 'SET_ACCOUNT'),
authenticated: createReducer(null, 'SET_AUTHENTICATED'),
activeItem: createReducer(null, 'SET_ACTIVE_VAR'), activeItem: createReducer(null, 'SET_ACTIVE_VAR'),
activeSkill: createReducer(null, 'SET_ACTIVE_SKILL'), activeSkill: createReducer(null, 'SET_ACTIVE_SKILL'),

View File

@ -270,6 +270,7 @@ function createSocket(events) {
// this object wraps the reply types to a function // this object wraps the reply types to a function
const handlers = { const handlers = {
AccountState: onAccount, AccountState: onAccount,
AccountAuthenticated: events.setAuthenticated,
AccountConstructs: onAccountConstructs, AccountConstructs: onAccountConstructs,
AccountTeam: onAccountTeam, AccountTeam: onAccountTeam,
AccountInstances: onAccountInstances, AccountInstances: onAccountInstances,
@ -299,6 +300,8 @@ function createSocket(events) {
ChatWheel: wheel => events.setChatWheel(wheel), ChatWheel: wheel => events.setChatWheel(wheel),
// Joining: () => events.notify('Searching for instance...'), // Joining: () => events.notify('Searching for instance...'),
StartTutorial: () => events.startTutorial(),
Processing: () => true, Processing: () => true,
Error: errHandler, Error: errHandler,
}; };

View File

@ -44,6 +44,7 @@ use http::{AUTH_CLEAR, TOKEN_HEADER};
#[derive(Debug,Clone,Serialize)] #[derive(Debug,Clone,Serialize)]
pub enum RpcMessage { pub enum RpcMessage {
AccountState(Account), AccountState(Account),
AccountAuthenticated(()),
AccountConstructs(Vec<Construct>), AccountConstructs(Vec<Construct>),
AccountTeam(Vec<Construct>), AccountTeam(Vec<Construct>),
AccountInstances(Vec<Instance>), AccountInstances(Vec<Instance>),
@ -61,6 +62,7 @@ pub enum RpcMessage {
SubscriptionState(Option<Subscription>), SubscriptionState(Option<Subscription>),
Pong(()), Pong(()),
StartTutorial(()),
QueueRequested(()), QueueRequested(()),
QueueJoined(()), QueueJoined(()),

View File

@ -49,9 +49,8 @@ impl User for Anonymous {
info!("anonymous connection"); info!("anonymous connection");
self.send(RpcMessage::AccountState(self.account.clone()), events, ws)?; self.send(RpcMessage::AccountState(self.account.clone()), events, ws)?;
self.send(RpcMessage::StartTutorial(()), events, ws)?;
self.send(RpcMessage::ItemInfo(item_info()), events, ws)?; self.send(RpcMessage::ItemInfo(item_info()), events, ws)?;
// self.send(RpcMessage::StartTutorial(()), events, ws)?;
self.send(RpcMessage::InstanceState(pg::instance_demo(&self.account)?), events, ws)?;
Ok(()) Ok(())
} }
@ -59,70 +58,81 @@ impl User for Anonymous {
fn receive(&mut self, data: Vec<u8>, _db: &Db, _begin: Instant, _events: &CbSender<Event>, _stripe: &StripeClient) -> Result<RpcMessage, Error> { fn receive(&mut self, data: Vec<u8>, _db: &Db, _begin: Instant, _events: &CbSender<Event>, _stripe: &StripeClient) -> Result<RpcMessage, Error> {
match from_slice::<RpcRequest>(&data) { match from_slice::<RpcRequest>(&data) {
Ok(v) => { Ok(v) => {
let mut instance = match self.instance { let get_instance = || {
Some(ref i) => i.clone(), match self.instance {
None => return Err(err_msg("instance missing")), Some(ref i) => Ok(i.clone()),
None => return Err(err_msg("instance missing")),
}
}; };
let game = match self.game { let get_game = || {
Some(ref i) => Some(i.clone()), match self.game {
None => None Some(ref i) => Ok(i.clone()),
None => return Err(err_msg("game missing")),
}
}; };
match v { match v {
RpcRequest::Ping {} => return Ok(RpcMessage::Pong(())), RpcRequest::Ping {} => return Ok(RpcMessage::Pong(())),
RpcRequest::InstancePractice {} =>
Ok(RpcMessage::InstanceState(pg::instance_demo(&self.account)?)),
RpcRequest::InstanceReady { instance_id: _ } => { RpcRequest::InstanceReady { instance_id: _ } => {
match instance.player_ready(self.account.id)? { match get_instance()?.player_ready(self.account.id)? {
Some(g) => Ok(RpcMessage::GameState(g)), Some(g) => Ok(RpcMessage::GameState(g)),
None => Ok(RpcMessage::InstanceState(instance)), None => Ok(RpcMessage::InstanceState(get_instance()?)),
} }
}, },
RpcRequest::InstanceState { instance_id: _ } => RpcRequest::InstanceState { instance_id: _ } =>
Ok(RpcMessage::InstanceState(instance)), Ok(RpcMessage::InstanceState(get_instance()?)),
RpcRequest::InstanceAbandon { instance_id: _ } => RpcRequest::InstanceAbandon { instance_id: _ } => {
Err(err_msg("don't give up!")), let mut instance = get_instance()?;
instance.finish();
Ok(RpcMessage::InstanceState(instance))
},
RpcRequest::VboxBuy { instance_id: _, group, index, construct_id } => RpcRequest::VboxBuy { instance_id: _, group, index, construct_id } =>
Ok(RpcMessage::InstanceState(instance.vbox_buy(self.account.id, group, index, construct_id)?)), Ok(RpcMessage::InstanceState(get_instance()?.vbox_buy(self.account.id, group, index, construct_id)?)),
RpcRequest::VboxApply { instance_id: _, construct_id, index } => RpcRequest::VboxApply { instance_id: _, construct_id, index } =>
Ok(RpcMessage::InstanceState(instance.vbox_apply(self.account.id, index, construct_id)?)), Ok(RpcMessage::InstanceState(get_instance()?.vbox_apply(self.account.id, index, construct_id)?)),
RpcRequest::VboxCombine { instance_id: _, inv_indices, vbox_indices } => RpcRequest::VboxCombine { instance_id: _, inv_indices, vbox_indices } =>
Ok(RpcMessage::InstanceState(instance.vbox_combine(self.account.id, inv_indices, vbox_indices)?)), Ok(RpcMessage::InstanceState(get_instance()?.vbox_combine(self.account.id, inv_indices, vbox_indices)?)),
RpcRequest::VboxRefill { instance_id: _ } => RpcRequest::VboxRefill { instance_id: _ } =>
Ok(RpcMessage::InstanceState(instance.vbox_refill(self.account.id)?)), Ok(RpcMessage::InstanceState(get_instance()?.vbox_refill(self.account.id)?)),
RpcRequest::VboxRefund { instance_id: _, index } => RpcRequest::VboxRefund { instance_id: _, index } =>
Ok(RpcMessage::InstanceState(instance.vbox_refund(self.account.id, index)?)), Ok(RpcMessage::InstanceState(get_instance()?.vbox_refund(self.account.id, index)?)),
RpcRequest::VboxUnequip { instance_id: _, construct_id, target } => RpcRequest::VboxUnequip { instance_id: _, construct_id, target } =>
Ok(RpcMessage::InstanceState(instance.vbox_unequip(self.account.id, target, construct_id, None)?)), Ok(RpcMessage::InstanceState(get_instance()?.vbox_unequip(self.account.id, target, construct_id, None)?)),
RpcRequest::VboxUnequipApply { instance_id: _, construct_id, target, target_construct_id } => RpcRequest::VboxUnequipApply { instance_id: _, construct_id, target, target_construct_id } =>
Ok(RpcMessage::InstanceState(instance.vbox_unequip(self.account.id, target, construct_id, Some(target_construct_id))?)), Ok(RpcMessage::InstanceState(get_instance()?.vbox_unequip(self.account.id, target, construct_id, Some(target_construct_id))?)),
RpcRequest::GameState { id: _ } => RpcRequest::GameState { id: _ } =>
Ok(RpcMessage::GameState(game.unwrap())), Ok(RpcMessage::GameState(get_game()?)),
RpcRequest::GameSkill { game_id: _, construct_id, target_construct_id, skill } => { RpcRequest::GameSkill { game_id: _, construct_id, target_construct_id, skill } => {
let mut game = game.unwrap(); let mut game = get_game()?;
game.add_skill(self.account.id, construct_id, target_construct_id, skill)?; game.add_skill(self.account.id, construct_id, target_construct_id, skill)?;
Ok(RpcMessage::GameState(game)) Ok(RpcMessage::GameState(game))
}, },
RpcRequest::GameSkillClear { game_id: _ } => { RpcRequest::GameSkillClear { game_id: _ } => {
let mut game = game.unwrap(); let mut game = get_game()?;
game.clear_skill(self.account.id)?; game.clear_skill(self.account.id)?;
Ok(RpcMessage::GameState(game)) Ok(RpcMessage::GameState(game))
}, },
RpcRequest::GameReady { id: _ } => { RpcRequest::GameReady { id: _ } => {
let mut game = game.unwrap(); let mut game = get_game()?;
game.player_ready(self.account.id)?; game.player_ready(self.account.id)?;
if game.skill_phase_finished() { if game.skill_phase_finished() {
game = game.resolve_phase_start(); game = game.resolve_phase_start();
@ -132,10 +142,10 @@ impl User for Anonymous {
}, },
RpcRequest::GameConcede { game_id: _ } => RpcRequest::GameConcede { game_id: _ } =>
Ok(RpcMessage::GameState(game.unwrap().concede(self.account.id)?)), Ok(RpcMessage::GameState(get_game()?.concede(self.account.id)?)),
RpcRequest::GameOfferDraw { game_id: _ } => RpcRequest::GameOfferDraw { game_id: _ } =>
Ok(RpcMessage::GameState(game.unwrap().offer_draw(self.account.id)?)), Ok(RpcMessage::GameState(get_game()?.offer_draw(self.account.id)?)),
_ => Err(format_err!("unhandled anonymous request request={:?}", v)), _ => Err(format_err!("unhandled anonymous request request={:?}", v)),
} }

View File

@ -71,6 +71,7 @@ impl User for Authorised {
info!("authenticated connection account={:?}", self.account); info!("authenticated connection account={:?}", self.account);
let a = &self.account; let a = &self.account;
ws.send(RpcMessage::AccountAuthenticated(()))?;
// tell events we have connected // tell events we have connected
events.send(Event::Connect(self.id, a.clone(), ws.clone()))?; events.send(Event::Connect(self.id, a.clone(), ws.clone()))?;