131 lines
7.7 KiB
Plaintext
131 lines
7.7 KiB
Plaintext
|
|
{% if page.data.bluesky_article == blank and post.data.bluesky_comments_id == blank %}
|
|
<div class="article-content">
|
|
<h2>Comments</h2>
|
|
<p>
|
|
You can use your Bluesky account to
|
|
<a class="button-link" href="https://bsky.app/profile/{{ page.data.bluesky_article }}/post/{{ post.data.bluesky_comments_id }}"
|
|
target="_blank">reply</a>
|
|
to this post. Learn how this is implemented
|
|
<a class="link" href="https://www.menzel.it/post/2024/11/set-comments-experience-bluesky-posts/">here</a>.
|
|
</p>
|
|
<div id="bluesky-comments-list"></div>
|
|
<noscript>You need JavaScript to view the comments.</noscript>
|
|
<script src="/assets/js/purify.min.js"></script>
|
|
<script type="text/javascript">
|
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
const commentList = document.getElementById('bluesky-comments-list');
|
|
commentList.innerHTML = "Loading comments...";
|
|
|
|
fetch(`https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=at://{{ page.data.bluesky_article }}/app.bsky.feed.post/{{ post.data.bluesky_comments_id }}&depth=10`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const replies = data.thread.replies || [];
|
|
replies.sort((a, b) => new Date(a.post.record.createdAt) - new Date(b.post.record.createdAt)); // Sort by date
|
|
|
|
commentList.innerHTML = ""; // Clear loading text
|
|
|
|
const renderComments = (comments, parentElement) => {
|
|
comments.forEach(reply => {
|
|
const author = reply.post.author;
|
|
let content = reply.post.record.text;
|
|
const createdAt = new Date(reply.post.record.createdAt).toLocaleString();
|
|
|
|
// Validate counts, ensure they are numbers
|
|
const replyCount = Number(reply.post.replyCount) || 0;
|
|
const repostCount = Number(reply.post.repostCount) || 0;
|
|
const likeCount = Number(reply.post.likeCount) || 0;
|
|
|
|
// Process facets to embed links and mentions
|
|
const facets = reply.post.record.facets || [];
|
|
facets.sort((a, b) => a.index.byteStart - b.index.byteStart); // Ensure facets are in order
|
|
|
|
let offset = 0;
|
|
facets.forEach(facet => {
|
|
const start = facet.index.byteStart + offset;
|
|
const end = facet.index.byteEnd + offset;
|
|
const originalText = content.slice(start, end);
|
|
let replacementText = originalText;
|
|
|
|
facet.features.forEach(feature => {
|
|
if (feature.$type === 'app.bsky.richtext.facet#link') {
|
|
replacementText = `<a class="link" href="${feature.uri}" target="_blank" rel="noopener noreferrer">${originalText}</a>`;
|
|
} else if (feature.$type === 'app.bsky.richtext.facet#mention') {
|
|
replacementText = `<a class="link" href="https://bsky.app/profile/${feature.did}" target="_blank" rel="noopener noreferrer">${originalText}</a>`;
|
|
}
|
|
});
|
|
|
|
content = content.slice(0, start) + replacementText + content.slice(end);
|
|
offset += replacementText.length - originalText.length;
|
|
});
|
|
|
|
const safeContent = DOMPurify.sanitize(content); // Sanitize the content
|
|
|
|
const commentHtml = `
|
|
<div class="comment-container">
|
|
<img src="${author.avatar}" alt="${author.displayName}'s avatar" class="comment-avatar">
|
|
<div class="comment-details">
|
|
<div class="comment-header">
|
|
<a href="https://bsky.app/profile/${author.did}" target="_blank" class="username-link">${author.displayName}</a>
|
|
<span class="comment-handle">@${author.handle}</span>
|
|
</div>
|
|
<div class="comment-text">${safeContent}</div>
|
|
<div class="comment-timestamp">${createdAt}</div>
|
|
<div class="comment-meta">
|
|
<!-- Comment Icon and Count -->
|
|
<span class="meta-item">
|
|
<svg class="icon icon-comment" viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
|
<path d="M12 2C6.48 2 2 5.58 2 10c0 2.5 1.64 4.71 4.11 6.13L5 21l5.11-2.11c.61.08 1.24.11 1.89.11 5.52 0 10-3.58 10-8s-4.48-8-10-8zm0 14c-.55 0-1.1-.05-1.64-.14l-.36-.07-3.09 1.27.64-2.73-.24-.14C5.14 13.88 4 12.03 4 10c0-3.31 3.58-6 8-6s8 2.69 8 6-3.58 6-8 6z"/>
|
|
</svg>
|
|
<span class="icon-text">${replyCount}</span>
|
|
</span>
|
|
|
|
<!-- Reshare Icon and Count -->
|
|
<span class="meta-item">
|
|
<svg class="icon icon-reshare" viewBox="0 0 24 24" width="21" height="21" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="m13.5 13.5 3 3 3-3"/>
|
|
<path d="M9.5 4.5h3a4 4 0 0 1 4 4v8m-9-9-3-3-3 3"/>
|
|
<path d="M11.5 16.5h-3a4 4 0 0 1-4-4v-8"/>
|
|
</svg>
|
|
<span class="icon-text">${repostCount}</span>
|
|
</span>
|
|
|
|
<!-- Like Icon and Count -->
|
|
<span class="meta-item">
|
|
<svg class="icon icon-like" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3 9.24 3 10.91 3.81 12 5.09 13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
|
</svg>
|
|
<span class="icon-text">${likeCount}</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const commentElement = document.createElement("div");
|
|
commentElement.classList.add("comment");
|
|
commentElement.innerHTML = commentHtml;
|
|
parentElement.appendChild(commentElement);
|
|
|
|
// Render child comments recursively
|
|
if (reply.replies && reply.replies.length > 0) {
|
|
const childContainer = document.createElement("div");
|
|
childContainer.classList.add("child-comments");
|
|
commentElement.appendChild(childContainer);
|
|
renderComments(reply.replies, childContainer);
|
|
}
|
|
});
|
|
};
|
|
|
|
renderComments(replies, commentList);
|
|
})
|
|
.catch(error => {
|
|
console.error("Error fetching comments:", error);
|
|
commentList.innerHTML = "<p>Error loading comments.</p>";
|
|
});
|
|
});
|
|
</script>
|
|
</div>
|
|
{% endif %}
|