Changes to style

- added backend limits
- added frontend limits
- added bingo statusbar
- improved bingo stylesheet
- added redirect on missing player
pull/15/head
Trivernis 6 years ago
parent d566505c3f
commit 6943719c7c

@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- bingo lobbys - bingo lobbys
- kick function for bingo - kick function for bingo
- grid size input - grid size input
- bingo status bar
## Changed ## Changed
@ -33,11 +34,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `bin/www` now calls the init function of `app.js` - `bin/www` now calls the init function of `app.js`
- graphql bingo api - graphql bingo api
- bingo frontend - bingo frontend
- moved some bingo pug files to ./bingo/includes/
### Removed ### Removed
- sqlite3 sesssion storage - sqlite3 sesssion storage
- old frontend - old frontend
- old bingo pug files
### Fixed ### Fixed

@ -59,7 +59,7 @@ async function init() {
app.use('/sass', compileSass({ app.use('/sass', compileSass({
root: './public/stylesheets/sass', root: './public/stylesheets/sass',
sourceMap: true, sourceMap: true,
watchFiles: false, // TODO: set true watchFiles: true,
logToConsole: true logToConsole: true
})); }));
app.use(express.static(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'public')));

@ -11,3 +11,7 @@ Goblin Slayer
useless Aqua useless Aqua
theP0wner79 theP0wner79
Pr0wn Pr0wn
Cool User 68
My name Jeff
Bingo Bingo Duolingo
Max Mustermann

