mirror of
https://github.com/svemagie/blog-eleventy-indiekit.git
synced 2026-04-02 16:44:56 +02:00
- Add post-type header (Reply/Like/Repost/Bookmark/Note) for titleless posts - Add left accent border to user content on interaction posts - Collapse AI Usage box into a compact <details> summary line - Collapse comments section when empty, auto-open when comments exist - Collapse webmention send form behind <details> toggle Confab-Link: http://localhost:8080/sessions/648a550c-4f65-46be-b9a9-6b7e0fd90751
152 lines
3.9 KiB
JavaScript
152 lines
3.9 KiB
JavaScript
/**
|
|
* Client-side comments component (Alpine.js)
|
|
* Handles IndieAuth flow, comment submission, and display
|
|
*
|
|
* Registered via Alpine.data() so the component is available
|
|
* regardless of script loading order.
|
|
*/
|
|
|
|
document.addEventListener("alpine:init", () => {
|
|
Alpine.data("commentsSection", (targetUrl) => ({
|
|
targetUrl,
|
|
user: null,
|
|
meUrl: "",
|
|
commentText: "",
|
|
comments: [],
|
|
loading: true,
|
|
authLoading: false,
|
|
submitting: false,
|
|
statusMessage: "",
|
|
statusType: "info",
|
|
maxLength: 2000,
|
|
showForm: false,
|
|
|
|
async init() {
|
|
await this.checkSession();
|
|
await this.loadComments();
|
|
this.handleAuthReturn();
|
|
},
|
|
|
|
async checkSession() {
|
|
try {
|
|
const res = await fetch("/comments/api/session", {
|
|
credentials: "include",
|
|
});
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
if (data.user) this.user = data.user;
|
|
}
|
|
} catch {
|
|
// No session
|
|
}
|
|
},
|
|
|
|
handleAuthReturn() {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const authError = params.get("auth_error");
|
|
if (authError) {
|
|
this.showStatus(`Authentication failed: ${authError}`, "error");
|
|
window.history.replaceState(
|
|
{},
|
|
"",
|
|
window.location.pathname + "#comments",
|
|
);
|
|
}
|
|
},
|
|
|
|
async loadComments() {
|
|
this.loading = true;
|
|
try {
|
|
const url = `/comments/api/comments?target=${encodeURIComponent(this.targetUrl)}`;
|
|
const res = await fetch(url);
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
this.comments = data.children || [];
|
|
}
|
|
} catch (e) {
|
|
console.error("[Comments] Load error:", e);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async startAuth() {
|
|
this.authLoading = true;
|
|
try {
|
|
const res = await fetch("/comments/api/auth", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
me: this.meUrl,
|
|
returnUrl: window.location.pathname + "#comments",
|
|
}),
|
|
credentials: "include",
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const data = await res.json();
|
|
this.showStatus(data.error || "Auth failed", "error");
|
|
return;
|
|
}
|
|
|
|
const data = await res.json();
|
|
if (data.authUrl) {
|
|
window.location.href = data.authUrl;
|
|
}
|
|
} catch {
|
|
this.showStatus("Failed to start authentication", "error");
|
|
} finally {
|
|
this.authLoading = false;
|
|
}
|
|
},
|
|
|
|
async submitComment() {
|
|
if (!this.commentText.trim()) return;
|
|
this.submitting = true;
|
|
|
|
try {
|
|
const res = await fetch("/comments/api/submit", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
content: this.commentText,
|
|
target: this.targetUrl,
|
|
}),
|
|
credentials: "include",
|
|
});
|
|
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
if (data.comment) {
|
|
this.comments.unshift(data.comment);
|
|
}
|
|
this.commentText = "";
|
|
this.showStatus("Comment posted!", "success");
|
|
} else {
|
|
const data = await res.json();
|
|
this.showStatus(data.error || "Failed to post", "error");
|
|
}
|
|
} catch {
|
|
this.showStatus("Error posting comment", "error");
|
|
} finally {
|
|
this.submitting = false;
|
|
}
|
|
},
|
|
|
|
signOut() {
|
|
document.cookie =
|
|
"comment_session=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
this.user = null;
|
|
this.showStatus("Signed out", "info");
|
|
},
|
|
|
|
showStatus(message, type = "info") {
|
|
this.statusMessage = message;
|
|
this.statusType = type;
|
|
setTimeout(() => {
|
|
this.statusMessage = "";
|
|
}, 5000);
|
|
},
|
|
}));
|
|
});
|