Nothing brings a site's users together (for better or worse) like a comment section. But comments are complex (replies of replies, anyone?), so third party plugins are definitely best suited to handle all the fun stuff that comes with them. So off I went to search for one to use.
Disqus was the first plugin that came to mind and turned out to be best suited for the task at hand, with a few caveats that I'll explain.
The first big gotcha is that it does not allow for much customization of its embed and thus couldn't be simply plugged in and customized to match the site's look and feel. Phooey.
Luckily the Disqus API has plenty of resources and I was able to use it to help satisfy my requirements:
- replying to
- and flagging comments.
Disqus gives prospective API users a fair warning though:
...[the API] is designed as a supplemental data source, and as such won't provide an easy method for replacing the comments embed.
Now, I wouldn't say it was easy, but it wasn't difficult to fully deliver the feature with the help of a database and a couple of weeks of dev work. Happy clients first!
Here's how I went about it.
Disqus lays out its data relationships nicely, and given the structure, each article on my client's site represents a Thread that can contain Posts (comments and their replies). So far, so good.
But I couldn't send users over to Disqus to authenticate and then back to the site so they could make comments on their own behalf (it wouldn't be a user-friendly flow as they already have to sign into the site with Auth0).
So I had to set the site itself up to be the lone, authenticated Disqus User taking actions on behalf of the site's users (see the "Authenticating as the Account Owner" section here) by doing the following:
- getting a Disqus account
- creating a Disqus site, which represents a Forum in the Disqus data model
- registering a Disqus API application and using the API token, key and secret provided as environment variables in the code where needed
When a user submits a comment on an article using my fancy, 100% custom form, I hit the Disqus API to create a new Thread (only for an article's very first comment) and then create a Post under that Thread. Subsequent comments on that article are created under the same Thread.
In order to tie an "Article" item (in my Contentful model) to its comments (in Disqus), I just save the following in a comment_sections table upon comment creation:
- the unique id that Contentful automatically provides with every item in a model (call it cms_id)
- the unique id Disqus provides on creation of a new Thread (comment_section_id)
I use a second table called comments to tie each comment to the article it was made on (using the comment_section_id), as well as to persist the commenter's name (among other things).
Loading an Article's Comments
When an article is loaded, I simply query my table for the relevant comment section using the article's cms_id, then retrieve its comments from Disqus using the comment_section_id in the endpoint's thread param. Finally, I augment each of the fetched comments with data from the comments table.
Editing and Deleting Comments
I was able to easily use the /posts/update and /posts/remove endpoints to implement editing and deleting. It was mainly about the UI for these pieces of functionality.
Implementing the ability to reply to a comment was fairly straightforward. Use the same /posts/create endpoint that's called when a top-level comment is made, but set the parent param to the id of the comment being replied to.
Now retrieving, processing and properly displaying replies was a large chunk of the overall work. I ended up having to loop through comments twice to set replies. Here's a visual of the logic:
Throw in some recursive component action and I was able to support n-level comment nesting...not something you ever want to do twice!
The requirement here was to simply allow likes ("recommendations") and display the number of likes each comment has. Surpisingly, no endpoint supports this (there is a likes field in the response data, but that represents the number of likes made on the comment from an actual Disqus embed, not a custom UI). So I just use my comments table for this functionality.
For this site, any comment made by someone with a certain Auth0 metadata field is automatically pinned. There's no Disqus endpoint for pinning either, so I leverage my comments table again to persist that metadata field for each comment.
From there, I just give special treatment to any pinned comment on the frontend (i.e. extract it out and render it above the rest of the comments). Not ideal to have to do this manually with no help from the API, but it worked out.
The simple ask here was just a call-to-action on each comment allowing users to flag other people's comments with no further input (such as the reason for flagging), and for the comment to be hidden immediately upon flagging.
I went in and tried to use the /posts/report API for this, but I learned that Disqus users cannot report their own comments (makes sense). And because the site itself is the User, from Disqus' perspective, calls to that endpoint look like the User trying to report itself.
You can mark your own comment as spam in Disqus, so I had to use the /posts/spam endpoint instead.
Misnomer aside, to handle flagged (spam) comments, the site admin just looks under a "Spam" tab on the Disqus dashboard and approves or leaves them there as desired. Approved comments get cleared out of "Spam" while comments left in there are held out of response data and remain hidden to users.
An important thing to note is that any time a comment is added, edited, deleted or flagged, I go and retrieve the article's comments fresh from Disqus and get a re-render. This allows Disqus to remain the single source of truth and reduces the burden of state management in my code.
Also of note is that the Disqus API is slow to return updated data (e.g. it takes a few seconds between calling the endpoint to create a comment and getting that comment back in a fetch), so I unfortunately have to poll for updates and show a small pending state to the user at times.
Using the Disqus API for a custom comment section UI isn't techincally supposed to be done, but I wanted to share my experience with it for anyone out there that may be facing a similar task.
I always enjoy a learning experience, and a happy client in the end made it all worthwhile!