edit names

This commit is contained in:
AlecM33
2023-08-14 02:50:46 -04:00
parent 905d683ab9
commit a2ed634558
14 changed files with 172 additions and 45 deletions

View File

@@ -1,6 +1,6 @@
import { toast } from './Toast.js';
export const Confirmation = (message, onYes = null, isDOMNode = false) => {
export const Confirmation = (message, onYes = null, isDOMNode = false, confirmButtonText = 'Yes') => {
document.querySelector('#confirmation')?.remove();
document.querySelector('#confirmation-background')?.remove();
@@ -11,7 +11,7 @@ export const Confirmation = (message, onYes = null, isDOMNode = false) => {
? `<div id="confirmation-message"></div>
<div class="confirmation-buttons">
<button id="confirmation-cancel-button" class="app-button cancel">Cancel</button>
<button id="confirmation-yes-button" class="app-button">Yes</button>
<button id="confirmation-yes-button" class="app-button">` + confirmButtonText + `</button>
</div>`
: `<div id="confirmation-message"></div>
<div class="confirmation-buttons-centered">

View File

@@ -94,7 +94,7 @@ export const HTMLFragments = {
</div>
<div id='game-people-container'>
<div id="current-moderator" class="moderator">
<div id="current-moderator-name"></div>
<div id="current-moderator-name" class="person-name-element"></div>
<div id="current-moderator-type"></div>
</div>
<label id='players-alive-label'></label>
@@ -128,7 +128,7 @@ export const HTMLFragments = {
</div>
<div id='game-people-container'>
<div id="current-moderator" class="moderator">
<div id="current-moderator-name"></div>
<div id="current-moderator-name" class="person-name-element"></div>
<div id="current-moderator-type"></div>
</div>
<label id='players-alive-label'></label>
@@ -245,7 +245,7 @@ export const HTMLFragments = {
</div>`,
MODERATOR_PLAYER:
`<div>
<div class='game-player-name'></div>
<div class='game-player-name person-name-element'></div>
<div class='game-player-role'></div>
</div>
<div class='player-action-buttons'>
@@ -254,7 +254,7 @@ export const HTMLFragments = {
</div>`,
GAME_PLAYER:
`<div>
<div class='game-player-name'></div>
<div class='game-player-name person-name-element'></div>
<div class='game-player-role'></div>
</div>`,
INITIAL_GAME_DOM:
@@ -265,6 +265,9 @@ export const HTMLFragments = {
<div id='client-name'></div>
<div id='client-user-type'></div>
</div>
<button id="edit-name-button">
<img alt="edit name" src="../../images/pencil.svg"/>
</button>
</div>
<div id='game-state-container'></div>`,
// via https://loading.io/css/
@@ -283,18 +286,10 @@ export const HTMLFragments = {
<div></div>
<div></div>
</div>`,
NAME_CHANGE_MODAL:
`<div id='change-name-modal-background' class='modal-background'></div>
<div tabindex='-1' id='change-name-modal' class='modal'>
<form id='change-name-form'>
<div id='transfer-mod-form-content'>
<label for='player-new-name'>Your name:</label>
<input id='player-new-name' autocomplete='given-name' type='text'/>
</div>
<div class='modal-button-container'>
<input type='submit' id='submit-new-name' value='Set Name'/>
</div>
</form>
NAME_CHANGE_FORM:
`<div id='change-name-form-content'>
<label for='client-new-name'>Your name:</label>
<input maxlength="40" id='client-new-name' autocomplete='off' type='text'/>
</div>`,
ROLE_INFO_MODAL:
`<div id='role-info-modal-background' class='modal-background'></div>
@@ -314,7 +309,7 @@ export const HTMLFragments = {
</div>
<div id='game-people-container'>
<div id="current-moderator" class="moderator">
<div id="current-moderator-name"></div>
<div id="current-moderator-name" class="person-name-element"></div>
<div id="current-moderator-type"></div>
</div>
<label id='players-alive-label'></label>

View File

@@ -98,7 +98,7 @@ export class GameCreationStepManager {
this.incrementStep();
this.renderStep('creation-step-container', this.step);
} else {
toast('Name must be between 1 and 30 characters.', 'error', true);
toast('Name must be between 1 and 40 characters.', 'error', true);
}
}
},
@@ -569,8 +569,8 @@ function initializeRemainingEventListeners (deckManager, roleBox) {
}
function processNewCustomRoleSubmission (name, description, team, deckManager, isUpdate, roleBox, option = null) {
if (name.length > 40) {
toast('Your name is too long (max 40 characters).', 'error', true);
if (name.length > 50) {
toast('Your name is too long (max 50 characters).', 'error', true);
return;
}
if (description.length > 500) {
@@ -596,5 +596,5 @@ function hasTimer (hours, minutes) {
}
function validateName (name) {
return typeof name === 'string' && name.length > 0 && name.length <= 30;
return typeof name === 'string' && name.length > 0 && name.length <= 40;
}

View File

@@ -53,6 +53,7 @@ function renderGroupOfPlayers (
for (const player of people) {
const playerEl = document.createElement('div');
playerEl.classList.add('game-player');
playerEl.dataset.pointer = player.id;
playerEl.innerHTML = HTMLFragments.GAME_PLAYER;
playerEl.querySelector('.game-player-name').innerText = player.name;

View File

@@ -322,6 +322,7 @@ export class InProgress {
for (const player of people) {
const playerEl = document.createElement('div');
playerEl.classList.add('game-player');
playerEl.dataset.pointer = player.id;
// add a reference to the player's id for each corresponding element in the list
if (moderatorType) {

View File

@@ -354,8 +354,9 @@ function getTimeString (gameState) {
function renderLobbyPerson (person, gameState, socket) {
const el = document.createElement('div');
el.dataset.pointer = person.id;
const personNameEl = document.createElement('div');
personNameEl.classList.add('lobby-player-name');
personNameEl.classList.add('lobby-player-name', 'person-name-element');
const personTypeEl = document.createElement('div');
personNameEl.innerText = person.name;
personTypeEl.innerText = person.userType + USER_TYPE_ICONS[person.userType];

View File

@@ -100,8 +100,9 @@ export const SharedStateUtil = {
} else {
for (const spectator of spectators) {
const spectatorEl = document.createElement('div');
spectatorEl.dataset.pointer = spectator.id;
spectatorEl.classList.add('spectator');
spectatorEl.innerHTML = '<div class=\'spectator-name\'></div>' +
spectatorEl.innerHTML = '<div class=\'spectator-name person-name-element\'></div>' +
'<div>' + 'spectator' + USER_TYPE_ICONS.spectator + '</div>';
spectatorEl.querySelector('.spectator-name').innerText = spectator.name;
list.appendChild(spectatorEl);
@@ -123,6 +124,7 @@ export const SharedStateUtil = {
},
displayCurrentModerator: (moderator) => {
document.getElementById('current-moderator').dataset.pointer = moderator.id;
document.getElementById('current-moderator-name').innerText = moderator.name;
document.getElementById('current-moderator-type').innerText = moderator.userType + USER_TYPE_ICONS[moderator.userType];
},
@@ -183,9 +185,30 @@ export const SharedStateUtil = {
});
},
displayClientInfo: (name, userType) => {
document.getElementById('client-name').innerText = name;
document.getElementById('client-user-type').innerText = userType;
document.getElementById('client-user-type').innerText += USER_TYPE_ICONS[userType];
displayClientInfo: (gameState, socket) => {
document.getElementById('client-name').innerText = gameState.client.name;
document.getElementById('client-user-type').innerText = gameState.client.userType;
document.getElementById('client-user-type').innerText += USER_TYPE_ICONS[gameState.client.userType];
const nameForm = document.createElement('form');
nameForm.setAttribute('id', 'name-change-form');
nameForm.onsubmit = (e) => {
e.preventDefault();
document.getElementById('confirmation-yes-button').click();
};
nameForm.innerHTML = HTMLFragments.NAME_CHANGE_FORM;
nameForm.querySelector('#client-new-name').value = gameState.client.name;
document.getElementById('edit-name-button').addEventListener('click', () => {
Confirmation(nameForm, () => {
socket.emit(
SOCKET_EVENTS.IN_GAME_MESSAGE,
EVENT_IDS.CHANGE_NAME,
gameState.accessCode,
{ personId: gameState.client.id, newName: document.getElementById('client-new-name').value },
(response) => {
toast(response.message, response.errorFlag === 1 ? 'error' : 'success', true);
}
);
}, true, 'Update');
});
}
};

View File

@@ -135,7 +135,7 @@ function processGameState (
});
}
SharedStateUtil.displayClientInfo(currentGameState.client.name, currentGameState.client.userType);
SharedStateUtil.displayClientInfo(currentGameState, socket);
switch (currentGameState.status) {
case STATUS.LOBBY:
@@ -236,6 +236,20 @@ function setClientSocketHandlers (stateBucket, socket) {
);
});
socket.on(EVENT_IDS.CHANGE_NAME, (changedId, newName) => {
const person = stateBucket.currentGameState.people.find(person => person.id === changedId);
if (person) {
person.name = newName;
if (stateBucket.currentGameState.client.id === changedId) {
stateBucket.currentGameState.client.name = newName;
SharedStateUtil.displayClientInfo(stateBucket.currentGameState, socket);
}
document.querySelectorAll('[data-pointer="' + person.id + '"]').forEach((node) => {
node.querySelector('.person-name-element').innerText = newName;
});
}
});
socket.on(EVENT_IDS.END_GAME, (people) => {
stateBucket.currentGameState.people = people;
stateBucket.currentGameState.status = STATUS.ENDED;

View File

@@ -55,7 +55,7 @@ const joinHandler = (e) => {
resetJoinButtonState(e, joinHandler);
});
} else {
toast('Name must be between 1 and 30 characters.', 'error', true, true, 'long');
toast('Name must be between 1 and 40 characters.', 'error', true, true, 'long');
}
};
@@ -90,7 +90,7 @@ function resetJoinButtonState (e, joinHandler) {
}
function validateName (name) {
return typeof name === 'string' && name.length > 0 && name.length <= 30;
return typeof name === 'string' && name.length > 0 && name.length <= 40;
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {

View File

@@ -564,8 +564,34 @@ h1 {
}
#client-container {
max-width: 35em;
max-width: 25em;
width: 75%;
margin: 1em 0;
position: relative;
}
#client-container button {
background-color: transparent;
height: fit-content;
margin: 0 8px;
cursor: pointer;
padding: 5px;
border-radius: 5px;
border: 1px solid transparent;
position: absolute;
right: -50px;
top: 32px;
}
#client-container button:hover {
cursor: pointer;
filter: brightness(1.5);
background-color: #8080804d;
}
#client-container button img {
width: 22px;
pointer-events: none;
}
#client {
@@ -575,16 +601,19 @@ h1 {
align-items: center;
justify-content: space-between;
border-radius: 5px;
min-width: 15em;
border: 1px solid #46455299;
background: #4645525c;
}
#client-name {
max-width: 13em;
color: #e7e7e7;
font-family: 'signika-negative', sans-serif;
font-size: 25px;
margin: 0.25em 2em 0.25em 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#client-user-type {
@@ -920,8 +949,22 @@ canvas {
justify-content: center;
}
#change-name-modal-background {
cursor: default;
#change-name-form-content {
display: flex;
flex-direction: column;
margin: 0 auto;
max-width: 15em;
text-align: left;
margin: 2px auto;
}
#change-name-form-content input {
font-size: 20px;
}
#change-name-form-content label {
display: flex;
margin-bottom: 0.5em;
}
#lobby-people-container , #game-people-container {
@@ -954,6 +997,16 @@ canvas {
}
@media(max-width: 500px) {
#client-container button img {
width: 18px;
pointer-events: none;
}
#client-container button {
right: -46px;
top: 27px;
}
label {
font-size: 18px;
}
@@ -968,6 +1021,7 @@ canvas {
#client-name {
font-size: 20px;
margin: 0.25em 0 0.25em 0;
}
#client-user-type, #game-parameters {

View File

@@ -10,7 +10,8 @@ const PRIMITIVES = {
USER_SIGNATURE_LENGTH: 25,
INSTANCE_ID_LENGTH: 75,
MAX_SPECTATORS: 25,
MOCK_AUTH: 'mock_auth'
MOCK_AUTH: 'mock_auth',
MAX_PERSON_NAME_LENGTH: 40
};
const LOG_LEVEL = {
@@ -88,6 +89,7 @@ const SYNCABLE_EVENTS = function () {
EVENT_IDS.KILL_PLAYER,
EVENT_IDS.REVEAL_PLAYER,
EVENT_IDS.TRANSFER_MODERATOR,
EVENT_IDS.CHANGE_NAME,
EVENT_IDS.END_GAME,
EVENT_IDS.RESTART_GAME,
EVENT_IDS.PLAYER_JOINED,

View File

@@ -1,6 +1,6 @@
const GameStateCurator = require('./GameStateCurator');
const GameCreationRequest = require('../model/GameCreationRequest');
const { EVENT_IDS, STATUS, USER_TYPES, GAME_PROCESS_COMMANDS, REDIS_CHANNELS } = require('../config/globals');
const { EVENT_IDS, STATUS, USER_TYPES, GAME_PROCESS_COMMANDS, REDIS_CHANNELS, PRIMITIVES } = require('../config/globals');
const Events = [
{
@@ -55,6 +55,42 @@ const Events = [
);
}
},
{
id: EVENT_IDS.CHANGE_NAME,
stateChange: async (game, socketArgs, vars) => {
const toChangeIndex = game.people.findIndex(
(person) => person.id === socketArgs.personId
);
if (toChangeIndex >= 0) {
if (vars.gameManager.isNameTaken(game, socketArgs.newName)) {
vars.hasNameChanged = false;
if (game.people[toChangeIndex].name.toLowerCase().trim() === socketArgs.newName.toLowerCase().trim()) {
return;
}
vars.ackFn({ errorFlag: 1, message: 'This name is taken.' });
} else if (socketArgs.newName.length > PRIMITIVES.MAX_PERSON_NAME_LENGTH) {
vars.ackFn({ errorFlag: 1, message: 'Your new name is too long - the max is 30 characters.' });
vars.hasNameChanged = false;
} else if (socketArgs.newName.length === 0) {
vars.ackFn({ errorFlag: 1, message: 'Your new name cannot be empty.' });
vars.hasNameChanged = false;
} else {
game.people[toChangeIndex].name = socketArgs.newName;
vars.ackFn({ errorFlag: 0, message: 'Name updated!' });
vars.hasNameChanged = true;
}
}
},
communicate: async (game, socketArgs, vars) => {
if (vars.hasNameChanged) {
vars.gameManager.namespace.in(game.accessCode).emit(
EVENT_IDS.CHANGE_NAME,
socketArgs.personId,
socketArgs.newName
);
}
}
},
{
id: EVENT_IDS.UPDATE_GAME_ROLES,
stateChange: async (game, socketArgs, vars) => {

View File

@@ -182,7 +182,7 @@ class GameManager {
if (matchingPerson) {
return Promise.resolve(matchingPerson.cookie);
}
if (isNameTaken(game, name)) {
if (this.isNameTaken(game, name)) {
return Promise.reject({ status: 400, reason: 'This name is taken.' });
}
if (joinAsSpectator
@@ -334,6 +334,11 @@ class GameManager {
findPersonByField = (game, fieldName, value) => {
return game.people.find(person => person[fieldName] === value);
}
isNameTaken (game, name) {
const processedName = name.toLowerCase().trim();
return game.people.find((person) => person.name.toLowerCase().trim() === processedName);
}
}
function getRandomInt (max) {
@@ -383,11 +388,6 @@ function createRandomId () {
return id;
}
function isNameTaken (game, name) {
const processedName = name.toLowerCase().trim();
return game.people.find((person) => person.name.toLowerCase().trim() === processedName);
}
async function addSpectator (game, name, logger, namespace, eventManager, instanceId, refreshGame) {
const spectator = new Person(
createRandomId(),

View File

@@ -18,7 +18,7 @@ describe('GameManager', () => {
const inObj = { emit: () => {} };
namespace = { in: () => { return inObj; }, to: () => { return inObj; } };
socket = { id: '123', emit: () => {}, to: () => { return { emit: () => {} }; } };
gameManager = GameManager.instance ? GameManager.instance : new GameManager(logger, globals.ENVIRONMENT.PRODUCTION, 'test');
gameManager = GameManager.instance ? GameManager.instance : new GameManager(logger, globals.ENVIRONMENTS.PRODUCTION, 'test');
timerManager = TimerManager.instance ? TimerManager.instance : new TimerManager(logger, 'test');
eventManager = EventManager.instance ? EventManager.instance : new EventManager(logger, 'test');
eventManager.publisher = { publish: async (...a) => {} };