video-privacy-today/plugin-api.js

283 lines
7.7 KiB
JavaScript

const MAX_COUNTDOWN = 60;
let allEventsFromOfficialSchedule = null;
var nextId = 0;
var countdown = MAX_COUNTDOWN;
var now = Date.now();
var eventsOnNow = [];
var eventsOnNext = [];
var intervalId = null;
grist.ready({
requiredAccess: 'read table'
});
grist.onRecords(function (records) {
eventsOnNow = [];
eventsOnNext = [];
now = Date.now();
getCampScheduleAndAddToLists();
for (const r of records) {
const venue = r["venue"];
const recordOverride = r["Record_override"];
if (venueUnderInterest(venue)) {
if (r["type"] == "talk" || recordOverride){
let e = new Event(
nextId++,
r["title"],
venue,
r["video_privacy"],
recordOverride,
r["Comments"],
r["Close_up"],
Date.parse(r["start_date"]),
Date.parse(r["end_date"])
);
addEventToAppropriateList(e);
}
}
}
sortEventLists();
populateListHtml("happeningNow", eventsOnNow, false);
populateListHtml("happeningNext", eventsOnNext, true);
if (intervalId != null) {
window.clearInterval(intervalId);
}
intervalId = window.setInterval(() => handleCountdown(), 1000);
});
grist.onOptions(function(options, interaction) {
console.info("Options reloaded");
});
class Event {
constructor(id, name, venue, privacy, recordOverride, comments, closeUp, startTime, endTime) {
this.id = id;
this.name = name;
this.venue = venue;
this.privacy = privacy;
this.recordOverride = recordOverride;
this.comments = comments;
this.startTime = startTime;
this.closeUp = closeUp;
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 "<b>" + text + "</b>";
}
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 getCampScheduleAndAddToLists() {
// Get stuff from official schedule and async add to list
const apiUrl = "https://www.emfcamp.org/schedule/2026.json";
// Make a GET request
fetch(apiUrl)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
for (const e of data) {
if (!((eventsOnNow.concat(eventsOnNext)).map((x) => x.title).includes(e.title))) {
for (const o of e.occurrences) {
if (venueUnderInterest(o.venue)) {
const event = new Event(
nextId++,
e.title,
o.venue,
e.video_privacy,
false,
"",
new Date(o.start_time),
new Date(o.end_time)
);
addEventToAppropriateList(event);
sortEventLists();
}
}
}
}
})
.catch(error => {
console.error('Error:', error);
});
}
function addEventToAppropriateList(event) {
if (event.hasStarted() && !event.hasEnded()) {
eventsOnNow.push(event);
} else if (!event.hasStarted() && !event.hasEnded()) {
eventsOnNext.push(event);
}
}
function venueUnderInterest(venue) {
return (["Stage A", "Stage B", "Stage C", "Arts", "Arcade Workshop"].includes(venue));
}
function sortEventLists() {
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)});
}
function populateListHtml(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);
}
if (i.closeUp != "NO") {
var closeUpBadge = document.createElement("span");
closeUpBadge.setAttribute("class", "badge text-bg-dark rounded-pill")
if (i.closeUp == "YES"){
closeUpBadge.innerHTML = "Needs close up camera";
} else {
closeUpBadge.innerHTML = "Maybe needs close up camera";
}
listItem.appendChild(closeUpBadge);
}
document.getElementById(elementName).appendChild(listItem);
document.getElementById(elementName).appendChild(listItem);
}
}
function refreshLists() {
getCampScheduleAndAddToLists();
// 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;
// now = Date.now();
now = Date.now();
refreshLists();
getCampScheduleAndAddToLists();
populateListHtml("happeningNow", eventsOnNow, false);
populateListHtml("happeningNext", eventsOnNext, true);
}
let counterElement = document.getElementById("counter");
counterElement.innerHTML = "Next refresh in " + countdown;
}