Compare commits

..

No commits in common. "623b53d5425a487908f5a887d7283e6aa9fcc0f1" and "5716a9338a8de145e949f46493b9b8370430edf8" have entirely different histories.

11 changed files with 105 additions and 2937 deletions

View File

@ -16,4 +16,4 @@
"engines": { "engines": {
"node": "12.x" "node": "12.x"
} }
} }

View File

@ -1 +0,0 @@
main.svelte-1tky8bj{text-align:center;padding:1em;max-width:240px;margin:0 auto}h1.svelte-1tky8bj{color:#ff3e00;text-transform:uppercase;font-size:4em;font-weight:100}@media(min-width: 640px){main.svelte-1tky8bj{max-width:none}}main.svelte-1tky8bj{text-align:center;padding:1em;max-width:240px;margin:0 auto}h1.svelte-1tky8bj{color:#ff3e00;text-transform:uppercase;font-size:4em;font-weight:100}@media(min-width: 640px){main.svelte-1tky8bj{max-width:none}}main.svelte-1tky8bj{text-align:center;padding:1em;max-width:240px;margin:0 auto}h1.svelte-1tky8bj{color:#ff3e00;text-transform:uppercase;font-size:4em;font-weight:100}@media(min-width: 640px){main.svelte-1tky8bj{max-width:none}}main.svelte-1tky8bj{text-align:center;padding:1em;max-width:240px;margin:0 auto}h1.svelte-1tky8bj{color:#ff3e00;text-transform:uppercase;font-size:4em;font-weight:100}@media(min-width: 640px){main.svelte-1tky8bj{max-width:none}}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@ body {
} }
h1 { h1 {
font-style: italic;
color: #373fff; color: #373fff;
line-height: 1.1; line-height: 1.1;
} }
@ -21,30 +22,22 @@ form {
padding: 1em; padding: 1em;
max-width: 40ch; max-width: 40ch;
margin: 0 auto; margin: 0 auto;
box-shadow: grey 1px 1px 3px;
border-radius: .25em;
}
label {
font-weight: bold;
} }
input[type=text] { input[type=text] {
text-transform: lowercase; text-transform: lowercase;
border-radius: .25em;
} }
input { input {
border: 1px solid silver; border: 1px solid silver;
display: block; display: block;
text-align: center;
font-size: 16px; font-size: 16px;
margin-bottom: 10px; margin-bottom: 10px;
padding: 5px; padding: 5px;
width: 100%; width: 100%;
} }
button { form button {
background-color: #bbbbf2; background-color: #bbbbf2;
border: 2px solid currentColor; border: 2px solid currentColor;
border-radius: .25em; border-radius: .25em;
@ -64,20 +57,6 @@ footer {
border-top: 1px solid lightgrey; border-top: 1px solid lightgrey;
} }
blockquote {
background-color: #eee;
display: block;
margin: 1em auto;
padding: 1em;
max-width: 40ch;
box-shadow: grey 1px 1px 3px;
border-radius: .25em;
}
blockquote a {
color: #373fff;
}
#buzzer { #buzzer {
width: 95vw; width: 95vw;
height: 95vw; height: 95vw;
@ -94,11 +73,6 @@ blockquote a {
text-shadow: 2px 2px 2px black; text-shadow: 2px 2px 2px black;
} }
#buzzer.active {
background: greenyellow;
box-shadow: inset 4px 4px 4px rgba(0,0,0,.5);
}
#buzzer:active { #buzzer:active {
box-shadow: inset 4px 4px 4px rgba(0,0,0,.5); box-shadow: inset 4px 4px 4px rgba(0,0,0,.5);
} }
@ -138,7 +112,7 @@ figure.participant figcaption {
10%, 90% { 10%, 90% {
transform: rscale(1.3) otate(-10deg); transform: rscale(1.3) otate(-10deg);
} }
20%, 80% { 20%, 80% {
transform: rscale(1.3) otate(20deg); transform: rscale(1.3) otate(20deg);
} }
@ -174,8 +148,4 @@ figure.participant figcaption {
#messageBox.hide { #messageBox.hide {
display: none; display: none;
} }
#participants {
margin-top: 1em;
}

103
room.js
View File

