From 14e158c0b40edc08631715dcfe6e7367976aeed8 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 25 Jan 2022 00:08:01 -0500 Subject: [PATCH] fix homepage join functionality --- client/src/model/Game.js | 2 +- client/src/modules/GameCreationStepManager.js | 16 ++++---- client/src/modules/GameStateRenderer.js | 16 ++++---- client/src/scripts/game.js | 8 +--- client/src/scripts/home.js | 33 ++++++++++++++-- client/src/scripts/join.js | 39 +++++++++---------- server/api/GamesAPI.js | 21 +++++----- server/modules/GameManager.js | 21 +++++----- server/modules/ServerBootstrapper.js | 2 +- 9 files changed, 92 insertions(+), 66 deletions(-) diff --git a/client/src/model/Game.js b/client/src/model/Game.js index 96d9705..d030581 100644 --- a/client/src/model/Game.js +++ b/client/src/model/Game.js @@ -4,7 +4,7 @@ export class Game { this.hasTimer = hasTimer; this.timerParams = timerParams; this.hasDedicatedModerator = hasDedicatedModerator; - this.moderatorName = moderatorName + this.moderatorName = moderatorName; this.accessCode = null; } } diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index 3a9129c..4101e72 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -5,7 +5,7 @@ import { XHRUtility } from './XHRUtility.js'; import { globals } from '../config/globals.js'; import { templates } from './Templates.js'; import { defaultCards } from '../config/defaultCards'; -import {UserUtility} from "./UserUtility"; +import { UserUtility } from './UserUtility'; export class GameCreationStepManager { constructor (deckManager) { @@ -91,7 +91,7 @@ export class GameCreationStepManager { title: 'Enter your name:', forwardHandler: (e) => { if (e.type === 'click' || e.code === 'Enter') { - let name = document.getElementById("moderator-name").value; + const name = document.getElementById('moderator-name').value; if (validateName(name)) { this.currentGame.moderatorName = name; this.removeStepElementsFromDOM(this.step); @@ -122,8 +122,8 @@ export class GameCreationStepManager { && Object.prototype.hasOwnProperty.call(res, 'content') && typeof res.content === 'string' ) { - let json = JSON.parse(res.content); - UserUtility.setAnonymousUserId(json.cookie, json.environment) + const json = JSON.parse(res.content); + UserUtility.setAnonymousUserId(json.cookie, json.environment); window.location = ('/game/' + json.accessCode); } }).catch((e) => { @@ -188,15 +188,15 @@ export class GameCreationStepManager { } } -function renderNameStep(containerId, step, game, steps) { +function renderNameStep (containerId, step, game, steps) { const stepContainer = document.createElement('div'); setAttributes(stepContainer, { id: 'step-' + step, class: 'flex-row-container step' }); stepContainer.innerHTML = templates.ENTER_NAME_STEP; document.getElementById(containerId).appendChild(stepContainer); - let nameInput = document.querySelector('#moderator-name'); + const nameInput = document.querySelector('#moderator-name'); nameInput.value = game.moderatorName; - nameInput.addEventListener('keyup', steps["4"].forwardHandler) + nameInput.addEventListener('keyup', steps['4'].forwardHandler); nameInput.focus(); } @@ -397,7 +397,7 @@ function renderReviewAndCreateStep (containerId, stepNumber, game) { div.querySelector('#roles-option').appendChild(roleEl); } - div.querySelector("#mod-name").innerText = game.moderatorName; + div.querySelector('#mod-name').innerText = game.moderatorName; document.getElementById(containerId).appendChild(div); } diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 0533e8d..cbbcddc 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -68,21 +68,21 @@ export class GameStateRenderer { } time.innerText = timeString; } else { - timeString = 'untimed' + timeString = 'untimed'; time.innerText = timeString; } - let link = window.location.protocol + "//" + window.location.host - + '/join/' + this.stateBucket.currentGameState.accessCode - + '?playerCount=' + getGameSize(this.stateBucket.currentGameState.deck) - + '&timer=' + encodeURIComponent(timeString); + const link = window.location.protocol + '//' + window.location.host + + '/join/' + this.stateBucket.currentGameState.accessCode + + '?playerCount=' + getGameSize(this.stateBucket.currentGameState.deck) + + '&timer=' + encodeURIComponent(timeString); const linkCopyHandler = (e) => { if (e.type === 'click' || e.code === 'Enter') { navigator.clipboard.writeText(link) - .then(() => { - toast('Link copied!', 'success', true); - }); + .then(() => { + toast('Link copied!', 'success', true); + }); } }; gameLinkContainer.addEventListener('click', linkCopyHandler); diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index c94dc26..97a944d 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -39,8 +39,8 @@ const game = () => { }); setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager); }).catch((res) => { - toast(res.content, 'error', true); - }); + toast(res.content, 'error', true); + }); }; function syncWithGame (stateBucket, gameTimerManager, gameStateRenderer, timerWorker, socket, cookie) { @@ -298,10 +298,6 @@ function displayStartGamePromptForModerators (gameState, gameStateRenderer) { document.body.appendChild(div); } -function validateName (name) { - return typeof name === 'string' && name.length > 0 && name.length <= 30; -} - function removeStartGameFunctionalityIfPresent (gameStateRenderer) { document.querySelector('#start-game-prompt')?.removeEventListener('click', gameStateRenderer.startGameHandler); document.querySelector('#start-game-prompt')?.remove(); diff --git a/client/src/scripts/home.js b/client/src/scripts/home.js index bd13a35..77364d9 100644 --- a/client/src/scripts/home.js +++ b/client/src/scripts/home.js @@ -16,19 +16,23 @@ const home = () => { }; function roomCodeIsValid (code) { - return typeof code === 'string' && /^[a-z0-9]{6}$/.test(code.toLowerCase()); + return typeof code === 'string' && /^[A-Z0-9]{6}$/.test(code.toUpperCase().trim()); } function attemptToJoinGame (code) { XHRUtility.xhr( - '/api/games/' + code.toLowerCase().trim() + 'availability', + '/api/games/' + code.toUpperCase().trim() + '/availability', 'GET', null, null ) .then((res) => { if (res.status === 200) { - window.location = '/join/' + res.content; + let json = JSON.parse(res.content); + window.location = window.location.protocol + '//' + window.location.host + + '/join/' + encodeURIComponent(json.accessCode) + + '?playerCount=' + encodeURIComponent(json.playerCount) + + '&timer=' + encodeURIComponent(getTimeString(json.timerParams)); } }).catch((res) => { if (res.status === 404) { @@ -41,6 +45,29 @@ function attemptToJoinGame (code) { }); } +function getTimeString(timerParams) { + let timeString = ''; + if (timerParams) { + const hours = timerParams.hours; + const minutes = timerParams.minutes; + if (hours) { + timeString += hours > 1 + ? hours + ' hours ' + : hours + ' hour '; + } + if (minutes) { + timeString += minutes > 1 + ? minutes + ' minutes ' + : minutes + ' minute '; + } + + return timeString; + } else { + timeString = 'untimed'; + return timeString; + } +} + if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = home; } else { diff --git a/client/src/scripts/join.js b/client/src/scripts/join.js index aa8b059..f9b929f 100644 --- a/client/src/scripts/join.js +++ b/client/src/scripts/join.js @@ -1,37 +1,36 @@ -import {injectNavbar} from "../modules/Navbar.js"; -import {toast} from "../modules/Toast.js"; -import {XHRUtility} from "../modules/XHRUtility.js"; -import {UserUtility} from "../modules/UserUtility.js"; -import {globals} from "../config/globals.js"; +import { injectNavbar } from '../modules/Navbar.js'; +import { toast } from '../modules/Toast.js'; +import { XHRUtility } from '../modules/XHRUtility.js'; +import { UserUtility } from '../modules/UserUtility.js'; +import { globals } from '../config/globals.js'; const join = () => { injectNavbar(); const splitUrl = window.location.pathname.split('/join/'); const accessCode = splitUrl[1]; if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) { - document.getElementById("game-code").innerText = accessCode; - document.getElementById("game-time").innerText - = decodeURIComponent((new URL(document.location)).searchParams.get('timer')); - document.getElementById("game-player-count").innerText - = decodeURIComponent((new URL(document.location)).searchParams.get('playerCount')) + ' Players'; - let playerCount = decodeURIComponent((new URL(document.location)).searchParams.get('playerCount')) - let form = document.getElementById('join-game-form'); - document.getElementById("player-new-name").focus(); + document.getElementById('game-code').innerText = accessCode; + document.getElementById('game-time').innerText = + decodeURIComponent((new URL(document.location)).searchParams.get('timer')); + document.getElementById('game-player-count').innerText = + decodeURIComponent((new URL(document.location)).searchParams.get('playerCount')) + ' Players'; + const form = document.getElementById('join-game-form'); + document.getElementById('player-new-name').focus(); form.onsubmit = joinHandler; } else { window.location = '/not-found?reason=' + encodeURIComponent('invalid-access-code'); } -} +}; -const joinHandler = (e) => { +const joinHandler = (e) => { const splitUrl = window.location.pathname.split('/join/'); const accessCode = splitUrl[1]; e.preventDefault(); const name = document.getElementById('player-new-name').value; if (validateName(name)) { document.getElementById('join-game-form').onsubmit = null; - document.getElementById("submit-new-name").classList.add('submitted'); - document.getElementById("submit-new-name").setAttribute('value', 'Joining...'); + document.getElementById('submit-new-name').classList.add('submitted'); + document.getElementById('submit-new-name').setAttribute('value', 'Joining...'); XHRUtility.xhr( '/api/games/' + accessCode + '/players', 'PATCH', @@ -39,13 +38,13 @@ const joinHandler = (e) => { JSON.stringify({ playerName: name, accessCode: accessCode }) ) .then((res) => { - let json = JSON.parse(res.content); + const json = JSON.parse(res.content); UserUtility.setAnonymousUserId(json.cookie, json.environment); window.location = '/game/' + accessCode; }).catch((res) => { document.getElementById('join-game-form').onsubmit = joinHandler; - document.getElementById("submit-new-name").classList.remove('submitted'); - document.getElementById("submit-new-name").setAttribute('value', 'Join Game'); + document.getElementById('submit-new-name').classList.remove('submitted'); + document.getElementById('submit-new-name').setAttribute('value', 'Join Game'); if (res.status === 404) { toast('This game was not found.', 'error', true, true, 8); } else if (res.status === 400) { diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index 64264dd..e81a348 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -27,7 +27,7 @@ const corsOptions = process.env.NODE_ENV.trim() === 'development' }; router.use(cors(corsOptions)); -//router.options('/:code/players', cors(corsOptions)); +// router.options('/:code/players', cors(corsOptions)); if (process.env.NODE_ENV.trim() === 'production') { // in prod, limit clients to creating 5 games per 10 minutes. router.use('/create', apiLimiter); @@ -50,13 +50,14 @@ router.post('/create', function (req, res) { }); router.get('/:code/availability', function (req, res) { + console.log(req.params.code); const availabilityPromise = gameManager.checkAvailability(req.params.code); availabilityPromise.then((result) => { if (result === 404) { res.status(404).send(); } else if (result instanceof Error) { res.status(400).send(result.message); - } else if (typeof result === 'string') { + } else if (typeof result === 'object') { logger.debug(result); res.status(200).send(result); } else { @@ -91,18 +92,18 @@ router.get('/environment', function (req, res) { res.status(200).send(gameManager.environment); }); -function validateName(name) { +function validateName (name) { return typeof name === 'string' && name.length > 0 && name.length <= 30; } -function validateCookie(cookie) { - return cookie === null - || (typeof cookie !== 'string' && cookie !== false) - || (cookie.length !== globals.USER_SIGNATURE_LENGTH && cookie !== false) -} +// function validateCookie (cookie) { +// return cookie === null +// || (typeof cookie !== 'string' && cookie !== false) +// || (cookie.length !== globals.USER_SIGNATURE_LENGTH && cookie !== false); +// } -function validateAccessCode(accessCode) { - return /^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH +function validateAccessCode (accessCode) { + return /^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH; } module.exports = router; diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index c87ff11..fd04b0e 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -187,13 +187,13 @@ class GameManager { }; checkAvailability = (code) => { - const game = this.activeGameRunner.activeGames[code]; + const game = this.activeGameRunner.activeGames[code.toUpperCase()]; if (game) { const unassignedPerson = game.people.find((person) => person.assigned === false); if (!unassignedPerson) { return Promise.resolve(new Error(globals.ERROR_MESSAGE.GAME_IS_FULL)); } else { - return Promise.resolve(code); + return Promise.resolve({ accessCode: code, playerCount: getGameSize(game.deck), timerParams: game.timerParams }); } } else { return Promise.resolve(404); @@ -300,13 +300,7 @@ class GameManager { ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, clientSocket, logger)); } } else { - const namespaceSockets = await namespace.in(accessCode).fetchSockets(); - if (!namespaceSockets.find((namespaceSocket) => namespaceSocket.id === clientSocket.id)) { - let newlyAssignedPerson = this.joinGame(game); - clientSocket.join(accessCode); - newlyAssignedPerson.socketId = clientSocket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, newlyAssignedPerson, gameRunner, clientSocket, logger)); - } + rejectClientRequestForGameState(ackFn); } } else { rejectClientRequestForGameState(ackFn); @@ -461,6 +455,15 @@ function pruneStaleGames (activeGames, timerThreads, logger) { } } +function getGameSize (cards) { + let quantity = 0; + for (const card of cards) { + quantity += card.quantity; + } + + return quantity; +} + class Singleton { constructor (logger, environment) { if (!Singleton.instance) { diff --git a/server/modules/ServerBootstrapper.js b/server/modules/ServerBootstrapper.js index 2031a38..a14ccb4 100644 --- a/server/modules/ServerBootstrapper.js +++ b/server/modules/ServerBootstrapper.js @@ -3,7 +3,7 @@ const http = require('http'); const https = require('https'); const path = require('path'); const fs = require('fs'); -const secure = require("express-force-https"); +const secure = require('express-force-https'); const ServerBootstrapper = { processCLIArgs: () => {