Skip to content

Commit

Permalink
franklin-update
Browse files Browse the repository at this point in the history
  • Loading branch information
rdboyes committed Nov 26, 2024
1 parent 2f1218c commit 108ccbc
Show file tree
Hide file tree
Showing 8 changed files with 675 additions and 185 deletions.
468 changes: 285 additions & 183 deletions Manifest.toml

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion _layout/foot_highlight.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
<script src="/libs/highlight/highlight.min.js"></script>
<script>hljs.highlightAll();hljs.configure({tabReplace: ' '});</script>
<script>
hljs.highlightAll();
hljs.configure({ tabReplace: " " });
</script>
1 change: 0 additions & 1 deletion _layout/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

<!-- Content appended here -->
</body>
<div class="debug-grid"></div>
<script>
function gridCellDimensions() {
const element = document.createElement("div");
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"dependencies": {
"bluesky-comments": "^0.4.0",
"highlight.js": "^11.10.0"
}
}
32 changes: 32 additions & 0 deletions posts/blueskycomments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@def title = "Bluesky comments sections in Franklin.jl"
@def date = "11/26/2024"
@def tags = ["julia", "BlueSky"]

@def rss_pubdate = Date(2024, 11, 26)

## Adding a comments section from BlueSky

This [post](https://emilyliu.me/blog/comments) shows how you can add Bluesky posts as blog comments in general, and the code was added to an npm package [here](https://www.npmjs.com/package/bluesky-comments). We can steal that code to add it to a Franklin.jl site easily. Download the bsky-comments.js file [here](https://gist.githubusercontent.com/LoueeD/b7dec10b2ea56c825cbb0b3a514720ed/raw/1caceb84ec7612503db3a955a55af4501bcf0150/bsky-comments.js) (or grab my slightly modified one from this website's github repo under posts/bsky-comments.js) and add the following function to your utils.jl file:

```
function hfun_add_bsky_comments(post_url::Vector{String})
post = post_url[1]
html = "
<script src=\"../bsky-comments.js\"></script>
<bsky-comments post=\"$(post)\"></bsky-comments>
"
return html
end
```

Now add the following to the end of your post markdown (data-bluesky-uri can be seen if you click "embed post" on any post, amoung other places):

```
{{ add_bsky_comments data-bluesky-uri_of_your_post_in_quotes }}
```

Which should give you:

{{ add_bsky_comments "at://did:plc:2h5e6whhbk5vnnerqqoi256k/app.bsky.feed.post/3lbupbkcn4s2n" }}

---
251 changes: 251 additions & 0 deletions posts/bsky-comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
class BskyComments extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.visibleCount = 3;
this.thread = null;
this.error = null;
}

connectedCallback() {
const postUri = this.getAttribute("post");
if (!postUri) {
this.renderError("Post URI is required");
return;
}
this.loadThread(postUri);
}

async loadThread(uri) {
try {
const thread = await this.fetchThread(uri);
this.thread = thread;
this.render();
} catch (err) {
this.renderError("Error loading comments");
}
}

async fetchThread(uri) {
if (!uri || typeof uri !== "string") {
throw new Error("Invalid URI: A valid string URI is required.");
}

const params = new URLSearchParams({ uri });
const url = `https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?${params.toString()}`;

try {
const response = await fetch(url, {
method: "GET",
headers: {
Accept: "application/json",
},
cache: "no-store",
});

if (!response.ok) {
const errorText = await response.text();
console.error("Fetch Error: ", errorText);
throw new Error(`Failed to fetch thread: ${response.statusText}`);
}

const data = await response.json();

if (!data.thread || !data.thread.replies) {
throw new Error("Invalid thread data: Missing expected properties.");
}

return data.thread;
} catch (error) {
console.error("Error fetching thread:", error.message);
throw error;
}
}

