commit 4eb3b081e0600b1a28d3b448562f10a00faddca9 (patch)
parent 24f8594070f3b458d6f3bc5500d552245483e77c
Author: Chris Karle <chriskarle@hotmail.com>
Date: Mon, 6 Apr 2020 01:48:22 -0400
App, CardTable, SeatPicker
Integrates first websocket comms for joining a room and
choosing a seat. Introduces new SeatPicker class with basic
sit function, unstyled.
Adds new node modules (websocket, lodash)
Diffstat:
7 files changed, 286 insertions(+), 21 deletions(-)
diff --git a/assets/app.js b/assets/app.js
@@ -3,15 +3,23 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Lobby from './components/Lobby';
import CardTable from './components/CardTable';
+import { w3cwebsocket as W3CWebSocket } from 'websocket';
+
+const client = new W3CWebSocket('ws://localhost:3000/play');
+
+
+const tableDebug = false;
class App extends React.Component {
constructor(props) {
super(props);
+ const initialName = tableDebug ? 'Alex' : '';
+ const initialTable = tableDebug ? 'periodic' : '';
this.state = {
- playerName: '',
- tableName: '',
- showTable: false
+ playerName: initialName,
+ tableName: initialTable,
+ showTable: tableDebug
};
}
@@ -23,12 +31,16 @@ class App extends React.Component {
chooseTable = tableName => {
const show = tableName && tableName != '';
- this.setState(
- {
+ this.setState( {
tableName: tableName,
showTable: show
- }
- );
+ }, () => {
+ client.send(JSON.stringify({
+ action:'join_game',
+ player_name: this.state.playerName,
+ game_id: tableName
+ }));
+ });
}
render () {
@@ -47,6 +59,8 @@ class App extends React.Component {
chooseTable={this.chooseTable}
name={playerName}
tableName={tableName}
+ active={showTable}
+ client={client}
/>
)}
</div>
diff --git a/assets/app.scss b/assets/app.scss
@@ -1,8 +1,14 @@
@import 'carbon-components/scss/globals/scss/styles.scss';
+.lobby__outer {
+ margin-top: 3rem;
+ margin-left: 3rem;
+}
+
.textRow {
display: flex;
align-items: flex-start;
+ margin-top: 1rem;
margin-left: 15px;
width: 30rem;
.bx--btn--icon-only {
@@ -10,6 +16,15 @@
}
}
+.exit__row {
+ display: flex;
+ justify-content: flex-end;
+ align-items: baseline;
+}
+.leave__button {
+ font-size: 1.3rem;
+}
+
.table__header {
height: 10vh;
}
diff --git a/assets/components/CardTable.js b/assets/components/CardTable.js
@@ -1,11 +1,63 @@
-import React, {Component} from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
+import _ from 'lodash';
import {Button, Grid, Row, Column} from 'carbon-components-react';
import {Logout32} from '@carbon/icons-react';
+import SeatPicker from './SeatPicker';
export default class CardTable extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ playerNames: [],
+ mySeat: -1
+ };
+ };
+
+ componentDidMount () {
+ const websoc = this.props.client;
+ websoc.onmessage = (event) => this.processResponse(event);
+ };
+
+ processResponse = (event) => {
+ let msg = JSON.parse(event.data);
+ console.log(msg);
+ if ('game_state' == msg.msg_type) {
+ if ('lobby' == msg.game.phase){
+ this.processLobby(msg);
+ }
+ };
+ };
+
+ processLobby = (msg) => {
+ if (msg && msg.game && msg.game.players) {
+ const plAr = msg.game.players;
+ const mySeat = plAr.findIndex( x => x == this.props.name );
+ this.setState({
+ playerNames: plAr,
+ mySeat: mySeat
+ })
+ };
+ };
+
+ sendSit = (index) => {
+ const { client } = this.props;
+ client.send(JSON.stringify({
+ action:'take_seat',
+ seat: index
+ }));
+ };
+
+ sendStand = () => {
+ const { client } = this.props;
+ client.send(JSON.stringify({
+ action:'stand_up'
+ }));
+ };
+
render () {
+ const { playerNames, mySeat } = this.state;
const {name, tableName} = this.props;
const welcomeMsg = 'Welcome to the ' + tableName + ' table, ' + name + '!';
return (
@@ -16,12 +68,13 @@ export default class CardTable extends React.Component {
<h3>{welcomeMsg}</h3>
</Column>
<Column className="hd__right" sm={1}>
- Exit
- <Button
- className="leave-button"
- hasIconOnly
- onClick={()=>{this.props.chooseTable('')}}
- renderIcon={Logout32}></Button>
+ <div className="exit__row">
+ <Button
+ className="leave__button"
+ kind="ghost"
+ onClick={()=>{this.props.chooseTable('')}}
+ renderIcon={Logout32}>Exit</Button>
+ </div>
</Column>
</Row>
<Row className="table__top">
@@ -40,7 +93,13 @@ export default class CardTable extends React.Component {
<div>yo!</div>
</Column>
<Column className="tm__center" sm={2}>
- <div>yo!</div>
+ {/* <div>yo!</div> */}
+ <SeatPicker
+ names={playerNames}
+ handleSit={this.sendSit}
+ handleStand={this.sendStand}
+ mySeat={mySeat}
+ />
</Column>
<Column className="tm__right" sm={1}>
<div>yo!</div>
@@ -68,5 +127,7 @@ export default class CardTable extends React.Component {
CardTable.propTypes = {
chooseTable: PropTypes.func,
name: PropTypes.string,
- tableName: PropTypes.string
+ tableName: PropTypes.string,
+ active: PropTypes.bool,
+ client: PropTypes.object
}
\ No newline at end of file
diff --git a/assets/components/Lobby.js b/assets/components/Lobby.js
@@ -1,4 +1,4 @@
-import React, {Component} from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
import {Button, TextInput} from 'carbon-components-react';
import {Login32, CheckmarkOutline32} from '@carbon/icons-react';
@@ -15,6 +15,19 @@ export default class Lobby extends React.Component {
}
}
+ componentDidMount () {
+ if (this.nameText) {
+ this.nameText.focus();
+ }
+ }
+
+ componentDidUpdate (prevProps) {
+ const { name } = this.props;
+ if (name && (name != prevProps.name)){
+ this.tableText.focus();
+ }
+ }
+
handlePlayerIn = event => {
const value = event.target.value;
const error = this.checkName(value);
@@ -42,7 +55,7 @@ export default class Lobby extends React.Component {
const {name} = this.props;
const {nameIn, nameError, tableIn, tableError} = this.state;
return (
- <div id="lobby">
+ <div id="lobby" className="lobby__outer">
<h2>Welcome to the Lobby</h2>
{!name &&
<p>First tell us the name that you'll be using for this game...</p>
@@ -56,13 +69,15 @@ export default class Lobby extends React.Component {
invalidText="Sorry, letters A-Z a-z and spaces only"
invalid={nameError}
onChange={this.handlePlayerIn}
+ ref={(input) => {this.nameText = input;}}
/>
<Button
className="name__button"
hasIconOnly={true}
onClick={()=>this.props.setName(nameIn)}
renderIcon={CheckmarkOutline32}
- iconDescription=""
+ iconDescription="set name"
+ tooltipPosition="bottom"
disabled={nameError}
/>
</div>
@@ -81,13 +96,15 @@ export default class Lobby extends React.Component {
invalidText="Sorry, letters A-Z a-z and spaces only"
invalid={tableError}
onChange={this.handleTableIn}
+ ref={(input) => {this.tableText = input;}}
/>
<Button
className="table__button"
hasIconOnly
onClick={()=>this.props.chooseTable(tableIn)}
renderIcon={Login32}
- iconDescription=""
+ iconDescription="go!"
+ tooltipPosition="bottom"
disabled={tableError || !name || name==''}
/>
</div>
diff --git a/assets/components/SeatPicker.js b/assets/components/SeatPicker.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {Button, Grid, Row, Column} from 'carbon-components-react';
+import {Package32, Export32} from '@carbon/icons-react';
+
+class SeatPicker extends React.Component {
+
+ constructor(props) {
+ super(props);
+ //this.state - unneeded?
+ }
+
+
+ tableSeat = (name, index) => {
+ const taken = name != 'Empty';
+ const mine = index === this.props.mySeat;
+ return (
+ <div className="table__seat">
+ {!taken && (
+ <Button
+ className="sit__button"
+ kind="ghost"
+ onClick={()=>{this.props.handleSit(index)}}
+ renderIcon={Package32}>Choose seat</Button>
+ )}
+ {taken && (<span className="spName">{name}</span>)}
+ {taken && mine && (
+ <Button
+ className="stand__button"
+ kind="ghost"
+ onClick={()=>{this.props.handleStand(index)}}
+ renderIcon={Export32}>Stand</Button>
+ )}
+ </div>
+ )
+ }
+
+ render () {
+ const {names} = this.props;
+ return (
+ <div id="seatPicker" className="seat__picker">
+ <Grid>
+ <Row className="sp__top">
+ {this.tableSeat(names[2], 2)}
+ </Row>
+ <Row className="sp__mid">
+ <Column className="spm__left">
+ {this.tableSeat(names[1], 1)}
+ </Column>
+ <Column className="spm__right">
+ {this.tableSeat(names[3], 3)}
+ </Column>
+ </Row>
+ <Row className="sp__bot">
+ {this.tableSeat(names[0], 0)}
+ </Row>
+ </Grid>
+ </div>
+ )
+ }
+}
+SeatPicker.propTypes = {
+ mySeat: PropTypes.number,
+ names: PropTypes.arrayOf(PropTypes.string),
+ handleSit: PropTypes.func,
+ handleStand: PropTypes.func
+}
+export default SeatPicker;
diff --git a/package-lock.json b/package-lock.json
@@ -2557,6 +2557,15 @@
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk="
},
+ "d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "requires": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -2862,6 +2871,35 @@
"is-symbol": "^1.0.2"
}
},
+ "es5-ext": {
+ "version": "0.10.53",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+ "requires": {
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.3",
+ "next-tick": "~1.0.0"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "es6-symbol": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+ "requires": {
+ "d": "^1.0.1",
+ "ext": "^1.1.2"
+ }
+ },
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -2967,6 +3005,21 @@
"homedir-polyfill": "^1.0.1"
}
},
+ "ext": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+ "requires": {
+ "type": "^2.0.0"
+ },
+ "dependencies": {
+ "type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
+ "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow=="
+ }
+ }
+ },
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -4998,6 +5051,11 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
},
+ "next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+ },
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -7578,11 +7636,24 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+ },
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
+ "typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -7967,6 +8038,18 @@
}
}
},
+ "websocket": {
+ "version": "1.0.31",
+ "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.31.tgz",
+ "integrity": "sha512-VAouplvGKPiKFDTeCCO65vYHsyay8DqoBSlzIO3fayrfOgU94lQN5a1uWVnFrMLceTJw/+fQXR5PGbUVRaHshQ==",
+ "requires": {
+ "debug": "^2.2.0",
+ "es5-ext": "^0.10.50",
+ "nan": "^2.14.0",
+ "typedarray-to-buffer": "^3.1.5",
+ "yaeti": "^0.0.6"
+ }
+ },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -8050,6 +8133,11 @@
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
+ "yaeti": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
+ "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
+ },
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
diff --git a/package.json b/package.json
@@ -15,6 +15,7 @@
"carbon-icons": "^7.0.7",
"css-loader": "^3.4.2",
"html-webpack-plugin": "^4.0.3",
+ "lodash": "^4.17.15",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.3",
@@ -24,6 +25,7 @@
"terser-webpack-plugin": "^2.3.5",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11",
- "webpack-plugin-hash-output": "^3.2.1"
+ "webpack-plugin-hash-output": "^3.2.1",
+ "websocket": "^1.0.31"
}
}