let offset; let timelineElement = document.getElementById("messages"); let moreButton = document.getElementById("more-button"); function escapeHTML(content) { return content.replaceAll("&", "&").replaceAll("<", ">").replaceAll(">", "<"); } function getMessageHTML(messageJSON, canRespond) { let el = document.createElement("div"); el.classList.add("message-container"); el.dataset.messageId = messageJSON.id; el.innerHTML = `
${messageJSON.from || "Anonymous"} writes: ${timeSince(messageJSON.timestamp)}
${escapeHTML(messageJSON.content)}
${messageJSON.response ? `${timeSince(messageJSON.response_timestamp)}
\n${escapeHTML(messageJSON.response)}
` : ""} ${ canRespond ? `
${messageJSON.response ? "" : '
'}
${messageJSON.response ? "" : ``}
` : `No response` }`; return el } function timeSince(date) { let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; let dateObject = new Date(date * 1000); let dateString = `${months[dateObject.getMonth()]} ${dateObject.getDate()}, ${dateObject.getFullYear()}, ${String(dateObject.getHours()).padStart(2, "0")}:${String(dateObject.getMinutes()).padStart(2, "0")}:${String(dateObject.getSeconds()).padStart(2, "0")}`; let seconds = Math.floor((+(new Date()) / 1000 - date + 1)); let unit = "second"; let amount = seconds > 0 ? seconds : 0; const timeAmounts = [ { name: "minute", amount: 60 }, { name: "hour", amount: 3600 }, { name: "day", amount: 86400 }, { name: "month", amount: 2592000 }, { name: "year", amount: 31536000 } ]; for (const info of timeAmounts) { let interval = seconds / info.amount; if (interval >= 1) { unit = info.name; amount = Math.floor(interval); } } return `${Math.floor(amount)} ${unit}${Math.floor(amount) == 1 ? "" : "s"} ago`; } function replyTo(messageID) { let msgContainer = document.querySelector(`.message-container[data-message-id="${messageID}"]`); let self = msgContainer.querySelector(".reply-button") let err = msgContainer.querySelector(`.msg-error`); self.setAttribute("disabled", ""); fetch("/api/messages/", { method: "POST", body: JSON.stringify({ id: messageID, content: msgContainer.querySelector("textarea").value }) }) .then((response) => (response.json())) .then((json) => { if (json.success) { let response = document.createElement("div"); response.innerHTML = `${timeSince(json.timestamp)}
${escapeHTML(json.content)}
`; err.innerHTML = ""; msgContainer.querySelector("textarea").replaceWith(response); self.remove(); } else { err.innerHTML = "Something went wrong!"; self.removeAttribute("disabled"); } }) .catch((err) => { err.innerHTML = `Something went wrong: ${err}`; self.removeAttribute("disabled"); }); } function deleteMessage(messageID) { let msgContainer = document.querySelector(`.message-container[data-message-id="${messageID}"]`); let err = msgContainer.querySelector(`.msg-error`); fetch("/api/messages/", { method: "DELETE", body: JSON.stringify({ id: messageID }) }) .then((response) => (response.json())) .then((json) => { if (json.success) { msgContainer.remove(); } else { err.innerHTML = "Something went wrong!"; } }) .catch((err) => { err.innerHTML = `Something went wrong: ${err}`; }); } function _updateURL_replaceElement(element, to) { let newElement = document.createElement(to); newElement.dataset.url = element.dataset.url; newElement.dataset.id = element.dataset.id; newElement.innerHTML = element.innerHTML; newElement.setAttribute("href", element.getAttribute("href")); if (to == "a") { newElement.classList.add("not-bold"); } element.replaceWith(newElement); } function updateURL(switchID) { let switchElement = document.getElementById("switch").querySelector(`[data-id="${switchID}"]`); for (const el of document.getElementById("switch").querySelectorAll("b")) { _updateURL_replaceElement(el, "a"); } _updateURL_replaceElement(switchElement, "b"); url = switchElement.dataset.url; fetchMessages(true); } function fetchMessages(fetchFromStart=false) { document.getElementById("refresh").setAttribute("disabled", ""); let err, append; if (fetchFromStart) { timelineElement.innerHTML = "Loading..." moreButton.hidden = true; } else { err = document.querySelector(".delete-if-more-messages"); append = !!err; err = err || document.createElement("i"); err.classList.add("delete-if-more-messages") } fetch(url + (fetchFromStart === true ? "" : `${url.includes("?") ? "&" : "?"}offset=${offset}`)) .then((response) => (response.json())) .then((json) => { if (json.success) { let frag = document.createDocumentFragment(); console.log(fetchFromStart); if (fetchFromStart && json.messages.length == 0) { let el = document.createElement("i"); el.innerText = "No messages"; frag.append(el) } else { for (const message of json.messages) { frag.append(getMessageHTML(message, json.canRespond)); offset = message.id; } } moreButton.hidden = !json.more; timelineElement.append(frag); for (el of document.getElementsByClassName("delete-if-more-messages")) { el.remove(); } } else { err = document.querySelector(".delete-if-more-messages"); append = !!err; err = err || document.createElement("i"); err.classList.add("delete-if-more-messages") err.innerText = "Something went wrong!"; append && timelineElement.append(err); } document.getElementById("refresh").removeAttribute("disabled"); }) .catch((error) => { err = document.querySelector(".delete-if-more-messages"); append = !!err; err = err || document.createElement("i"); err.classList.add("delete-if-more-messages") err.innerText = `Couldn't fetch timeline: ${error}`; append && timelineElement.append(err); document.getElementById("refresh").removeAttribute("disabled"); }); } fetchMessages(true); setInterval(function () { for (const timestamp of document.querySelectorAll("[data-timestamp]")) { timestamp.innerHTML = timeSince(Number(timestamp.dataset.timestamp)); } }, 5000);