render() {
if (!this.thread || !this.thread.replies) {
this.renderError("No comments found");
return;
}

const sortedReplies = this.thread.replies.sort(
(a, b) => (b.post.likeCount ?? 0) - (a.post.likeCount ?? 0),
);

const container = document.createElement("div");
container.innerHTML = `
<comments>
<p class="reply-info">
Reply on Bluesky
<a href="https://bsky.app/profile/${this.thread.post?.author?.did}/post/${this.thread.post?.uri.split("/").pop()}" target="_blank" rel="noopener noreferrer">
here
</a> to join the conversation.
</p>
<div id="comments"></div>
<button id="show-more">
Show more comments
</button>
</comments>
`;

const commentsContainer = container.querySelector("#comments");
sortedReplies.slice(0, this.visibleCount).forEach((reply) => {
commentsContainer.appendChild(this.createCommentElement(reply));
});

const showMoreButton = container.querySelector("#show-more");
if (this.visibleCount >= sortedReplies.length) {
showMoreButton.style.display = "none";
}
showMoreButton.addEventListener("click", () => {
this.visibleCount += 5;
this.render();
});

this.shadowRoot.innerHTML = "";
this.shadowRoot.appendChild(container);

if (!this.hasAttribute("no-css")) {
this.addStyles();
}
}

escapeHTML(htmlString) {
return htmlString
.replace(/&/g, "&amp;") // Escape &
.replace(/</g, "&lt;") // Escape <
.replace(/>/g, "&gt;") // Escape >
.replace(/"/g, "&quot;") // Escape "
.replace(/'/g, "&#039;"); // Escape '
}

createCommentElement(reply) {
const comment = document.createElement("div");
comment.classList.add("comment");

const author = reply.post.author;
const text = reply.post.record?.text || "";

comment.innerHTML = `
<div class="author">
<a href="https://bsky.app/profile/${author.did}" target="_blank" rel="noopener noreferrer">
${author.avatar ? `<img width="22px" src="${author.avatar}" />` : ""}
${author.displayName ?? author.handle} @${author.handle}
</a>
<p class="comment-text">${this.escapeHTML(text)}</p>
<small class="comment-meta">
${reply.post.likeCount ?? 0} likes • ${reply.post.replyCount ?? 0} replies
</small>
</div>
`;

if (reply.replies && reply.replies.length > 0) {
const repliesContainer = document.createElement("div");
repliesContainer.classList.add("replies-container");

reply.replies
.sort((a, b) => (b.post.likeCount ?? 0) - (a.post.likeCount ?? 0))
.forEach((childReply) => {
repliesContainer.appendChild(this.createCommentElement(childReply));
});

comment.appendChild(repliesContainer);
}

return comment;
}

renderError(message) {
this.shadowRoot.innerHTML = `<p class="error">${message}</p>`;
}

addStyles() {
const style = document.createElement("style");
style.textContent = `
:host {
--text-color: #1185FE;
--link-color: gray;
--link-hover-color: black;
--comment-meta-color: gray;
--error-color: red;
--reply-border-color: #ccc;
--button-background-color: rgba(0,0,0,0.05);
--button-hover-background-color: rgba(0,0,0,0.1);
--author-avatar-border-radius: 100%;
}
comments {
margin: 0 auto;
padding: 1.2em;
max-width: 720px;
display: block;
background-color: var(--background-color);
color: var(--text-color);
}
.reply-info {
font-size: 14px;
color: var(--text-color);
}
#show-more {
margin-top: 10px;
width: 100%;
padding: 1em;
font: inherit;
box-sizing: border-box;
background: var(--button-background-color);
border-radius: 0.8em;
cursor: pointer;
border: 0;
&:hover {
background: var(--button-hover-background-color);
}
}
.comment {
margin-bottom: 2em;
}
.author {
a {
font-size: 0.9em;
margin-bottom: 0.4em;
display: inline-block;
color: var(--link-color);
&:not(:hover) {
text-decoration: none;
}
&:hover {
color: var(--link-hover-color);
}
img {
margin-right: 0.4em;
border-radius: var(--author-avatar-border-radius);
vertical-align: middle;
}
}
}
.comment-text {
margin: 5px 0;
white-space: pre-line;
}
.comment-meta {
color: var(--comment-meta-color);
display: block;
margin: 1em 0 2em;
}
.replies-container {
border-left: 1px solid var(--reply-border-color);
margin-left: 1.6em;
padding-left: 1.6em;
}
.error {
color: var(--error-color);
}
`;
this.shadowRoot.appendChild(style);
}
}

customElements.define("bsky-comments", BskyComments);
Loading

0 comments on commit 108ccbc

Please sign in to comment.