Better handling of room creation and management
This commit is contained in:
1 parent
5716a9338a
commit
aad99975c6
11 files changed
+2898
-91
No files matched your search
+1
-1
@@ -16,4 +16,4 @@
|
||||
"engines": {
|
||||
"node": "12.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"bundle.js","sources":["../../node_modules/svelte/internal/index.mjs","../../node_modules/page/page.js","../../node_modules/random-words/index.js","../../src/pages/Home.svelte","../../src/pages/Room.svelte","../../src/pages/Audience.svelte","../../src/pages/Player.svelte","../../src/App.svelte","../../src/main.js"],"sourcesContent":["function noop() { }\nconst identity = x => x;\nfunction assign(tar, src) {\n // @ts-ignore\n for (const k in src)\n tar[k] = src[k];\n return tar;\n}\nfunction is_promise(value) {\n return value && typeof value === 'object' && typeof value.then === 'function';\n}\nfunction add_location(element, file, line, column, char) {\n element.__svelte_meta = {\n loc: { file, line, column, char }\n };\n}\nfunction run(fn) {\n return fn();\n}\nfunction blank_object() {\n return Object.create(null);\n}\nfunction run_all(fns) {\n fns.forEach(run);\n}\nfunction is_function(thing) {\n return typeof thing === 'function';\n}\nfunction safe_not_equal(a, b) {\n return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');\n}\nfunction not_equal(a, b) {\n return a != a ? b == b : a !== b;\n}\nfunction is_empty(obj) {\n return Object.keys(obj).length === 0;\n}\nfunction validate_store(store, name) {\n if (store != null && typeof store.subscribe !== 'function') {\n throw new Error(`'${name}' is not a store with a 'subscribe' method`);\n }\n}\nfunction subscribe(store, ...callbacks) {\n if (store == null) {\n return noop;\n }\n const unsub = store.subscribe(...callbacks);\n return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;\n}\nfunction get_store_value(store) {\n let value;\n subscribe(store, _ => value = _)();\n return value;\n}\nfunction component_subscribe(component, store, callback) {\n component.$$.on_destroy.push(subscribe(store, callback));\n}\nfunction create_slot(definition, ctx, $$scope, fn) {\n if (definition) {\n const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);\n return definition[0](slot_ctx);\n }\n}\nfunction get_slot_context(definition, ctx, $$scope, fn) {\n return definition[1] && fn\n ? assign($$scope.ctx.slice(), definition[1](fn(ctx)))\n : $$scope.ctx;\n}\nfunction get_slot_changes(definition, $$scope, dirty, fn) {\n if (definition[2] && fn) {\n const lets = definition[2](fn(dirty));\n if ($$scope.dirty === undefined) {\n return lets;\n }\n if (typeof lets === 'object') {\n const merged = [];\n const len = Math.max($$scope.dirty.length, lets.length);\n for (let i = 0; i < len; i += 1) {\n merged[i] = $$scope.dirty[i] | lets[i];\n }\n return merged;\n }\n return $$scope.dirty | lets;\n }\n return $$scope.dirty;\n}\nfunction update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) {\n const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);\n if (slot_changes) {\n const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);\n slot.p(slot_context, slot_changes);\n }\n}\nfunction update_slot_spread(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_spread_changes_fn, get_slot_context_fn) {\n const slot_changes = get_slot_spread_changes_fn(dirty) | get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);\n if (slot_changes) {\n const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);\n slot.p(slot_context, slot_changes);\n }\n}\nfunction exclude_internal_props(props) {\n const result = {};\n for (const k in props)\n if (k[0] !== '$')\n result[k] = props[k];\n return result;\n}\nfunction compute_rest_props(props, keys) {\n const rest = {};\n keys = new Set(keys);\n for (const k in props)\n if (!keys.has(k) && k[0] !== '$')\n rest[k] = props[k];\n return rest;\n}\nfunction compute_slots(slots) {\n const result = {};\n for (const key in slots) {\n result[key] = true;\n }\n return result;\n}\nfunction once(fn) {\n let ran = false;\n return function (...args) {\n if (ran)\n return;\n ran = true;\n fn.call(this, ...args);\n };\n}\nfunction null_to_empty(value) {\n return value == null ? '' : value;\n}\nfunction set_store_value(store, ret, value = ret) {\n store.set(value);\n return ret;\n}\nconst has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);\nfunction action_destroyer(action_result) {\n return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;\n}\n\nconst is_client = typeof window !== 'undefined';\nlet now = is_client\n ? () => window.performaLine truncated
|
||||
+12
-2
@@ -24,6 +24,10 @@ form {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
@@ -31,6 +35,7 @@ input[type=text] {
|
||||
input {
|
||||
border: 1px solid silver;
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
padding: 5px;
|
||||
@@ -73,6 +78,11 @@ footer {
|
||||
text-shadow: 2px 2px 2px black;
|
||||
}
|
||||
|
||||
#buzzer.active {
|
||||
background: greenyellow;
|
||||
box-shadow: inset 4px 4px 4px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
#buzzer:active {
|
||||
box-shadow: inset 4px 4px 4px rgba(0,0,0,.5);
|
||||
}
|
||||
@@ -112,7 +122,7 @@ figure.participant figcaption {
|
||||
10%, 90% {
|
||||
transform: rscale(1.3) otate(-10deg);
|
||||
}
|
||||
|
||||
|
||||
20%, 80% {
|
||||
transform: rscale(1.3) otate(20deg);
|
||||
}
|
||||
@@ -148,4 +158,4 @@ figure.participant figcaption {
|
||||
|
||||
#messageBox.hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ function shuffle(a) {
|
||||
|
||||
function getOrCreateRoom(roomId) {
|
||||
let room = rooms[roomId];
|
||||
|
||||
|
||||
if (!room) {
|
||||
room = {
|
||||
roomId: roomId,
|
||||
@@ -22,43 +22,49 @@ function getOrCreateRoom(roomId) {
|
||||
};
|
||||
rooms[roomId] = room;
|
||||
}
|
||||
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
function addParticipant(roomId, participantId, participantName) {
|
||||
let room = getOrCreateRoom(roomId);
|
||||
|
||||
|
||||
room.participants.push({
|
||||
participantId,
|
||||
participantId,
|
||||
participantName,
|
||||
character: room.characters[room.participants.length],
|
||||
active: true
|
||||
});
|
||||
|
||||
|
||||
rooms[roomId] = room;
|
||||
}
|
||||
|
||||
function removeParticipant(roomId, participantId) {
|
||||
let room = getOrCreateRoom(roomId);
|
||||
room.participants.find(p => p.participantId === participantId).active = false;
|
||||
room.audience.forEach(ws => {
|
||||
ws.send(JSON.stringify({
|
||||
type: "participants",
|
||||
participants: room.participants
|
||||
}));
|
||||
});
|
||||
let participant = room.participants.find(p => p.participantId === participantId);
|
||||
if (participant) {
|
||||
participant.active = false;
|
||||
room.audience.forEach(ws => {
|
||||
ws.send(JSON.stringify({
|
||||
type: "participants",
|
||||
participants: room.participants
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addParticipantWS(roomId, participantId, ws) {
|
||||
let room = getOrCreateRoom(roomId);
|
||||
room.participants.find(p => p.participantId === participantId).ws = ws;
|
||||
room.audience.forEach(ws => {
|
||||
ws.send(JSON.stringify({
|
||||
type: "participants",
|
||||
participants: room.participants
|
||||
}));
|
||||
});
|
||||
let participant = room.participants.find(p => p.participantId === participantId);
|
||||
if (participant) {
|
||||
participant.ws = ws;
|
||||
room.audience.forEach(ws => {
|
||||
ws.send(JSON.stringify({
|
||||
type: "participants",
|
||||
participants: room.participants
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addAudienceWS(roomId, ws) {
|
||||
@@ -74,26 +80,53 @@ function buzz(roomId, participant) {
|
||||
let room = getOrCreateRoom(roomId);
|
||||
if (room.canBuzz) {
|
||||
room.canBuzz = false;
|
||||
setTimeout(() => room.canBuzz = true, 5000);
|
||||
|
||||
|
||||
participant = room.participants.find(p => p.participantId === participant.participantId);
|
||||
|
||||
room.participants.forEach(p => {
|
||||
if (p.ws && p.participantId !== participant.participantId) {
|
||||
p.ws.send(JSON.stringify({
|
||||
if (participant) {
|
||||
room.participants.forEach(p => {
|
||||
if (p.ws && p.participantId !== participant.participantId) {
|
||||
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",
|
||||
participant: participant.participantName,
|
||||
msg: `<img src="${participant.character}"><div>${participant.participantName} buzzed!</div>`
|
||||
participant: participant
|
||||
}));
|
||||
}
|
||||
});
|
||||
room.audience.forEach(ws => {
|
||||
ws.send(JSON.stringify({
|
||||
type: "buzz",
|
||||
participant: participant
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {getOrCreateRoom, addParticipant, addParticipantWS, addAudienceWS, buzz, removeParticipant}
|
||||
function reset(roomId) {
|
||||
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}
|
||||
@@ -6,6 +6,7 @@ const WebSocket = require('ws');
|
||||
const http = require('http');
|
||||
const randomWords = require('random-words');
|
||||
const rooms = require('./room');
|
||||
const room = require("./room");
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
@@ -25,32 +26,33 @@ app.get("/", (request, response) => {
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/:roomId/join", (request, response) => {
|
||||
let room = rooms.getOrCreateRoom(request.params.roomId.toLowerCase());
|
||||
app.get("/:roomId", (request, response) => {
|
||||
let room = rooms.getOrCreateRoom(request.params.roomId.trim().toLowerCase());
|
||||
let participant = room.participants.find(p => p.participantId === request.fingerprint.hash);
|
||||
|
||||
if (participant) {
|
||||
if (room.audience.length === 0) {
|
||||
response.render('audience', {
|
||||
layout: false,
|
||||
room: request.params.roomId.trim().toLowerCase(),
|
||||
participants: room.participants,
|
||||
});
|
||||
} else if (participant) {
|
||||
response.render('room', {
|
||||
layout: false,
|
||||
room: request.params.roomId.toLowerCase(),
|
||||
room: request.params.roomId.trim().toLowerCase(),
|
||||
name: participant.participantName,
|
||||
participantName: participant.participantName,
|
||||
participantId: participant.participantId,
|
||||
character: participant.character,
|
||||
});
|
||||
} else {
|
||||
response.render('join', {layout: false, room: request.params.roomId.toLowerCase()});
|
||||
response.render('join', {layout: false, room: request.params.roomId.trim().toLowerCase()});
|
||||
}
|
||||
});
|
||||
|
||||
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) => {
|
||||
app.post("/:roomId", (request, response) => {
|
||||
rooms.addParticipant(request.params.roomId.toLowerCase(), request.fingerprint.hash, request.body.name);
|
||||
response.redirect(`/${request.params.roomId.toLowerCase()}/join`);
|
||||
response.redirect(`/${request.params.roomId.toLowerCase()}`);
|
||||
});
|
||||
|
||||
server.listen(process.env.PORT, () => {
|
||||
@@ -65,6 +67,17 @@ wss.on('connection', (ws, req) => {
|
||||
if (roomId.includes("/audience")) {
|
||||
roomId = roomId.replace("/audience", "");
|
||||
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 {
|
||||
let participant;
|
||||
ws.on('message', (message) => {
|
||||
|
||||
@@ -14,22 +14,32 @@
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<p>
|
||||
Share this link for players to join:<br>
|
||||
<a id="playerRoom" href=""></a>
|
||||
</p>
|
||||
|
||||
<button id="resetBuzzers">Reset</button>
|
||||
|
||||
<h2>Participants</h2>
|
||||
|
||||
|
||||
<div id="participants"></div>
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
let socket = new WebSocket(`wss://${window.location.hostname}/{{room}}/audience`);
|
||||
document.getElementById("playerRoom").href = window.location;
|
||||
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) {
|
||||
let msg = JSON.parse(event.data);
|
||||
if (msg.type === "buzz") {
|
||||
beep();
|
||||
let buzzed = document.getElementById(`p-${msg.participant.participantId}`);
|
||||
buzzed = document.getElementById(`p-${msg.participant.participantId}`);
|
||||
buzzed.classList.add('buzzed');
|
||||
setTimeout(() => buzzed.classList.remove('buzzed'), 5000)
|
||||
} else if (msg.type === "participants") {
|
||||
let participantContainer = document.getElementById('participants');
|
||||
let contents = '';
|
||||
@@ -44,10 +54,17 @@
|
||||
participantContainer.innerHTML = contents;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function beep() {
|
||||
window.navigator.vibrate(500);
|
||||
}
|
||||
|
||||
document.getElementById('resetBuzzers').addEventListener('click', function() {
|
||||
socket.send(JSON.stringify({
|
||||
type: "reset"
|
||||
}));
|
||||
buzzed.classList.remove('buzzed');
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -14,12 +14,8 @@
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h2>Oh hi!</h2>
|
||||
<h2>Create or join a room</h2>
|
||||
|
||||
<p>
|
||||
Create or join a room
|
||||
</p>
|
||||
|
||||
<form>
|
||||
<label>
|
||||
Room ID
|
||||
@@ -28,13 +24,12 @@
|
||||
<button type="submit" id="submit-name">Submit</button>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
|
||||
<script>
|
||||
document.querySelector('form').addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
let roomId = document.getElementById('roomId').value;
|
||||
window.location = `//${window.location.host}/${roomId}/join`;
|
||||
|
||||
window.location = `//${window.location.host}/${roomId}`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -10,13 +10,11 @@
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Join {{room}}</h1>
|
||||
<h1>Join '{{room}}'</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h2>Oh hi,</h2>
|
||||
|
||||
<p>Tell me your (team) name:</p>
|
||||
<h2>Please tell me your (team) name:</h2>
|
||||
|
||||
<form method="POST">
|
||||
<label>
|
||||
@@ -25,10 +23,6 @@
|
||||
</label>
|
||||
<button type="submit" id="submit-name">Submit</button>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<a href="/{{room}}/audience">Join the audience</a>
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
+35
-18
@@ -14,19 +14,20 @@
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h2>Oh hi, {{name}} <img src="{{character}}"/></h2>
|
||||
|
||||
<h2>{{name}} <img src="{{character}}"/></h2>
|
||||
|
||||
<button id="buzzer">
|
||||
BUZZ
|
||||
</button>
|
||||
</main>
|
||||
|
||||
|
||||
<div id="messageBox" class="hide">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
let socket = new WebSocket(`wss://${window.location.hostname}/{{room}}`);
|
||||
let proto = location.protocol == 'http:' ? 'ws' : 'wss'
|
||||
let socket = new WebSocket(`${proto}://${window.location.hostname}:${window.location.port}/{{room}}`);
|
||||
|
||||
socket.onopen = function(e) {
|
||||
socket.send(JSON.stringify({
|
||||
@@ -42,35 +43,51 @@
|
||||
let msg = JSON.parse(event.data);
|
||||
if (msg.type === "buzz") {
|
||||
showMessage(msg.msg);
|
||||
} else if (msg.type === "reset") {
|
||||
reset();
|
||||
} else if (msg.type === "close") {
|
||||
roomClosed();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
document.getElementById('buzzer').addEventListener('touchstart', function() {
|
||||
beep();
|
||||
socket.send(JSON.stringify({
|
||||
type: "buzz"
|
||||
socket.send(JSON.stringify({
|
||||
type: "buzz"
|
||||
}));
|
||||
document.getElementById('buzzer').classList.add('active');
|
||||
});
|
||||
|
||||
|
||||
document.getElementById('buzzer').addEventListener('mousedown', function() {
|
||||
beep();
|
||||
socket.send(JSON.stringify({
|
||||
type: "buzz"
|
||||
socket.send(JSON.stringify({
|
||||
type: "buzz"
|
||||
}));
|
||||
document.getElementById('buzzer').classList.add('active');
|
||||
});
|
||||
|
||||
|
||||
function showMessage(msg) {
|
||||
window.navigator.vibrate(500);
|
||||
document.getElementById('buzzer').disabled = true;
|
||||
let mb = document.getElementById('messageBox');
|
||||
mb.innerHTML = msg;
|
||||
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() {
|
||||
window.navigator.vibrate(500);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user