import React, { useState, useEffect, useContext, useRef, useCallback, useMemo } from "react"; 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 'moment-timezone'; import "./Chat.css"; import { PlayerColor } from './PlayerColor.js'; import { Resource } from './Resource.js'; import { Dice } from './Dice.js'; import { GlobalContext } from "./GlobalContext.js"; const Chat = () => { const [lastTop, setLastTop] = useState(0); const [autoScroll, setAutoScroll] = useState(true); const [latest, setLatest] = useState(''); const [scrollTime, setScrollTime] = useState(0); const [chat, setChat] = useState([]); const [startTime, setStartTime] = useState(0); const { ws, name } = useContext(GlobalContext); const fields = useMemo(() => [ 'chat', 'startTime' ], []); const onWsMessage = (event) => { const data = JSON.parse(event.data); switch (data.type) { case 'game-update': console.log(`chat - game update`); if (data.update.chat && data.update.chat.length !== chat.length) { console.log(`chat - game update - ${data.update.chat.length} lines`); setChat(data.update.chat); } if (data.update.startTime && data.update.startTime !== startTime) { setStartTime(data.update.startTime); } break; default: break; } }; const refWsMessage = useRef(onWsMessage); useEffect(() => { refWsMessage.current = onWsMessage; }); useEffect(() => { if (!ws) { return; } const cbMessage = e => refWsMessage.current(e); ws.addEventListener('message', cbMessage); return () => { ws.removeEventListener('message', cbMessage); } }, [ws, refWsMessage]); useEffect(() => { if (!ws) { return; } ws.send(JSON.stringify({ type: 'get', fields })); }, [ws, fields]); const chatKeyPress = useCallback((event) => { if (event.key === "Enter") { if (!autoScroll) { setAutoScroll(true); } ws.send(JSON.stringify({ type: 'chat', message: event.target.value })); event.target.value = ""; } }, [ws, setAutoScroll, autoScroll]); 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 messages = chat.map((item, index) => { let message; /* Do not perform extra parsing on player-generated * messages */ if (item.normalChat) { message =
{item.message}
; } else { const punctuation = item.message.match(/(\.+$)/); let period; if (punctuation) { period = punctuation[1]; } else { period = ''; } let lines = item.message.split('.'); message = lines .filter(line => line.trim() !== '') .map((line, index) => { /* If the date is in the future, set it to now */ const dice = line.match(/^(.*rolled )([1-6])(, ([1-6]))?(.*)$/); if (dice) { if (dice[4]) { return
{dice[1]} , {dice[5]}{ period }
; } else { return
{dice[1]} {dice[5]}{ period }
; } } let start = line, 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[5]}{message}; start = resource[1]; } else { message = <>{start}{message}; start = ''; } } return
{ message }{ period }
; }); } return ( { item.color && } Date.now() ? Date.now() : item.date} interval={1000}/>} /> ); }); if (chat.length && chat[chat.length - 1].date !== latest) { setLatest(chat[chat.length - 1].date); setAutoScroll(true); } return ( { messages } Game duration: } variant="outlined"/> ); } export { Chat };