Implement auto update

Every 5 seconds the system attempts to fetch the most recent vote state
of the poll and display it to the user.
This commit is contained in:
Wolvan 2022-01-06 20:44:29 +01:00
parent dca2cc3eeb
commit e9aaedb494
5 changed files with 42 additions and 3 deletions

View file

@ -9,6 +9,8 @@
"MAX_POLL_OPTIONS": "readonly", "MAX_POLL_OPTIONS": "readonly",
"MAX_CHARACTER_LENGTH": "readonly", "MAX_CHARACTER_LENGTH": "readonly",
"POLL_VOTE_DATA_STRING": "readonly", "POLL_VOTE_DATA_STRING": "readonly",
"POLL_BACKEND_URL": "readonly",
"POLL_ID": "readonly",
"textFit": "readonly", "textFit": "readonly",
"google": "readonly" "google": "readonly"

View file

@ -13,6 +13,8 @@
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js" defer="true"></script> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js" defer="true"></script>
<script type="text/javascript"> <script type="text/javascript">
const POLL_VOTE_DATA_STRING = `{{ POLL_OPTION_VOTES }}`; const POLL_VOTE_DATA_STRING = `{{ POLL_OPTION_VOTES }}`;
const POLL_BACKEND_URL = "{{ BACKEND_BASE_PATH }}";
const POLL_ID = "{{ POLL_ID }}";
</script> </script>
<script type="text/javascript" src="/static/js/result.js" defer="true"></script> <script type="text/javascript" src="/static/js/result.js" defer="true"></script>
</head> </head>

View file

@ -41,7 +41,7 @@ main .notepad .poll-option .progress .poll-bar {
vertical-align: top; vertical-align: top;
display: inline-block; display: inline-block;
height: 100%; height: 100%;
width: calc(100% - 60px); width: calc(100% - 50px);
} }
main .notepad .poll-option .progress .poll-bar .poll-bar-fill { main .notepad .poll-option .progress .poll-bar .poll-bar-fill {
height: calc(100% - 5px); height: calc(100% - 5px);
@ -51,6 +51,10 @@ main .notepad .poll-option .progress .poll-bar .poll-bar-fill {
} }
main .notepad .poll-option .progress .poll-bar-text { main .notepad .poll-option .progress .poll-bar-text {
width: 50px; width: 50px;
}
main .notepad .poll-option .progress .poll-bar-text::after {
content: "%";
margin-left: 5px;
padding-right: 10px; padding-right: 10px;
} }
@ -60,6 +64,7 @@ aside#chart {
right: -200px; right: -200px;
width: 200px; width: 200px;
height: 200px; height: 200px;
visibility: hidden;
} }
@media screen and (max-width: 750px) { @media screen and (max-width: 750px) {

View file

@ -1,5 +1,7 @@
"use strict"; "use strict";
const POLL_REFRESH_INTERVAL = 5000;
const textFitOptions = { const textFitOptions = {
multiLine: true multiLine: true
}; };
@ -42,6 +44,33 @@ function domLoaded() {
textFit(document.querySelector(".poll-title"), textFitOptions); textFit(document.querySelector(".poll-title"), textFitOptions);
document.querySelectorAll(".poll-option .poll-option-text").forEach(element => textFit(element, textFitOptions)); document.querySelectorAll(".poll-option .poll-option-text").forEach(element => textFit(element, textFitOptions));
}); });
let prevResult = null;
async function fetchNewestResults() {
try {
const response = await fetch(POLL_BACKEND_URL + "/_backend/poll-result/" + POLL_ID);
const json = await response.json();
if (json.error) throw new Error(json.error);
const votes = json.votes;
const totalVotes = Object.values(votes).reduce((a, b) => a + b, 0);
if (!prevResult || Object.entries(votes).some(([key, value]) => value !== prevResult[key])) {
drawChart(Object.entries(votes));
Object.entries(votes).forEach(([key, value]) => {
const el = document.querySelector("main .notepad .poll-option[option='" + key + "']");
if (!el) return;
el.querySelector(".poll-option-votes").innerText = value;
el.querySelector(".poll-bar-fill").style.width = (value / totalVotes * 100) + "%";
el.querySelector(".poll-bar-text").innerText = Math.round(value / totalVotes * 100);
});
prevResult = votes;
}
} catch (error) {
console.warn(error);
}
setTimeout(fetchNewestResults, POLL_REFRESH_INTERVAL);
}
setTimeout(fetchNewestResults, POLL_REFRESH_INTERVAL);
} }
if (document.readyState === "complete" || document.readyState === "loaded") domLoaded(); if (document.readyState === "complete" || document.readyState === "loaded") domLoaded();

View file

@ -106,14 +106,14 @@ export default function init(router: Router): void {
if (!poll || poll.error) return res.redirect("/"); if (!poll || poll.error) return res.redirect("/");
const totalVotes = Object.values(poll.votes).reduce((acc, cur) => acc + cur, 0); const totalVotes = Object.values(poll.votes).reduce((acc, cur) => acc + cur, 0);
const pollOptionsDivs = Object.entries(poll.votes).map(([option, votes]) => ` const pollOptionsDivs = Object.entries(poll.votes).map(([option, votes]) => `
<div class="poll-option"> <div class="poll-option" option="${ option }">
<div class="poll-option-info"> <div class="poll-option-info">
<div class="poll-option-text">${ option }</div><div class="poll-option-votes">${ votes }</div> <div class="poll-option-text">${ option }</div><div class="poll-option-votes">${ votes }</div>
</div> </div>
<div class="progress"> <div class="progress">
<div class="poll-bar"> <div class="poll-bar">
<div class="poll-bar-fill" style="width: ${ (votes / totalVotes) * 100 }%"></div> <div class="poll-bar-fill" style="width: ${ (votes / totalVotes) * 100 }%"></div>
</div><div class="poll-bar-text">${ Math.round((votes / totalVotes) * 100) }%</div> </div><div class="poll-bar-text">${ Math.round((votes / totalVotes) * 100) }</div>
</div> </div>
</div> </div>
`).join(""); `).join("");
@ -123,6 +123,7 @@ export default function init(router: Router): void {
"POLL_TITLE": poll.title, "POLL_TITLE": poll.title,
"POLL_OPTION_DIVS": pollOptionsDivs, "POLL_OPTION_DIVS": pollOptionsDivs,
"POLL_VOTES_TOTAL": totalVotes, "POLL_VOTES_TOTAL": totalVotes,
"BACKEND_BASE_PATH": (program.opts().backendBaseUrl || ""),
"POLL_OPTION_VOTES": JSON.stringify(Object.entries(poll.votes)) "POLL_OPTION_VOTES": JSON.stringify(Object.entries(poll.votes))
}); });
} catch (error) { } catch (error) {