@ -11,7 +11,7 @@ function shuffle(a) {
function getOrCreateRoom(roomId) { function getOrCreateRoom(roomId) {
let room = rooms[roomId]; let room = rooms[roomId];
if (!room) { if (!room) {
room = { room = {
roomId: roomId, roomId: roomId,
@ -22,49 +22,43 @@ function getOrCreateRoom(roomId) {
}; };
rooms[roomId] = room; rooms[roomId] = room;
} }
return room; return room;
} }
function addParticipant(roomId, participantId, participantName) { function addParticipant(roomId, participantId, participantName) {
let room = getOrCreateRoom(roomId); let room = getOrCreateRoom(roomId);
room.participants.push({ room.participants.push({
participantId, participantId,
participantName, participantName,
character: room.characters[room.participants.length], character: room.characters[room.participants.length],
active: true active: true
}); });
rooms[roomId] = room; rooms[roomId] = room;
} }
function removeParticipant(roomId, participantId) { function removeParticipant(roomId, participantId) {
let room = getOrCreateRoom(roomId); let room = getOrCreateRoom(roomId);
let participant = room.participants.find(p => p.participantId === participantId); room.participants.find(p => p.participantId === participantId).active = false;
if (participant) { room.audience.forEach(ws => {
participant.active = false; ws.send(JSON.stringify({
room.audience.forEach(ws => { type: "participants",
ws.send(JSON.stringify({ participants: room.participants
type: "participants", }));
participants: room.participants });
}));
});
}
} }
function addParticipantWS(roomId, participantId, ws) { function addParticipantWS(roomId, participantId, ws) {
let room = getOrCreateRoom(roomId); let room = getOrCreateRoom(roomId);
let participant = room.participants.find(p => p.participantId === participantId); room.participants.find(p => p.participantId === participantId).ws = ws;
if (participant) { room.audience.forEach(ws => {
participant.ws = ws; ws.send(JSON.stringify({
room.audience.forEach(ws => { type: "participants",
ws.send(JSON.stringify({ participants: room.participants
type: "participants", }));
participants: room.participants });
}));
});
}
} }
function addAudienceWS(roomId, ws) { function addAudienceWS(roomId, ws) {
@ -80,53 +74,26 @@ function buzz(roomId, participant) {
let room = getOrCreateRoom(roomId); let room = getOrCreateRoom(roomId);
if (room.canBuzz) { if (room.canBuzz) {
room.canBuzz = false; room.canBuzz = false;
setTimeout(() => room.canBuzz = true, 5000);
participant = room.participants.find(p => p.participantId === participant.participantId); participant = room.participants.find(p => p.participantId === participant.participantId);
if (participant) { room.participants.forEach(p => {
room.participants.forEach(p => { if (p.ws && p.participantId !== participant.participantId) {
if (p.ws && p.participantId !== participant.participantId) { p.ws.send(JSON.stringify({
p.ws.send(JSON.stringify({
type: "buzz",
participant: participant.participantName,
msg: `<img src="${participant.character}"><div>${participant.participantName} buzzed!</div>`
}));
}
});
room.audience.forEach(ws => {
ws.send(JSON.stringify({
type: "buzz", type: "buzz",
participant: participant participant: participant.participantName,
msg: `<img src="${participant.character}"><div>${participant.participantName} buzzed!</div>`
})); }));
}); }
} });
room.audience.forEach(ws => {
ws.send(JSON.stringify({
type: "buzz",
participant: participant
}));
});
} }
} }
function reset(roomId) { module.exports = {getOrCreateRoom, addParticipant, addParticipantWS, addAudienceWS, buzz, removeParticipant}
let room = getOrCreateRoom(roomId);
room.canBuzz = true;
room.participants.forEach(p => {
p.ws.send(JSON.stringify({
type: "reset"
}));
});
}
function closeRoom(roomId) {
let room = getOrCreateRoom(roomId);
room.canBuzz = false;
room.participants.forEach(p => {
if (p.ws) {
p.ws.send(JSON.stringify({
type: "close"
}));
}
});
delete rooms[roomId];
}
module.exports = {getOrCreateRoom, addParticipant, addParticipantWS, addAudienceWS, buzz, removeParticipant, reset, closeRoom}

View File

@ -6,7 +6,6 @@ const WebSocket = require('ws');
const http = require('http'); const http = require('http');
const randomWords = require('random-words'); const randomWords = require('random-words');
const rooms = require('./room'); const rooms = require('./room');
const room = require("./room");
const app = express(); const app = express();
const server = http.createServer(app); const server = http.createServer(app);
@ -26,33 +25,32 @@ app.get("/", (request, response) => {
}); });
}); });
app.get("/:roomId", (request, response) => { app.get("/:roomId/join", (request, response) => {
let room = rooms.getOrCreateRoom(request.params.roomId.trim().toLowerCase()); let room = rooms.getOrCreateRoom(request.params.roomId.toLowerCase());
let participant = room.participants.find(p => p.participantId === request.fingerprint.hash); let participant = room.participants.find(p => p.participantId === request.fingerprint.hash);
if (room.audience.length === 0) { if (participant) {
response.render('audience', {
layout: false,
room: request.params.roomId.trim().toLowerCase(),
participants: room.participants,
});
} else if (participant) {
response.render('room', { response.render('room', {
layout: false, layout: false,
room: request.params.roomId.trim().toLowerCase(), room: request.params.roomId.toLowerCase(),
name: participant.participantName, name: participant.participantName,
participantName: participant.participantName, participantName: participant.participantName,
participantId: participant.participantId, participantId: participant.participantId,
character: participant.character, character: participant.character,
}); });
} else { } else {
response.render('join', {layout: false, room: request.params.roomId.trim().toLowerCase()}); response.render('join', {layout: false, room: request.params.roomId.toLowerCase()});
} }
}); });
app.post("/:roomId", (request, response) => { app.get("/:roomId/audience", (request, response) => {
let room = rooms.getOrCreateRoom(request.params.roomId.toLowerCase());
response.render('audience', {layout: false, room: request.params.roomId.toLowerCase(), participants: room.participants });
});
app.post("/:roomId/join", (request, response) => {
rooms.addParticipant(request.params.roomId.toLowerCase(), request.fingerprint.hash, request.body.name); rooms.addParticipant(request.params.roomId.toLowerCase(), request.fingerprint.hash, request.body.name);
response.redirect(`/${request.params.roomId.toLowerCase()}`); response.redirect(`/${request.params.roomId.toLowerCase()}/join`);
}); });
server.listen(process.env.PORT, () => { server.listen(process.env.PORT, () => {
@ -67,17 +65,6 @@ wss.on('connection', (ws, req) => {
if (roomId.includes("/audience")) { if (roomId.includes("/audience")) {
roomId = roomId.replace("/audience", ""); roomId = roomId.replace("/audience", "");
rooms.addAudienceWS(roomId, ws); rooms.addAudienceWS(roomId, ws);
ws.on('message', (message) => {
message = JSON.parse(message);
if (message.type === "reset") {
rooms.reset(roomId);
}
});
ws.on('close', () => {
room.closeRoom(roomId);
});
} else { } else {
let participant; let participant;
ws.on('message', (message) => { ws.on('message', (message) => {

View File

@ -14,61 +14,40 @@
</header> </header>
<main> <main>
<blockquote>
Share this link for players to join:<br>
<a id="playerRoom" href=""></a>
</blockquote>
<h2>Participants</h2> <h2>Participants</h2>
<button id="resetBuzzers">Reset Buzzers</button> <div id="participants"></div>
<div id="participants">
No participants yet :(
</div>
</main> </main>
<script> <script>
document.getElementById("playerRoom").href = window.location; let socket = new WebSocket(`wss://${window.location.hostname}/{{room}}/audience`);
document.getElementById("playerRoom").innerText = window.location;
let proto = location.protocol == 'http:' ? 'ws' : 'wss'
let socket = new WebSocket(`${proto}://${window.location.hostname}:${window.location.port}/{{room}}/audience`);
let buzzed;
socket.onmessage = function(event) { socket.onmessage = function(event) {
let msg = JSON.parse(event.data); let msg = JSON.parse(event.data);
if (msg.type === "buzz") { if (msg.type === "buzz") {
beep(); beep();
buzzed = document.getElementById(`p-${msg.participant.participantId}`); let buzzed = document.getElementById(`p-${msg.participant.participantId}`);
buzzed.classList.add('buzzed'); buzzed.classList.add('buzzed');
setTimeout(() => buzzed.classList.remove('buzzed'), 5000)
} else if (msg.type === "participants") { } else if (msg.type === "participants") {
let participantContainer = document.getElementById('participants'); let participantContainer = document.getElementById('participants');
if (msg.participants.length) { let contents = '';
let contents = ''; msg.participants.forEach(p => {
msg.participants.forEach(p => { contents += `
contents += ` <figure class="participant ${!p.active ? 'hide': ''}" id="p-${p.participantId}">
<figure class="participant ${!p.active ? 'hide': ''}" id="p-${p.participantId}"> <img src="${p.character}" />
<img src="${p.character}" /> <figcaption>${p.participantName}</figcaption>
<figcaption>${p.participantName}</figcaption> </figure>
</figure> `;
`; });
}); participantContainer.innerHTML = contents;
participantContainer.innerHTML = contents;
}
} }
}; };
function beep() { function beep() {
window.navigator.vibrate(500); window.navigator.vibrate(500);
} }
document.getElementById('resetBuzzers').addEventListener('click', function() {
socket.send(JSON.stringify({
type: "reset"
}));
buzzed.classList.remove('buzzed');
})
</script> </script>
</body> </body>
</html> </html>

View File

@ -10,13 +10,16 @@
</head> </head>
<body> <body>
<header> <header>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAADuUlEQVRoge2YW2hcVRSGv31ym2YyoraasanFEWtaQW0StAYRB2JRFAKCpm1C6c0bSCg++dKHWH32QX0Sq7QkLZlSxcubUGpS0Je0RtJipZnElIIWhlDmkrn1LB9mjMnkzLnMnDO2MN/LMGuvtde/zl57nz0DderUuaNRbk0ku0JPguoD9QxIJ/Ag0FYcTgALoP5Ayc+InFXjc9Nu5K2qAOnvDODLvoXSDoFscxh+GTiGvu5zdfpSolINFRUg4XAjwYVhhCPAvZUmLyiQGKI+4u/Nn6lz5/KOw50GyJ6HtqJrp4DtTmMtZr6Izh51eu6KkyhHBcjuUD+ixvivt1fzaDfseAm2dMGGjdB2N0RnYGSX3RRxRIZUZO57uwGNdh1ld2gvor40jXn/GPjvsjulEQGU+loGQgdUZG7UToCtFSg++TOUiu/pg3wOpicK35/th/s2wew0XL8KiZuQTTsroUAeeFWNR3+wcrQsQAYf3sItpoDAsrGhAfaPwM5BSMXhoMvboUACTX9KnZr/3cxJMxuUcLiRW0QoFX/404L4bBq++sAduWtpQ9fGJBw2bXPTAgguDFN62uwfgadfhORNODoIk99UK9SMbtoX3jVzKNtCsrfdT9Y/D2xYNva+Aoc/KTz5o0Nw9VfXlJZXKDFSzSH13ZW40XD5Fci0vsNK8QD7jhQ+j39YG/EAotazLvdmueHy/VW4Hqy2nf8WtAY4O+6aPpscAj42GjBsoeLFrEaP2C7qcTU+O1NqNW4h4QXP9ThG+oysxgUobYenWipCeo2sZTaxbPVSSmWoTiNrmRWQjZ5qqYxNRsYye0AFDO3/L4aa7F3mzvAAOvNAs5uKTMghhNQA160cza8S/6LzNrUTD9CEouzLayXWt9EIzSj+BIJVy3LGDfxsVi+TMXOyXgGNAWovHuB+krxm5WRdgDDsipzKeM/Kwfgqcbn0EnR7oB5bq9feJr6Nsf2j3gkXoh1EfnmC2b/WA/BIMMbrvb/RHbI8FR3jeguNTnRxYqLHcGzf81MMPXex0qm9b6GpaAcnJnoQIJNKkFiMEV+MkU4lEeD4Tz1ciHa4mdLdAkYnuwHIpJJklpbQdR3RdbJLKTKpZNGny82U7hSQzjUyNtnFpWvtAOQza/8LyqULtplrQU6e304m7872M9wDOwffqOoYjS/GEF1fnUhrIHBPdf8D/3jyi9oco00tPgNbixepvDlGW1r9AOSKrdTU4lu2uY0nBSjA1+rH55Holdzxb+J6AXXq1KlTFf8AtEULoLOzQvEAAAAASUVORK5CYII=" /> <h1>Join room</h1>
<h1>Buzzers</h1>
</header> </header>
<main> <main>
<h2>Create or join a game room</h2> <h2>Oh hi!</h2>
<p>
Create or join a room
</p>
<form> <form>
<label> <label>
Room ID Room ID
@ -25,12 +28,13 @@
<button type="submit" id="submit-name">Submit</button> <button type="submit" id="submit-name">Submit</button>
</form> </form>
</main> </main>
<script> <script>
document.querySelector('form').addEventListener('submit', function(event) { document.querySelector('form').addEventListener('submit', function(event) {
event.preventDefault(); event.preventDefault();
let roomId = document.getElementById('roomId').value; let roomId = document.getElementById('roomId').value;
window.location = `//${window.location.host}/${roomId}`; window.location = `//${window.location.host}/${roomId}/join`;
}); });
</script> </script>
</body> </body>

View File

@ -10,11 +10,13 @@
</head> </head>
<body> <body>
<header> <header>
<h1>Join '{{room}}'</h1> <h1>Join {{room}}</h1>
</header> </header>
<main> <main>
<h2>Please tell me your (team) name:</h2> <h2>Oh hi,</h2>
<p>Tell me your (team) name:</p>
<form method="POST"> <form method="POST">
<label> <label>
@ -23,6 +25,10 @@
</label> </label>
<button type="submit" id="submit-name">Submit</button> <button type="submit" id="submit-name">Submit</button>
</form> </form>
<p>
<a href="/{{room}}/audience">Join the audience</a>
</p>
</main> </main>
</body> </body>
</html> </html>

View File

@ -14,20 +14,19 @@
</header> </header>
<main> <main>
<h2>{{name}} <img src="{{character}}"/></h2> <h2>Oh hi, {{name}} <img src="{{character}}"/></h2>
<button id="buzzer"> <button id="buzzer">
BUZZ BUZZ
</button> </button>
</main> </main>
<div id="messageBox" class="hide"> <div id="messageBox" class="hide">
</div> </div>
<script> <script>
let proto = location.protocol == 'http:' ? 'ws' : 'wss' let socket = new WebSocket(`wss://${window.location.hostname}/{{room}}`);
let socket = new WebSocket(`${proto}://${window.location.hostname}:${window.location.port}/{{room}}`);
socket.onopen = function(e) { socket.onopen = function(e) {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
@ -43,51 +42,35 @@
let msg = JSON.parse(event.data); let msg = JSON.parse(event.data);
if (msg.type === "buzz") { if (msg.type === "buzz") {
showMessage(msg.msg); showMessage(msg.msg);
} else if (msg.type === "reset") {
reset();
} else if (msg.type === "close") {
roomClosed();
} }
}; };
document.getElementById('buzzer').addEventListener('touchstart', function() { document.getElementById('buzzer').addEventListener('touchstart', function() {
beep(); beep();
socket.send(JSON.stringify({ socket.send(JSON.stringify({
type: "buzz" type: "buzz"
})); }));
document.getElementById('buzzer').classList.add('active');
}); });
document.getElementById('buzzer').addEventListener('mousedown', function() { document.getElementById('buzzer').addEventListener('mousedown', function() {
beep(); beep();
socket.send(JSON.stringify({ socket.send(JSON.stringify({
type: "buzz" type: "buzz"
})); }));
document.getElementById('buzzer').classList.add('active');
}); });
function showMessage(msg) { function showMessage(msg) {
window.navigator.vibrate(500); window.navigator.vibrate(500);
document.getElementById('buzzer').disabled = true; document.getElementById('buzzer').disabled = true;
let mb = document.getElementById('messageBox'); let mb = document.getElementById('messageBox');
mb.innerHTML = msg; mb.innerHTML = msg;
mb.classList.remove('hide'); mb.classList.remove('hide');
setTimeout(() => {
document.getElementById('buzzer').disabled = false;
mb.classList.add('hide');
}, 5000)
} }
function reset() {
document.getElementById('buzzer').disabled = false;
document.getElementById('buzzer').classList.remove('active');
let mb = document.getElementById('messageBox');
mb.classList.add('hide');
}
function roomClosed() {
document.getElementById('buzzer').disabled = true;
let mb = document.getElementById('messageBox');
mb.innerHTML = "Room closed";
mb.classList.remove('hide');
}
function beep() { function beep() {
window.navigator.vibrate(500); window.navigator.vibrate(500);
} }