Adding a Comments Section with Mastodon
Mastodon
From Wikipedia, the free encyclopedia
Mastodon is a free and open-source self-hosted social networking service that allows anyone to host their own server node in the network, with user bases distributed across different servers.
These servers are interconnected as a federated social network, allowing their users to interact seamlessly with one another. Mastodon is part of a larger federation that enables users to interact with other free platforms such as PeerTube and Friendica that support the same protocols.
Adding a Comments Section with Mastodon
One of the challenges with static websites is adding a comments section. There are various options for doing this. One simple solution is Disqus. However, Disqus was never an appealing option for me because it is heavy, loads a lot of unnecessary scripts on pages, and most importantly is not free software. For more alternatives, you can see this post by Mehdi Sadeghi.
Thanks to @carl for writing the code and the report in this post, you can now send me your comments using a Mastodon account, and view what others have written.
JavaScript Code
Since the original code was written for #HUGO, I made minor modifications to make it compatible with #JEKYLL and placed it in the mastodon.html file inside the _includes folder:
<div class="page-content">
<h2>Comments</h2>
<p>You can view the comments for this post on Mastodon and <a class="link" href="https://{{ page.mastodon.host }}/@{{ page.mastodon.username }}/{{ page.mastodon.id }}" target="_blank" rel="noopener noreferrer"> here</a>. To write your own comment, click the link below.</p>
<p><a class="button" href="https://{{ page.mastodon.host }}/@{{ page.mastodon.username }}/{{ page.mastodon.id }}" target="_blank" rel="noopener noreferrer">Write a comment</a></p>
<p></p>
<p id="mastodon-comments-list"><a id="load-comment" class="button">View comments</a></p>
<div id="comments-wrapper">
<noscript><p>JavaScript must be enabled for this section, or view the <a href="https://{{ page.mastodon.host }}/@{{ page.mastodon.username }}/{{ page.mastodon.id }}" target="_blank" rel="noopener noreferrer">original post</a> on Mastodon</p></noscript>
</div>
<script src="/assets/js/purify.min.js"></script>
<script type="text/javascript">
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function emojify(input, emojis) {
let output = input;
emojis.forEach(emoji => {
let picture = document.createElement("picture");
let source = document.createElement("source");
source.setAttribute("srcset", escapeHtml(emoji.url));
source.setAttribute("media", "(prefers-reduced-motion: no-preference)");
let img = document.createElement("img");
img.className = "emoji";
img.setAttribute("src", escapeHtml(emoji.static_url));
img.setAttribute("alt", `:${ emoji.shortcode }:`);
img.setAttribute("title", `:${ emoji.shortcode }:`);
img.setAttribute("width", "20");
img.setAttribute("height", "20");
picture.appendChild(source);
picture.appendChild(img);
output = output.replace(`:${ emoji.shortcode }:`, picture.outerHTML);
});
return output;
}
function loadcomments() {
document.getElementById("load-comment").innerHTML = "Loading comments";
fetch('https://{{ page.mastodon.host }}/api/v1/statuses/{{ page.mastodon.id }}/context')
.then(function(response) {
return response.json();
})
.then(function(data) {
let descendants = data['descendants'];
if(
descendants &&
Array.isArray(descendants) &&
descendants.length > 0
) {
document.getElementById('mastodon-comments-list').innerHTML = "";
descendants.forEach(function(reply) {
if( reply.account.display_name.length > 0 ) {
reply.account.display_name = escapeHtml(reply.account.display_name);
reply.account.display_name = emojify(reply.account.display_name, reply.account.emojis);
} else {
reply.account.display_name = reply.account.username;
};
reply.content = emojify(reply.content, reply.emojis);
reply.created_at = new Date( reply.created_at ).toLocaleString('en-US', {
dateStyle: "long",
timeStyle: "short",
});
let instance = "";
if( reply.account.acct.includes("@") ) {
instance = reply.account.acct.split("@")[1];
} else {
instance = "{{ page.mastodon.host }}";
}
mastodonComment =
`<div class="mastodon-comment">
<div class="avatar">
<img src="${escapeHtml(reply.account.avatar_static)}" height=60 width=60 alt="">
</div>
<div class="content">
<div class="author" title="profile: @${ reply.account.username }@${ instance }">
<a href="${reply.account.url}" target="_blank" rel="external nofollow">
<span>${reply.account.display_name}</span>
<span class="instance">${instance}</span>
</a>
</div>
<div title="View comment on ${ instance }">
<a class="date" href="${reply.uri}" target="_blank" rel="external nofollow">
${reply.created_at}
</a>
</div>
<div class="mastodon-comment-content">${reply.content}</div>
</div>
</div>`;
document.getElementById('mastodon-comments-list').appendChild(DOMPurify.sanitize(mastodonComment, {'RETURN_DOM_FRAGMENT': true}));
});
} else {
document.getElementById('mastodon-comments-list').innerHTML = "<p>No comments yet</p>";
}
});
}
document.getElementById("load-comment").addEventListener("click", loadcomments);
</script>
</div>Then in the post layout, I added a condition so that whenever mastodon is present in the post’s frontmatter, the comments section is included:
{% if page.mastodon %}
{% include mastodon.html %}
{% endif %}And in the frontmatter, mastodon should be added with this structure:
mastodon:
host: mas.to
username: mz
id: 105582586560918183Where the following must be specified:
- host: the Mastodon server address
- username: the Mastodon username
- id: the unique ID of the toot related to the post
Finally, I used these few lines of CSS for better comment display.
You can try the final result here:
Comments
You can view the comments for this post on Mastodon here . Click the link below to write a comment.