GameInterface.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import React from 'react/addons';
  2. import {Map} from 'immutable';
  3. import GameHeader from './GameHeader';
  4. import Chat from './Chat';
  5. import Modal from './Modal';
  6. import GameActions from '../actions/GameActions';
  7. import GameStore from '../stores/GameStore';
  8. import ChessboardInterface from './ChessboardInterface';
  9. const GameInterface = React.createClass({
  10. propTypes: {
  11. io: React.PropTypes.object.isRequired,
  12. params: React.PropTypes.array.isRequired
  13. },
  14. getInitialState() {
  15. return {
  16. isOpponentAvailable: false,
  17. color: 'white',
  18. modal: Map({
  19. open: false,
  20. message: '',
  21. type: 'info',
  22. callbacks: {
  23. hide: this._hideModal,
  24. accept: this._acceptRematch,
  25. decline: this._declineRematch
  26. }
  27. }),
  28. soundsEnabled: false,
  29. gameOver: GameStore.getState().gameOver
  30. };
  31. },
  32. componentDidMount() {
  33. const {io, params} = this.props;
  34. io.on('token-invalid', () => this.setState({
  35. modal: this.state.modal
  36. .set('open', true)
  37. .set('message', 'Game link is invalid or has expired.')
  38. .set('type', 'info')
  39. }));
  40. io.emit('join', {
  41. token: params[0],
  42. time: params[1] * 60,
  43. inc: params[2]
  44. });
  45. io.on('joined', data => {
  46. if (data.color === 'black') {
  47. this.setState({color: 'black'});
  48. }
  49. });
  50. io.on('both-joined', () =>
  51. this.setState({isOpponentAvailable: true}, () => {
  52. if (this.state.color === 'white') {
  53. io.emit('clock-run', {
  54. token: params[0],
  55. color: 'white'
  56. });
  57. }
  58. }));
  59. io.on('full', () => {
  60. window.alert(
  61. 'This game already has two players. You have to create a new one.');
  62. window.location = '/';
  63. });
  64. io.on('player-resigned', data => {
  65. GameActions.gameOver({
  66. type: 'resign',
  67. winner: data.color === 'black' ? 'White' : 'Black'
  68. });
  69. });
  70. io.on('rematch-offered', () =>
  71. this._openModal('offer', 'Your opponent has sent you a rematch offer.'));
  72. io.on('rematch-declined', () =>
  73. this._openModal('info', 'Rematch offer has been declined.'));
  74. io.on('rematch-accepted', () => {
  75. GameActions.rematch();
  76. this.setState({
  77. color: this.state.color === 'white' ? 'black' : 'white',
  78. modal: this.state.modal.set('open', false)
  79. }, () => {
  80. if (this.state.color === 'white') {
  81. io.emit('clock-run', {
  82. token: this.props.params[0],
  83. color: 'white'
  84. });
  85. }
  86. });
  87. });
  88. io.on('opponent-disconnected', () => {
  89. if (!this.state.gameOver.get('status')) {
  90. this._openModal('info', 'Your opponent has disconnected.');
  91. }
  92. this.setState({isOpponentAvailable: false});
  93. });
  94. GameStore.on('change', this._onGameChange);
  95. },
  96. componentWillUnmount() {
  97. GameStore.off('change', this._onGameChange);
  98. },
  99. render() {
  100. const {io, params} = this.props;
  101. const {color, soundsEnabled, gameOver, isOpponentAvailable} = this.state;
  102. const commonProps = {
  103. io: io,
  104. color: color,
  105. openModal: this._openModal,
  106. isOpponentAvailable: isOpponentAvailable
  107. };
  108. return (
  109. <div>
  110. <GameHeader
  111. {...commonProps}
  112. params={params}
  113. gameOver={gameOver.get('status')} />
  114. <label id="sounds-label">
  115. <input type="checkbox"
  116. checked={soundsEnabled}
  117. onChange={this._toggleSounds} />
  118. <span> Enable sounds</span>
  119. </label>
  120. <Chat
  121. {...commonProps}
  122. token={params[0]}
  123. soundsEnabled={soundsEnabled} />
  124. <ChessboardInterface
  125. {...commonProps}
  126. token={params[0]}
  127. soundsEnabled={soundsEnabled}
  128. gameOver={gameOver} />
  129. <Modal data={this.state.modal} />
  130. </div>
  131. );
  132. },
  133. _onGameChange() {
  134. this.setState({gameOver: GameStore.getState().gameOver});
  135. },
  136. _openModal(type, message) {
  137. this.setState({
  138. modal: this.state.modal
  139. .set('open', true)
  140. .set('message', message)
  141. .set('type', type)
  142. });
  143. },
  144. _hideModal() {
  145. this.setState({modal: this.state.modal.set('open', false)});
  146. },
  147. _acceptRematch() {
  148. const {io, params} = this.props;
  149. io.emit('rematch-accept', {
  150. token: params[0],
  151. time: params[1] * 60,
  152. inc: params[2]
  153. });
  154. this._hideModal();
  155. },
  156. _declineRematch() {
  157. const {io, params} = this.props;
  158. io.emit('rematch-decline', {
  159. token: params[0]
  160. });
  161. this._hideModal();
  162. },
  163. _toggleSounds(e) {
  164. this.setState({
  165. soundsEnabled: !this.state.soundsEnabled
  166. });
  167. },
  168. });
  169. export default GameInterface;