Browse Source

more io events; layout changes; add game flux store

romanmatiasko 9 years ago
parent
commit
e21f08f36d

+ 0 - 4
src/css/_game.scss

@@ -47,10 +47,6 @@ span#game-type {
   margin-right: 10px;
 }
 
-.rematch {
-  display: none;
-}
-
 #sounds-label {
   width: 800px;
   height: 30px;

+ 1 - 1
src/css/_layout.scss

@@ -52,7 +52,7 @@ input, button {
   }
 }
 
-a.btn, .btn.ok {
+a.btn {
   width: 120px;
   line-height: 46px;
   text-align: center;

+ 2 - 2
src/js/actions/ChatActions.js

@@ -1,7 +1,7 @@
 const ChatConstants = require('../constants/ChatConstants');
-let AppDispatcher = require('../dispatcher/AppDispatcher');
+const AppDispatcher = require('../dispatcher/AppDispatcher');
 
-let ChatActions = {
+const ChatActions = {
   toggleChat() {
     AppDispatcher.handleViewAction({
       actionType: ChatConstants.TOGGLE_CHAT

+ 18 - 0
src/js/actions/GameActions.js

@@ -0,0 +1,18 @@
+const GameConstants = require('../constants/GameConstants');
+const AppDispatcher = require('../dispatcher/AppDispatcher');
+
+const GameActions = {
+  rematch() {
+    AppDispatcher.handleViewAction({
+      actionType: GameConstants.REMATCH
+    });
+  },
+  gameOver(options) {
+    AppDispatcher.handleViewAction({
+      actionType: GameConstants.GAME_OVER,
+      options: options
+    });
+  }
+};
+
+module.exports = GameActions;

+ 5 - 4
src/js/components/Clock.js

@@ -1,6 +1,7 @@
 'use strict';
 
 const React = require('react/addons');
+const GameActions = require('../actions/GameActions');
 const PureRenderMixin = React.addons.PureRenderMixin;
 
 const Clock = React.createClass({
@@ -31,10 +32,10 @@ const Clock = React.createClass({
 
     io.on('countdown-gameover', data => {
       this.setState({countdown: null});
-      // GameStore.gameOver({
-      //   timeout: true,
-      //   winner: data.color === 'black' ? 'White' : 'Black'
-      // });
+      GameActions.gameOver({
+        type: 'timeout',
+        winner: data.color === 'black' ? 'White' : 'Black'
+      });
     });
   },
   render() {

+ 15 - 17
src/js/components/GameHeader.js

@@ -11,7 +11,8 @@ const GameHeader = React.createClass({
     io: React.PropTypes.object,
     params: React.PropTypes.array.isRequired,
     color: React.PropTypes.string,
-    openModal: React.PropTypes.func.isRequired
+    openModal: React.PropTypes.func.isRequired,
+    gameOver: React.PropTypes.bool.isRequired
   },
   mixins: [React.addons.PureRenderMixin],
 
@@ -50,17 +51,17 @@ const GameHeader = React.createClass({
 
         <a className="btn" href="/">New game</a>
 
-        <button type="button"
-                className="btn btn--red resign"
-                onClick={this._onResign}>
-          Resign
-        </button>
+        {!this.props.gameOver ?
+          <a className="btn btn--red resign"
+              onClick={this._onResign}>
+            Resign
+          </a> :
 
-        <button type="button"
-                className="btn btn--red rematch"
-                onClick={this._onRematch}>
-          Rematch
-        </button>
+          <a className="btn btn--red rematch"
+             onClick={this._onRematch}>
+            Rematch
+          </a>
+        }
 
         <a id="chat-icon"
            onClick={this._toggleChat}>
@@ -80,13 +81,11 @@ const GameHeader = React.createClass({
       isChatHidden: ChatStore.getState().isChatHidden
     });
   },
-  _toggleChat(e) {
-    e.preventDefault();
+  _toggleChat() {
     this.setState({newMessage: false});
     ChatActions.toggleChat();
   },
-  _onResign(e) {
-    e.preventDefault();
+  _onResign() {
     let {io, params, color} = this.props;
 
     io.emit('resign', {
@@ -94,8 +93,7 @@ const GameHeader = React.createClass({
       color: color
     });
   },
-  _onRematch(e) {
-    e.preventDefault();
+  _onRematch() {
     let {io, params, openModal} = this.props;
 
     io.emit('rematch-offer', {

+ 55 - 3
src/js/components/GameInterface.js

@@ -4,6 +4,8 @@ const React = require('react/addons');
 const GameHeader = require('./GameHeader');
 const Chat = require('./Chat');
 const Modal = require('./Modal');
+const GameActions = require('../actions/GameActions');
+const GameStore = require('../stores/GameStore');
 const Immutable = require('immutable');
 const {Map} = Immutable;
 
@@ -27,7 +29,8 @@ const GameInterface = React.createClass({
           decline: this._declineRematch
         }
       }),
-      soundsEnabled: false
+      soundsEnabled: false,
+      gameOver: GameStore.getState().gameOver
     };
   },
   componentDidMount() {
@@ -55,10 +58,53 @@ const GameInterface = React.createClass({
         this.setState({color: 'black'});
       }
     });
+
+    io.on('full', () => {
+      window.alert(
+        'This game already has two players. You have to create a new one.');
+      window.location = '/';
+    });
+
+    io.on('player-resigned', data => {
+      let winner = data.color === 'black' ? 'White' : 'Black';
+      let loser = winner === 'Black' ? 'White' : 'Black';
+
+      GameActions.gameOver({
+        type: 'resign',
+        winner: winner
+      });
+      this._openModal('info', `${loser} has resigned. ${winner} wins!`);
+    });
+
+    io.on('rematch-offered', () => {
+      this._openModal('offer', 'Your opponent has sent you a rematch offer.');
+    });
+
+    io.on('rematch-declined', () => {
+      this._openModal('info', 'Rematch offer has been declined.');
+    });
+
+    io.on('rematch-confirmed', data => {
+      GameActions.rematch();
+      this.setState({
+        color: this.state.color === 'white' ? 'black' : 'white',
+        modal: this.state.modal.set('open', false)
+      }, () => {
+        if (this.state.color === 'white') {
+          io.emit('timer-white', {
+            token: this.props.params[0]
+          });
+        }
+      });
+    });
+    GameStore.on('change', this._onGameChange);
+  },
+  componentWillUnmount() {
+    GameStore.off('change', this._onGameChange);
   },
   render() {
     let {io, params} = this.props;
-    let {color, soundsEnabled} = this.state;
+    let {color, soundsEnabled, gameOver} = this.state;
 
     return (
       <div>
@@ -66,7 +112,8 @@ const GameInterface = React.createClass({
           io={io}
           params={params}
           color={color}
-          openModal={this._openModal} />
+          openModal={this._openModal}
+          gameOver={gameOver.get('status')} />
 
         <audio preload="auto" ref="moveSnd">
           <source src="/snd/move.mp3" />
@@ -89,6 +136,9 @@ const GameInterface = React.createClass({
       </div>
     );
   },
+  _onGameChange() {
+    this.setState({gameOver: GameStore.getState().gameOver});
+  },
   _openModal(type, message) {
     this.setState({
       modal: this.state.modal
@@ -108,6 +158,7 @@ const GameInterface = React.createClass({
       time: params[1] * 60,
       inc: params[2]
     });
+    this._hideModal();
   },
   _declineRematch() {
     let {io, params} = this.props;
@@ -115,6 +166,7 @@ const GameInterface = React.createClass({
     io.emit('rematch-decline', {
       token: params[0]
     });
+    this._hideModal();
   },
   _toggleSounds() {
     this.setState({

+ 21 - 31
src/js/components/Modal.js

@@ -9,11 +9,13 @@ const Modal = React.createClass({
   },
   mixins: [React.addons.PureRenderMixin],
 
-  componentDidMount() {
-    document.addEventListener('keydown', this._onKeydown);
-  },
-  componentWillUnmount() {
-    document.removeEventListener('keydown', this._onKeydown);
+  componentDidUpdate() {
+    let isOpen = this.props.data.get('open');
+
+    if (isOpen)
+      document.addEventListener('keydown', this._onKeydown);
+    else
+      document.removeEventListener('keydown', this._onKeydown);
   },
   render() {
     let data = this.props.data;
@@ -34,35 +36,23 @@ const Modal = React.createClass({
           <p>{data.get('message')}</p>
 
           {type === 'info' ? 
-            <button type="button"
-                    className="btn ok"
-                    onClick={e => {
-                      e.preventDefault();
-                      callbacks.hide();
-                    }}>
+            <a className="btn"
+               onClick={callbacks.hide}>
               OK
-            </button> : [
+            </a> : [
 
-            <button key="a"
-                    type="button"
-                    className="btn btn--red"
-                    style={{left: '4em'}}
-                    onClick={e => {
-                      e.preventDefault();
-                      callbacks.decline();
-                    }}>
-              Decline
-            </button>,
-            <button key="b"
-                    type="button"
-                    className="btn"
-                    style={{right: '4em'}}
-                    onClick={e => {
-                      e.preventDefault();
-                      callbacks.accept();
-                    }}>
+            <a key="a"
+               className="btn"
+               style={{left: '4em'}}
+               onClick={callbacks.accept}>
               Accept
-            </button>
+            </a>,
+            <a key="b"
+               className="btn btn--red"
+               style={{right: '4em'}}
+               onClick={callbacks.decline}>
+              Decline
+            </a>
           ]}
         </div>
       </div>

+ 6 - 0
src/js/constants/GameConstants.js

@@ -0,0 +1,6 @@
+const keyMirror = require('react/lib/keyMirror');
+
+module.exports = keyMirror({
+  REMATCH: null,
+  GAME_OVER: null
+});

+ 61 - 0
src/js/stores/GameStore.js

@@ -0,0 +1,61 @@
+'use strict';
+
+const AppDispatcher = require('../dispatcher/AppDispatcher');
+const EventEmitter = require('eventemitter2').EventEmitter2; 
+const GameConstants = require('../constants/GameConstants');
+const Immutable = require('immutable');
+const {List, Map, Set} = Immutable;
+const CHANGE_EVENT = 'change';
+  
+var _gameOver = Map({
+  status: false,
+  type: null,
+  winner: null
+});
+var _moves = List();
+
+var GameStore = Object.assign({}, EventEmitter.prototype, {
+  getState() {
+    return {
+      gameOver: _gameOver,
+      moves: _moves
+    };
+  }
+});
+
+function rematch() {
+  _gameOver = _gameOver
+    .set('status', false)
+    .set('winner', null)
+    .set('type', null);
+}
+
+function gameOver(options) {
+  _gameOver = _gameOver
+    .set('status', true)
+    .set('winner', options.winner)
+    .set('type', options.type);
+}
+
+AppDispatcher.register(payload => {
+  var action = payload.action;
+
+  switch (action.actionType) {
+
+    case GameConstants.REMATCH:
+      rematch();
+      break;
+
+    case GameConstants.GAME_OVER:
+      gameOver(action.options);
+      break;
+
+    default:
+      return true;
+  }
+
+  GameStore.emit(CHANGE_EVENT);
+  return true;
+});
+
+module.exports = GameStore;