From 0d143ccbcfdff06cfaca395f0a6a5ea6ef147a25 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Tue, 17 Aug 2021 20:00:37 +0100 Subject: [PATCH 01/15] Add quiz queue interface --- CHANGELOG.md | 5 +- background.js | 9 +- stylesheets/interface.css | 12 +++ xporcle.js | 215 ++++++++++++++++++++++++++++++++++---- 4 files changed, 220 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88ef3da..94108a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,10 @@ Changelog [Unreleased] ------------ -- +### Added +- Quiz Queue + - Button for hosts to add the quiz they are on to the queue. + - Draggable (for hosts) list of quizzes in the queue. [v2.2.0] - 2021-08-16 --------------------- diff --git a/background.js b/background.js index 1fdf80f..87e68dd 100644 --- a/background.js +++ b/background.js @@ -41,6 +41,7 @@ let pollData = {}; let voteData = {}; let voted = false; let saveName = null; +let queue = []; chrome.runtime.onConnect.addListener( (port) => @@ -70,7 +71,8 @@ chrome.runtime.onConnect.addListener( poll_data: pollData, vote_data: voteData, saveName: saveName, - voted: voted + voted: voted, + queue: queue } ); ws.send(JSON.stringify({type: "url_update", url: message["url"]})) @@ -319,6 +321,10 @@ function forwardMessage(event) { voteData = message["vote_data"]; } + else if (messageType === "queue_update") + { + queue = message["queue"]; + } if (messagePort !== null) { @@ -340,4 +346,5 @@ function reset() voteData = {}; saveName = null; voted = false; + queue = []; } diff --git a/stylesheets/interface.css b/stylesheets/interface.css index 46c9d3b..c4f7b02 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -366,3 +366,15 @@ text-align: center; } +li[draggable="true"] +{ + cursor: move; +} +.moving +{ + color: transparent; +} +li.moving::marker +{ + color: black; +} diff --git a/xporcle.js b/xporcle.js index ce8f820..3c54a2e 100644 --- a/xporcle.js +++ b/xporcle.js @@ -143,7 +143,8 @@ async function init() const pollData = Object.keys(statusResponse["poll_data"]).length !== 0 ? statusResponse["poll_data"] : undefined; const voteData = Object.keys(statusResponse["vote_data"]).length !== 0 ? statusResponse["vote_data"] : undefined; - onRoomConnect(statusResponse["scores"], pollData, voteData); + const queue = Object.keys(statusResponse["queue"]).length !== 0 ? statusResponse["queue"] : undefined; + onRoomConnect(statusResponse["scores"], pollData, voteData, queue); } else { @@ -353,9 +354,7 @@ function resetInterface(errorElement, lastUsername, lastCode) setQuizStartProvention(false); document.querySelectorAll("#startCountdown").forEach(elm => elm.remove()); - document.querySelector("#pollBox")?.remove(); - document.querySelector("#voteInfoBox")?.remove(); - document.querySelector("#ballotPopout")?.remove(); + document.querySelectorAll(".interfaceSection:not(#interfaceBox)").forEach(section => section.remove()); init().then( () => @@ -466,6 +465,16 @@ function processMessage(message) updateLeaderboardUrls(); updateContextMenuHandling(); addSaveRoomButton(); + if (onQuizPage) + { + interfaceBox.append(addQuizToQueueButton()); + } + if (message["queue"] !== null) + { + document.querySelector("#quizQueueBox")?.remove(); + addQuizQueueBox(message["queue"]); + } + if (message["poll_data"] !== null) { addCreatePollBox(message["poll_data"]); @@ -528,6 +537,9 @@ function processMessage(message) case "vote_update": updateVoteInfoBox(message["vote_data"]); break; + case "queue_update": + updateQuizQueue(message["queue"]); + break; } } @@ -639,7 +651,7 @@ async function joinRoom(event) username: username, code: roomCode, }; - + let queue = undefined; try { port.postMessage({type: "startConnection", initialMessage: message}); @@ -656,6 +668,7 @@ async function joinRoom(event) { hosts = message["hosts"]; port.postMessage({type: "url_update", url: window.location.pathname}) + queue = message["queue"] ?? undefined; // Set up message handing port.onMessage.addListener(processMessage); @@ -689,7 +702,7 @@ async function joinRoom(event) return; } - onRoomConnect(); + onRoomConnect(undefined, undefined, undefined, queue); } async function loadRoom(event, form) @@ -774,7 +787,7 @@ async function loadRoom(event, form) onRoomConnect(); } -function onRoomConnect(existingScores, existingPollData, currentVoteData) +function onRoomConnect(existingScores, existingPollData, currentVoteData, queue) { // Set up message handing if not done already. if (!port.onMessage.hasListener(processMessage)) @@ -833,6 +846,8 @@ function onRoomConnect(existingScores, existingPollData, currentVoteData) if (host) { addSaveRoomButton(); + + // Poll if (existingPollData) { addCreatePollBox(existingPollData); @@ -841,6 +856,12 @@ function onRoomConnect(existingScores, existingPollData, currentVoteData) { addCreatePollButton(); } + + // Add Quiz to Queue + if (onQuizPage) + { + interfaceBox.append(addQuizToQueueButton()); + } } // Add vote info for current poll @@ -853,6 +874,13 @@ function onRoomConnect(existingScores, existingPollData, currentVoteData) } } + // Quiz Queue + if (queue) + { + addQuizQueueBox(queue); + } + + // If on a quiz page, observe for the start of the quiz if (onQuizPage) { @@ -929,11 +957,19 @@ function addChangeQuizButton() interfaceBox.insertBefore(changeQuizButton, interfaceBox.querySelector(`#roomCodeHeader`).nextElementSibling); } -function addSuggestionQuizButton() +function currentQuizInfo() { + const url = window.location.href; const shortTitle = document.querySelector(`title`).textContent; const longTitle = document.querySelector("#gameMeta>h2").textContent; + return {url: url, short_title: shortTitle, long_title: longTitle}; +} + +function addSuggestionQuizButton() +{ + const quizInfo = currentQuizInfo(); + const suggestQuizButton = document.createElement("button"); suggestQuizButton.id = "suggestQuizButton"; suggestQuizButton.textContent = "Suggest Quiz to Hosts"; @@ -943,9 +979,9 @@ function addSuggestionQuizButton() port.postMessage( { type: "suggest_quiz", - url: window.location.href, - short_title: shortTitle, - long_title: longTitle + url: quizInfo.url, + short_title: quizInfo.short_title, + long_title: quizInfo.long_title } ); } @@ -989,12 +1025,7 @@ function addCreatePollBox(pollData) if (onQuizPage) { // Get info about quiz from page - const currentQuiz = - { - url: window.location.href, - short_title: document.querySelector(`title`).textContent, - long_title: document.querySelector("#gameMeta>h2").textContent - }; + const quizInfo = currentQuizInfo(); const addCurrentQuizToPollButton = document.createElement("button"); addCurrentQuizToPollButton.textContent = "Add Quiz to Poll"; @@ -1007,7 +1038,7 @@ function addCreatePollBox(pollData) pollData.entries.push(currentQuiz); const newEntryListItem = document.createElement("li"); - newEntryListItem.textContent = currentQuiz.short_title; + newEntryListItem.textContent = quizInfo.short_title; removeEntryButton = document.createElement("div"); removeEntryButton.textContent = "×"; @@ -1015,7 +1046,7 @@ function addCreatePollBox(pollData) (event) => { newEntryListItem.remove(); - pollData.entries = pollData.entries.filter(existingEntry => existingEntry.url !== currentQuiz.url); + pollData.entries = pollData.entries.filter(existingEntry => existingEntry.url !== quizInfo.url); port.postMessage({type:"poll_data_update", poll_data: pollData}); } ); @@ -2345,3 +2376,149 @@ function collapseToggle() return toggle; } + +function addQuizToQueueButton() +{ + const button = document.createElement("button"); + button.textContent = "Add Quiz to Queue"; + button.addEventListener("click", + (event) => + { + const quizInfo = currentQuizInfo(); + const existingQueuedQuizes = Array.from(document.querySelectorAll("#quizQueueBox ol li")); + + if (!existingQueuedQuizes.find(queuedQuiz => queuedQuiz.short_title === quizInfo.short_title)) + { + port.postMessage({type: "add_to_queue", quiz: quizInfo}); + } + } + ); + return button; +} + +function addQuizQueueBox(queue = []) +{ + const quizQueueBox = document.createElement("div"); + quizQueueBox.classList.add("interfaceSection"); + quizQueueBox.id = "quizQueueBox"; + + quizQueueBox.append(closeButton(quizQueueBox)); + + const header = document.createElement("h2"); + header.textContent = "Quiz Queue"; + quizQueueBox.append(header); + + quizQueueBox.append(collapseToggle()); + + const queueList = document.createElement("ol"); + quizQueueBox.append(queueList); + + sectionContainer.append(quizQueueBox); + + updateQuizQueue(queue); +} + +function updateQuizQueue(queue) +{ + let quizQueueBox = document.querySelector("#quizQueueBox"); + if (quizQueueBox === null) + { + return addQuizQueueBox(queue); + } + + const queueList = quizQueueBox.querySelector("ol"); + Array.from(queueList.querySelectorAll("li")) + .filter(li => queue.some(queuedQuiz => queuedQuiz.short_title === li.textContent)) + .forEach(li => li.remove()); + + queue.forEach( + (queuedQuiz) => + { + let li = Array.from(queueList.querySelectorAll("li")).find(li => li.textContent === queuedQuiz.short_title); + if (li === undefined) + { + li = document.createElement("li"); + li.textContent = queuedQuiz.short_title; + if (host) + { + li.setAttribute("draggable", "true"); + li.addEventListener("dragstart", + (event) => + { + const quizInfoString = JSON.stringify(queuedQuiz); + event.dataTransfer.setData("text/quizinfo", quizInfoString); + const dragImage = document.createElement("div"); + dragImage.id = "tempDragImage"; + dragImage.style = + ` + position: absolute; + top: -2000vh; + left: -2000vw; + width: ${event.target.clientWidth}px; + `; + dragImage.textContent = event.target.textContent; + document.body.append(dragImage); + + event.dataTransfer.setDragImage(dragImage, 30, dragImage.clientHeight/2); + event.dataTransfer.effectAllowed = "move"; + event.target.classList.add("moving"); + } + ); + li.addEventListener("dragend", + (event) => + { + event.target.classList.remove("moving"); + event.target.style = ""; + document.querySelector("#tempDragImage")?.remove(); + } + ); + li.addEventListener("dragenter", + (event) => + { + if (event.dataTransfer.types.includes("text/quizinfo")) + { + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + const beingDragged = queueList.querySelector(".moving"); + + const indexOffset = + Array.from(queueList.childNodes).indexOf(event.target) + - Array.from(queueList.childNodes).indexOf(beingDragged); + if (indexOffset > 0) + { + // Element being dragged is above the target. + // So we want to move the dragged element to just below/after it. + queueList.insertBefore(beingDragged, event.target.nextElementSibling); + } + else + { + // Element being dragged is below the target + // So we want to move the dragged element to just above/before it. + queueList.insertBefore(beingDragged, event.target); + } + } + } + ); + li.addEventListener("dragover", + (event) => + { + if (event.dataTransfer.types.includes("text/quizinfo")) + { + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + } + } + ); + li.addEventListener("drop", + (event) => + { + const newIndex = Array.from(queueList.children).indexOf(document.querySelector(".moving")); + port.postMessage({type: "reorder_queue", quiz: queuedQuiz, index: newIndex}); + } + ); + } + } + queueList.append(li); + } + ); +} From 93978e0b6fafb75d85a457c8d6dbee2136eab5e1 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Wed, 18 Aug 2021 14:43:34 +0100 Subject: [PATCH 02/15] Add remove button to quiz queue for hosts --- CHANGELOG.md | 1 + stylesheets/interface.css | 34 +++++++++++++++++++++++++++++++--- xporcle.js | 14 ++++++++++++-- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94108a8..21f72f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Changelog - Quiz Queue - Button for hosts to add the quiz they are on to the queue. - Draggable (for hosts) list of quizzes in the queue. + - Remove button for hosts to remove quizzes from the queue. [v2.2.0] - 2021-08-16 --------------------- diff --git a/stylesheets/interface.css b/stylesheets/interface.css index c4f7b02..c6861aa 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -366,15 +366,43 @@ text-align: center; } -li[draggable="true"] + +#quizQueueBox li[draggable="true"] { cursor: move; + position: relative; + padding-right: 2rem; } -.moving +#quizQueueBox li.moving { color: transparent; } -li.moving::marker +#quizQueueBox li.moving::marker { color: black; } + #quizQueueBox li .closeButton + { + display: none; + visibility: hidden; + top: 0; + } + #quizQueueBox li:hover .closeButton + { + display: unset; + visibility: visible; + } + #quizQueueBox li:active .closeButton, + #quizQueueBox li.moving .closeButton, + #quizQueueBox li.moving:hover .closeButton, + #quizQueueBox li.moving ~ li .closeButton + { + display: none; + visibility: hidden; + } + #quizQueueBox li .closeButton:hover + { + display: unset; + visibility: visible; + } + diff --git a/xporcle.js b/xporcle.js index 3c54a2e..3d4dee4 100644 --- a/xporcle.js +++ b/xporcle.js @@ -2428,7 +2428,7 @@ function updateQuizQueue(queue) const queueList = quizQueueBox.querySelector("ol"); Array.from(queueList.querySelectorAll("li")) - .filter(li => queue.some(queuedQuiz => queuedQuiz.short_title === li.textContent)) + .filter(li => !queue.some(queuedQuiz => queuedQuiz.short_title === li.textContent)) .forEach(li => li.remove()); queue.forEach( @@ -2441,6 +2441,16 @@ function updateQuizQueue(queue) li.textContent = queuedQuiz.short_title; if (host) { + // Add Remove Button + li.append( + closeButton(li, + (event) => + { + port.postMessage({type: "remove_from_queue", quiz: queuedQuiz}); + } + ) + ); + // Make list draggable to reorder queue. li.setAttribute("draggable", "true"); li.addEventListener("dragstart", (event) => @@ -2454,7 +2464,7 @@ function updateQuizQueue(queue) position: absolute; top: -2000vh; left: -2000vw; - width: ${event.target.clientWidth}px; + width: calc(${event.target.clientWidth}px - ${window.getComputedStyle(event.target).paddingRight}); `; dragImage.textContent = event.target.textContent; document.body.append(dragImage); From adc25a4907ff048e1996b2ca2ee32efc2d6bc25d Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Wed, 18 Aug 2021 15:57:34 +0100 Subject: [PATCH 03/15] Make all quiz queue items same size for better dragging behaviour --- stylesheets/interface.css | 69 +++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/stylesheets/interface.css b/stylesheets/interface.css index c6861aa..52df1a4 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -349,8 +349,8 @@ } .toggle:checked ~ * { - display: none; - visibility: hidden; + display: none !important; + visibility: hidden !important; } .closeButton::after, @@ -367,42 +367,47 @@ } -#quizQueueBox li[draggable="true"] +#quizQueueBox ol { - cursor: move; - position: relative; - padding-right: 2rem; -} -#quizQueueBox li.moving -{ - color: transparent; -} -#quizQueueBox li.moving::marker -{ - color: black; + display: grid; + grid-auto-rows: 1fr; } - #quizQueueBox li .closeButton - { - display: none; - visibility: hidden; - top: 0; - } - #quizQueueBox li:hover .closeButton + #quizQueueBox li[draggable="true"] { - display: unset; - visibility: visible; + cursor: move; + position: relative; + padding-right: 2rem; } - #quizQueueBox li:active .closeButton, - #quizQueueBox li.moving .closeButton, - #quizQueueBox li.moving:hover .closeButton, - #quizQueueBox li.moving ~ li .closeButton + #quizQueueBox li.moving { - display: none; - visibility: hidden; + color: transparent; } - #quizQueueBox li .closeButton:hover + #quizQueueBox li.moving::marker { - display: unset; - visibility: visible; + color: black; } + #quizQueueBox li .closeButton + { + display: none; + visibility: hidden; + top: 0; + } + #quizQueueBox li:hover .closeButton + { + display: unset; + visibility: visible; + } + #quizQueueBox li:active .closeButton, + #quizQueueBox li.moving .closeButton, + #quizQueueBox li.moving:hover .closeButton, + #quizQueueBox li.moving ~ li .closeButton + { + display: none; + visibility: hidden; + } + #quizQueueBox li .closeButton:hover + { + display: unset; + visibility: visible; + } From bd8081aa7d88eb507e4d1b7beb62626bfc8dd836 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Wed, 18 Aug 2021 18:22:13 +0100 Subject: [PATCH 04/15] Make quiz queue box remove itself if being updated with an empty queue --- stylesheets/interface.css | 1 + xporcle.js | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/stylesheets/interface.css b/stylesheets/interface.css index 52df1a4..b69435f 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -371,6 +371,7 @@ { display: grid; grid-auto-rows: 1fr; + margin: 0; } #quizQueueBox li[draggable="true"] { diff --git a/xporcle.js b/xporcle.js index 3d4dee4..b729ed7 100644 --- a/xporcle.js +++ b/xporcle.js @@ -2421,11 +2421,16 @@ function addQuizQueueBox(queue = []) function updateQuizQueue(queue) { let quizQueueBox = document.querySelector("#quizQueueBox"); + if (queue.length === 0) + { + return quizQueueBox?.remove(); + } if (quizQueueBox === null) { return addQuizQueueBox(queue); } + const queueList = quizQueueBox.querySelector("ol"); Array.from(queueList.querySelectorAll("li")) .filter(li => !queue.some(queuedQuiz => queuedQuiz.short_title === li.textContent)) From 8df0f6b6bac9ed745ae6b2964d5efc2a0968ca6c Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Wed, 18 Aug 2021 19:05:35 +0100 Subject: [PATCH 05/15] Add go to quiz button to queued quizzes for hosts --- CHANGELOG.md | 1 + stylesheets/interface.css | 32 ++++++++++++++++++++++++++------ xporcle.js | 7 ++++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21f72f9..9ecc27c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Changelog - Button for hosts to add the quiz they are on to the queue. - Draggable (for hosts) list of quizzes in the queue. - Remove button for hosts to remove quizzes from the queue. + - Go to quiz button for hosts to visit the page of quizzes in the queue. [v2.2.0] - 2021-08-16 --------------------- diff --git a/stylesheets/interface.css b/stylesheets/interface.css index b69435f..f8427f7 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -366,6 +366,20 @@ text-align: center; } +.goTo +{ + appearance: none; + border: none; + padding: 0; + position: absolute; + top: 0; + right: 2rem; + width: 1rem; + height: 1rem; + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ij48cGF0aCBkPSJNOSAyTDkgMyAxMi4zIDMgNiA5LjMgNi43IDEwIDEzIDMuNyAxMyA3IDE0IDcgMTQgMlpNNCA0QzIuOSA0IDIgNC45IDIgNkwyIDEyQzIgMTMuMSAyLjkgMTQgNCAxNEwxMCAxNEMxMS4xIDE0IDEyIDEzLjEgMTIgMTJMMTIgNyAxMSA4IDExIDEyQzExIDEyLjYgMTAuNiAxMyAxMCAxM0w0IDEzQzMuNCAxMyAzIDEyLjYgMyAxMkwzIDZDMyA1LjQgMy40IDUgNCA1TDggNSA5IDRaIi8+PC9zdmc+) no-repeat; + background-size: contain; + cursor: pointer; +} #quizQueueBox ol { @@ -377,7 +391,7 @@ { cursor: move; position: relative; - padding-right: 2rem; + padding-right: 3rem; } #quizQueueBox li.moving { @@ -387,13 +401,15 @@ { color: black; } - #quizQueueBox li .closeButton + #quizQueueBox li .closeButton, + #quizQueueBox li .goTo { display: none; visibility: hidden; top: 0; } - #quizQueueBox li:hover .closeButton + #quizQueueBox li:hover .closeButton, + #quizQueueBox li:hover .goTo { display: unset; visibility: visible; @@ -401,14 +417,18 @@ #quizQueueBox li:active .closeButton, #quizQueueBox li.moving .closeButton, #quizQueueBox li.moving:hover .closeButton, - #quizQueueBox li.moving ~ li .closeButton + #quizQueueBox li.moving ~ li .closeButton, + #quizQueueBox li:active .goTo, + #quizQueueBox li.moving .goTo, + #quizQueueBox li.moving:hover .goTo, + #quizQueueBox li.moving ~ li .goTo { display: none; visibility: hidden; } - #quizQueueBox li .closeButton:hover + #quizQueueBox li .closeButton:hover, + #quizQueueBox li .goTo:hover { display: unset; visibility: visible; } - diff --git a/xporcle.js b/xporcle.js index b729ed7..177b677 100644 --- a/xporcle.js +++ b/xporcle.js @@ -2446,6 +2446,11 @@ function updateQuizQueue(queue) li.textContent = queuedQuiz.short_title; if (host) { + // Add go to button + const goToButton = document.createElement("button"); + goToButton.classList.add("goTo"); + goToButton.addEventListener("click", (event) => {window.location = queuedQuiz.url}); + li.append(goToButton); // Add Remove Button li.append( closeButton(li, @@ -2471,7 +2476,7 @@ function updateQuizQueue(queue) left: -2000vw; width: calc(${event.target.clientWidth}px - ${window.getComputedStyle(event.target).paddingRight}); `; - dragImage.textContent = event.target.textContent; + dragImage.textContent = queuedQuiz.short_title; document.body.append(dragImage); event.dataTransfer.setDragImage(dragImage, 30, dragImage.clientHeight/2); From c8171415905b552cb5acc2a7311c5429c9ed0065 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Wed, 18 Aug 2021 19:18:04 +0100 Subject: [PATCH 06/15] Store queue in background on join room success --- background.js | 1 + 1 file changed, 1 insertion(+) diff --git a/background.js b/background.js index 87e68dd..d6e08e9 100644 --- a/background.js +++ b/background.js @@ -214,6 +214,7 @@ function forwardMessage(event) if (message["success"]) { hosts = message["hosts"]; + queue = message["queue"]; } else { From 931ccbc170813541c4662781f1d34a1c0c165d46 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Wed, 18 Aug 2021 19:30:49 +0100 Subject: [PATCH 07/15] Store queue in background on host promotion --- background.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/background.js b/background.js index d6e08e9..0990640 100644 --- a/background.js +++ b/background.js @@ -244,7 +244,8 @@ function forwardMessage(event) { host = true; urls = message["urls"]; - poll_data = message["poll_data"] ?? {} + poll_data = message["poll_data"] ?? {}; + queue = message["queue"]; } else if ( messageType === "users_update" From 42daf6325b39136f33065dec6becf1a231004892 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Wed, 18 Aug 2021 20:53:33 +0100 Subject: [PATCH 08/15] Add auto change to next quiz interface for hosts --- CHANGELOG.md | 2 ++ background.js | 8 ++++- stylesheets/interface.css | 41 +++++++++++++++--------- xporcle.js | 66 +++++++++++++++++++++++++++++++++------ 4 files changed, 92 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ecc27c..624be33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ Changelog - Draggable (for hosts) list of quizzes in the queue. - Remove button for hosts to remove quizzes from the queue. - Go to quiz button for hosts to visit the page of quizzes in the queue. + - Controls for hosts to toggle and customise interval for auto changing to + next quiz. [v2.2.0] - 2021-08-16 --------------------- diff --git a/background.js b/background.js index 0990640..3e52288 100644 --- a/background.js +++ b/background.js @@ -42,6 +42,7 @@ let voteData = {}; let voted = false; let saveName = null; let queue = []; +let queueInterval; chrome.runtime.onConnect.addListener( (port) => @@ -72,7 +73,8 @@ chrome.runtime.onConnect.addListener( vote_data: voteData, saveName: saveName, voted: voted, - queue: queue + queue: queue, + queue_interval: queueInterval } ); ws.send(JSON.stringify({type: "url_update", url: message["url"]})) @@ -215,6 +217,7 @@ function forwardMessage(event) { hosts = message["hosts"]; queue = message["queue"]; + queueInterval = message["queue_interval"]; } else { @@ -246,6 +249,7 @@ function forwardMessage(event) urls = message["urls"]; poll_data = message["poll_data"] ?? {}; queue = message["queue"]; + queueInterval = message["queue_interval"]; } else if ( messageType === "users_update" @@ -326,6 +330,7 @@ function forwardMessage(event) else if (messageType === "queue_update") { queue = message["queue"]; + queueInterval = message["queue_interval"]; } if (messagePort !== null) @@ -349,4 +354,5 @@ function reset() saveName = null; voted = false; queue = []; + queueInterval = null; } diff --git a/stylesheets/interface.css b/stylesheets/interface.css index f8427f7..1842fe8 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -15,20 +15,6 @@ max-width: 400px; overflow: auto; } - #interfaceContainer form - { - display: flex; - flex-direction: column; - } - #interfaceContainer form input, - #interfaceContainer form button, - #interfaceContainer form select - { - width: min(100%, 10em); - box-sizing: border-box; - margin: 0 auto; - - } .interfaceSection { @@ -52,6 +38,20 @@ margin: 0; } +#interfaceBox form +{ + display: flex; + flex-direction: column; +} + #interfaceBox form input, + #interfaceBox form button, + #interfaceBox form select + { + width: min(100%, 10em); + box-sizing: border-box; + margin: 0 auto; + + } #leaderboard, #liveScores @@ -432,3 +432,16 @@ display: unset; visibility: visible; } +#quizQueueBox form +{ + display: block; +} + #quizQueueBox form input + { + display: inline; + vertical-align: baseline; + } + #quizQueueBox form input[type="number"] + { + width: 3.5em; + } diff --git a/xporcle.js b/xporcle.js index 177b677..8aa40d3 100644 --- a/xporcle.js +++ b/xporcle.js @@ -144,7 +144,8 @@ async function init() const pollData = Object.keys(statusResponse["poll_data"]).length !== 0 ? statusResponse["poll_data"] : undefined; const voteData = Object.keys(statusResponse["vote_data"]).length !== 0 ? statusResponse["vote_data"] : undefined; const queue = Object.keys(statusResponse["queue"]).length !== 0 ? statusResponse["queue"] : undefined; - onRoomConnect(statusResponse["scores"], pollData, voteData, queue); + const queueInterval = statusResponse["queue_interval"]; + onRoomConnect(statusResponse["scores"], pollData, voteData, queue, queueInterval); } else { @@ -472,7 +473,7 @@ function processMessage(message) if (message["queue"] !== null) { document.querySelector("#quizQueueBox")?.remove(); - addQuizQueueBox(message["queue"]); + addQuizQueueBox(message["queue"], message["queue_interval"]); } if (message["poll_data"] !== null) @@ -538,7 +539,7 @@ function processMessage(message) updateVoteInfoBox(message["vote_data"]); break; case "queue_update": - updateQuizQueue(message["queue"]); + updateQuizQueue(message["queue"], message["queue_interval"]); break; } @@ -787,7 +788,7 @@ async function loadRoom(event, form) onRoomConnect(); } -function onRoomConnect(existingScores, existingPollData, currentVoteData, queue) +function onRoomConnect(existingScores, existingPollData, currentVoteData, queue, queueInterval) { // Set up message handing if not done already. if (!port.onMessage.hasListener(processMessage)) @@ -877,7 +878,7 @@ function onRoomConnect(existingScores, existingPollData, currentVoteData, queue) // Quiz Queue if (queue) { - addQuizQueueBox(queue); + addQuizQueueBox(queue, queueInterval); } @@ -1093,7 +1094,7 @@ function addCreatePollBox(pollData) pollDurationInput.addEventListener("change", (event) => { - pollData.duration = Math.max(10, Math.min(pollDurationInput.value, 60)); + pollData.duration = Math.max(10, Math.min(Number(pollDurationInput.value), 60)); // Send updated data port.postMessage({type:"poll_data_update", poll_data: pollData}); } @@ -2396,7 +2397,7 @@ function addQuizToQueueButton() return button; } -function addQuizQueueBox(queue = []) +function addQuizQueueBox(queue = [], queueInterval) { const quizQueueBox = document.createElement("div"); quizQueueBox.classList.add("interfaceSection"); @@ -2413,12 +2414,47 @@ function addQuizQueueBox(queue = []) const queueList = document.createElement("ol"); quizQueueBox.append(queueList); + if (host) + { + const queueAutoChangeForm = document.createElement("form"); + + const autoChangeToggleInput = document.createElement("input"); + autoChangeToggleInput.type = "checkbox"; + autoChangeToggleInput.checked = !!queueInterval; + const intervalLabel = document.createElement("label"); + intervalLabel.textContent = "auto change quiz after "; + const intervalInput = document.createElement("input"); + intervalInput.type = "number"; + intervalInput.min = 10; + intervalInput.max = 60*5; + intervalInput.value = queueInterval ?? "60"; + intervalInput.disabled = !autoChangeToggleInput.checked; + [autoChangeToggleInput, intervalInput].forEach( + (input) => + { + input.addEventListener("change", + (event) => + { + intervalInput.disabled = !autoChangeToggleInput.checked; + intervalInput.value = Math.max(10, Math.min(Number(intervalInput.value), 60*5)); + const newQueueInterval = autoChangeToggleInput.checked ? Number(intervalInput.value) : null; + port.postMessage({type: "change_queue_interval", queue_interval: newQueueInterval}); + } + ); + } + ); + queueAutoChangeForm.append( + autoChangeToggleInput, intervalLabel, intervalInput, document.createTextNode("s") + ); + quizQueueBox.append(queueAutoChangeForm); + } + sectionContainer.append(quizQueueBox); - updateQuizQueue(queue); + updateQuizQueue(queue, queueInterval); } -function updateQuizQueue(queue) +function updateQuizQueue(queue, queueInterval) { let quizQueueBox = document.querySelector("#quizQueueBox"); if (queue.length === 0) @@ -2427,7 +2463,7 @@ function updateQuizQueue(queue) } if (quizQueueBox === null) { - return addQuizQueueBox(queue); + return addQuizQueueBox(queue, queueInterval); } @@ -2541,4 +2577,14 @@ function updateQuizQueue(queue) queueList.append(li); } ); + + if (host) + { + // Update Queue Interval + const autoChangeToggleInput = quizQueueBox.querySelector(`form input[type="checkbox"]`); + const intervalInput = quizQueueBox.querySelector(`form input[type="number"]`); + autoChangeToggleInput.checked = !!queueInterval; + intervalInput.disabled = !queueInterval; + intervalInput.value = queueInterval ?? intervalInput.value; + } } From a401d8c3cf5deeace7d5d28d870cdd5bc9a7d851 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Thu, 19 Aug 2021 14:03:58 +0100 Subject: [PATCH 09/15] Add handling of host demotion to remove host only queue features --- xporcle.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/xporcle.js b/xporcle.js index 8aa40d3..20e171f 100644 --- a/xporcle.js +++ b/xporcle.js @@ -442,7 +442,16 @@ function processMessage(message) updateLeaderboardUrls(); suggestions = []; - document.querySelectorAll(`#changeQuizButton, #suggestionsHeader, #suggestionsList, #saveButton, #createPollButton`).forEach(element => element.remove()); + document.querySelectorAll( + ` + #changeQuizButton, + #suggestionsHeader, #suggestionsList, + #saveButton, + #createPollButton, #pollBox, + #addQuizToQueueButton, #quizQueueBox form, #quizQueueBox ol button + ` + ).forEach(element => element.remove()); + document.querySelectorAll(`#quizQueueBox ol li`).forEach(li => li.removeAttribute("draggable")); // Add non host features if (onQuizPage) From 5b27d43c590f21e738faada9a7590afaf9669d4d Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Thu, 19 Aug 2021 16:29:31 +0100 Subject: [PATCH 10/15] Add change quiz countdown timer and cancel button --- CHANGELOG.md | 3 +++ stylesheets/interface.css | 7 +++++++ xporcle.js | 42 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 624be33..7451ac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ Changelog - Go to quiz button for hosts to visit the page of quizzes in the queue. - Controls for hosts to toggle and customise interval for auto changing to next quiz. + - Countdown under leaderboard displaying time left until change to next + quiz. + - Button to cancel the countdown for hosts. [v2.2.0] - 2021-08-16 --------------------- diff --git a/stylesheets/interface.css b/stylesheets/interface.css index 1842fe8..1e436f9 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -52,6 +52,13 @@ margin: 0 auto; } +#interfaceBox #changeQuizCountdown +{ + display: flex; + flex-direction: row; + justify-content: space-evenly; + flex-wrap: wrap; +} #leaderboard, #liveScores diff --git a/xporcle.js b/xporcle.js index 20e171f..b7383ee 100644 --- a/xporcle.js +++ b/xporcle.js @@ -550,8 +550,48 @@ function processMessage(message) case "queue_update": updateQuizQueue(message["queue"], message["queue_interval"]); break; + case "start_change_quiz_countdown": + { + const endTime = (new Date()).getTime() + message["countdown_length"]*1000; + const timeLeft = () => (Math.max(0, endTime - (new Date()).getTime())/1000).toFixed(1); + const countdown = document.createElement("div"); + countdown.id = "changeQuizCountdown"; + countdown.append(document.createElement("span")); + countdown.firstChild.textContent = `Changing to next quiz in ${timeLeft()}s`; + setInterval( + () => + { + if (countdown.firstChild.textContent !== "Change quiz countdown cancelled") + { + countdown.firstChild.textContent = `Changing to next quiz in ${timeLeft()}s`; + } + } + , 100 + ); + if (host) + { + const cancelButton = document.createElement("button"); + cancelButton.textContent = "Cancel Countdown"; + cancelButton.addEventListener("click", + (event) => + { + port.postMessage({type: "change_queue_interval", queue_interval: null}); + } + ); + countdown.append(cancelButton); + } + document.querySelector("#interfaceBox")?.insertBefore(countdown, document.querySelector("#leaderboard").nextElementSibling); + break; + } + case "cancel_change_quiz_countdown": + { + const countdownMessage = document.querySelector("#changeQuizCountdown span"); + countdownMessage.textContent = "Change quiz countdown cancelled"; + document.querySelector("#changeQuizCountdown button")?.remove(); + setTimeout(() => countdownMessage.parentNode.remove(), 2000); + break; + } } - } function updateHostsInLeaderboard() From 9032ba2b707f96ebe5961040b0dcb968e7838fd5 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Thu, 19 Aug 2021 16:52:03 +0100 Subject: [PATCH 11/15] Add collapse toggle to main interface box --- stylesheets/interface.css | 10 +++++++++- xporcle.js | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/stylesheets/interface.css b/stylesheets/interface.css index 1e436f9..6279d3d 100644 --- a/stylesheets/interface.css +++ b/stylesheets/interface.css @@ -38,6 +38,10 @@ margin: 0; } +#interfaceBox .toggle +{ + top: 0.5rem; +} #interfaceBox form { display: flex; @@ -335,7 +339,7 @@ margin: 0; border: 0; position: absolute; - right: 2rem; + right: 1rem; top: 1rem; color: grey; cursor: pointer; @@ -345,6 +349,10 @@ transform: rotate(0deg); transition: transform 0.5s; } +.closeButton ~ .toggle +{ + right: 2rem; +} .toggle::after { content: "\02228"; diff --git a/xporcle.js b/xporcle.js index b7383ee..ef1d39d 100644 --- a/xporcle.js +++ b/xporcle.js @@ -858,6 +858,8 @@ function onRoomConnect(existingScores, existingPollData, currentVoteData, queue, roomCodeHeader.lastChild.textContent = roomCode; interfaceBox.insertBefore(roomCodeHeader, interfaceBox.firstElementChild); + interfaceBox.insertBefore(collapseToggle(),roomCodeHeader.nextElementSibling); + // If the user is a host and is on a quiz, // add a button to send the quiz to the rest of the room if (host && onQuizPage) @@ -1004,7 +1006,7 @@ function addChangeQuizButton() ); // The button goes just after the room code header - interfaceBox.insertBefore(changeQuizButton, interfaceBox.querySelector(`#roomCodeHeader`).nextElementSibling); + interfaceBox.insertBefore(changeQuizButton, interfaceBox.querySelector(`.toggle`).nextElementSibling); } function currentQuizInfo() From d3343d3be24bfc9383fb5b21e3b545eb07f216f3 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Thu, 19 Aug 2021 16:54:11 +0100 Subject: [PATCH 12/15] Increment version number to 2.3.0 --- CHANGELOG.md | 7 ++++++- manifest.json | 2 +- options.html | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7451ac7..d54f5f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Changelog [Unreleased] ------------ +- + +[v2.3.0] - 2021-08-16 +--------------------- +[GitHub Release Page](https://github.com/ToranSharma/Xporcle-Extension/releases/tag/v2.3.0) ### Added - Quiz Queue - Button for hosts to add the quiz they are on to the queue. @@ -25,7 +30,6 @@ Changelog ### Changed - Xporcle interface now scrollable if longer than available space. - [v2.1.0] - 2021-08-14 --------------------- [GitHub Release Page](https://github.com/ToranSharma/Xporcle-Extension/releases/tag/v2.1.0) @@ -159,6 +163,7 @@ Changelog - Options page popup placeholder. [Unreleased]: https://github.com/ToranSharma/Xporcle-Extension/compare/master...develop +[v2.3.0]: https://github.com/ToranSharma/Xporcle-Extension/compare/v2.2.0...v2.3.0 [v2.2.0]: https://github.com/ToranSharma/Xporcle-Extension/compare/v2.1.0...v2.2.0 [v2.1.0]: https://github.com/ToranSharma/Xporcle-Extension/compare/v2.0.0...v2.1.0 [v2.0.0]: https://github.com/ToranSharma/Xporcle-Extension/compare/v1.2.1...v2.0.0 diff --git a/manifest.json b/manifest.json index 2ab9c3c..e530ad7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name" : "Xporcle", "description" : "Adds real-time multiplayer abilities to Sporcle.com", - "version" : "2.2.0", + "version" : "2.3.0", "manifest_version" : 2, "icons" : { diff --git a/options.html b/options.html index 7ca2524..94f66d5 100644 --- a/options.html +++ b/options.html @@ -6,7 +6,7 @@ -

