From d2a593cfa529d3a8e199b512b246125e9403090b Mon Sep 17 00:00:00 2001 From: Sampath Kumar Krishnan Date: Wed, 26 Aug 2020 23:14:18 +0530 Subject: [PATCH 1/4] feat: Home page - add routes for home and room - install react router package - Have a separate route for home and room - Idea is to have options to select/create rooms in home --- client/package-lock.json | 83 +++ client/package.json | 1 + client/src/App.css | 102 ---- client/src/App.js | 497 +----------------- .../components/dialogs/AddNicknameDialog.js | 1 + client/src/components/home/index.css | 0 client/src/components/home/index.js | 8 + client/src/components/room/index.css | 74 +++ client/src/components/room/index.js | 490 +++++++++++++++++ 9 files changed, 672 insertions(+), 584 deletions(-) create mode 100644 client/src/components/home/index.css create mode 100644 client/src/components/home/index.js create mode 100644 client/src/components/room/index.css create mode 100644 client/src/components/room/index.js 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..cc91b9c 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,490 +1,23 @@ -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 { 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 React from 'react'; +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'; 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/dialogs/AddNicknameDialog.js b/client/src/components/dialogs/AddNicknameDialog.js index 6d7ea87..f40d2db 100644 --- a/client/src/components/dialogs/AddNicknameDialog.js +++ b/client/src/components/dialogs/AddNicknameDialog.js @@ -22,6 +22,7 @@ const AddNicknameDialog = (props) => { return ( { + return
Home
; +}; + +export default Home; diff --git a/client/src/components/room/index.css b/client/src/components/room/index.css new file mode 100644 index 0000000..769a8d6 --- /dev/null +++ b/client/src/components/room/index.css @@ -0,0 +1,74 @@ +.Room { + text-align: center; +} + +.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/components/room/index.js b/client/src/components/room/index.js new file mode 100644 index 0000000..a0bf802 --- /dev/null +++ b/client/src/components/room/index.js @@ -0,0 +1,490 @@ +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 { 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 '../appbar'; +import AddNicknameDialog from '../dialogs/AddNicknameDialog'; +import LogWindow from '../log-window'; +import UserScoreList from '../player-list/UserScoreList'; +import PlayersIcon from '../icons/PlayersIcon'; +import UserScoreListDialog from '../dialogs/UserScoreListDialog'; +import * as GameStateConstants from '../../constants/AppConstants'; +import GameStateDisplay from '../game-state-display/GameStateDisplay'; +import CanvasToolbox from '../toolbox'; + +import './index.css'; + +const DEFAULT_ROOM = 'main'; +const ROUND_DURATION = 60; + +function Room () { + 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 Room; From 900e0e9e8b0211fd1e9d77d4d53e4c9f53cca086 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Krishnan Date: Fri, 28 Aug 2020 23:06:07 +0530 Subject: [PATCH 2/4] feat: Add welcome screen - Display nickname, play and rooms button in a container - Get rid of styled components for button --- client/src/App.js | 15 +++--- client/src/components/button/button.css | 3 +- client/src/components/button/index.js | 14 +++--- client/src/components/home/WelcomeLayout.css | 11 ++++ client/src/components/home/WelcomeLayout.js | 53 ++++++++++++++++++++ client/src/components/home/index.css | 13 +++++ client/src/components/home/index.js | 9 +++- client/src/components/room/index.js | 43 ++++++++-------- 8 files changed, 124 insertions(+), 37 deletions(-) create mode 100644 client/src/components/home/WelcomeLayout.css create mode 100644 client/src/components/home/WelcomeLayout.js diff --git a/client/src/App.js b/client/src/App.js index cc91b9c..a4cf03a 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,4 +1,5 @@ import React from 'react'; +import { StylesProvider } from '@material-ui/core/styles'; import { BrowserRouter as Router, Switch, @@ -11,12 +12,14 @@ import Room from './components/room'; function App () { return ( - - - - - - + + + + + + + + ); }; diff --git a/client/src/components/button/button.css b/client/src/components/button/button.css index 1103830..9de3616 100644 --- a/client/src/components/button/button.css +++ b/client/src/components/button/button.css @@ -1,3 +1,4 @@ .button { - background-color: var(buttonColor); + background-image: var(--buttonColor); + min-width: 100px; } 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