Chatting works!
Signed-off-by: James Ketrenos <james.p.ketrenos@intel.com>
This commit is contained in:
parent
28746f41c9
commit
10c3020b8e
@ -15,6 +15,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.9.11",
|
||||
"@material-ui/lab": "^4.0.0-alpha.50",
|
||||
"animakit-expander": "^2.1.4",
|
||||
"bluebird": "^3.5.5",
|
||||
"bootstrap": "^4.4.1",
|
||||
|
@ -85,7 +85,7 @@ app.use(function(req, res, next){
|
||||
app.use(session({
|
||||
store: new SQLiteStore({ db: config.get("sessions.db") }),
|
||||
secret: config.get("sessions.store-secret"),
|
||||
cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 }, // 1 week
|
||||
cookie: { maxAge: 21 * 24 * 60 * 60 * 1000 }, // 3 weeks
|
||||
saveUninitialized: false,
|
||||
resave: true
|
||||
}));
|
||||
|
@ -102,7 +102,60 @@ const games = {};
|
||||
router.get("/:id", (req, res/*, next*/) => {
|
||||
console.log("GET games/" + req.params.id);
|
||||
if (req.params.id in games) {
|
||||
return res.status(200).send(games[req.params.id]);
|
||||
const game = games[req.params.id];
|
||||
return res.status(200).send(Object.assign({}, game, {
|
||||
timestamp: Date.now(),
|
||||
activePlayer: req.session.activePlayer
|
||||
}));
|
||||
} else {
|
||||
const error = `Game not found: ${req.params.id}`;
|
||||
return res.status(404).send(error);
|
||||
}
|
||||
});
|
||||
|
||||
router.put("/:id", (req, res/*, next*/) => {
|
||||
console.log("PUT games/" + req.params.id);
|
||||
if (req.params.id in games) {
|
||||
const game = games[req.params.id],
|
||||
changes = req.body;
|
||||
|
||||
console.log(JSON.stringify(changes, null, 2));
|
||||
|
||||
for (let change in changes) {
|
||||
switch (change) {
|
||||
case "players":
|
||||
console.log("Player change.");
|
||||
for (let player in changes.players) {
|
||||
const playerChange = changes.players[player];
|
||||
if (playerChange.name != "") {
|
||||
game.chat.push({ from: player, date: Date.now(), message: `${player} is now '${playerChange.name}'.` });
|
||||
req.session.activePlayer = player;
|
||||
game.players[player].status = `Just joined`;
|
||||
} else {
|
||||
game.chat.push({ from: player, date: Date.now(), message: `${player} is no longer claimed.` });
|
||||
req.session.activePlayer = "";
|
||||
game.players[player].status = `Not active`;
|
||||
}
|
||||
game.players[player].name = playerChange.name;
|
||||
}
|
||||
break;
|
||||
case "chat":
|
||||
console.log("Chat change.");
|
||||
game.chat.push({
|
||||
from: changes.chat.player,
|
||||
date: Date.now(),
|
||||
message: changes.chat.message
|
||||
});
|
||||
if (game.chat.length > 10) {
|
||||
game.chat.splice(0, game.chat.length - 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res.status(200).send(Object.assign({}, game, {
|
||||
timestamp: Date.now(),
|
||||
activePlayer: req.session.activePlayer
|
||||
}));
|
||||
} else {
|
||||
const error = `Game not found: ${req.params.id}`;
|
||||
return res.status(404).send(error);
|
||||
@ -117,12 +170,13 @@ router.post("/", (req, res/*, next*/) => {
|
||||
tiles: [],
|
||||
pips: [],
|
||||
borders: [],
|
||||
tokens: [
|
||||
{ player: "R", roads: 15, cities: 4, settlements: 5, points: 0 },
|
||||
{ player: "O", roads: 15, cities: 4, settlements: 5, points: 0 },
|
||||
{ player: "B", roads: 15, cities: 4, settlements: 5, points: 0 },
|
||||
{ player: "W", roads: 15, cities: 4, settlements: 5, points: 0 }
|
||||
],
|
||||
tokens: [],
|
||||
players: {
|
||||
R: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" },
|
||||
O: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" },
|
||||
B: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" },
|
||||
W: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" }
|
||||
},
|
||||
developmentCards: assetData.developmentCards.slice(),
|
||||
dice: [ 0, 0 ],
|
||||
sheep: 19,
|
||||
@ -154,7 +208,10 @@ router.post("/", (req, res/*, next*/) => {
|
||||
games[game.id] = game;
|
||||
|
||||
console.log(`New game created: ${game.id}`);
|
||||
return res.status(200).send(game);
|
||||
return res.status(200).send(Object.assign({}, game, {
|
||||
timestamp: Date.now(),
|
||||
activePlayer: req.session.activePlayer
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
BIN
sessions.db
BIN
sessions.db
Binary file not shown.
@ -43,8 +43,6 @@
|
||||
}
|
||||
|
||||
.Chat {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 0.5em;
|
||||
width: 30vmax;
|
||||
display: inline-block;
|
||||
@ -60,6 +58,25 @@
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.Players {
|
||||
padding: 0.5em;
|
||||
width: 30vmax;
|
||||
display: inline-block;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#ChatList {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.Players > * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Players .nameInput {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Stack > *:not(:first-child) {
|
||||
margin-left: -4.5em;
|
||||
}
|
||||
|
204
src/Board.js
204
src/Board.js
@ -7,9 +7,38 @@ import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { deepOrange, lightBlue, red, grey } from '@material-ui/core/colors';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Switch from '@material-ui/core/Switch';
|
||||
import Moment from 'react-moment';
|
||||
import moment from 'moment';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
'& > *': {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
R: {
|
||||
color: theme.palette.getContrastText(red[500]),
|
||||
backgroundColor: red[500],
|
||||
},
|
||||
O: {
|
||||
color: theme.palette.getContrastText(deepOrange[500]),
|
||||
backgroundColor: deepOrange[500],
|
||||
},
|
||||
W: {
|
||||
color: theme.palette.getContrastText(grey[100]),
|
||||
backgroundColor: grey[100],
|
||||
},
|
||||
B: {
|
||||
color: theme.palette.getContrastText(lightBlue[500]),
|
||||
backgroundColor: lightBlue[500],
|
||||
},
|
||||
}));
|
||||
|
||||
const hexagonRatio = 1.1547005,
|
||||
tileHeight = 0.16,
|
||||
tileWidth = tileHeight * hexagonRatio,
|
||||
@ -179,49 +208,145 @@ class Resource extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
class Chat extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.chatInput = this.chatInput.bind(this);
|
||||
}
|
||||
|
||||
chatInput(event) {
|
||||
const Chat = ({ game, promoteGameState }) => {
|
||||
const chatInput = (event) => {
|
||||
console.log(event.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
chatKeyPress(event) {
|
||||
const chatKeyPress = (event) => {
|
||||
if (event.key == "Enter") {
|
||||
console.log(`Send: ${event.target.value}`);
|
||||
promoteGameState({ chat: { player: game.activePlayer, message: event.target.value }});
|
||||
event.target.value = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
//this.props.game.messages
|
||||
const messages =[ { from: "R", date: Date.now(), message: "Hello, world!" } ].map((item, index) => {
|
||||
const timestamp = moment(item.date).fromNow();
|
||||
const classes = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
const chatList = document.getElementById("ChatList");
|
||||
chatList.scrollTop = chatList.scrollHeight - chatList.offsetHeight;
|
||||
})
|
||||
console.log(JSON.stringify(game, null, 2));
|
||||
|
||||
const timeDelta = game.timestamp - Date.now();
|
||||
|
||||
const messages = game.chat.map((item, index) => {
|
||||
const timestamp = moment(item.date - timeDelta).fromNow();
|
||||
return (
|
||||
<ListItem key={`msg-${index}`}>
|
||||
<ListItemAvatar>
|
||||
{item.from}
|
||||
<Avatar className={classes[item.from]}>{item.from}</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={item.message} secondary={timestamp} />
|
||||
</ListItem>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Paper className="Chat">
|
||||
<List>
|
||||
<List id="ChatList">
|
||||
{ messages }
|
||||
</List>
|
||||
<TextField id="chat-input"
|
||||
onChange={this.chatInput}
|
||||
onKeyPress={this.chatKeyPress}
|
||||
<TextField className="chatInput"
|
||||
disabled={!game.activePlayer}
|
||||
onChange={chatInput}
|
||||
onKeyPress={chatKeyPress}
|
||||
label={(<Moment format="h:mm:ss" interval={1000}/>)} variant="outlined"/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
/* This needs to take in a mechanism to declare the
|
||||
* player's active item in the game */
|
||||
const Players = ({ game, promoteGameState }) => {
|
||||
const [selected, setSelected] = useState("");
|
||||
const [name, setName] = useState("");
|
||||
|
||||
const nameInput = (event) => {
|
||||
console.log(event.target.value);
|
||||
};
|
||||
|
||||
const nameKeyPress = (event) => {
|
||||
if (event.key == "Enter") {
|
||||
console.log(`Send: ${event.target.value}`);
|
||||
setName(event.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const change = { players: {} };
|
||||
|
||||
/* Joining: selected != "", activePlayer == "", and name != "" */
|
||||
if (selected && !game.activePlayer && name != "") {
|
||||
change.players[selected] = { name: name }
|
||||
promoteGameState(change)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Leaving: selected = "", name = "", activePlayer != "" */
|
||||
if (!selected && game.activePlayer != "") {
|
||||
change.players[game.activePlayer] = { name: "" }
|
||||
promoteGameState(change)
|
||||
}
|
||||
|
||||
/* Updating name: selected != "", activePlayer != "", name != "", name != activePlayer.name*/
|
||||
if (selected != "" &&
|
||||
game.activePlayer != "" &&
|
||||
name != "" &&
|
||||
game.players[game.activePlayer].name != name) {
|
||||
change.players[game.activePlayer] = { name: name }
|
||||
promoteGameState(change)
|
||||
}
|
||||
});
|
||||
|
||||
const toggleSelected = (key) => {
|
||||
if (selected == key) {
|
||||
setSelected("");
|
||||
setName("");
|
||||
} else {
|
||||
setSelected(key);
|
||||
}
|
||||
}
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
const players = [];
|
||||
for (let key in game.players) {
|
||||
const item = game.players[key];
|
||||
players.push((
|
||||
<ListItem key={`player-${key}`}>
|
||||
<ListItemAvatar>
|
||||
<Avatar className={classes[key]}>{key}</Avatar>
|
||||
</ListItemAvatar>
|
||||
<> { /* so flex-grow works we put in a fragment */ }
|
||||
{ selected == key && item.name == "" &&
|
||||
<TextField className="nameInput"
|
||||
onChange={nameInput}
|
||||
onKeyPress={nameKeyPress}
|
||||
inputRef={input => input && input.focus()}
|
||||
disabled={name != item.name}
|
||||
label="Name"
|
||||
variant="outlined" autoFocus/>
|
||||
}
|
||||
{ (selected != key || item.name != "") &&
|
||||
<ListItemText primary={item.name} secondary={item.status} />
|
||||
}
|
||||
</>
|
||||
<Switch edge="end"
|
||||
disabled={selected && selected != key} checked={selected == key}
|
||||
onChange={() => toggleSelected(key)}/>
|
||||
</ListItem>
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper className="Players">
|
||||
<List>
|
||||
{ players }
|
||||
</List>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
class Board extends React.Component {
|
||||
@ -233,7 +358,8 @@ class Board extends React.Component {
|
||||
sheep: 0,
|
||||
brick: 0,
|
||||
stone: 0,
|
||||
wheat: 0
|
||||
wheat: 0,
|
||||
game: null
|
||||
};
|
||||
this.componentDidMount = this.componentDidMount.bind(this);
|
||||
this.updateDimensions = this.updateDimensions.bind(this);
|
||||
@ -245,6 +371,7 @@ class Board extends React.Component {
|
||||
this.mouseMove = this.mouseMove.bind(this);
|
||||
this.randomize = this.randomize.bind(this);
|
||||
this.throwDice = this.throwDice.bind(this);
|
||||
this.promoteGameState = this.promoteGameState.bind(this);
|
||||
|
||||
this.mouse = { x: 0, y: 0 };
|
||||
this.radius = 0.317;
|
||||
@ -284,7 +411,10 @@ class Board extends React.Component {
|
||||
// body: JSON.stringify(data) // body data type must match "Content-Type" header
|
||||
}).then((res) => {
|
||||
if (res.status > 400) {
|
||||
throw `Unable to load game ${props.match.params.id}`;
|
||||
const base = document.querySelector("base");
|
||||
window.location.href = base ? base.href : "/";
|
||||
console.log(`Unable to find game ${props.match.params.id}`);
|
||||
throw `Unable to find requested game. Staring new one.`;
|
||||
}
|
||||
return res.json();
|
||||
}).then((game) => {
|
||||
@ -295,6 +425,7 @@ class Board extends React.Component {
|
||||
}
|
||||
|
||||
this.game = game;
|
||||
this.setState({ game: game });
|
||||
this.pips = Pips(this);
|
||||
this.tiles = Tiles(this);
|
||||
this.table = Table(this);
|
||||
@ -308,6 +439,29 @@ class Board extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
promoteGameState(change) {
|
||||
console.log("Requesting state change: ", change);
|
||||
|
||||
window.fetch(`api/v1/games/${this.state.game.id}`, {
|
||||
method: "PUT",
|
||||
cache: 'no-cache',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(change)
|
||||
}).then((res) => {
|
||||
if (res.status > 400) {
|
||||
console.error(res);
|
||||
alert(`Unable to change state`);
|
||||
}
|
||||
return res.json();
|
||||
}).then((game) => {
|
||||
console.log (`Game state changed.`);
|
||||
this.setState({ game: game });
|
||||
});
|
||||
}
|
||||
|
||||
randomize() {
|
||||
//this.borders = shuffle(this.borders);
|
||||
this.tiles = shuffle(this.tiles);
|
||||
@ -853,11 +1007,17 @@ class Board extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const game = this.state.game;
|
||||
return (
|
||||
<div className="Board" ref={el => this.el = el}>
|
||||
<canvas className="Display" ref={el => this.canvas = el}></canvas>
|
||||
<div className="Cards" ref={el => this.cards = el}>
|
||||
<Chat/>
|
||||
{ game &&
|
||||
<>
|
||||
<Chat game={game} promoteGameState={this.promoteGameState}/>
|
||||
<Players game={game} promoteGameState={this.promoteGameState}/>
|
||||
</>
|
||||
}
|
||||
<div>In hand</div>
|
||||
<div className="Hand">
|
||||
<Resource type="wood" count={this.state.wood}/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user