Bingo lobby improvements

- bingo added kicking of player
- bingo added display of admin
- bingo added grid size input
pull/15/head
Trivernis 6 years ago
parent 1a552a0661
commit d566505c3f

@ -23,12 +23,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- css for startpage (wip) - css for startpage (wip)
- file for css animations - file for css animations
- pug file for startpage - pug file for startpage
- bingo lobbys
- kick function for bingo
- grid size input
## Changed ## Changed
- changed export of `app.js` to the asynchronous init function that returns the app object - changed export of `app.js` to the asynchronous init function that returns the app object
- `bin/www` now calls the init function of `app.js` - `bin/www` now calls the init function of `app.js`
- graphql api - graphql bingo api
- bingo frontend
### Removed ### Removed

@ -19,7 +19,7 @@ type LobbyMutation {
leave: Boolean leave: Boolean
"kicks a player from the lobby" "kicks a player from the lobby"
kickPlayer(playerId: ID!): BingoLobby kickPlayer(pid: ID!): BingoPlayer
"starts a round in a lobby if the user is the admin" "starts a round in a lobby if the user is the admin"
startRound: BingoRound startRound: BingoRound

@ -9,3 +9,5 @@ Angry Koala
Dragonslayer Dragonslayer
Goblin Slayer Goblin Slayer
useless Aqua useless Aqua
theP0wner79
Pr0wn

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

@ -101,6 +101,32 @@ async function leaveLobby() {
} }
} }
/**
* Kicks a player by id.
* @param playerId
* @returns {Promise<void>}
*/
async function kickPlayer(pid) {
let response = await postGraphqlQuery(`
mutation ($lobbyId: ID!, $playerId:ID!) {
bingo {
mutateLobby(id: $lobbyId) {
kickPlayer(pid: $playerId) {
id
}
}
}
}
`, {lobbyId: getLobbyParam(), playerId: pid});
if (response.status === 200) {
let kickId = response.data.bingo.mutateLobby.kickPlayer.id;
document.querySelector(`.playerEntryContainer[b-pid='${kickId}'`).remove();
} else {
showError('Failed to kick player!');
console.error(response);
}
}
/** /**
* Sends a message to the chat * Sends a message to the chat
* @returns {Promise<void>} * @returns {Promise<void>}
@ -138,21 +164,27 @@ async function sendChatMessage() {
/** /**
* Sets the words for the lobby * Sets the words for the lobby
* @param words * @param words
* @param gridSize
* @returns {Promise<LobbyWrapper.words|*|properties.words|{default, type}|boolean>} * @returns {Promise<LobbyWrapper.words|*|properties.words|{default, type}|boolean>}
*/ */
async function setLobbyWords(words) { async function setLobbySettings(words, gridSize) {
gridSize = Number(gridSize);
let response = await postGraphqlQuery(` let response = await postGraphqlQuery(`
mutation($lobbyId:ID!, $words:[String!]!){ mutation ($lobbyId: ID!, $words: [String!]!, $gridSize:Int!) {
bingo { bingo {
mutateLobby(id:$lobbyId) { mutateLobby(id: $lobbyId) {
setWords(words:$words) { setWords(words: $words) {
words { words {
content content
} }
} }
setGridSize(gridSize: $gridSize) {
gridSize
}
} }
} }
}`, {lobbyId: getLobbyParam(), words: words}); }
`, {lobbyId: getLobbyParam(), words: words, gridSize: gridSize});
if (response.status === 200) { if (response.status === 200) {
return response.data.bingo.mutateLobby.setWords.words; return response.data.bingo.mutateLobby.setWords.words;
} else { } else {
@ -168,7 +200,8 @@ async function setLobbyWords(words) {
async function startRound() { async function startRound() {
let textinput = document.querySelector('#input-bingo-words'); let textinput = document.querySelector('#input-bingo-words');
let words = getLobbyWords(); let words = getLobbyWords();
let resultWords = await setLobbyWords(words); let gridSize = document.querySelector('#input-grid-size').value || 3;
let resultWords = await setLobbySettings(words, gridSize);
textinput.value = resultWords.map(x => x.content).join('\n'); textinput.value = resultWords.map(x => x.content).join('\n');
let response = await postGraphqlQuery(` let response = await postGraphqlQuery(`
mutation($lobbyId:ID!){ mutation($lobbyId:ID!){
@ -351,11 +384,17 @@ function addChatMessage(messageObject) {
* Adds a player to the player view * Adds a player to the player view
* @param player * @param player
*/ */
function addPlayer(player) { function addPlayer(player, options) {
let playerContainer = document.createElement('div'); let playerContainer = document.createElement('div');
playerContainer.setAttribute('class', 'playerEntryContainer'); playerContainer.setAttribute('class', 'playerEntryContainer');
playerContainer.setAttribute('b-pid', player.id); playerContainer.setAttribute('b-pid', player.id);
playerContainer.innerHTML = `<span class="playernameSpan">${player.username}</span>`;
if (options.isAdmin && player.id !== options.admin)
playerContainer.innerHTML = `<button class="kickPlayerButton" onclick="kickPlayer(${player.id})">❌</button>`;
playerContainer.innerHTML += `<span class="playernameSpan">${player.username}</span>`;
if (player.id === options.admin)
playerContainer.innerHTML += "<span class='adminSpan'> 👑</span>";
document.querySelector('#player-list').appendChild(playerContainer); document.querySelector('#player-list').appendChild(playerContainer);
} }
@ -403,22 +442,31 @@ async function refreshChat() {
async function refreshPlayers() { async function refreshPlayers() {
try { try {
let response = await postGraphqlQuery(` let response = await postGraphqlQuery(`
query($lobbyId:ID!){ query ($lobbyId: ID!) {
bingo { bingo {
lobby(id:$lobbyId) { player {
id
}
lobby(id: $lobbyId) {
players { players {
id id
username username
wins(lobbyId:$lobbyId) wins(lobbyId: $lobbyId)
}
admin {
id
} }
} }
} }
}`, {lobbyId: getLobbyParam()}); }
`, {lobbyId: getLobbyParam()});
if (response.status === 200) { if (response.status === 200) {
let players = response.data.bingo.lobby.players; let players = response.data.bingo.lobby.players;
let adminId = response.data.bingo.lobby.admin.id;
let isAdmin = response.data.bingo.player.id === adminId;
for (let player of players) for (let player of players)
if (!document.querySelector(`.playerEntryContainer[b-pid="${player.id}"]`)) if (!document.querySelector(`.playerEntryContainer[b-pid="${player.id}"]`))
addPlayer(player); addPlayer(player, {admin: adminId, isAdmin: isAdmin});
} else { } else {
showError('Failed to refresh players'); showError('Failed to refresh players');
console.error(response); console.error(response);
@ -572,7 +620,9 @@ window.addEventListener("unhandledrejection", function (promiseRejectionEvent) {
window.addEventListener("keydown", async (e) => { window.addEventListener("keydown", async (e) => {
if (e.which === 83 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) { if (e.which === 83 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
e.preventDefault(); e.preventDefault();
if (document.querySelector('#input-bingo-words')) if (document.querySelector('#input-bingo-words')) {
await setLobbyWords(getLobbyWords()); let gridSize = document.querySelector('#input-grid-size').value || 3;
await setLobbySettings(getLobbyWords(), gridSize);
}
} }
}, false); }, false);

@ -82,9 +82,13 @@
#input-bingo-words #input-bingo-words
width: 100% width: 100%
height: calc(100% - 7rem) height: calc(100% - 10rem)
margin: 0 margin: 0
#input-grid-size
height: 3rem
width: 4rem
#button-round-start, #button-leave #button-round-start, #button-leave
height: 3rem height: 3rem
width: 100% width: 100%
@ -116,6 +120,13 @@
height: calc(100% - 6rem) height: calc(100% - 6rem)
overflow-y: auto overflow-y: auto
.kickPlayerButton
background-color: #0000
border: none
padding: 0
margin: 0 0.5rem 0 0
font-size: 1em
#container-grid #container-grid
display: table display: table
height: calc(100% - 2em) height: calc(100% - 2em)
@ -183,7 +194,7 @@
#container-bingo-lobby #container-bingo-lobby
display: grid display: grid
grid-template: 5% 5% 85% 5% / 5% 30% 30% 30% 5% grid-template: 0 10% 85% 5% / 5% 30% 30% 30% 5%
height: 100% height: 100%
width: 100% width: 100%

@ -46,11 +46,20 @@ button:active
background-color: lighten($secondary, 15%) background-color: lighten($secondary, 15%)
input input
@include default-element background-color: lighten($primary, 10%)
color: $primarySurface
border: 1px solid $inactive
transition-duration: 0.2s
font-size: 1.2rem font-size: 1.2rem
background-color: lighten($primary, 15%)
padding: 0.7rem padding: 0.7rem
input:focus
background-color: lighten($primary, 15%)
border: 1px solid $primarySurface
input[type='number']
text-align: center
textarea textarea
background-color: lighten($primary, 15%) background-color: lighten($primary, 15%)
color: $primarySurface color: $primarySurface

@ -854,6 +854,15 @@ class LobbyWrapper {
} }
} }
/**
* Returns if the lobby exists (based on one loaded attribute)
* @returns {Promise<boolean>}
*/
async exists() {
await this._loadLobbyInfo();
return !!this.expire;
}
/** /**
* returns the players in the lobby * returns the players in the lobby
* @returns {Promise<Array>} * @returns {Promise<Array>}
@ -1025,6 +1034,18 @@ class LobbyWrapper {
await this._loadLobbyInfo(true); await this._loadLobbyInfo(true);
} }
/**
* Removes a player from the lobby
* @param playerId
* @returns {Promise<void>}
*/
async removePlayer(playerId) {
await bdm.removePlayerFromLobby(playerId, this.id);
let username = await new PlayerWrapper(playerId).username();
await bdm.addInfoMessage(this.id, `${username} left.`);
await this._loadLobbyInfo(true);
}
/** /**
* Returns if the lobby is in an active round * Returns if the lobby is in an active round
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
@ -1236,9 +1257,9 @@ router.get('/', async (req, res) => {
let playerId = req.session.bingoPlayerId; let playerId = req.session.bingoPlayerId;
if (!playerId) if (!playerId)
req.session.bingoPlayerId = playerId = (await bdm.addPlayer(shuffleArray(playerNames)[0])).id; req.session.bingoPlayerId = playerId = (await bdm.addPlayer(shuffleArray(playerNames)[0])).id;
if (req.query.g) { let lobbyWrapper = new LobbyWrapper(req.query.g);
if (req.query.g && await lobbyWrapper.exists()) {
let lobbyId = req.query.g; let lobbyId = req.query.g;
let lobbyWrapper = new LobbyWrapper(lobbyId);
if (!(await lobbyWrapper.roundActive())) { if (!(await lobbyWrapper.roundActive())) {
if (!await lobbyWrapper.hasPlayer(playerId)) if (!await lobbyWrapper.hasPlayer(playerId))
@ -1249,17 +1270,34 @@ router.get('/', async (req, res) => {
res.render('bingo/bingo-lobby', { res.render('bingo/bingo-lobby', {
players: playerData, players: playerData,
isAdmin: (playerId === admin.id), isAdmin: (playerId === admin.id),
adminId: admin.id,
words: words, words: words,
wordString: words.join('\n')}); wordString: words.join('\n'),
gridSize: await lobbyWrapper.gridSize()
});
} else { } else {
if (await lobbyWrapper.hasPlayer(playerId)) { if (await lobbyWrapper.hasPlayer(playerId)) {
let playerData = await getPlayerData(lobbyWrapper); let playerData = await getPlayerData(lobbyWrapper);
let grid = await getGridData(lobbyId, playerId); let grid = await getGridData(lobbyId, playerId);
res.render('bingo/bingo-round', {players: playerData, grid: grid}); let admin = await lobbyWrapper.admin();
res.render('bingo/bingo-round', {
players: playerData,
grid: grid,
isAdmin: (playerId === admin.id),
adminId: admin.id
});
} else { } else {
let playerData = await getPlayerData(lobbyWrapper); let playerData = await getPlayerData(lobbyWrapper);
let admin = await lobbyWrapper.admin(); let admin = await lobbyWrapper.admin();
res.render('bingo/bingo-lobby', {players: playerData, isAdmin: (playerId === admin.id)}); let words = await getWordsData(lobbyWrapper);
res.render('bingo/bingo-lobby', {
players: playerData,
isAdmin: (playerId === admin.id),
adminId: admin.id,
words: words,
wordString: words.join('\n'),
gridSize: await lobbyWrapper.gridSize()
});
} }
} }
} else { } else {
@ -1314,15 +1352,15 @@ router.graphqlResolver = async (req, res) => {
return { return {
join: async () => { join: async () => {
if (playerId) { if (playerId) {
let result = await bdm.addPlayerToLobby(playerId, lobbyId); await lobbyWrapper.addPlayer(playerId);
return new LobbyWrapper(result.lobby_id); return lobbyWrapper;
} else { } else {
res.status(400); res.status(400);
} }
}, },
leave: async () => { leave: async () => {
if (playerId) { if (playerId) {
await bdm.removePlayerFromLobby(playerId, lobbyId); await lobbyWrapper.removePlayer(playerId);
return true; return true;
} else { } else {
res.status(400); res.status(400);
@ -1331,8 +1369,8 @@ router.graphqlResolver = async (req, res) => {
kickPlayer: async ({pid}) => { kickPlayer: async ({pid}) => {
let admin = await lobbyWrapper.admin(); let admin = await lobbyWrapper.admin();
if (admin.id === playerId) { if (admin.id === playerId) {
let result = await bdm.removePlayerFromLobby(pid, lobbyId); await lobbyWrapper.removePlayer(pid);
return new LobbyWrapper(result.id, result); return new PlayerWrapper(pid);
} }
}, },
startRound: async () => { startRound: async () => {
@ -1369,7 +1407,7 @@ router.graphqlResolver = async (req, res) => {
submitBingo: async () => { submitBingo: async () => {
let isBingo = await (await (new PlayerWrapper(playerId)).grid({lobbyId: lobbyId})).bingo(); let isBingo = await (await (new PlayerWrapper(playerId)).grid({lobbyId: lobbyId})).bingo();
let currentRound = await lobbyWrapper.currentRound(); let currentRound = await lobbyWrapper.currentRound();
if (isBingo) { if (isBingo && await lobbyWrapper.hasPlayer(playerId)) {
let result = await currentRound.setWinner(playerId); let result = await currentRound.setWinner(playerId);
if (result) if (result)
return currentRound; return currentRound;

@ -7,6 +7,8 @@ block content
div(id='container-lobby-settings') div(id='container-lobby-settings')
h1 Words h1 Words
if isAdmin if isAdmin
span Grid Size:
input(id='input-grid-size' type='number' value=gridSize)
textarea(id='input-bingo-words')= wordString textarea(id='input-bingo-words')= wordString
button(id='button-round-start' onclick='startRound()') Start Round button(id='button-round-start' onclick='startRound()') Start Round
else else

@ -3,4 +3,8 @@ div(id='container-players')
div(id='player-list') div(id='player-list')
each player in players each player in players
div(class='playerEntryContainer', b-pid=`${player.id}`) div(class='playerEntryContainer', b-pid=`${player.id}`)
if isAdmin && player.id !== adminId
button(class='kickPlayerButton' onclick=`kickPlayer(${player.id})`) ❌
span(class='playerNameSpan')= player.username span(class='playerNameSpan')= player.username
if player.id === adminId
span(class='adminSpan') 👑

Loading…
Cancel
Save