const MAX_COUNTDOWN = 60; var countdown = MAX_COUNTDOWN; var now = Date.now(); var eventsOnNow = []; var eventsOnNext = []; grist.ready({ requiredAccess: 'read table' }); grist.onRecords(function (records) { eventsOnNow = []; eventsOnNext = []; now = Date.now() + (60 + 50) * 60 * 1000; var i = 0; for (const r of records) { let e = new Event( i++, r["title"], r["venue"], r["video_privacy"], r["Record_override"], r["Comments"], Date.parse(r["start_date"]), Date.parse(r["end_date"]) ); if (e.hasStarted() && !e.hasEnded()) { eventsOnNow.push(e); } else if (!e.hasStarted() && !e.hasEnded()) { eventsOnNext.push(e); } } eventsOnNow.sort((e1, e2) => { return (e1.timeUntilEnd() > e2.timeUntilEnd()) ? 1 : ((e2.timeUntilEnd() > e1.timeUntilEnd()) ? -1 : 0) }); eventsOnNext.sort((e1, e2) => { return (e1.timeUntilStart() > e2.timeUntilStart()) ? 1 : ((e2.timeUntilStart() > e1.timeUntilStart()) ? -1 : 0) }); populateList("happeningNow", eventsOnNow, false); populateList("happeningNext", eventsOnNext, true); window.setInterval(() => handleCountdown(), 1000); }); grist.onOptions(function(options, interaction) { console.info("Options reloaded"); }); class Event { constructor(id, name, venue, privacy, recordOverride, comments, startTime, endTime) { this.id = id; this.name = name; this.venue = venue; this.privacy = privacy; this.recordOverride = recordOverride; this.comments = comments; this.startTime = startTime; this.endTime = endTime; } hasStarted() { return this.startTime < now; } hasEnded() { return this.endTime < now; } timeUntilStart() { return new Date(this.startTime - now); } timeUntilEnd() { return new Date(this.endTime - now); } } function makeTextBold(text) { return "" + text + ""; } function convertTimeToString(time) { // Using getHours() is bullshit for this usage, it won't include any days in the hours let hours = Math.floor(time / (1000 * 60 * 60)); let minutes = Math.ceil((time - (hours * 60 * 60 * 1000)) / (1000 * 60)); return hours + " hours, " + minutes + " minutes"; } function populateList(elementName, items, getStartsIn) { document.getElementById(elementName).innerHTML = ""; for (const i of items) { var listItem = document.createElement("li"); listItem.setAttribute("class", "list-group-item shadow-sm d-flex flex-wrap align-items-center m-2 rounded-3") listItem.setAttribute("id", i.id); // Basic info of event name, venue and time var title = document.createElement("h4"); title.setAttribute("class", "me-auto") title.innerHTML = i.name; var venue = document.createElement("small"); venue.setAttribute("class", "text-body-secondary"); venue.innerHTML = " " + i.venue + " | "; var time = document.createElement("small"); time.setAttribute("id", i.id + "_time"); time.setAttribute("class", "text-body-secondary"); if (getStartsIn) { time.innerHTML = convertTimeToString(i.timeUntilStart()); } else { time.innerHTML = "(ends in) " + convertTimeToString(i.timeUntilEnd()); } // Red border if show starts in 15 minutes if (getStartsIn) { if (i.timeUntilStart() <= 15 * 60 * 1000){ listItem.setAttribute("class", listItem.getAttribute("class") + " border-2 border-danger"); time.setAttribute("class", "text-danger"); } } title.appendChild(venue); title.appendChild(time); listItem.appendChild(title); // Badges var privacyBadge = document.createElement("span"); if (i.privacy == "none") { privacyBadge.innerHTML = "Do not record" privacyBadge.setAttribute("class", "badge text-bg-danger rounded-pill") } else if (i.privacy == "review") { privacyBadge.innerHTML = "Review" privacyBadge.setAttribute("class", "badge text-bg-warning rounded-pill") } else { privacyBadge.innerHTML = "Record"; privacyBadge.setAttribute("class", "badge text-bg-success rounded-pill") } listItem.appendChild(privacyBadge); if (i.recordOverride) { var recordOverrideBadge = document.createElement("span"); recordOverrideBadge.innerHTML = "Record override"; recordOverrideBadge.setAttribute("class", "badge text-bg-primary rounded-pill border border-danger") listItem.appendChild(recordOverrideBadge); } if (i.comments) { var commentsBadge = document.createElement("span"); commentsBadge.innerHTML = i.comments commentsBadge.setAttribute("class", "badge text-bg-info rounded-pill") listItem.appendChild(commentsBadge); } document.getElementById(elementName).appendChild(listItem); } } function refreshLists() { // Clear anything that's finished while (eventsOnNow.length >0 && eventsOnNow.at(0).hasEnded()) { eventsOnNow.shift(); } // Get anything that's started and add it to the "Happening now" list while (eventsOnNext.length > 0 && eventsOnNext.at(0).hasStarted()) { eventsOnNow.push(eventsOnNext.shift()); } } function handleCountdown() { countdown--; if (countdown == 0) { countdown = MAX_COUNTDOWN; refreshLists(); populateList("happeningNow", eventsOnNow, false); populateList("happeningNext", eventsOnNext, true); } let counterElement = document.getElementById("counter"); counterElement.innerHTML = "Next refresh in " + countdown; }