Xporcle v2.2.0

+

Xporcle v2.3.0

Enable or Disable Features Here

  • From 4a82c205c25a780a353b51757b78d8d0a664e8b3 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Thu, 19 Aug 2021 16:59:20 +0100 Subject: [PATCH 13/15] Fix blur room code option --- xporcle.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xporcle.js b/xporcle.js index ef1d39d..12871ad 100644 --- a/xporcle.js +++ b/xporcle.js @@ -262,11 +262,11 @@ function applyOptions() // Blur Room Code if (options.blurRoomCode) { - document.body.classList.add(".blurRoomCode"); + document.body.classList.add("blurRoomCode"); } else { - document.body.classList.remove(".blurRoomCode"); + document.body.classList.remove("blurRoomCode"); } } From a96c32d2db9d35e1a4893237d1fd8a5a626b8137 Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Thu, 19 Aug 2021 17:51:27 +0100 Subject: [PATCH 14/15] Add options for default poll duration and auto change interval for queued quizzes --- options.html | 8 ++++++++ options.js | 5 +++++ stylesheets/options.css | 31 +++++++++++++++++++++++-------- xporcle.js | 8 +++++--- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/options.html b/options.html index 94f66d5..4a6ea48 100644 --- a/options.html +++ b/options.html @@ -24,6 +24,14 @@

    Enable or Disable Features Here

  • +
  • + + +
  • +
  • + + +
