allow moderators to kick players and spectators

This commit is contained in:
AlecM33
2023-08-02 23:23:01 -04:00
parent d00f3d630a
commit 0d82227824
14 changed files with 195 additions and 38 deletions

View File

@@ -53,7 +53,8 @@ export const globals = {
ADD_SPECTATOR: 'addSpectator',
UPDATE_SPECTATORS: 'updateSpectators',
RESTART_GAME: 'restartGame',
ASSIGN_DEDICATED_MOD: 'assignDedicatedMod'
ASSIGN_DEDICATED_MOD: 'assignDedicatedMod',
KICK_PERSON: 'kickPerson'
},
TIMER_EVENTS: function () {
return [
@@ -66,7 +67,8 @@ export const globals = {
LOBBY_EVENTS: function () {
return [
this.EVENT_IDS.PLAYER_JOINED,
this.EVENT_IDS.ADD_SPECTATOR
this.EVENT_IDS.ADD_SPECTATOR,
this.EVENT_IDS.KICK_PERSON
];
},
IN_PROGRESS_EVENTS: function () {

View File

@@ -139,6 +139,15 @@ export const HTMLFragments = {
<button id='close-mod-transfer-modal-button' class='app-button cancel'>Cancel</button>
</div>
</div>`,
PLAYER_OPTIONS_MODAL:
`<div id='player-options-modal-background' class='modal-background'></div>
<div tabindex='-1' id='player-options-modal' class='modal'>
<h2>Person Options:</h2>
<div id='player-options-modal-content'></div>
<div class='modal-button-container'>
<button id='close-player-options-modal-button' class='app-button cancel'>Close</button>
</div>
</div>`,
MODERATOR_GAME_VIEW:
`<div id='game-header'>
<div id='timer-container-moderator'>

View File

@@ -51,7 +51,7 @@ export class InProgress {
this.stateBucket.currentGameState.accessCode
);
setTimeout(() => {
if (this.socket.hasListeners(globals.EVENT_IDS.GET_TIME_REMAINING)) {
if (this.socket.hasListeners(globals.EVENT_IDS.GET_TIME_REMAINING) && document.getElementById('game-timer') !== null) {
document.getElementById('game-timer').innerText = 'Timer not found.';
document.getElementById('game-timer').classList.add('timer-error');
}
@@ -65,8 +65,16 @@ export class InProgress {
const spectatorCount = this.container.querySelector('#spectator-count');
const spectatorHandler = (e) => {
if (e.type === 'click' || e.code === 'Enter') {
Confirmation(SharedStateUtil.buildSpectatorList(this.stateBucket.currentGameState.people
.filter(p => p.userType === globals.USER_TYPES.SPECTATOR)), null, true);
Confirmation(
SharedStateUtil.buildSpectatorList(
this.stateBucket.currentGameState.people
.filter(p => p.userType === globals.USER_TYPES.SPECTATOR),
this.stateBucket.currentGameState.client,
this.socket,
this.stateBucket.currentGameState),
null,
true
);
}
};

View File

@@ -61,8 +61,14 @@ export class Lobby {
const spectatorHandler = (e) => {
if (e.type === 'click' || e.code === 'Enter') {
Confirmation(SharedStateUtil.buildSpectatorList(this.stateBucket.currentGameState.people
.filter(p => p.userType === globals.USER_TYPES.SPECTATOR), this.stateBucket.currentGameState.client), null, true);
Confirmation(
SharedStateUtil.buildSpectatorList(this.stateBucket.currentGameState.people
.filter(p => p.userType === globals.USER_TYPES.SPECTATOR),
this.stateBucket.currentGameState.client,
this.socket,
this.stateBucket.currentGameState),
null, true
);
}
};
@@ -95,7 +101,7 @@ export class Lobby {
}
);
for (const person of sorted.filter(p => p.userType !== globals.USER_TYPES.SPECTATOR)) {
lobbyPlayersContainer.appendChild(renderLobbyPerson(person.name, person.userType, this.stateBucket.currentGameState.client));
lobbyPlayersContainer.appendChild(renderLobbyPerson(person, this.stateBucket.currentGameState, this.socket));
}
const playerCount = this.stateBucket.currentGameState.people.filter(
p => p.userType !== globals.USER_TYPES.MODERATOR && p.userType !== globals.USER_TYPES.SPECTATOR
@@ -126,6 +132,32 @@ export class Lobby {
document.getElementById('spectator-count')
);
});
this.socket.on(globals.EVENT_IDS.KICK_PERSON, (kickedId, gameIsFull) => {
if (kickedId === this.stateBucket.currentGameState.client.id) {
window.location = '/?message=' + encodeURIComponent('You were kicked by the moderator.');
} else {
const kickedIndex = this.stateBucket.currentGameState.people.findIndex(person => person.id === kickedId);
if (kickedIndex >= 0) {
this.stateBucket.currentGameState.people
.splice(kickedIndex, 1);
}
this.stateBucket.currentGameState.isFull = gameIsFull;
SharedStateUtil.setNumberOfSpectators(
this.stateBucket.currentGameState.people.filter(p => p.userType === globals.USER_TYPES.SPECTATOR).length,
document.getElementById('spectator-count')
);
this.populatePlayers();
if ((
this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR
|| this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
)
) {
toast('player kicked.', 'success', true, true, 'short');
this.displayStartGamePromptForModerators();
}
}
});
}
displayStartGamePromptForModerators () {
@@ -192,23 +224,25 @@ function getTimeString (gameState) {
}
}
function renderLobbyPerson (name, userType, client) {
function renderLobbyPerson (person, gameState, socket) {
const el = document.createElement('div');
const personNameEl = document.createElement('div');
personNameEl.classList.add('lobby-player-name');
const personTypeEl = document.createElement('div');
personNameEl.innerText = name;
personTypeEl.innerText = userType + globals.USER_TYPE_ICONS[userType];
personNameEl.innerText = person.name;
personTypeEl.innerText = person.userType + globals.USER_TYPE_ICONS[person.userType];
el.classList.add('lobby-player');
if (userType === globals.USER_TYPES.MODERATOR || userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
if (person.userType === globals.USER_TYPES.MODERATOR || person.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
el.classList.add('moderator');
}
el.appendChild(personNameEl);
el.appendChild(personTypeEl);
if (client.userType === globals.USER_TYPES.MODERATOR || client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
SharedStateUtil.addPlayerOptions(el);
if ((gameState.client.userType === globals.USER_TYPES.MODERATOR || gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR)
&& person.userType !== globals.USER_TYPES.MODERATOR && person.userType !== globals.USER_TYPES.TEMPORARY_MODERATOR) {
SharedStateUtil.addPlayerOptions(el, person, socket, gameState);
el.dataset.pointer = person.id;
}
return el;

View File

@@ -24,7 +24,7 @@ export const SharedStateUtil = {
},
restartHandler: (stateBucket, status = globals.STATUS.IN_PROGRESS) => {
console.log("HEY")
console.log('HEY');
XHRUtility.xhr(
'/api/games/' + stateBucket.currentGameState.accessCode + '/restart?status=' + status,
'PATCH',
@@ -175,17 +175,46 @@ export const SharedStateUtil = {
}
},
addPlayerOptions: (personEl) => {
const kickButton = document.createElement('img');
kickButton.setAttribute('tabIndex', '0');
kickButton.setAttribute('className', 'role-remove');
kickButton.setAttribute('src', '../images/3-vertical-dots-icon.svg');
kickButton.setAttribute('title', 'Kick Player');
kickButton.setAttribute('alt', 'Kick Player');
personEl.appendChild(kickButton);
addPlayerOptions: (personEl, person, socket, gameState) => {
const optionsButton = document.createElement('img');
const optionsHandler = (e) => {
if (e.type === 'click' || e.code === 'Enter') {
document.getElementById('player-options-modal-content').innerHTML = '';
const kickOption = document.createElement('button');
kickOption.setAttribute('class', 'player-option');
kickOption.innerText = 'Kick Person';
kickOption.addEventListener('click', () => {
ModalManager.dispelModal('player-options-modal', 'player-options-modal-background');
Confirmation('Kick \'' + person.name + '\'?', () => {
socket.emit(
globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
globals.EVENT_IDS.KICK_PERSON,
gameState.accessCode,
{ personId: person.id }
);
});
});
document.getElementById('player-options-modal-content').appendChild(kickOption);
ModalManager.displayModal(
'player-options-modal',
'player-options-modal-background',
'close-player-options-modal-button'
);
}
};
optionsButton.addEventListener('click', optionsHandler);
optionsButton.addEventListener('keyup', optionsHandler);
optionsButton.setAttribute('tabIndex', '0');
optionsButton.setAttribute('className', 'role-remove');
optionsButton.setAttribute('src', '../images/3-vertical-dots-icon.svg');
optionsButton.setAttribute('title', 'Player Options');
optionsButton.setAttribute('alt', 'Player Options');
personEl.appendChild(optionsButton);
},
buildSpectatorList (people, client) {
buildSpectatorList (people, client, socket, gameState) {
const list = document.createElement('div');
const spectators = people.filter(p => p.userType === globals.USER_TYPES.SPECTATOR);
if (spectators.length === 0) {
@@ -200,7 +229,8 @@ export const SharedStateUtil = {
list.appendChild(spectatorEl);
if (client.userType === globals.USER_TYPES.MODERATOR || client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
this.addPlayerOptions(spectatorEl);
this.addPlayerOptions(spectatorEl, spectator, socket, gameState);
spectatorEl.dataset.pointer = spectator.id;
}
}
}
@@ -259,13 +289,12 @@ function processGameState (
lobby.populatePlayers();
globals.LOBBY_EVENTS().forEach(e => socket.removeAllListeners(e));
lobby.setSocketHandlers();
if ((
currentGameState.client.userType === globals.USER_TYPES.MODERATOR
|| currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
)
&& refreshPrompt
) {
lobby.displayStartGamePromptForModerators();
if (currentGameState.client.userType === globals.USER_TYPES.MODERATOR
|| currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
if (refreshPrompt) {
lobby.displayStartGamePromptForModerators();
}
document.getElementById('player-options-prompt').innerHTML = HTMLFragments.PLAYER_OPTIONS_MODAL;
}
break;
case globals.STATUS.IN_PROGRESS:

View File

@@ -1,9 +1,15 @@
import { XHRUtility } from '../modules/utility/XHRUtility.js';
import { toast } from '../modules/front_end_components/Toast.js';
import { injectNavbar } from '../modules/front_end_components/Navbar.js';
import { Confirmation } from '../modules/front_end_components/Confirmation.js';
const home = () => {
injectNavbar();
const urlParams = new URLSearchParams(window.location.search);
const message = urlParams.get('message');
if (message && message.length > 0) {
Confirmation(message);
}
document.getElementById('join-form').addEventListener('submit', attemptToJoinGame);
};

View File

@@ -273,11 +273,36 @@ h1 {
width: 90%;
}
#role-info-modal .modal-button-container {
#role-info-modal .modal-button-container, #player-options-modal .modal-button-container {
margin-top: 1em;
justify-content: center;
}
#player-options-modal-content {
margin: 1em 0;
}
.player-option {
background-color: #4645525c;
border: 2px solid #46455299;
color: #d7d7d7;
display: flex;
font-family: 'signika-negative', sans-serif !important;
padding: 10px 30px;
border-radius: 5px;
font-size: 16px;
flex-direction: column;
align-items: flex-start;
text-align: left;
margin: 10px 0;
}
.player-option:hover, .player-option:active {
border: 2px solid #e7e7e7;
background-color: #33343c;
cursor: pointer;
}
#game-role-info-container .role-info-name {
border-radius: 5px;
font-size: 20px;

View File

@@ -76,6 +76,14 @@
border: 1px solid #46455299;
}
#player-options-modal {
z-index: 200001;
}
#player-options-modal-background {
z-index: 200000;
}
#custom-role-info-modal-name {
font-family: 'signika-negative', sans-serif;
font-size: 23px;

View File

@@ -1,6 +1,7 @@
const template =
`<div id="role-info-prompt"></div>
<div id="transfer-mod-prompt"></div>
<div id="player-options-prompt"></div>
<div class="spinner-background"></div>
<div class="spinner-container">
<div class="lds-spinner">

View File

@@ -19,6 +19,7 @@
<link rel="stylesheet" href="./styles/GLOBAL.css">
<link rel="stylesheet" href="./styles/home.css">
<link rel="stylesheet" href="/styles/hamburgers.css">
<link rel="stylesheet" href="./styles/confirmation.css">
</head>
<body>
<div id="mobile-menu-background-overlay"></div>