Client-side comments with Mastodon on a static Hugo website
Posted by
ads
on
Thursday, 2024-05-23 Posted in [Hugo][Myself][Online][Social-Networks][Software]
Ever since I moved this blog to Hugo from Serendipity, I missing the comment option a bit. This is something which a static blogging engine does not offer out of the box. And I did not migrate all the comments from my old blog to my new blog.
Inspired by a couple of postings which integrate Mastodon as “comments” into static blogs, I set out to add this to my own blog.
Show an option to load all comments under this Mastodon posting as comments
Some of the implementations I’ve seen load the comments right away when loading the post. I rather opted for a “click here to load comments” button. This minimizes the number of web requests during website load, and minimizes the number of requests on the Mastodon server.
This blog is using Bootstrap in the theme, I can use CSS to design the button.
Two more features are on my wishlist:
Some of the implementations use one Mastodon config for all posts, I rather implement the config per posting: this allows me to post on different accounts, it also allows to move my primary Mastodon account to another server and keep the old postings/comments.
I want a basic block list: comments (Mastodon replies under my post) shall not be shown when I deem them unfriendly.
Implementation
Basic configuration and Frontmatter
First I need a basic config, which defines my currently used Mastodon account. This goes into config.toml:
This however is only used in the Archetypes template when creating a new post. If both parameters are set, and not empty, this fills in a piece of Frontmatter and sets the currently configured host and username. Both settings can be changed on a per-posting basis later on. The id field is empty for now, can only be filled in once the Mastodon posting is out and the posting id is known.
1
2
3
4
5
6
7
8
9
{{- if and (isset .Site.Params.comments "host") (isset .Site.Params.comments "username") }}{{- if and (ne .Site.Params.comments.host "") (ne .Site.Params.comments.username "") }}comments:host:{{.Site.Params.comments.host }}username:{{.Site.Params.comments.username }}id:blockedcomments:{{end -}}{{end -}}
The files go into static/js, not assets. More about this later.
Add the comments section
The template for a single shown blog posting is in themes/andreas/layouts/_default/single.html. Your path likely differs, I’m using my own theme in my blog. Look for a single.html file in your theme.
At the position where you want to include the comments section, add the following code:
1
{{partial "comments" . }}
This code checks if:
host, username and id are specified in Frontmatter
all three values are set (not nil)
have a certain length
If all of this is true, the partial comments.html is included.
Comments template
The template for the comments goes into layouts/partials/comments.html. Here is the file:
{{- if and (isset .Params.comments "host") (isset .Params.comments "username") (isset .Params.comments "id") }}
{{- if and (.Params.comments.host) (.Params.comments.username) (.Params.comments.id) }}
{{- if and (ge (len (printf "%s" .Params.comments.host)) 5) (ge (len (printf "%s" .Params.comments.username)) 1) (ge (len (printf "%s" .Params.comments.id)) 5) }}
<hr><h2>Comments</h2><noscript><divid="error"> Please enable *JavaScript* to view the comments powered by the Fediverse.
</div></noscript><p>With an account on the Fediverse or Mastodon, you can respond to this <ahref="https://{{ .Params.comments.host }}/@{{ .Params.comments.username }}/{{ .Params.comments.id }}">post</a>. Known replies are displayed below:</p><pid="mastodon-comments-list"class="d-none"><buttontype="button"class="btn btn-outline-secondary"type="button"onclick="respondToVisibility(comments, loadComments)">Load Comments from Fediverse</button><br/><br/>Note: This will load data from <ahref="https://{{ .Params.comments.host }}/">{{ .Params.comments.host }}</a>.</p><p>Learn how this is implemented <aclass="link"href="https://andreas.scherbaum.la/post/2024-05-23_client-side-comments-with-mastodon-on-a-static-hugo-website/">here</a>.</p><scriptsrc="{{ "js/purify.min.js"|relURL}}"></script><scriptsrc="{{ "js/comments.js"|relURL}}"></script><scripttype="text/javascript">varhost='{{ .Params.comments.host }}';varuser='{{ .Params.comments.username }}';varid='{{ .Params.comments.id }}'varcommentsLoaded=false;varblockedToots=[{{-if(isset.Params.comments"blockedcomments")}}{{-if(.Params.comments.blockedcomments)}}{{range.Params.comments.blockedcomments}}{{.}},{{end-}}{{end-}}{{end-}}];varcomments=document.getElementById("mastodon-comments-list");document.addEventListener('DOMContentLoaded',function(){// toggle the "Load Comments" button visible
if(comments.classList.contains('d-none')){comments.classList.remove('d-none');}});</script>{{ $comments := resources.Get "css/comments.scss" }}
{{ $style := $comments | resources.ToCSS | resources.Fingerprint }}
<linkrel="stylesheet"href="{{ $style.Permalink }}"integrity="{{ $style.Data.Integrity }}">{{ end -}}
{{ end -}}
{{ end -}}
You may want to modify this a bit, maybe change the texts. This file does a couple of tasks:
Create a <p> (name: mastodon-comments-list) which is initially hidden, using the Bootstrapd-none class.
This <p> includes the Load Comments from Fediverse button
It loads the purify.min.jsJavaScript
By moving the JavaScript loading here, the scripts are only loaded if the Frontmatter specifies that comments are enabled
It loads the comments.jsJavaScript
It defines the data for the comments
It creates the block list, if it is specified in Frontmatter
It sets the Load Comments from Fediverse button visible
By using document.addEventListener() for DOMContentLoaded: this will be executed once all resources are loaded
By doing this in JavaScript, I make sure that JavaScript is enabled - otherwise the comments will not work
It defines and loads the CSS file comments.scss for styling the comments
By the way, did you know that your Mastodon server provides all the replies and caches all the necessary files? No need for the user to connect more than just your Mastodon server.
JavaScript for loading comments
The JavaScript file resides in static/js/comments.js. I first tried to load this as Hugo Asset but apparently then the script is loaded too late, and the functions in the file are not visible in DOM. Hence it’s in static now. Here is the file:
The entry point for loading the comments is the loadComments() function, which will be called when clicking the Load Comments from Fediverse button.
This first removes the button, and adds a “loading” message. Then loads and shows all the comments.
The render_toot() function includes the blocklist part: it checks if any of the comment URLs are in the defined block list, and if yes then this Toot is not shown.
CSS
Last but not least this also needs some styling. This happens in assets/css/comments.css:
With a few enhancements in my theme, and a couple of additional files, I have a way to add comments for blog postings. All you need is a Mastodon or Fediverse account on a server of your choice.
Feel free to copy this code, and implement in your own blog. And please leave a comment.
I exported my entire Mastodon history, and used a script to add the comment details to each post which was previously posted on Mastodon. This blog is using Tags to link similar posts to each other. The comment-option hashtag is added to all posts which have the comment option (aka: have been posted on Mastodon).
Wrote a small tool find-postings-without-comment-id.py (Source) which reminds me when I forgot to add the comments id to a blog posting.
What is Hugo
Hugo is a popular and open source static website generator. This blog is created using Hugo.
Development started around 2013, and the software is written in Go. The content of the website is written in Markdown, and then compiled into a website using the pre-defined layout. During development of new content, Hugo offers a life preview using the built-in server. It is very flexible, and offers a wide range of available themes and shortcuts.