Add Resource replacement in Chat
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
7439eaf21f
commit
44cd877c4e
81
client/src/Chat.css
Normal file
81
client/src/Chat.css
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
|
||||||
|
.Chat {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList {
|
||||||
|
/* for Firefox */
|
||||||
|
min-height: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
overflow: auto;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
align-items: flex-start;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .System {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatInput {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .MuiListItem-gutters {
|
||||||
|
padding: 2px 0 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .MuiTypography-body1 {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .System .MuiTypography-body1 {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .MuiTypography-body2 {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .MuiListItemText-multiline {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 4px 0px 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .PlayerColor {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 6px;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .Resource {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 3em;
|
||||||
|
height: 4.3em;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .Stack {
|
||||||
|
margin-left: 0;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .Stack > *:not(:first-child) {
|
||||||
|
margin-left: 0;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatList .Dice {
|
||||||
|
margin-left: 0.25em;
|
||||||
|
}
|
143
client/src/Chat.js
Normal file
143
client/src/Chat.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import "./Chat.css";
|
||||||
|
import PlayerColor from './PlayerColor.js';
|
||||||
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import List from '@material-ui/core/List';
|
||||||
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
|
import Moment from 'react-moment';
|
||||||
|
import TextField from '@material-ui/core/TextField';
|
||||||
|
|
||||||
|
import Resource from './Resource.js';
|
||||||
|
import Dice from './Dice.js';
|
||||||
|
|
||||||
|
const Chat = ({ table }) => {
|
||||||
|
const [lastTop, setLastTop] = useState(0),
|
||||||
|
[autoScroll, setAutoscroll] = useState(true),
|
||||||
|
[latest, setLatest] = useState(''),
|
||||||
|
[scrollTime, setScrollTime] = useState(0);
|
||||||
|
|
||||||
|
const chatInput = (event) => {
|
||||||
|
};
|
||||||
|
|
||||||
|
const chatKeyPress = (event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
if (!autoScroll) {
|
||||||
|
setAutoscroll(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sendChat(event.target.value);
|
||||||
|
|
||||||
|
event.target.value = "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const chatScroll = (event) => {
|
||||||
|
const chatList = event.target,
|
||||||
|
fromBottom = Math.round(Math.abs((chatList.scrollHeight - chatList.offsetHeight) - chatList.scrollTop));
|
||||||
|
|
||||||
|
/* If scroll is within 20 pixels of the bottom, turn on auto-scroll */
|
||||||
|
const shouldAutoscroll = (fromBottom < 20);
|
||||||
|
|
||||||
|
if (shouldAutoscroll !== autoScroll) {
|
||||||
|
setAutoscroll(shouldAutoscroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the list should not auto scroll, then cache the current
|
||||||
|
* top of the list and record when we did this so we honor
|
||||||
|
* the auto-scroll for at least 500ms */
|
||||||
|
if (!shouldAutoscroll) {
|
||||||
|
const target = Math.round(chatList.scrollTop);
|
||||||
|
if (target !== lastTop) {
|
||||||
|
setLastTop(target);
|
||||||
|
setScrollTime(Date.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const chatList = document.getElementById("ChatList"),
|
||||||
|
currentTop = Math.round(chatList.scrollTop);
|
||||||
|
|
||||||
|
if (autoScroll) {
|
||||||
|
/* Auto-scroll to the bottom of the chat window */
|
||||||
|
const target = Math.round(chatList.scrollHeight - chatList.offsetHeight);
|
||||||
|
if (currentTop !== target) {
|
||||||
|
chatList.scrollTop = target;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Maintain current position in scrolled view if the user hasn't
|
||||||
|
* been scrolling in the past 0.5s */
|
||||||
|
if ((Date.now() - scrollTime) > 500 && currentTop !== lastTop) {
|
||||||
|
chatList.scrollTop = lastTop;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//const timeDelta = game.timestamp - Date.now();
|
||||||
|
if (!table.game) {
|
||||||
|
console.log("Why no game?");
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = table.game && table.game.chat.map((item, index) => {
|
||||||
|
let message;
|
||||||
|
/* If the date is in the future, set it to now */
|
||||||
|
const dice = item.message.match(/^(.*rolled )([1-6])(, ([1-6]))?(.*)$/);
|
||||||
|
if (dice) {
|
||||||
|
if (dice[4]) {
|
||||||
|
message = <>{dice[1]}<Dice pips={dice[2]}/>, <Dice pips={dice[4]}/>{dice[5]}</>;
|
||||||
|
} else {
|
||||||
|
message = <>{dice[1]}<Dice pips={dice[2]}/>{dice[5]}</>;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let start = item.message;
|
||||||
|
while (start) {
|
||||||
|
const resource = start.match(/^(.*)(([0-9]+) (wood|sheep|wheat|stone|brick),?)(.*)$/);
|
||||||
|
if (resource) {
|
||||||
|
const count = resource[3] ? parseInt(resource[3]) : 1;
|
||||||
|
message = <><Resource count={count} type={resource[4]}/>{resource[5]}{message}</>;
|
||||||
|
start = resource[1];
|
||||||
|
} else {
|
||||||
|
message = <>{start}{message}</>;
|
||||||
|
start = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem key={`msg-${item.date}`} className={item.color ? '' : 'System'}>
|
||||||
|
{ item.color &&
|
||||||
|
<PlayerColor color={item.color}/>
|
||||||
|
}
|
||||||
|
<ListItemText primary={message}
|
||||||
|
secondary={item.color && <Moment fromNow date={item.date > Date.now() ?
|
||||||
|
Date.now() : item.date} interval={1000}/>} />
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (table.game && table.game.chat &&
|
||||||
|
table.game.chat.length &&
|
||||||
|
table.game.chat[table.game.chat.length - 1].date !== latest) {
|
||||||
|
setLatest(table.game.chat[table.game.chat.length - 1].date);
|
||||||
|
setAutoscroll(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = table.game ? table.game.name : "Why no game?";
|
||||||
|
const elapsed = table.game ? (table.game.timestamp - table.game.startTime) : undefined;
|
||||||
|
return (
|
||||||
|
<Paper className="Chat">
|
||||||
|
<List className="ChatList" id="ChatList" onScroll={chatScroll}>
|
||||||
|
{ messages }
|
||||||
|
</List>
|
||||||
|
<TextField className="ChatInput"
|
||||||
|
disabled={!name}
|
||||||
|
onChange={chatInput}
|
||||||
|
onKeyPress={chatKeyPress}
|
||||||
|
label={elapsed && <Moment tz={"Etc/GMT"} format="h:mm:ss" durationFromNow interval={1000} date={table.game.startTime}></Moment>} variant="outlined"/>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Chat;
|
@ -264,66 +264,6 @@
|
|||||||
* Chat
|
* Chat
|
||||||
* Action
|
* Action
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.Chat {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList {
|
|
||||||
/* for Firefox */
|
|
||||||
min-height: 0;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
overflow: auto;
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
align-items: flex-start;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList .System {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
transform: scale(0.8);
|
|
||||||
border-radius: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatInput {
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList .MuiListItem-gutters {
|
|
||||||
padding: 2px 0 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList .MuiTypography-body1 {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList .System .MuiTypography-body1 {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList .MuiTypography-body2 {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList .MuiListItemText-multiline {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding: 4px 0px 4px 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ChatList .PlayerColor {
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
padding: 0;
|
|
||||||
margin-top: 6px;
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Players {
|
.Players {
|
||||||
flex: 1 0;
|
flex: 1 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState } from "react";
|
||||||
import "./Table.css";
|
import "./Table.css";
|
||||||
import history from "./history.js";
|
import history from "./history.js";
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from '@material-ui/core/TextField';
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
import Moment from 'react-moment';
|
import Moment from 'react-moment';
|
||||||
import Board from './Board.js';
|
import Board from './Board.js';
|
||||||
@ -17,6 +16,7 @@ import Resource from './Resource.js';
|
|||||||
import ViewCard from './ViewCard.js';
|
import ViewCard from './ViewCard.js';
|
||||||
import Winner from './Winner.js';
|
import Winner from './Winner.js';
|
||||||
import ChooseCard from './ChooseCard.js';
|
import ChooseCard from './ChooseCard.js';
|
||||||
|
import Chat from './Chat.js';
|
||||||
import { CircularProgress } from "@material-ui/core";
|
import { CircularProgress } from "@material-ui/core";
|
||||||
import 'moment-timezone';
|
import 'moment-timezone';
|
||||||
|
|
||||||
@ -119,123 +119,6 @@ const Development = ({table, type, card, onClick}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Chat = ({ table }) => {
|
|
||||||
const [lastTop, setLastTop] = useState(0),
|
|
||||||
[autoScroll, setAutoscroll] = useState(true),
|
|
||||||
[latest, setLatest] = useState(''),
|
|
||||||
[scrollTime, setScrollTime] = useState(0);
|
|
||||||
|
|
||||||
const chatInput = (event) => {
|
|
||||||
};
|
|
||||||
|
|
||||||
const chatKeyPress = (event) => {
|
|
||||||
if (event.key === "Enter") {
|
|
||||||
if (!autoScroll) {
|
|
||||||
setAutoscroll(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
table.sendChat(event.target.value);
|
|
||||||
|
|
||||||
event.target.value = "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const chatScroll = (event) => {
|
|
||||||
const chatList = event.target,
|
|
||||||
fromBottom = Math.round(Math.abs((chatList.scrollHeight - chatList.offsetHeight) - chatList.scrollTop));
|
|
||||||
|
|
||||||
/* If scroll is within 20 pixels of the bottom, turn on auto-scroll */
|
|
||||||
const shouldAutoscroll = (fromBottom < 20);
|
|
||||||
|
|
||||||
if (shouldAutoscroll !== autoScroll) {
|
|
||||||
setAutoscroll(shouldAutoscroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the list should not auto scroll, then cache the current
|
|
||||||
* top of the list and record when we did this so we honor
|
|
||||||
* the auto-scroll for at least 500ms */
|
|
||||||
if (!shouldAutoscroll) {
|
|
||||||
const target = Math.round(chatList.scrollTop);
|
|
||||||
if (target !== lastTop) {
|
|
||||||
setLastTop(target);
|
|
||||||
setScrollTime(Date.now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const chatList = document.getElementById("ChatList"),
|
|
||||||
currentTop = Math.round(chatList.scrollTop);
|
|
||||||
|
|
||||||
if (autoScroll) {
|
|
||||||
/* Auto-scroll to the bottom of the chat window */
|
|
||||||
const target = Math.round(chatList.scrollHeight - chatList.offsetHeight);
|
|
||||||
if (currentTop !== target) {
|
|
||||||
chatList.scrollTop = target;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Maintain current position in scrolled view if the user hasn't
|
|
||||||
* been scrolling in the past 0.5s */
|
|
||||||
if ((Date.now() - scrollTime) > 500 && currentTop !== lastTop) {
|
|
||||||
chatList.scrollTop = lastTop;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//const timeDelta = game.timestamp - Date.now();
|
|
||||||
if (!table.game) {
|
|
||||||
console.log("Why no game?");
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = table.game && table.game.chat.map((item, index) => {
|
|
||||||
let message;
|
|
||||||
/* If the date is in the future, set it to now */
|
|
||||||
const dice = item.message.match(/^(.*rolled )([1-6])(, ([1-6]))?(.*)$/);
|
|
||||||
if (dice) {
|
|
||||||
if (dice[4]) {
|
|
||||||
message = <>{dice[1]}<Dice pips={dice[2]}/>, <Dice pips={dice[4]}/>{dice[5]}</>;
|
|
||||||
} else {
|
|
||||||
message = <>{dice[1]}<Dice pips={dice[2]}/>{dice[5]}</>;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
message = item.message;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ListItem key={`msg-${item.date}`} className={item.color ? '' : 'System'}>
|
|
||||||
{ item.color &&
|
|
||||||
<PlayerColor color={item.color}/>
|
|
||||||
}
|
|
||||||
<ListItemText primary={message}
|
|
||||||
secondary={item.color && <Moment fromNow date={item.date > Date.now() ?
|
|
||||||
Date.now() : item.date} interval={1000}/>} />
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (table.game && table.game.chat &&
|
|
||||||
table.game.chat.length &&
|
|
||||||
table.game.chat[table.game.chat.length - 1].date !== latest) {
|
|
||||||
setLatest(table.game.chat[table.game.chat.length - 1].date);
|
|
||||||
setAutoscroll(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = table.game ? table.game.name : "Why no game?";
|
|
||||||
const elapsed = table.game ? (table.game.timestamp - table.game.startTime) : undefined;
|
|
||||||
return (
|
|
||||||
<Paper className="Chat">
|
|
||||||
<List className="ChatList" id="ChatList" onScroll={chatScroll}>
|
|
||||||
{ messages }
|
|
||||||
</List>
|
|
||||||
<TextField className="ChatInput"
|
|
||||||
disabled={!name}
|
|
||||||
onChange={chatInput}
|
|
||||||
onKeyPress={chatKeyPress}
|
|
||||||
label={elapsed && <Moment tz={"Etc/GMT"} format="h:mm:ss" durationFromNow interval={1000} date={table.game.startTime}></Moment>} variant="outlined"/>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const StartButton = ({ table }) => {
|
const StartButton = ({ table }) => {
|
||||||
const startClick = (event) => {
|
const startClick = (event) => {
|
||||||
table.setGameState("game-order").then((state) => {
|
table.setGameState("game-order").then((state) => {
|
||||||
|
@ -1785,7 +1785,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
game.turn.actions = [];
|
game.turn.actions = [];
|
||||||
game.turn.limits = {};
|
game.turn.limits = {};
|
||||||
addChatMessage(game, session,
|
addChatMessage(game, session,
|
||||||
`${session.name} randomly stole ${type} from ${playerNameFromColor(game, value)}.`);
|
`${session.name} randomly stole 1 ${type} from ${playerNameFromColor(game, value)}.`);
|
||||||
}
|
}
|
||||||
debugChat(game, 'After steal');
|
debugChat(game, 'After steal');
|
||||||
|
|
||||||
@ -2891,9 +2891,7 @@ const createGame = (id) => {
|
|||||||
setBeginnerGame(game);
|
setBeginnerGame(game);
|
||||||
resetGame(game);
|
resetGame(game);
|
||||||
|
|
||||||
console.log(`New game created with Beginner's Layout: ${game.id}`);
|
addChatMessage(game, null, `New game created with Beginner's Layout: ${game.id}`);
|
||||||
addChatMessage(game, null,
|
|
||||||
`New game created with Beginner's Layout: ${game.id}`);
|
|
||||||
|
|
||||||
games[game.id] = game;
|
games[game.id] = game;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user