diff --git a/options.js b/options.js index c1809cc..814a207 100644 --- a/options.js +++ b/options.js @@ -101,6 +101,7 @@ function getOptions(firstLoad=false) document.getElementById(option).checked = options[option]; break; case "string": + case "number": document.getElementById(option).value = options[option]; break; } @@ -120,6 +121,10 @@ function saveOptions() { options[element.id] = element.checked; } + else if (element.type === "number") + { + options[element.id] = Number(element.value); + } else { options[element.id] = element.value; diff --git a/stylesheets/options.css b/stylesheets/options.css index 49d0cb2..832fe84 100644 --- a/stylesheets/options.css +++ b/stylesheets/options.css @@ -78,17 +78,21 @@ ul { color: grey; } -#optionTree .labelFirst +#optionTree li.labelFirst +{ + grid-template-columns: max-content 4em; +} +#optionTree ul li.labelFirst { grid-template-columns: 3em max-content 3em; } - #optionTree .labelFirst > label - { - padding-left: 0; - padding-right: 0.5em; - z-index: 3; - background-color: white; - } +#optionTree .labelFirst > label +{ + padding-left: 0; + padding-right: 0.5em; + z-index: 3; + background-color: white; +} #optionTree .labelFirst.containsTextInput { @@ -119,6 +123,17 @@ ul line-height: 1.5em; } +#optionTree .containsTime::after +{ + content:"s"; + height: 1.5em; + line-height: 1.5em; +} +#optionTree > li.labelFirst.containsTime +{ + grid-template-columns: max-content 4em auto; +} + button { width: 6em; diff --git a/xporcle.js b/xporcle.js index 12871ad..865bc98 100644 --- a/xporcle.js +++ b/xporcle.js @@ -205,7 +205,9 @@ function retrieveOptions() { useDefaultUsername: false, defaultUsername: "", - blurRoomCode: false + blurRoomCode: false, + defaultPollDuration: 30, + defaultQuizQueueInterval: 60 }; if (Object.entries(data).length === 0) @@ -1050,7 +1052,7 @@ function addCreatePollButton() createPollButton.addEventListener("click", (event) => { - const pollData = {duration: 30, entries: []}; + const pollData = {duration: options.defaultPollDuration, entries: []}; addCreatePollBox(pollData); port.postMessage({type: "poll_create", poll_data: pollData}); createPollButton.remove(); @@ -2478,7 +2480,7 @@ function addQuizQueueBox(queue = [], queueInterval) intervalInput.type = "number"; intervalInput.min = 10; intervalInput.max = 60*5; - intervalInput.value = queueInterval ?? "60"; + intervalInput.value = queueInterval ?? options.defaultQuizQueueInterval; intervalInput.disabled = !autoChangeToggleInput.checked; [autoChangeToggleInput, intervalInput].forEach( (input) => From 95c6f20e552e41b96eb6036605b6476dc3e2426c Mon Sep 17 00:00:00 2001 From: Toran Sharma Date: Thu, 19 Aug 2021 17:54:48 +0100 Subject: [PATCH 15/15] Update changelog with addition of new options --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d54f5f9..2e5bef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,11 @@ Changelog - Go to quiz button for hosts to visit the page of quizzes in the queue. - Controls for hosts to toggle and customise interval for auto changing to next quiz. + - Option to set default interval for auto chaning. - Countdown under leaderboard displaying time left until change to next quiz. - Button to cancel the countdown for hosts. +- Option to set default duration for next quiz polls. [v2.2.0] - 2021-08-16 ---------------------