@ -55,6 +55,14 @@ async function submitUsername() {
} }
} }
/**
* TODO: real join logic
*/
async function joinLobby() {
await submitUsername();
window.location.reload();
}
/** /**
* Creates a lobby and redirects to the lobby. * Creates a lobby and redirects to the lobby.
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
@ -136,6 +144,15 @@ async function sendChatMessage() {
if (messageInput.value && messageInput.value.length > 0) { if (messageInput.value && messageInput.value.length > 0) {
let message = messageInput.value; let message = messageInput.value;
messageInput.value = ''; messageInput.value = '';
if (message === '/hideinfo' || message === '/showinfo') {
let jsStyle = document.querySelector('#js-style');
if (message === '/hideinfo')
jsStyle.innerHTML = '.chatMessage[msg-type="INFO"] {display: none}';
else
jsStyle.innerHTML = '.chatMessage[msg-type="INFO"] {}';
let chatContent = document.querySelector('#chat-content');
chatContent.scrollTop = chatContent.scrollHeight;
} else {
let response = await postGraphqlQuery(` let response = await postGraphqlQuery(`
mutation($lobbyId:ID!, $message:String!){ mutation($lobbyId:ID!, $message:String!){
bingo { bingo {
@ -159,6 +176,7 @@ async function sendChatMessage() {
showError('Error when sending message.'); showError('Error when sending message.');
} }
} }
}
} }
/** /**
@ -195,7 +213,7 @@ async function setLobbySettings(words, gridSize) {
/** /**
* Starts a new round of bingo * Starts a new round of bingo
* @returns {Promise<void>} * @returns {Promise<boolean>}
*/ */
async function startRound() { async function startRound() {
let textinput = document.querySelector('#input-bingo-words'); let textinput = document.querySelector('#input-bingo-words');
@ -242,6 +260,8 @@ function getLobbyWords() {
async function submitFieldToggle(wordPanel) { async function submitFieldToggle(wordPanel) {
let row = Number(wordPanel.getAttribute('b-row')); let row = Number(wordPanel.getAttribute('b-row'));
let column = Number(wordPanel.getAttribute('b-column')); let column = Number(wordPanel.getAttribute('b-column'));
let wordClass = wordPanel.getAttribute('class');
wordPanel.setAttribute('class', wordClass + ' pending');
let response = await postGraphqlQuery(` let response = await postGraphqlQuery(`
mutation($lobbyId:ID!, $row:Int!, $column:Int!){ mutation($lobbyId:ID!, $row:Int!, $column:Int!){
bingo { bingo {
@ -255,6 +275,7 @@ async function submitFieldToggle(wordPanel) {
} }
} }
}`, {lobbyId: getLobbyParam(), row: row, column: column}); }`, {lobbyId: getLobbyParam(), row: row, column: column});
wordPanel.setAttribute('class', wordClass);
if (response.status === 200) { if (response.status === 200) {
wordPanel.setAttribute('b-sub', response.data.bingo.mutateLobby.toggleGridField.submitted); wordPanel.setAttribute('b-sub', response.data.bingo.mutateLobby.toggleGridField.submitted);
@ -325,7 +346,32 @@ function displayWinner(roundInfo) {
* @param errorMessage * @param errorMessage
*/ */
function showError(errorMessage) { function showError(errorMessage) {
// TODO: Implement let errorContainer = document.querySelector('#error-message');
let indicator = document.querySelector('#status-indicator');
indicator.setAttribute('status', 'error');
errorContainer.innerText = errorMessage;
setTimeout(() => {
errorContainer.innerText = '';
indicator.setAttribute('status', 'idle');
}, 5000);
}
/**
* Wraps a function in a status report to display the status
* @param func
*/
async function statusWrap(func) {
let indicator = document.querySelector('#status-indicator');
indicator.setAttribute('status', 'pending');
try {
await func();
indicator.setAttribute('status', 'success');
setTimeout(() => {
indicator.setAttribute('status', 'idle');
}, 1000);
} catch (err) {
showError(err? err.message : 'Unknown error');
}
} }
/** /**
@ -365,6 +411,7 @@ async function loadWinnerInfo() {
function addChatMessage(messageObject) { function addChatMessage(messageObject) {
let msgSpan = document.createElement('span'); let msgSpan = document.createElement('span');
msgSpan.setAttribute('class', 'chatMessage'); msgSpan.setAttribute('class', 'chatMessage');
msgSpan.setAttribute('msg-type', messageObject.type);
msgSpan.setAttribute('msg-id', messageObject.id); msgSpan.setAttribute('msg-id', messageObject.id);
if (messageObject.type === "USER") { if (messageObject.type === "USER") {
msgSpan.innerHTML = ` msgSpan.innerHTML = `
@ -390,7 +437,7 @@ function addPlayer(player, options) {
playerContainer.setAttribute('b-pid', player.id); playerContainer.setAttribute('b-pid', player.id);
if (options.isAdmin && player.id !== options.admin) if (options.isAdmin && player.id !== options.admin)
playerContainer.innerHTML = `<button class="kickPlayerButton" onclick="kickPlayer(${player.id})"></button>`; playerContainer.innerHTML = `<button class="kickPlayerButton" onclick="kickPlayer(${player.id})"></button>`;
playerContainer.innerHTML += `<span class="playernameSpan">${player.username}</span>`; playerContainer.innerHTML += `<span class="playernameSpan">${player.username}</span>`;
if (player.id === options.admin) if (player.id === options.admin)
@ -499,7 +546,7 @@ function checkPlayerRefresh(players) {
if (!document.querySelector(`.playerEntryContainer[b-pid="${player.id}"]`)) if (!document.querySelector(`.playerEntryContainer[b-pid="${player.id}"]`))
playerRefresh = true; playerRefresh = true;
if (playerRefresh) if (playerRefresh)
refreshPlayers(); statusWrap(refreshPlayers);
} }
/** /**
@ -512,7 +559,7 @@ function checkMessageRefresh(messages) {
if (!document.querySelector(`.chatMessage[msg-id="${message.id}"]`)) if (!document.querySelector(`.chatMessage[msg-id="${message.id}"]`))
messageRefresh = true; messageRefresh = true;
if (messageRefresh) if (messageRefresh)
refreshChat(); statusWrap(refreshChat);
} }
/** /**
@ -613,7 +660,7 @@ async function refreshRound() {
window.addEventListener("unhandledrejection", function (promiseRejectionEvent) { window.addEventListener("unhandledrejection", function (promiseRejectionEvent) {
promiseRejectionEvent.promise.catch(err => console.log(err)); promiseRejectionEvent.promise.catch(err => console.log(err));
showError('Connection problems... Is the server down?'); showError('Connection problems...');
}); });
// prevent ctrl + s // prevent ctrl + s
@ -622,7 +669,7 @@ window.addEventListener("keydown", async (e) => {
e.preventDefault(); e.preventDefault();
if (document.querySelector('#input-bingo-words')) { if (document.querySelector('#input-bingo-words')) {
let gridSize = document.querySelector('#input-grid-size').value || 3; let gridSize = document.querySelector('#input-grid-size').value || 3;
await setLobbySettings(getLobbyWords(), gridSize); await statusWrap(async () => await setLobbySettings(getLobbyWords(), gridSize));
} }
} }
}, false); }, false);

@ -9,11 +9,12 @@
function postData(url, postBody) { function postData(url, postBody) {
let request = new XMLHttpRequest(); let request = new XMLHttpRequest();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let start = new Date().getTime();
request.onload = () => { request.onload = () => {
resolve({ resolve({
status: request.status, status: request.status,
data: request.responseText data: request.responseText,
ping: (new Date().getTime() - start)
}); });
}; };
@ -23,7 +24,11 @@ function postData(url, postBody) {
request.open('POST', url, true); request.open('POST', url, true);
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
try {
request.send(JSON.stringify(postBody)); request.send(JSON.stringify(postBody));
} catch (err) {
return err;
}
}); });
} }

@ -45,6 +45,12 @@
color: $primarySurface color: $primarySurface
border: 1px solid $inactive border: 1px solid $inactive
.playerWins
margin-left: 1em
.kickPlayerButton
color: $inactive
.chatMessage .chatMessage
display: block display: block
padding: 0.2em padding: 0.2em
@ -110,14 +116,14 @@
width: 100% width: 100%
h1 h1
height: 3rem height: 2rem
margin: 0.5rem margin: 0.5rem
text-align: center text-align: center
#player-list #player-list
padding: 1rem padding: 1rem
width: calc(100% - 2rem) width: calc(100% - 2rem)
height: calc(100% - 6rem) height: calc(100% - 5rem)
overflow-y: auto overflow-y: auto
.kickPlayerButton .kickPlayerButton
@ -128,11 +134,15 @@
font-size: 1em font-size: 1em
#container-grid #container-grid
display: table
height: calc(100% - 2em) height: calc(100% - 2em)
width: calc(100% - 2em) width: calc(100% - 2em)
padding: 1em padding: 1em
overflow: auto
#grid-table
display: table
height: 100%
width: 100%
.bingoWordRow .bingoWordRow
display: table-row display: table-row
@ -164,6 +174,34 @@
height: 100% height: 100%
width: 100% width: 100%
#statusbar
display: grid
grid-template: 100% / 1rem calc(80% - 1rem) 20%
background-color: darken($primary, 5%)
margin: 0.5rem 0 0 0
padding: 0.25rem
vertical-align: middle
font-size: 0.8em
text-align: start
#status-indicator
height: 1rem
width: 1rem
border-radius: 100%
@include gridPosition(1, 2, 1,2)
#error-message
@include gridPosition(1, 2, 2, 3)
margin: auto 1em
text-align: start
color: $errorText
#container-info
width: 100%
height: 100%
margin: auto
color: $inactive
/* main containers */ /* main containers */
#container-bingo-create #container-bingo-create
@ -186,13 +224,15 @@
margin: 0 0 0 3em margin: 0 0 0 3em
#lobby-form #lobby-form
@include gridPosition(3, 4, 2, 3) @include gridPosition(3, 5, 2, 3)
margin: auto margin: 10% auto
button button
width: 100% width: 100%
#container-bingo-lobby #container-bingo-lobby
@include fillWindow
overflow: hidden
display: grid display: grid
grid-template: 0 10% 85% 5% / 5% 30% 30% 30% 5% grid-template: 0 10% 85% 5% / 5% 30% 30% 30% 5%
height: 100% height: 100%
@ -214,20 +254,29 @@
@include gridPosition(3, 4, 4, 5) @include gridPosition(3, 4, 4, 5)
background-color: lighten($primary, 5%) background-color: lighten($primary, 5%)
#statusbar
@include gridPosition(4, 5, 1, 6)
#container-bingo-round #container-bingo-round
@include fillWindow
overflow: hidden
display: grid display: grid
height: 100% height: 100%
width: 100% width: 100%
grid-template: 10% 42.5% 42.5% 5% / 25% 75% grid-template: 10% 30% 55% 5% / 25% 75%
#container-players #container-players
@include gridPosition(2, 3, 1, 2) @include gridPosition(2, 3, 1, 2)
#container-chat #container-chat
@include gridPosition(3, 4, 1, 2) @include gridPosition(3, 4, 1, 2)
padding: 0 1rem
#container-grid #container-grid
@include gridPosition(2, 4, 2, 3) @include gridPosition(2, 4, 2, 3)
#container-bingo-button #container-bingo-button
@include gridPosition(1, 2, 1, 2) @include gridPosition(1, 2, 1, 2)
#statusbar
@include gridPosition(4, 5, 1, 3)

@ -27,16 +27,17 @@
border-radius: 1em border-radius: 1em
transition-duration: 1s transition-duration: 1s
.statusIndicator:before .statusIndicator[status='success']
content: "" background-color: $success
.statusIndicator[status='success']:before .statusIndicator[status='error']
content: "" background-color: $error
color: $success
.statusIndicator[status='error']:before .statusIndicator[status='idle']
content: "" background-color: $inactive
color: $error animation-name: pulse-opacity
animation-duration: 5s
animation-iteration-count: infinite
.statusIndicator[status='pending'] .statusIndicator[status='pending']
background-color: $pending background-color: $pending
@ -44,7 +45,7 @@
animation-duration: 5s animation-duration: 5s
animation-iteration-count: infinite animation-iteration-count: infinite
.idle .pending
background-color: $pending !important background-color: $pending !important
animation-name: pulse-opacity animation-name: pulse-opacity
animation-duration: 2s animation-duration: 2s

@ -6,6 +6,11 @@
border: 2px solid $primarySurface border: 2px solid $primarySurface
transition-duration: 0.2s transition-duration: 0.2s
@mixin fillWindow
position: absolute
top: 0
left: 0
@mixin gridPosition($rowStart, $rowEnd, $columnStart, $columnEnd) @mixin gridPosition($rowStart, $rowEnd, $columnStart, $columnEnd)
grid-row-start: $rowStart grid-row-start: $rowStart
grid-row-end: $rowEnd grid-row-end: $rowEnd

@ -4,7 +4,7 @@
@media (min-device-width: 320px) @media (min-device-width: 320px)
html html
font-size: 4.5vw font-size: 4vw
@media (min-device-width: 481px) @media (min-device-width: 481px)
html html
@ -72,6 +72,9 @@ mark
background-color: $secondary background-color: $secondary
color: $primarySurface color: $primarySurface
mark > a
color: white
::-webkit-scrollbar ::-webkit-scrollbar
width: 12px width: 12px
height: 12px height: 12px

@ -4,5 +4,6 @@ $secondary: teal
$borderRadius: 20px $borderRadius: 20px
$inactive: #aaa $inactive: #aaa
$error: #a00 $error: #a00
$errorText: #f44
$success: #0a0 $success: #0a0
$pending: #aa0 $pending: #aa0

@ -1,5 +1,6 @@
const express = require('express'), const express = require('express'),
router = express.Router(), router = express.Router(),
{ GraphQLError } = require('graphql'),
mdEmoji = require('markdown-it-emoji'), mdEmoji = require('markdown-it-emoji'),
mdMark = require('markdown-it-mark'), mdMark = require('markdown-it-mark'),
mdSmartarrows = require('markdown-it-smartarrows'), mdSmartarrows = require('markdown-it-smartarrows'),
@ -609,7 +610,16 @@ class GridWrapper {
*/ */
async toggleField(row, column) { async toggleField(row, column) {
let result = await bdm.toggleGridFieldSubmitted(this.id, row, column); let result = await bdm.toggleGridFieldSubmitted(this.id, row, column);
return new GridFieldWrapper(result); let gridField = new GridFieldWrapper(result);
let username = await (await this.player()).username();
let word = await gridField.word.content();
if (gridField.submitted)
await bdm.addInfoMessage(this.lobbyId,
`${username} heared "${word}"`);
else
await bdm.addInfoMessage(this.lobbyId,
`${username} unheared "${word}"`);
return gridField;
} }
} }
@ -1186,13 +1196,16 @@ function checkBingo(fg) {
*/ */
async function getPlayerData(lobbyWrapper) { async function getPlayerData(lobbyWrapper) {
let playerData = []; let playerData = [];
let adminId = (await lobbyWrapper.admin()).id;
for (let player of await lobbyWrapper.players()) for (let player of await lobbyWrapper.players())
playerData.push({ playerData.push({
id: player.id, id: player.id,
wins: await player.wins({lobbyId: lobbyWrapper.id}), wins: await player.wins({lobbyId: lobbyWrapper.id}),
username: await player.username()} username: await player.username(),
); isAdmin: (player.id === adminId)
});
playerData.sort((a, b) => (a.isAdmin? -1 : (b.wins - a.wins) || a.id));
return playerData; return playerData;
} }
@ -1255,10 +1268,10 @@ router.use(async (req, res, next) => {
router.get('/', async (req, res) => { 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;
let lobbyWrapper = new LobbyWrapper(req.query.g); let lobbyWrapper = new LobbyWrapper(req.query.g);
if (req.query.g && await lobbyWrapper.exists()) { if (playerId && req.query.g && await lobbyWrapper.exists()) {
let lobbyId = req.query.g; let lobbyId = req.query.g;
if (!(await lobbyWrapper.roundActive())) { if (!(await lobbyWrapper.roundActive())) {
@ -1338,12 +1351,14 @@ router.graphqlResolver = async (req, res) => {
return new PlayerWrapper(playerId); return new PlayerWrapper(playerId);
}, },
createLobby: async({gridSize}) => { createLobby: async({gridSize}) => {
if (playerId) { if (playerId)
if (gridSize > 0 && gridSize < 10) {
let result = await bdm.createLobby(playerId, gridSize); let result = await bdm.createLobby(playerId, gridSize);
return new LobbyWrapper(result.id); return new LobbyWrapper(result.id);
} else { } else {
res.status(400); res.status(413);
} }
res.status(400);
}, },
mutateLobby: async ({id}) => { mutateLobby: async ({id}) => {
let lobbyId = id; let lobbyId = id;
@ -1389,19 +1404,23 @@ router.graphqlResolver = async (req, res) => {
}, },
setWords: async({words}) => { setWords: async({words}) => {
let admin = await lobbyWrapper.admin(); let admin = await lobbyWrapper.admin();
if (admin.id === playerId) { if (admin.id === playerId)
if (words.length < 10000) {
await lobbyWrapper.setWords(words); await lobbyWrapper.setWords(words);
return lobbyWrapper; return lobbyWrapper;
} else { } else {
res.status(400); res.status(413); // request entity too large
} }
else
res.status(403); // forbidden
}, },
sendMessage: async ({message}) => { sendMessage: async ({message}) => {
if (await lobbyWrapper.hasPlayer(playerId)) { if (await lobbyWrapper.hasPlayer(playerId)) {
let result = await bdm.addUserMessage(lobbyId, playerId, message); let result = await bdm.addUserMessage(lobbyId, playerId, message);
return new MessageWrapper(result); return new MessageWrapper(result);
} else { } else {
res.status(400); res.status(401); // unautorized
} }
}, },
submitBingo: async () => { submitBingo: async () => {
@ -1412,9 +1431,10 @@ router.graphqlResolver = async (req, res) => {
if (result) if (result)
return currentRound; return currentRound;
else else
res.status(400); res.status(500);
} else { } else {
res.status(400); res.status(400);
return new GraphQLError('Bingo check failed. This is not a bingo!');
} }
}, },
toggleGridField: async ({location}) => { toggleGridField: async ({location}) => {

@ -1,4 +0,0 @@
div(id='container-chat')
//h1(id='chat-header') Chat
div(id='chat-content')
input(id='chat-input' type='text', placeholder='send message', onkeypress='submitOnEnter(event, sendChatMessage)' maxlength="250")

@ -1,4 +1,4 @@
extends bingo-layout extends includes/bingo-layout
block content block content
div(id='container-bingo-create') div(id='container-bingo-create')

@ -1,28 +0,0 @@
include bingo-layout
block content
if username === 'anonymous'
div(class='greyover')
div(id='username-form', onkeypress='submitOnEnter(event, submitUsername)')
input(type='text', id='username-input', placeholder=username, maxlength="30")
span Maximum is 30 characters.
button(onclick='submitUsername()') Set Username
div(id='content-container')
div(id='players-container')
h1 Players
each player in players
div(class='player-container', b-pid=`${player.id}`)
span(class='player-name-span')= player.username
div(id='chat-container')
div(id='chat-content')
input(id='chat-input' type='text', placeholder='chat', onkeypress='submitOnEnter(event, sendChatMessage)' maxlength="250")
div(id='words-container')
each val in grid
div(class='bingo-word-row')
each field in val
div(class='bingo-word-panel', onclick=`submitWord('${field.base64Word}')`, b-word=field.base64Word, b-sub=`${field.submitted}`)
span= field.word
div(id='button-container')
button(id='bingo-button', onclick='submitBingo()', class='hidden') Bingo!
div(id='chat-button-container')
button(id='chat-toggle-button', onclick='toggleChatView()') toggle Chat

@ -1,21 +1,22 @@
extends bingo-layout extends includes/bingo-layout
block content block content
div(id='container-bingo-lobby') div(id='container-bingo-lobby')
h1(id='lobby-title') Bingo Lobby h1(id='lobby-title') Bingo Lobby
include bingo-players include includes/bingo-players
div(id='container-lobby-settings') div(id='container-lobby-settings')
h1 Words h1 Words
if isAdmin if isAdmin
span Grid Size: span Grid Size:
input(id='input-grid-size' type='number' value=gridSize) input(id='input-grid-size' type='number' value=gridSize min='1' max='8')
textarea(id='input-bingo-words')= wordString textarea(id='input-bingo-words' placeholder='max. 10.000 phrases')= wordString
button(id='button-round-start' onclick='startRound()') Start Round button(id='button-round-start' onclick='statusWrap(startRound)') Start Round
else else
div(id='bingo-words') div(id='bingo-words')
for word in words for word in words
span(class='bingoWord')= word span(class='bingoWord')= word
button(id='button-leave' onclick='leaveLobby()') Leave button(id='button-leave' onclick='statusWrap(leaveLobby)') Leave
include bingo-chat include includes/bingo-chat
include includes/bingo-statusbar
script(type='text/javascript') refreshLobby(); script(type='text/javascript') refreshLobby();

@ -1,22 +1,24 @@
include bingo-layout include includes/bingo-layout
block content block content
div(id='container-bingo-round') div(id='container-bingo-round')
include bingo-players include includes/bingo-players
include bingo-chat include includes/bingo-chat
include includes/bingo-statusbar
if grid.bingo if grid.bingo
div(id='container-bingo-button') div(id='container-bingo-button')
button(id='bingo-button' onclick='submitBingo()') Bingo! button(id='bingo-button' onclick='statusWrap(submitBingo)') Bingo!
else else
div(id='container-bingo-button' class='hidden') div(id='container-bingo-button' class='hidden')
button(id='bingo-button' onclick='submitBingo()') Bingo! button(id='bingo-button' onclick='statusWrap(submitBingo)') Bingo!
div(id='container-grid') div(id='container-grid')
div(id='grid-table')
each val in grid.fields each val in grid.fields
div(class='bingoWordRow') div(class='bingoWordRow')
each field in val each field in val
div( div(
class='bingoWordPanel' class='bingoWordPanel'
onclick=`submitFieldToggle(this)` onclick=`statusWrap(() => submitFieldToggle(this))`
b-row=field.row b-row=field.row
b-column=field.column b-column=field.column
b-sub=`${field.submitted}`) b-sub=`${field.submitted}`)

@ -0,0 +1,10 @@
div(id='container-chat')
style(id='js-style')
div(id='chat-content')
input(
id='chat-input'
type='text'
placeholder='send message'
onkeypress='submitOnEnter(event, () => statusWrap(sendChatMessage))'
maxlength="250"
autocomplete='off')

@ -1,6 +1,6 @@
html html
head head
include ../includes/head include ../../includes/head
title Bingo by Trivernis title Bingo by Trivernis
script(type='text/javascript', src='/javascripts/bingo-web.js') script(type='text/javascript', src='/javascripts/bingo-web.js')
link(rel='stylesheet', href='/sass/bingo/style.sass') link(rel='stylesheet', href='/sass/bingo/style.sass')

@ -4,7 +4,11 @@ div(id='container-players')
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 if isAdmin && player.id !== adminId
button(class='kickPlayerButton' onclick=`kickPlayer(${player.id})`) ❌ button(class='kickPlayerButton' onclick=`statusWrap(() => kickPlayer(${player.id}))`)
span(class='playerNameSpan')= player.username
if player.id === adminId if player.id === adminId
span(class='adminSpan') 👑 span(class='adminSpan') 👑
span(class='playerNameSpan')= player.username
if player.wins > 1
span(class='playerWins')= `${player.wins}x🌟`
else if player.wins > 0
span(class='playerWins') 🌟

@ -0,0 +1,7 @@
div(id='statusbar')
div(id='status-indicator' class='statusIndicator' status='idle')
span(id='error-message')
span(id='container-info')
| please report bugs
|
a(href='https://github.com/Trivernis/whooshy/issues') here
Loading…
Cancel
Save