Farcaster as a comment system
Inspired by Paragraph I wanted to use Farcaster to add Disqus style comments to my digital garden.
Previously I handled “comments” using a Discuss on Twitter
CTA that linked to the search results for the posts public URL. The Twitter API did not factor into the equation, all conversation was abstracted away from the actual content. Simple, but far from ideal.
const comments = `https://mobile.twitter.com/search?q=${encodeURIComponent(
`https://your-domain.com/${slug}`
)}`;
The Farcaster approach is fairly straightforward — use the Searchcaster api to query for the public URL of a page. If any results are returned, display them (permissionlessly!) below the content. The one caveat is that the api updates every 30 minutes, so sometimes you need to wait before comments load on a post.
This takes two calls to get the basic functionality going. getTopLevelCasts
and getMerkleRootCasts
. Note: I am using Next13 for this, which extends fetch()
with some additional features.
getTopLevelCasts
returns any cast that directly mentions the content URL.
// getTopLevelCasts.tsx
export default async function getTopLevelCasts(path: string, slug: string) {
const uri = `https://searchcaster.xyz/api/search?text=your-domain.com/${slug}`;
const res = await fetch(uri, {
next: {
revalidate: 1 * 30,
},
}).then((res) => res.json());
const casts = res.casts;
return casts;
}
getMerkleRootCasts
is used in a loop, where each topLevelCast
's merkleRoot
is passed in to fetch any descendant casts.
// getMerkleRootCasts.tsx
export default async function getMerkleRoot(merkleRoot: string) {
const uri = `https://searchcaster.xyz/api/search?merkleRoot=${merkleRoot}`;
const res = await fetch(uri, {
next: {
revalidate: 1 * 30,
},
}).then((res) => res.json());
const casts = res.casts;
return casts;
}
Ultimately I only need the merkleRoot
from getTopLevelCasts
. I’m not rendering any content from that call directly. getMerkleRootCasts
returns the topLevelCasts
and its descendants, which makes the list structure a bit cleaner.
// comments.tsx
<ul>
{_topLevelComments.map((comment) => {
return (
<li key={comment.merkleRoot}>
<CommentBody {...comment} />
{comment.numReplyChildren > 0 && (
<ul>
{_merkleRootComments[0]
.map((comment) => {
return (
<li key={comment.merkleRoot}>
<CommentBody {...comment} />
</li>
);
})
.slice(0, -1)}
</ul>
)}
</li>
);
})}
</ul>
Comment threads can be structured various ways. This may change down the line as this digital garden grows, but it is perfectly sufficient for now. I encourage you to explore the response from the Searchcaster API and experiment with how to best display your threads.
When a post URL is shared on Farcaster (a sufficiently decentralized social network) the comments are displayed permissionessly below.
Missed that Searchcaster updates every minute now. That's a nice boost for my Farcaster comment threads! Thanks, @greg! https://iammatthias.com/md/1670705920366
This is great!
🫡
Some notes on using Farcaster as a comment system, inspired by Paragraph: https://portfolio.iammatthias.com/md/1670705920366
Published using a weird Obsidian based system I'm exploring. Zettelkasten tagged markdown docs with some YAML, stored in a private git repo. The repo is queried using GraphQL, and there is workflow that pushes assets to a Cloudflare R2 bucket to make them public.
Oooh. This nerd sniped me. Where can I learn more? Feels Gwern-esque
I'll do a write up on the Obsidian bit once I have things a bit more polished. It is a bit of a mess right now (but it works!). I got the idea from my normal Obsidian set up which syncs between my devices with git. Figured if the content was on Github, I should be able to get it from the GraphQL endpoint.
The YAML is parsed with grey-matter, and the markdown rendered with react-remark. Never got remarks frontmatter plugin to work.
There's also some really hacky leverage of html data-types to transform content into custom components for things like NFTs. remark-directives is a pain, and I'm hoping to avoid the dependency hell of MDX.