pyrom/data/static/js/thread.js

200 lines
6.1 KiB
JavaScript

{
const ta = document.getElementById("babycode-content");
for (let button of document.querySelectorAll(".reply-button")) {
button.addEventListener("click", (e) => {
ta.value += button.value;
ta.scrollIntoView()
ta.focus();
})
}
function supportsPopover() {
return Object.hasOwn(HTMLElement.prototype, "popover");
}
if (supportsPopover()){
let quotedPostContainer = null;
function isQuoteSelectionValid() {
const selection = document.getSelection();
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
return false;
}
const range = selection.getRangeAt(0);
const commonAncestor = range.commonAncestorContainer;
const ancestorElement = commonAncestor.nodeType === Node.TEXT_NODE
? commonAncestor.parentNode
: commonAncestor;
const container = ancestorElement.closest(".post-inner");
if (!container) {
return false;
}
const success = container.contains(ancestorElement);
if (success) {
quotedPostContainer = container;
}
return success;
}
let quotePopover = null;
let isSelecting = false;
document.addEventListener("mousedown", () => {
isSelecting = true;
})
document.addEventListener("mouseup", () => {
isSelecting = false;
handlePossibleSelection();
})
document.addEventListener("keyup", (e) => {
if (e.shiftKey && (e.key.startsWith('Arrow') || e.key === 'Home' || e.key === 'End')) {
handlePossibleSelection();
}
})
function handlePossibleSelection() {
setTimeout(() => {
const valid = isQuoteSelectionValid();
if (isSelecting || !valid) {
removePopover();
return;
}
const selection = document.getSelection();
const selectionStr = selection.toString().trim();
if (selection.isCollapsed || selectionStr === "") {
removePopover();
return;
}
showPopover();
}, 50)
}
function removePopover() {
quotePopover?.hidePopover();
}
function createPopover() {
quotePopover = document.createElement("div");
quotePopover.popover = "auto";
quotePopover.className = "quote-popover";
const quoteButton = document.createElement("button");
quoteButton.textContent = "Quote fragment"
quoteButton.className = "reduced"
quotePopover.appendChild(quoteButton);
document.body.appendChild(quotePopover);
return quoteButton;
}
function showPopover() {
if (!quotePopover) {
const quoteButton = createPopover();
quoteButton.addEventListener("click", () => {
console.log("Quoting:", document.getSelection().toString());
const postPermalink = quotedPostContainer.dataset.postPermalink;
const authorUsername = quotedPostContainer.dataset.authorUsername;
console.log(postPermalink, authorUsername);
if (ta.value.trim() !== "") {
ta.value += "\n"
}
ta.value += `[url=${postPermalink}]${authorUsername} said:[/url]\n[quote]${document.getSelection().toString()}[/quote]\n`;
ta.scrollIntoView()
ta.focus();
document.getSelection().empty();
removePopover();
})
}
const range = document.getSelection().getRangeAt(0);
const rect = range.getBoundingClientRect();
const scrollY = window.scrollY || window.pageYOffset;
quotePopover.style.setProperty("top", `${rect.top + scrollY - 55}px`)
quotePopover.style.setProperty("left", `${rect.left + rect.width/2}px`)
if (!quotePopover.matches(':popover-open')) {
quotePopover.showPopover();
}
}
}
const deleteDialog = document.getElementById("delete-dialog");
const deleteDialogCloseButton = document.getElementById("post-delete-dialog-close");
let deletionTargetPostContainer;
function closeDeleteDialog() {
deletionTargetPostContainer.style.removeProperty("background-color");
deleteDialog.close();
}
deleteDialogCloseButton.addEventListener("click", (e) => {
closeDeleteDialog();
})
deleteDialog.addEventListener("click", (e) => {
if (e.target === deleteDialog) {
closeDeleteDialog();
}
})
for (let button of document.querySelectorAll(".post-delete-button")) {
button.addEventListener("click", (e) => {
deleteDialog.showModal();
const postId = button.value;
deletionTargetPostContainer = document.getElementById("post-" + postId).querySelector(".post-content-container");
deletionTargetPostContainer.style.setProperty("background-color", "#fff");
const form = document.getElementById("post-delete-form");
form.action = `/post/${postId}/delete`
})
}
const threadEndpoint = document.getElementById("thread-subscribe-endpoint").value;
let now = Math.floor(new Date() / 1000);
function hideNotification() {
const notification = document.getElementById('new-post-notification');
notification.classList.add('hidden');
}
function showNewPostNotification(url) {
const notification = document.getElementById("new-post-notification");
notification.classList.remove("hidden");
document.getElementById("dismiss-new-post-button").onclick = () => {
now = Math.floor(new Date() / 1000);
hideNotification();
tryFetchUpdate();
}
document.getElementById("go-to-new-post-button").href = url;
document.getElementById("unsub-new-post-button").onclick = () => {
hideNotification();
}
}
function tryFetchUpdate() {
if (!threadEndpoint) return;
const body = JSON.stringify({'since': now});
fetch(threadEndpoint, {method: "POST", headers: {"Content-Type": "application/json"}, body: body})
.then(res => res.json())
.then(json => {
if (json.status === "none") {
setTimeout(tryFetchUpdate, 5000);
} else if (json.status === "new_post") {
showNewPostNotification(json.url);
}
})
.catch(error => console.log(error))
}
tryFetchUpdate();
}