feat: replace legacy vote buttons with AJAX submission and dynamic count updates

Replace static form-based vote buttons with a new VoteManager class that submits votes via fetch, returns JSON with net/up/down/current value, and updates vote count spans using data-vote-count attributes across all templates. Add JSONResponse endpoint in votes router for AJAX requests, switch VoteManager import from Http to Toast for error feedback, and refactor link_attachments to handle comma-separated UIDs.
This commit is contained in:
2026-05-27 19:06:18 +00:00
parent 9c55ef272e
commit 74571f7737
14 changed files with 182 additions and 32 deletions
+41 -6
View File
@@ -1,4 +1,4 @@
import { Http } from "./Http.js";
import { Toast } from "./Toast.js";
export class VoteManager {
constructor() {
@@ -6,12 +6,47 @@ export class VoteManager {
}
initVoteButtons() {
document.querySelectorAll(".post-action-btn[data-vote]").forEach((btn) => {
btn.addEventListener("click", () => {
const targetUid = btn.dataset.target;
const targetType = btn.dataset.type || "post";
Http.postForm(`/votes/${targetType}/${targetUid}`, { value: btn.dataset.vote });
document.querySelectorAll('form[action^="/votes/"] button[type="submit"]').forEach((button) => {
const form = button.closest("form");
button.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
this.cast(form, button);
});
});
}
async cast(form, button) {
const action = form.getAttribute("action");
const value = form.querySelector('input[name="value"]').value;
try {
const response = await fetch(action, {
method: "POST",
headers: {
"X-Requested-With": "fetch",
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({ value }),
});
if (!response.ok) {
throw new Error(`vote failed with status ${response.status}`);
}
const result = await response.json();
this.render(action, result);
} catch (error) {
console.error("vote failed", error);
Toast.flash(button, "Error", 1500);
}
}
render(action, result) {
const targetUid = action.split("/").pop();
document.querySelectorAll(`[data-vote-count="${targetUid}"]`).forEach((counter) => {
counter.textContent = result.net;
});
document.querySelectorAll(`form[action="${action}"] button[type="submit"]`).forEach((button) => {
const formValue = parseInt(button.closest("form").querySelector('input[name="value"]').value, 10);
button.classList.toggle("voted", result.value !== 0 && formValue === result.value);
});
}
}