Move some MastodonComments logic to Alpine
This commit is contained in:
parent
328e84ccc7
commit
d85522e4e6
2 changed files with 185 additions and 199 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "gallery.badmanners.xyz",
|
"name": "gallery.badmanners.xyz",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.10.1",
|
"version": "1.10.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "astro sync",
|
"postinstall": "astro sync",
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
|
|
|
@ -18,55 +18,65 @@ const { link, instance, user, postId, blacklistedComments } = Astro.props;
|
||||||
id="comments-section"
|
id="comments-section"
|
||||||
class="px-2 font-serif"
|
class="px-2 font-serif"
|
||||||
aria-describedby="title-comments-section"
|
aria-describedby="title-comments-section"
|
||||||
data-link={link}
|
x-data={`{ link: ${JSON.stringify(link)}, instance: ${JSON.stringify(instance)}, user: ${JSON.stringify(user)}, postId: ${JSON.stringify(postId)}, blacklistedComments: ${JSON.stringify(blacklistedComments ?? [])}, comments: null, commentsLoading: false, commentsError: null }`}
|
||||||
data-instance={instance}
|
@mastodon-comments.window="comments = $event.detail.comments; commentsLoading = false"
|
||||||
data-user={user}
|
@mastodon-comments-error.window="commentsError = $event.detail.error"
|
||||||
data-post-id={postId}
|
|
||||||
data-blacklisted={(blacklistedComments ?? []).join(",")}
|
|
||||||
>
|
>
|
||||||
<h2 id="title-comments-section" class="py-2 font-serif text-xl font-semibold text-stone-800 dark:text-stone-100">
|
<h2 id="title-comments-section" class="py-2 font-serif text-xl font-semibold text-stone-800 dark:text-stone-100">
|
||||||
Comments
|
Comments
|
||||||
</h2>
|
</h2>
|
||||||
<p id="comments-description" class="my-1 text-stone-800 dark:text-stone-100">
|
<p id="comments-description" class="my-1 text-stone-800 dark:text-stone-100">
|
||||||
<span data-noscript
|
<span x-show="false"
|
||||||
><a class="u-syndication text-link underline" href={link} target="_blank">View comments on Mastodon</a>.</span
|
><a class="u-syndication text-link underline" href={link} target="_blank">View comments on Mastodon</a>.</span
|
||||||
>
|
>
|
||||||
<span hidden data-no-comments
|
<template x-if="commentsError === null && comments !== null && comments.length == 0">
|
||||||
>No comments yet. <a class="text-link underline" href={link} target="_blank"
|
<span
|
||||||
>Be the first to join the conversation on Mastodon</a
|
>No comments yet. <a class="text-link underline" href={link} target="_blank"
|
||||||
>.</span
|
>Be the first to join the conversation on Mastodon</a
|
||||||
>
|
>.</span
|
||||||
<span hidden data-comments
|
|
||||||
>Join the conversation <a class="text-link underline" href={link} target="_blank">by replying on Mastodon</a
|
|
||||||
>.</span
|
|
||||||
>
|
|
||||||
<span hidden data-error>Unable to load comments. Please try again later.</span>
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
hidden
|
|
||||||
class="group mx-auto w-64 rounded-lg bg-bm-300 px-4 py-1 text-stone-800 disabled:bg-bm-400 dark:bg-green-800 dark:text-stone-100 dark:disabled:bg-green-900"
|
|
||||||
id="load-comments-button"
|
|
||||||
>
|
|
||||||
<span class="block hover:underline group-focus:underline group-disabled:hidden">Click to load comments</span>
|
|
||||||
<span class="hidden group-disabled:block">
|
|
||||||
<svg
|
|
||||||
style={{ width: "1.25rem", height: "1.25rem", display: "inline" }}
|
|
||||||
class="-mt-1 mr-1 animate-spin"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
>
|
||||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
</template>
|
||||||
<path
|
<template x-if="commentsError === null && comments !== null && comments.length > 0"
|
||||||
class="opacity-100"
|
><span>
|
||||||
fill="currentColor"
|
Join the conversation <a class="text-link underline" href={link} target="_blank">by replying on Mastodon</a
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
>.</span
|
||||||
></path>
|
></template
|
||||||
</svg>
|
>
|
||||||
Loading...
|
<template x-if="commentsError">
|
||||||
</span>
|
<span>Unable to load comments. Please try again later.</span>
|
||||||
</button>
|
</template>
|
||||||
<div id="comments" hidden></div>
|
</p>
|
||||||
|
<template x-if="comments === null && !commentsError">
|
||||||
|
<button
|
||||||
|
class="group mx-auto w-64 rounded-lg bg-bm-300 px-4 py-1 text-stone-800 disabled:bg-bm-400 dark:bg-green-800 dark:text-stone-100 dark:disabled:bg-green-900"
|
||||||
|
id="load-comments-button"
|
||||||
|
@click="if (!$el.disabled) { $el.disabled = true; commentsLoading = true; $dispatch('retrieveComments', { link, instance, user, postId, blacklistedComments })}"
|
||||||
|
>
|
||||||
|
<span class="block hover:underline group-focus:underline group-disabled:hidden">Click to load comments</span>
|
||||||
|
<span class="hidden group-disabled:block">
|
||||||
|
<svg
|
||||||
|
style={{ width: "1.25rem", height: "1.25rem", display: "inline" }}
|
||||||
|
class="-mt-1 mr-1 animate-spin"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
|
<path
|
||||||
|
class="opacity-100"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
Loading...
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<div id="comments" x-show="comments !== null && comments.length > 0">
|
||||||
|
<template x-for="comment in comments">
|
||||||
|
<div x-html="comment"></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<template id="template-comment-emoji">
|
<template id="template-comment-emoji">
|
||||||
|
@ -111,14 +121,14 @@ const { link, instance, user, postId, blacklistedComments } = Astro.props;
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-1 flex flex-row pb-2 pt-1">
|
<div class="ml-1 flex flex-row pb-2 pt-1">
|
||||||
<div class="flex" aria-label="Favorites">
|
<span aria-label="Favorites">
|
||||||
<span data-favorites></span>
|
<span data-favorites></span>
|
||||||
<IconStar width="1.25rem" height="1.25rem" class="ml-2" />
|
<IconStar width="1.25rem" height="1.25rem" class="ml-1 inline align-text-bottom" />
|
||||||
</div>
|
</span>
|
||||||
<div class="ml-4 flex" aria-label="Reblogs">
|
<span aria-label="Reblogs" class="ml-6">
|
||||||
<span data-reblogs></span>
|
<span data-reblogs></span>
|
||||||
<IconRetweet width="1.25rem" height="1.25rem" class="ml-2" />
|
<IconRetweet width="1.25rem" height="1.25rem" class="ml-1 inline align-text-bottom" />
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div data-comment-thread class="-mb-2" aria-hidden="true" aria-label="Replies"></div>
|
<div data-comment-thread class="-mb-2" aria-hidden="true" aria-label="Replies"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -165,166 +175,142 @@ const { link, instance, user, postId, blacklistedComments } = Astro.props;
|
||||||
descendants: Status[];
|
descendants: Status[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderComments(section: Element, post: MastodonPost, blacklistedComments: Set<string>) {
|
type RetrieveCommentsEvent = CustomEvent<{
|
||||||
const commentsDescription = section.querySelector<HTMLElementTagNameMap["p"]>("p#comments-description")!;
|
link: string;
|
||||||
const loadCommentsButton = section.querySelector<HTMLElementTagNameMap["button"]>("button#load-comments-button")!;
|
instance: string;
|
||||||
try {
|
user: string;
|
||||||
const response = await fetch(`https://${post.instance}/api/v1/statuses/${post.postId}/context`);
|
postId: string;
|
||||||
if (!response.ok) {
|
blacklistedComments: string[];
|
||||||
throw new Error(`Received error status ${response.status} - ${response.statusText}!`);
|
}>;
|
||||||
}
|
|
||||||
const data: StatusContext = await response.json();
|
|
||||||
|
|
||||||
const emojiTemplate = document.querySelector<HTMLElementTagNameMap["template"]>(
|
if (document.querySelector("#comments-section")) {
|
||||||
"template#template-comment-emoji",
|
async function renderComments(post: MastodonPost, blacklistedComments: Set<string>) {
|
||||||
)!;
|
try {
|
||||||
const emojiMap: Record<string, string> = {};
|
const response = await fetch(`https://${post.instance}/api/v1/statuses/${post.postId}/context`);
|
||||||
const replaceEmojis = (text: string, emojis: CustomEmoji[]) =>
|
if (!response.ok) {
|
||||||
emojis.reduce((acc, emoji) => {
|
throw new Error(`Received error status ${response.status} - ${response.statusText}!`);
|
||||||
let emojiHTML = emojiMap[emoji.url];
|
}
|
||||||
if (!emojiHTML) {
|
const data: StatusContext = await response.json();
|
||||||
const emojiPicture = emojiTemplate.content.cloneNode(true) as DocumentFragment;
|
|
||||||
const emojiStatic = emojiPicture.querySelector("source")!;
|
const emojiTemplate = document.querySelector<HTMLElementTagNameMap["template"]>(
|
||||||
emojiStatic.srcset = emoji.static_url;
|
"template#template-comment-emoji",
|
||||||
const emojiImg = emojiPicture.querySelector("img")!;
|
)!;
|
||||||
emojiImg.src = emoji.url;
|
const emojiMap: Record<string, string> = {};
|
||||||
emojiImg.alt = `:${emoji.shortcode}: emoji`;
|
const replaceEmojis = (text: string, emojis: CustomEmoji[]) =>
|
||||||
emojiHTML = emojiPicture.firstElementChild!.outerHTML;
|
emojis.reduce((acc, emoji) => {
|
||||||
emojiMap[emoji.url] = emojiHTML;
|
let emojiHTML = emojiMap[emoji.url];
|
||||||
|
if (!emojiHTML) {
|
||||||
|
const emojiPicture = emojiTemplate.content.cloneNode(true) as DocumentFragment;
|
||||||
|
const emojiStatic = emojiPicture.querySelector("source")!;
|
||||||
|
emojiStatic.srcset = emoji.static_url;
|
||||||
|
const emojiImg = emojiPicture.querySelector("img")!;
|
||||||
|
emojiImg.src = emoji.url;
|
||||||
|
emojiImg.alt = `:${emoji.shortcode}: emoji`;
|
||||||
|
emojiHTML = emojiPicture.firstElementChild!.outerHTML;
|
||||||
|
emojiMap[emoji.url] = emojiHTML;
|
||||||
|
}
|
||||||
|
return acc.replaceAll(`:${emoji.shortcode}:`, emojiHTML);
|
||||||
|
}, text);
|
||||||
|
const commentsList: DocumentFragment[] = [];
|
||||||
|
const commentMap: Record<string, number> = {};
|
||||||
|
const commentTemplate = document.querySelector<HTMLElementTagNameMap["template"]>(
|
||||||
|
"template#template-comment-box",
|
||||||
|
)!;
|
||||||
|
|
||||||
|
data.descendants.forEach((comment) => {
|
||||||
|
if (blacklistedComments.has(comment.id)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return acc.replaceAll(`:${emoji.shortcode}:`, emojiHTML);
|
if (blacklistedComments.has(comment.in_reply_to_id)) {
|
||||||
}, text);
|
blacklistedComments.add(comment.id);
|
||||||
const commentsList: DocumentFragment[] = [];
|
return;
|
||||||
const commentMap: Record<string, number> = {};
|
}
|
||||||
const commentTemplate = document.querySelector<HTMLElementTagNameMap["template"]>(
|
const commentBox = commentTemplate.content.cloneNode(true) as DocumentFragment;
|
||||||
"template#template-comment-box",
|
commentBox.firstElementChild!.id = `comment-${comment.id}`;
|
||||||
)!;
|
|
||||||
|
|
||||||
data.descendants.forEach((comment) => {
|
const commentBoxAuthor = commentBox.querySelector<HTMLElementTagNameMap["a"]>("a[data-author]")!;
|
||||||
if (blacklistedComments.has(comment.id)) {
|
commentBoxAuthor.href = comment.account.url;
|
||||||
return;
|
commentBoxAuthor.title = comment.account.acct;
|
||||||
}
|
commentBoxAuthor.setAttribute("aria-label", comment.account.acct);
|
||||||
if (blacklistedComments.has(comment.in_reply_to_id)) {
|
const avatar = commentBoxAuthor.querySelector<HTMLElementTagNameMap["img"]>("img[data-avatar]")!;
|
||||||
blacklistedComments.add(comment.id);
|
avatar.src = comment.account.avatar;
|
||||||
return;
|
avatar.alt = `Avatar of @${comment.account.acct}`;
|
||||||
}
|
const avatarStatic =
|
||||||
const commentBox = commentTemplate.content.cloneNode(true) as DocumentFragment;
|
commentBoxAuthor.querySelector<HTMLElementTagNameMap["source"]>("source[data-avatar-static]")!;
|
||||||
commentBox.firstElementChild!.id = `comment-${comment.id}`;
|
avatarStatic.srcset = comment.account.avatar_static;
|
||||||
|
const displayName = commentBoxAuthor.querySelector<HTMLElementTagNameMap["span"]>("span[data-display-name]")!;
|
||||||
|
displayName.insertAdjacentHTML(
|
||||||
|
"afterbegin",
|
||||||
|
replaceEmojis(comment.account.display_name, comment.account.emojis),
|
||||||
|
);
|
||||||
|
|
||||||
const commentBoxAuthor = commentBox.querySelector<HTMLElementTagNameMap["a"]>("a[data-author]")!;
|
const commentBoxPostLink = commentBox.querySelector<HTMLElementTagNameMap["a"]>("a[data-post-link]")!;
|
||||||
commentBoxAuthor.href = comment.account.url;
|
commentBoxPostLink.href = comment.url;
|
||||||
commentBoxAuthor.title = comment.account.acct;
|
const publishDate =
|
||||||
commentBoxAuthor.setAttribute("aria-label", comment.account.acct);
|
commentBoxPostLink.querySelector<HTMLElementTagNameMap["time"]>("time[data-published-date]")!;
|
||||||
const avatar = commentBoxAuthor.querySelector<HTMLElementTagNameMap["img"]>("img[data-avatar]")!;
|
publishDate.dateTime = comment.created_at;
|
||||||
avatar.src = comment.account.avatar;
|
publishDate.insertAdjacentText(
|
||||||
avatar.alt = `Avatar of @${comment.account.acct}`;
|
"afterbegin",
|
||||||
const avatarStatic =
|
new Date(Date.parse(comment.created_at)).toLocaleString("en-US", {
|
||||||
commentBoxAuthor.querySelector<HTMLElementTagNameMap["source"]>("source[data-avatar-static]")!;
|
month: "short",
|
||||||
avatarStatic.srcset = comment.account.avatar_static;
|
day: "numeric",
|
||||||
const displayName = commentBoxAuthor.querySelector<HTMLElementTagNameMap["span"]>("span[data-display-name]")!;
|
year: "numeric",
|
||||||
displayName.insertAdjacentHTML(
|
hour: "2-digit",
|
||||||
"afterbegin",
|
minute: "2-digit",
|
||||||
replaceEmojis(comment.account.display_name, comment.account.emojis),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const commentBoxPostLink = commentBox.querySelector<HTMLElementTagNameMap["a"]>("a[data-post-link]")!;
|
if (comment.edited_at) {
|
||||||
commentBoxPostLink.href = comment.url;
|
const edited = commentBoxPostLink.querySelector<HTMLElementTagNameMap["time"]>("time[data-edited-date]")!;
|
||||||
const publishDate =
|
edited.dateTime = comment.edited_at;
|
||||||
commentBoxPostLink.querySelector<HTMLElementTagNameMap["time"]>("time[data-published-date]")!;
|
edited.title = comment.edited_at;
|
||||||
publishDate.dateTime = comment.created_at;
|
edited.classList.remove("hidden");
|
||||||
publishDate.insertAdjacentText(
|
edited.classList.add("dt-updated");
|
||||||
"afterbegin",
|
edited.hidden = false;
|
||||||
new Date(Date.parse(comment.created_at)).toLocaleString("en-US", {
|
}
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
const commentBoxContent = commentBox.querySelector<HTMLElementTagNameMap["div"]>("div[data-content]")!;
|
||||||
year: "numeric",
|
commentBoxContent.insertAdjacentHTML("afterbegin", replaceEmojis(comment.content, comment.emojis));
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
const commentBoxFavorites = commentBox.querySelector<HTMLElementTagNameMap["span"]>("span[data-favorites]")!;
|
||||||
|
commentBoxFavorites.insertAdjacentText("afterbegin", comment.favourites_count.toString());
|
||||||
|
|
||||||
|
const commentBoxReblogs = commentBox.querySelector<HTMLElementTagNameMap["span"]>("span[data-reblogs]")!;
|
||||||
|
commentBoxReblogs.insertAdjacentText("afterbegin", comment.reblogs_count.toString());
|
||||||
|
|
||||||
|
if (comment.in_reply_to_id === post.postId || !(comment.in_reply_to_id in commentMap)) {
|
||||||
|
commentMap[comment.id] = commentsList.length;
|
||||||
|
commentsList.push(commentBox);
|
||||||
|
} else if (comment.in_reply_to_id in commentMap) {
|
||||||
|
const commentsIndex = commentMap[comment.in_reply_to_id]!;
|
||||||
|
commentMap[comment.id] = commentsIndex;
|
||||||
|
const parentThreadDiv =
|
||||||
|
commentsList[commentsIndex]!.querySelector<HTMLElementTagNameMap["div"]>("div[data-comment-thread]")!;
|
||||||
|
parentThreadDiv.setAttribute("aria-hidden", "false");
|
||||||
|
parentThreadDiv.appendChild(commentBox);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("mastodon-comments", {
|
||||||
|
detail: {
|
||||||
|
comments: commentsList.map((fragment) => {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.appendChild(fragment);
|
||||||
|
return div.innerHTML;
|
||||||
|
}),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
console.log(commentsList);
|
||||||
if (comment.edited_at) {
|
} catch (e) {
|
||||||
const edited = commentBoxPostLink.querySelector<HTMLElementTagNameMap["time"]>("time[data-edited-date]")!;
|
console.error("Fetch Mastodon comments error", e);
|
||||||
edited.dateTime = comment.edited_at;
|
window.dispatchEvent(new CustomEvent("mastodon-comments-error", { detail: { error: e } }));
|
||||||
edited.title = comment.edited_at;
|
|
||||||
edited.classList.remove("hidden");
|
|
||||||
edited.classList.add("dt-updated");
|
|
||||||
edited.hidden = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const commentBoxContent = commentBox.querySelector<HTMLElementTagNameMap["div"]>("div[data-content]")!;
|
|
||||||
commentBoxContent.insertAdjacentHTML("afterbegin", replaceEmojis(comment.content, comment.emojis));
|
|
||||||
|
|
||||||
const commentBoxFavorites = commentBox.querySelector<HTMLElementTagNameMap["span"]>("span[data-favorites]")!;
|
|
||||||
commentBoxFavorites.insertAdjacentText("afterbegin", comment.favourites_count.toString());
|
|
||||||
|
|
||||||
const commentBoxReblogs = commentBox.querySelector<HTMLElementTagNameMap["span"]>("span[data-reblogs]")!;
|
|
||||||
commentBoxReblogs.insertAdjacentText("afterbegin", comment.reblogs_count.toString());
|
|
||||||
|
|
||||||
if (comment.in_reply_to_id === post.postId || !(comment.in_reply_to_id in commentMap)) {
|
|
||||||
commentMap[comment.id] = commentsList.length;
|
|
||||||
commentsList.push(commentBox);
|
|
||||||
} else if (commentMap[comment.in_reply_to_id]) {
|
|
||||||
const commentsIndex = commentMap[comment.in_reply_to_id]!;
|
|
||||||
commentMap[comment.id] = commentsIndex;
|
|
||||||
const parentThreadDiv =
|
|
||||||
commentsList[commentsIndex]!.querySelector<HTMLElementTagNameMap["div"]>("div[data-comment-thread]")!;
|
|
||||||
parentThreadDiv.setAttribute("aria-hidden", "false");
|
|
||||||
parentThreadDiv.appendChild(commentBox);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (commentsList.length) {
|
|
||||||
const fragment = document.createDocumentFragment();
|
|
||||||
commentsList.forEach((comment) => fragment.appendChild(comment));
|
|
||||||
commentsDescription.querySelector<HTMLElementTagNameMap["span"]>("span[data-comments]")!.hidden = false;
|
|
||||||
const commentsDiv = section.querySelector<HTMLElementTagNameMap["div"]>("div#comments")!;
|
|
||||||
commentsDiv.appendChild(fragment);
|
|
||||||
commentsDiv.hidden = false;
|
|
||||||
} else {
|
|
||||||
commentsDescription.querySelector<HTMLElementTagNameMap["span"]>("span[data-no-comments]")!.hidden = false;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error("Fetch Mastodon comments error", e);
|
|
||||||
commentsDescription.querySelector<HTMLElementTagNameMap["span"]>("span[data-error]")!.hidden = false;
|
|
||||||
} finally {
|
|
||||||
loadCommentsButton.hidden = true;
|
|
||||||
loadCommentsButton.blur();
|
|
||||||
commentsDescription.hidden = false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function initCommentSection() {
|
document.addEventListener("retrieveComments", (e) => {
|
||||||
const commentSection = document.querySelector<HTMLElementTagNameMap["section"]>("section#comments-section");
|
const { link, instance, user, postId, blacklistedComments } = (e as RetrieveCommentsEvent).detail;
|
||||||
if (!commentSection) {
|
renderComments({ link, instance, user, postId }, new Set(blacklistedComments));
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
const post = {
|
|
||||||
link: commentSection.dataset.link,
|
|
||||||
instance: commentSection.dataset.instance,
|
|
||||||
user: commentSection.dataset.user,
|
|
||||||
postId: commentSection.dataset.postId,
|
|
||||||
};
|
|
||||||
if (!post.link || !post.instance || !post.user || !post.postId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const blacklisted = commentSection.dataset.blacklisted;
|
|
||||||
const blacklistedComments = new Set(blacklisted ? blacklisted.split(",") : undefined);
|
|
||||||
const loadCommentsButton =
|
|
||||||
commentSection.querySelector<HTMLElementTagNameMap["button"]>("button#load-comments-button")!;
|
|
||||||
loadCommentsButton.addEventListener(
|
|
||||||
"click",
|
|
||||||
(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
loadCommentsButton.disabled = true;
|
|
||||||
renderComments(commentSection, post as MastodonPost, blacklistedComments);
|
|
||||||
},
|
|
||||||
{ once: true },
|
|
||||||
);
|
|
||||||
const commentsDescription = commentSection.querySelector<HTMLElementTagNameMap["p"]>("p#comments-description")!;
|
|
||||||
commentsDescription.hidden = true;
|
|
||||||
commentsDescription.querySelector<HTMLElementTagNameMap["span"]>("span[data-noscript]")!.hidden = true;
|
|
||||||
loadCommentsButton.hidden = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initCommentSection();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue