diff --git a/client/package-lock.json b/client/package-lock.json index a9933a7..cfef93c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -6888,6 +6888,19 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -10186,6 +10199,15 @@ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz", "integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=" }, + "mini-create-react-context": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz", + "integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==", + "requires": { + "@babel/runtime": "^7.5.5", + "tiny-warning": "^1.0.3" + } + }, "mini-css-extract-plugin": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", @@ -13429,6 +13451,52 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + } + } + } + }, + "react-router-dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, "react-scripts": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.1.tgz", @@ -14088,6 +14156,11 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -15666,6 +15739,11 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, "tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", @@ -16086,6 +16164,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/client/package.json b/client/package.json index 8cf402f..4729dd2 100644 --- a/client/package.json +++ b/client/package.json @@ -14,6 +14,7 @@ "react-color": "^2.18.1", "react-countdown-clock": "^2.7.0", "react-dom": "^16.13.1", + "react-router-dom": "^5.2.0", "react-scripts": "3.4.1", "react-window": "^1.8.5", "socket.io-client": "^2.3.0", diff --git a/client/src/App.css b/client/src/App.css index 3ef5103..bbbe423 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -6,111 +6,9 @@ html { --buttonColor: linear-gradient(to bottom, #d1e4f6 0%, #c5d8e9 100%); } -.App { - text-align: center; -} - body { background-color: var(--backgroundColor); /* Disables pull-to-refresh but allows overscroll glow effects. */ overscroll-behavior-y: contain; } - - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -.layoutContainer { - width: auto; - max-height: 100vh; -} - -.userScoreGridItem { - margin-left: auto; - margin-right: auto; - margin-bottom: 10px; - margin-top: auto; - height: 42vh; - display: flex; -} - -.userScoreContainer { - width: 100%; - display: flex; -} - -.logWindowContainer { - margin-left: auto; - margin-right: auto; - margin-top: 20px; -} - -.midPane { - margin-left: auto; - margin-right: auto; -} - -.midPaneContainer { - margin: auto; -} - -.guessBox { - width: 100%; - background-color: white; - z-index: 100; -} - -.canvasContainer { - margin: auto; -} - -.inputContainer { - position: relative; - margin: auto; -} - -.canvasToolboxContainer { - margin: auto; -} - -.playersIconContainer { - width: inherit; - text-align: start; - position: absolute; - top: 10px; - left: 10px; -} - -.playersIconButton { - position: relative; - z-index: 100; -} - -.playersIcon { - border-radius: 50%; - background-color: var(--primaryColor); -} diff --git a/client/src/App.js b/client/src/App.js index f82346e..94cff54 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,490 +1,29 @@ -import React, { useState, useEffect, useRef } from 'react'; -import socket from 'socket.io-client'; -import { TextField, Hidden, IconButton, Badge, Paper, Toolbar, SvgIcon } from '@material-ui/core'; +import React from 'react'; import { StylesProvider } from '@material-ui/core/styles'; -import Grid from '@material-ui/core/Grid'; -import ElementResizeDetectorMaker from 'element-resize-detector'; -import Typography from '@material-ui/core/Typography'; -import { ReactComponent as AppLogo } from './assets/logo.svg'; - -import AppBar from './components/appbar'; -import AddNicknameDialog from './components/dialogs/AddNicknameDialog'; -import LogWindow from './components/log-window'; -import UserScoreList from './components/player-list/UserScoreList'; -import PlayersIcon from './components/icons/PlayersIcon'; -import UserScoreListDialog from './components/dialogs/UserScoreListDialog'; -import * as GameStateConstants from './constants/AppConstants'; -import GameStateDisplay from './components/game-state-display/GameStateDisplay'; -import CanvasToolbox from './components/toolbox'; - +import { + BrowserRouter as Router, + Switch, + Route +} from 'react-router-dom'; import './App.css'; -const DEFAULT_ROOM = 'main'; -const ROUND_DURATION = 60; +import Home from './components/home'; +import Room from './components/room'; +import { PlayerContextProvider } from './contexts/PlayerContext'; function App () { - const [socketIO, setSocketIO] = useState(null); - const [room, setRoom] = useState(DEFAULT_ROOM); - const [drawWord, setDrawWord] = useState(null); - const [playerNickname, setPlayerNickname] = useState(null); - const [shouldShowPlayersList, setShouldShowPlayersList] = useState(false); - const [showGuessBox, setShowGuessBox] = useState(false); - const [disableGuessBox, setDisableGuessBox] = useState(true); - const [guess, setGuess] = useState(''); - const [currentUser, setCurrentUser] = useState(null); - const [userScores, setUserScores] = useState(null); - const [previousWord, setPreviousWord] = useState(null); - const [roundDuration, setRoundDuration] = useState(0); - const [roundInfo, setRoundInfo] = useState({ current: 0, total: 0 }); - const [gameState, setGameState] = useState( - GameStateConstants.GAME_STATE_IDLE - ); - const [canvasOptions, setCanvasOptions] = useState({ color: '#000000', enabled: true }); - const [messageLog, setMessageLog] = useState([]); - const [liveMessage, setLiveMessage] = useState(''); - const [winners, setWinners] = useState([]); - - const guessBoxRef = useRef(null); - const keyboardRef = useRef(); - const canvasPaperRef = useRef(null); - - useEffect(() => { - if (guessBoxRef && guessBoxRef.current) guessBoxRef.current.focus(); - }, [showGuessBox]); - - useEffect(() => { - if (playerNickname) { - const io = socket('http://localhost:3001'); - io.on('connect', () => { - const user = { - id: `${playerNickname}_${+new Date()}`, - name: `${playerNickname}`, - score: 0 - }; - console.log('Socket connected', user); - - io.emit('C_S_LOGIN', user, room); - - io.on('S_C_LOGIN', cbSCLogin); - io.on('GE_NEW_GAME', cbNewGame); - io.on('GE_NEW_ROUND', cbNewRound); - io.on('GE_WAIT_FOR_NEXT_ROUND', cbWaitForNextRound); - io.on('GE_ANNOUNCE_WINNER', cbAnnounceWinner); - io.on('GE_NEW_WORD', cbNewWord); - io.on('GE_UPDATE_SCORE', cbUpdateScore); - io.on('GE_UPDATE_GUESS', cbUpdateGuess); - - io.on('disconnect', () => { - console.log('Socket disconnected', user); - io.removeEventListener('S_C_LOGIN', cbSCLogin); - io.removeEventListener('GE_NEW_GAME', cbNewGame); - io.removeEventListener('GE_NEW_ROUND', cbNewRound); - io.removeEventListener('GE_WAIT_FOR_NEXT_ROUND', cbWaitForNextRound); - io.removeEventListener('GE_ANNOUNCE_WINNER', cbAnnounceWinner); - io.removeEventListener('GE_NEW_WORD', cbNewWord); - io.removeEventListener('GE_UPDATE_SCORE', cbUpdateScore); - io.removeEventListener('GE_UPDATE_GUESS', cbUpdateGuess); - setCurrentUser(null); - }); - }); - // Currently adding this line to prevent link check fails as setRoom is unused. - // Once Rooms feature is implemented, this shall be used - setRoom(room); - - setSocketIO(io); - } else { - console.log('playerNickname is null'); - } - }, [playerNickname]); - - const cbNewWord = (word) => { - console.log('EVENT GE_NEW_WORD'); - setLiveMessage(''); - setDrawWord(word); - setShowGuessBox(false); - setMessageLog((messageLog) => [ - ...messageLog, - "msgSystemImp!!!It's your turn, Draw!" - ]); - setLiveMessage("msgSystemImp!!!It's your turn, Draw!"); - }; - - const cbAnnounceWinner = ({ previousWord, winners }) => { - console.log('EVENT GE_ANNOUNCE_WINNER'); - setShowGuessBox(false); - setLiveMessage(''); - setWinners(winners); - setPreviousWord(previousWord); - console.log('Announce Winner'); - setShowGuessBox(false); - setDrawWord(null); - setGameState(GameStateConstants.GAME_STATE_ANNOUNCE_WINNER); - if (winners) { - if (winners.length > 1) { - let winnersString = ''; - winnersString = winners.reduce((accumulator, winner) => { - if (accumulator) { - return `${accumulator}, ${winner.name}`; - } else { - return winner.name; - } - }, ''); - console.log(winnersString); - setMessageLog((messageLog) => [ - ...messageLog, - `msgSystemWinner!!!Game Over, And the Winners are ${winnersString}` - ]); - } else { - setMessageLog((messageLog) => [ - ...messageLog, - `msgSystemWinner!!!Game Over, And the Winner is ${winners[0].name}` - ]); - } - } else { - setMessageLog((messageLog) => [ - ...messageLog, - 'msgSystemWinner!!!Game Over, Wait for new game!' - ]); - } - }; - - const cbWaitForNextRound = ({ previousWord, round, total }) => { - console.log('EVENT GE_WAIT_FOR_NEW_ROUND'); - setShowGuessBox(false); - setDrawWord(null); - setLiveMessage(''); - setPreviousWord(previousWord); - setRoundInfo({ current: total - round, total }); - setGameState(GameStateConstants.GAME_STATE_WAIT_FOR_NEXT_ROUND); - setMessageLog((messageLog) => [ - ...messageLog, - 'msgSystem!!!Round finished, Wait for next round...' - ]); - }; - - const cbNewRound = ({ round, total, currentDrawingUser, startTimestamp }) => { - console.log('EVENT GE_NEW_ROUND'); - const secondsLeft = Math.min(ROUND_DURATION - ((+new Date() - startTimestamp) / 1000), ROUND_DURATION); - setRoundDuration(secondsLeft); - setDrawWord(null); - setLiveMessage(''); - setGuess(''); - setShowGuessBox(true); - setDisableGuessBox(false); - setPreviousWord(null); - setRoundInfo({ current: total - round + 1, total }); - setGameState(GameStateConstants.GAME_STATE_NEW_ROUND); - console.log(`New Round starting, Round: ${round}, Total: ${total}`); - const innerMessage = `${currentDrawingUser.id.split('_')[0]} is drawing. Start guessing!`; - setMessageLog((messageLog) => [ - ...messageLog, - `msgSystem!!!New Round starting, Round: ${(total - round + 1)}, Total: ${total}`, - `msgSystemImp!!!${innerMessage}` - ]); - setLiveMessage(`msgSystemImp!!!${innerMessage}`); - }; - - const cbNewGame = (roundDuration) => { - console.log('EVENT GE_NEW_GAME'); - setRoundDuration(roundDuration / 1000); - setCurrentUser((currentUser) => { return { ...currentUser, score: 0 }; }); - setLiveMessage(''); - setWinners([]); - setGameState(GameStateConstants.GAME_STATE_NEW_GAME); - setMessageLog((messageLog) => [ - ...messageLog, - 'msgSystem!!!New Game starting...' - ]); - }; - - const cbSCLogin = (loggedInUser) => { - console.log('EVENT S_C_LOGIN'); - setCurrentUser(loggedInUser); - setMessageLog((messageLog) => [ - ...messageLog, - 'msgSystem!!!Logged in' - ]); - }; - - const cbUpdateScore = (updatedUserScores) => { - console.log('EVENT GE_UPDATE_SCORE'); - console.table(updatedUserScores); - setUserScores(updatedUserScores); - }; - - const cbUpdateGuess = (liveMessage) => { - console.log('EVENT GE_UPDATE_GUESS'); - let message = ''; - if (liveMessage.found) { - const innerMessage = `${liveMessage.userName} has found the word!`; - message = `msgSystemFoundWord!!!${innerMessage}`; - setLiveMessage(message); - } else { - setCurrentUser(currentUser => { - const innerMessage = `[${liveMessage.userName}]: ${liveMessage.guess}`; - if (liveMessage.userName !== currentUser.name) { message = `msgSystemGuess!!!${innerMessage}`; } - // The above check doesn't apply to the liveMessage area - setLiveMessage(`msgSystemGuess!!!${innerMessage}`); - return currentUser; - }); - } - setMessageLog((messageLog) => [...messageLog, message]); - }; - - useEffect(() => { - if (userScores && currentUser) { - console.log(currentUser); - const latestUserScore = userScores.find( - (user) => user.id === currentUser.id - ).score; - if (latestUserScore > currentUser.score) { - console.log('Disable guess box'); - // disable guess box if guess is right - setDisableGuessBox(true); - currentUser.score = latestUserScore; - } - } - }, [userScores, currentUser]); - - useEffect(() => { - document.body.addEventListener('touchmove', function (e) { - // e.preventDefault(); - }); - }, []); - - const erd = ElementResizeDetectorMaker(); - - useEffect(() => { - erd.listenTo(document.getElementById('canvasContainer'), (element) => { - console.log('Resize', element.offsetWidth, element.offsetHeight); - canvasPaperRef.current.style.height = `${element.offsetWidth}px`; - }); - }, []); - - const guessBoxPressed = (e) => { - if (e.keyCode === 13) { - // Enter pressed. send it to server - const currentGuess = e.target.value; - sendGuessToServer(); - resetGuess(); - setMessageLog((messageLog) => [ - ...messageLog, - `msgUserGuess!!!${currentUser.name}: ${currentGuess}` - ]); - } - }; - - const sendGuessToServer = () => { - console.log('New guess -', currentUser.id, guess); - socketIO.emit('GE_NEW_GUESS', { - userId: currentUser.id, - guess: guess - }); - }; - - const resetGuess = () => { - setGuess(''); - if (keyboardRef.current) { - keyboardRef.current.setInput(''); - } - }; - - const onNicknameAdded = (nickname) => { - setPlayerNickname(nickname); - }; - - const handleColorChange = (color) => { - setCanvasOptions({ - color: color, - enabled: canvasOptions.enabled - }); - }; - - const handleClearCanvas = () => { - socketIO.emit('C_S_CLEAR_CANVAS', room); - }; - - const renderPlayersIcon = () => { - return ( - -
- { - setShouldShowPlayersList(true); - }}> - - - - -
-
- ); - }; - - const renderGameState = () => { - switch (gameState) { - case GameStateConstants.GAME_STATE_IDLE: - return ; - case GameStateConstants.GAME_STATE_NEW_ROUND: - return ( - - ); - - case GameStateConstants.GAME_STATE_WAIT_FOR_NEXT_ROUND: - return ; - case GameStateConstants.GAME_STATE_ANNOUNCE_WINNER: - return ; - default: - break; - } - }; - - const renderCanvasToolbox = () => { - console.log(`showGuessBox ${showGuessBox}, gamestate ${gameState}`); - if (gameState === GameStateConstants.GAME_STATE_NEW_ROUND && !showGuessBox) { - return ( - - - - - - ); - // return ; - } else { - return null; - } - }; - - const renderAppBar = () => { - return ( - - - - - - - Pictionary - - - - ); - }; - - const renderLeftPane = () => { - return ( - - - - - - - - - - - - - - - ); - }; - - const renderBottomPane = () => { - if (gameState === GameStateConstants.GAME_STATE_WAIT_FOR_NEXT_ROUND || - gameState === GameStateConstants.GAME_STATE_IDLE || - gameState === GameStateConstants.GAME_STATE_ANNOUNCE_WINNER) { - return - - - - ; - } else { - return
- {showGuessBox ? ( - - input && input.focus()} - disabled={disableGuessBox} - label="Guess!" - value={guess} - variant="outlined" - onKeyDown={(e) => guessBoxPressed(e)} - onChange={(e) => { - setGuess(e.target.value); - if (keyboardRef.current) { - keyboardRef.current.setInput(e.target.value); - } - }} - /> - - ) : ( - {drawWord} - )} -
; - } - }; - - const renderMidPane = () => { - return ( - - - - - {renderPlayersIcon()} - {renderGameState()} - - - {previousWord ? ( - - - Previous word was {previousWord} - - - ) : null} - - {renderBottomPane()} - - - - ); - }; - return ( -
- {renderAppBar()} - - {renderLeftPane()} - {renderMidPane()} - - {renderCanvasToolbox()} - - {!playerNickname && ( - - )} - {shouldShowPlayersList && ( - { - setShouldShowPlayersList(false); - }} - /> - )} - -
+ + + + + + + +
); -} +}; export default App; diff --git a/client/src/components/button/button.css b/client/src/components/button/button.css index 1103830..edfe36c 100644 --- a/client/src/components/button/button.css +++ b/client/src/components/button/button.css @@ -1,3 +1,5 @@ .button { - background-color: var(buttonColor); + background-color: var(--primaryColor); + min-width: 100px; + color: white; } diff --git a/client/src/components/button/index.js b/client/src/components/button/index.js index bf26aa9..a37e067 100644 --- a/client/src/components/button/index.js +++ b/client/src/components/button/index.js @@ -1,8 +1,10 @@ +import React from 'react'; import { Button } from '@material-ui/core'; -import styled from 'styled-components'; +import './button.css'; -export default styled(Button)` - && { - background-color: #d1e4f6; - } -`; +const UIButton = (props) => { + const { className, ...otherProps } = props; + return