add reactions support to thread

This commit is contained in:
2025-08-04 21:10:23 +03:00
parent acac6ed778
commit a529c1db65
14 changed files with 485 additions and 34 deletions

View File

@ -62,26 +62,26 @@
setTimeout(() => {
const valid = isQuoteSelectionValid();
if (isSelecting || !valid) {
removePopover();
removeQuotePopover();
return;
}
const selection = document.getSelection();
const selectionStr = selection.toString().trim();
if (selection.isCollapsed || selectionStr === "") {
removePopover();
removeQuotePopover();
return;
}
showPopover();
showQuotePopover();
}, 50)
}
function removePopover() {
function removeQuotePopover() {
quotePopover?.hidePopover();
}
function createPopover() {
function createQuotePopover() {
quotePopover = document.createElement("div");
quotePopover.popover = "auto";
quotePopover.className = "quote-popover";
@ -95,9 +95,9 @@
return quoteButton;
}
function showPopover() {
function showQuotePopover() {
if (!quotePopover) {
const quoteButton = createPopover();
const quoteButton = createQuotePopover();
quoteButton.addEventListener("click", () => {
console.log("Quoting:", document.getSelection().toString());
const postPermalink = quotedPostContainer.dataset.postPermalink;
@ -111,7 +111,7 @@
ta.focus();
document.getSelection().empty();
removePopover();
removeQuotePopover();
})
}
@ -196,4 +196,174 @@
.catch(error => console.log(error))
}
tryFetchUpdate();
if (supportsPopover()){
const reactionEmoji = document.getElementById("allowed-reaction-emoji").value.split(" ");
let reactionPopover = null;
let reactionTargetPostId = null;
function tryAddReaction(emoji, postId = reactionTargetPostId) {
const body = JSON.stringify({
"emoji": emoji,
});
fetch(`/api/add-reaction/${postId}`, {method: "POST", headers: {"Content-Type": "application/json"}, body: body})
.then(res => res.json())
.then(json => {
if (json.status === "added") {
const post = document.getElementById(`post-${postId}`);
const spans = Array.from(post.querySelectorAll(".reaction-count")).filter((span) => {
return span.dataset.emoji === emoji
});
if (spans.length > 0) {
const currentValue = spans[0].textContent;
spans[0].textContent = `${parseInt(currentValue) + 1}`;
const button = spans[0].closest(".reaction-button");
button.classList.add("active");
} else {
const span = document.createElement("span");
span.classList = "reaction-container";
span.dataset.emoji = emoji;
const button = document.createElement("button");
button.type = "button";
button.className = "reduced reaction-button active";
button.addEventListener("click", () => {
tryAddReaction(emoji, postId);
})
const img = document.createElement("img");
img.src = `/static/emoji/${emoji}.png`;
button.textContent = " x";
const reactionCountSpan = document.createElement("span")
reactionCountSpan.className = "reaction-count"
reactionCountSpan.textContent = "1"
button.insertAdjacentElement("afterbegin", img);
button.appendChild(reactionCountSpan);
span.appendChild(button);
const post = document.getElementById(`post-${postId}`);
post.querySelector(".post-reactions").insertBefore(span, post.querySelector(".add-reaction-button"));
}
} else if (json.error_code === 409) {
console.log("reaction exists, gonna try and remove");
tryRemoveReaction(emoji, postId);
} else {
console.warn(json)
}
})
.catch(error => console.error(error));
}
function tryRemoveReaction(emoji, postId = reactionTargetPostId) {
const body = JSON.stringify({
"emoji": emoji,
});
fetch(`/api/remove-reaction/${postId}`, {method: "POST", headers: {"Content-Type": "application/json"}, body: body})
.then(res => res.json())
.then(json => {
if (json.status === "removed") {
const post = document.getElementById(`post-${postId}`);
const spans = Array.from(post.querySelectorAll(".reaction-container")).filter((span) => {
return span.dataset.emoji === emoji
});
if (spans.length > 0) {
const reactionCountSpan = spans[0].querySelector(".reaction-count");
const currentValue = parseInt(reactionCountSpan.textContent);
if (currentValue - 1 === 0) {
spans[0].remove();
} else {
reactionCountSpan.textContent = `${parseInt(currentValue) - 1}`;
const button = reactionCountSpan.closest(".reaction-button");
button.classList.remove("active");
}
}
} else {
console.warn(json)
}
})
.catch(error => console.error(error));
}
function createReactionPopover() {
reactionPopover = document.createElement("div");
reactionPopover.className = "reaction-popover";
reactionPopover.popover = "auto";
const inner = document.createElement("div");
inner.className = "reaction-popover-inner";
reactionPopover.appendChild(inner);
for (let emoji of reactionEmoji) {
const img = document.createElement("img");
img.src = `/static/emoji/${emoji}.png`;
const button = document.createElement("button");
button.type = "button";
button.className = "reduced";
button.appendChild(img);
button.addEventListener("click", () => {
tryAddReaction(emoji);
})
button.dataset.emojiName = emoji;
inner.appendChild(button);
}
reactionPopover.addEventListener("beforetoggle", (e) => {
if (e.newState === "closed") {
reactionTargetPostId = null;
}
})
document.body.appendChild(reactionPopover);
}
function showReactionPopover() {
if (!reactionPopover) {
createReactionPopover();
}
if (!reactionPopover.matches(':popover-open')) {
reactionPopover.showPopover();
}
}
for (let button of document.querySelectorAll(".add-reaction-button")) {
button.addEventListener("click", (e) => {
showReactionPopover();
reactionTargetPostId = e.target.dataset.postId;
const rect = e.target.getBoundingClientRect();
const popoverRect = reactionPopover.getBoundingClientRect();
const scrollY = window.scrollY || window.pageYOffset;
reactionPopover.style.setProperty("top", `${rect.top + scrollY + rect.height}px`)
reactionPopover.style.setProperty("left", `${rect.left + rect.width/2 - popoverRect.width/2}px`)
})
}
for (let button of document.querySelectorAll(".reaction-button")) {
button.addEventListener("click", () => {
const reactionContainer = button.closest(".reaction-container")
const emoji = reactionContainer.dataset.emoji;
const postId = reactionContainer.dataset.postId;
console.log(reactionContainer);
tryAddReaction(emoji, postId);
})
}
} else {
for (let button of document.querySelectorAll(".add-reaction-button")) {
button.disabled = true;
button.title = "Enable JS to add reactions."
}
}
}