Display webmentions
This commit is contained in:
parent
30350030aa
commit
91326c03fc
11 changed files with 314 additions and 15 deletions
51
.eleventy.js
51
.eleventy.js
|
|
@ -7,6 +7,15 @@ const markdownItAttrs = require("markdown-it-attrs");
|
|||
const markdownItEleventyImg = require("markdown-it-eleventy-img");
|
||||
const pluginRss = require("@11ty/eleventy-plugin-rss");
|
||||
const embedYouTube = require("eleventy-plugin-youtube-embed");
|
||||
const sanitizeHtml = require("sanitize-html");
|
||||
const metadata = require("./src/_data/metadata.json");
|
||||
|
||||
const sanitize = (html) => {
|
||||
return sanitizeHtml(html, {
|
||||
allowedTags: ['b', 'i', 'em', 'strong', 'a'],
|
||||
allowedAttributes: { 'a': ['href'] }
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = function(eleventyConfig) {
|
||||
eleventyConfig.addPlugin(embedYouTube);
|
||||
|
|
@ -57,7 +66,49 @@ module.exports = function(eleventyConfig) {
|
|||
return '<iframe width="560" height="315" src="https://www.youtube.com/embed/' + videoId + '" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>';
|
||||
});
|
||||
});
|
||||
eleventyConfig.addFilter('webmentionsByUrl', function(webmentions, url) {
|
||||
const allowedTypes = ['in-reply-to', 'like-of', 'repost-of'];
|
||||
const hasRequiredFields = mention => {
|
||||
const { author, published, content } = mention;
|
||||
return author.name && published && content;
|
||||
};
|
||||
const matchesUrl = mention => {
|
||||
return mention['wm-target'] === url;
|
||||
};
|
||||
|
||||
const filteredMentions = webmentions
|
||||
.filter(mention => {
|
||||
const isAllowedType = allowedTypes.includes(mention['wm-property']);
|
||||
if (isAllowedType) {
|
||||
const isReply = mention['wm-property'] === 'in-reply-to';
|
||||
if (isReply) {
|
||||
return matchesUrl(mention) && hasRequiredFields(mention);
|
||||
}
|
||||
return matchesUrl(mention);
|
||||
}
|
||||
}
|
||||
);
|
||||
const sanitizedMentions = filteredMentions.map((mention) => {
|
||||
if (mention?.content?.html) {
|
||||
mention.content.html = sanitize(mention.content.html);
|
||||
const mastodonInstance = metadata.author.mastodon.instance;
|
||||
const mastodonUsername = metadata.author.mastodon.username;
|
||||
const mentionLink = `<a href="https://${mastodonInstance}/@${mastodonUsername}">@${metadata.author.mastodon.username}</a> `;
|
||||
if (mention.content.html.startsWith(mentionLink)) {
|
||||
mention.content.html = mention.content.html.substring(mentionLink.length)
|
||||
}
|
||||
}
|
||||
return mention;
|
||||
});
|
||||
const sortedMentions = sanitizedMentions.reduce((accumulator, mention) => {
|
||||
const key = mention['wm-property'];
|
||||
const curGroup = accumulator[key] ?? [];
|
||||
return { ...accumulator, [key]: [...curGroup, mention] };
|
||||
}, {});
|
||||
|
||||
console.log(sortedMentions);
|
||||
return sortedMentions;
|
||||
});
|
||||
|
||||
const markdownLibrary = markdownIt({ html: true })
|
||||
.use(markdownItAnchor, { slugify })
|
||||
|
|
|
|||
|
|
@ -325,6 +325,81 @@ p:first-of-type {
|
|||
justify-self: end;
|
||||
}
|
||||
|
||||
.webmentions-likes, .webmentions-boosts, .webmentions-replies {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.webmentions-replies li {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.webmentions-replies li:not(:last-child) {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.webmentions-replies li > a,
|
||||
.webmentions-likes li > a,
|
||||
.webmentions-boosts li > a {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.webmentions-likes li > a,
|
||||
.webmentions-boosts li > a {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
.webmentions-replies li > a {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.webmentions-replies .from-mastodon > a::before,
|
||||
.webmentions-likes .from-mastodon > a::before,
|
||||
.webmentions-boosts .from-mastodon > a::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background: url('/images/mastodon-logo.png') no-repeat center center;
|
||||
background-size: contain;
|
||||
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5));
|
||||
}
|
||||
.webmentions-replies .from-mastodon > a::before {
|
||||
bottom: 0.25rem;
|
||||
left: 0.25rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
.webmentions-likes .from-mastodon > a::before,
|
||||
.webmentions-boosts .from-mastodon > a::before {
|
||||
bottom: 0.15rem;
|
||||
left: 0.15rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
.webmentions-replies li img {
|
||||
width: 6rem;
|
||||
}
|
||||
.webmentions-replies li .content {
|
||||
flex: 1;
|
||||
}
|
||||
.webmentions-replies li .content .published-datetime {
|
||||
font-size: 0.8em;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.webmentions-replies li .content > a {
|
||||
color: var(--text);
|
||||
}
|
||||
.webmentions-likes, .webmentions-boosts {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.webmentions-likes li img, .webmentions-boosts li img {
|
||||
width: 4rem;
|
||||
}
|
||||
.mention-heading {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
100
package-lock.json
generated
100
package-lock.json
generated
|
|
@ -1621,6 +1621,12 @@
|
|||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"dev": true
|
||||
},
|
||||
"is-promise": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
||||
|
|
@ -2173,6 +2179,12 @@
|
|||
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
|
||||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true
|
||||
},
|
||||
"napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
|
|
@ -2396,6 +2408,12 @@
|
|||
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
|
||||
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
|
|
@ -2417,6 +2435,17 @@
|
|||
"semver-compare": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"posthtml": {
|
||||
"version": "0.16.6",
|
||||
"resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz",
|
||||
|
|
@ -2765,6 +2794,71 @@
|
|||
"truncate-utf8-bytes": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"sanitize-html": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz",
|
||||
"integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"htmlparser2": "^8.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"parse-srcset": "^1.0.2",
|
||||
"postcss": "^8.3.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3"
|
||||
}
|
||||
},
|
||||
"entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"dev": true
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||
|
|
@ -2894,6 +2988,12 @@
|
|||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"dev": true
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
"markdown-it-attrs": "^4.1.4",
|
||||
"markdown-it-eleventy-img": "^0.9.0",
|
||||
"markdown-it-obsidian": "github:raddevon/markdown-it-obsidian",
|
||||
"sanitize-html": "^2.13.0",
|
||||
"simpledotcss": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
src/_data/metadata.json
Normal file
14
src/_data/metadata.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"title": "devon.lol",
|
||||
"description": "I write essays to no one about video games, technology, the internet, and other things in the world.",
|
||||
"language": "en-us",
|
||||
"url": "https://devon.lol/",
|
||||
"author": {
|
||||
"name": "Devon Campbell",
|
||||
"email": "devon@devon.lol",
|
||||
"mastodon": {
|
||||
"username": "RadDevon",
|
||||
"instance": "techhub.social"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,10 @@ const EleventyFetch = require("@11ty/eleventy-fetch");
|
|||
module.exports = async function fetchMentions() {
|
||||
const url = `https://webmention.io/api/mentions.jf2?domain=devon.lol&token=${process.env.WEBMENTIONSIO_TOKEN}`;
|
||||
|
||||
const mentions = await EleventyFetch(url, {
|
||||
const mentionsResponse = await EleventyFetch(url, {
|
||||
duration: "1s",
|
||||
type: "json",
|
||||
});
|
||||
console.log('mentions:', mentions);
|
||||
console.log('mentions.children:', mentions.children);
|
||||
return mentions.children;
|
||||
const mentions = mentionsResponse.children;
|
||||
return mentions;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,10 +22,11 @@ layout: base.njk
|
|||
{{ content | safe }}
|
||||
</div>
|
||||
<a class="u-url hidden" href="{{ page.url }}">Permalink</a>
|
||||
{% include 'webmentions.njk' %}
|
||||
</article>
|
||||
<footer>
|
||||
<p>Talk to me <a href="https://techhub.social/@RadDevon" rel="me">on Mastodon.</a></p>
|
||||
<a href="https://techhub.social/@RadDevon" rel="me"><img src="/images/mastodon-logo.png" alt="Mastodon logo"></a>
|
||||
<p>Talk to me <a href="https://{{ metadata.author.mastodon.instance }}/@{{ metadata.author.mastodon.username }}" rel="me">on Mastodon.</a></p>
|
||||
<a href="https://{{ metadata.author.mastodon.instance }}/@{{ metadata.author.mastodon.username }}" rel="me"><img src="/images/mastodon-logo.png" alt="Mastodon logo"></a>
|
||||
|
||||
<a href="mailto:devon@devon.lol" rel="me" class="hidden">devon@devon.lol</a>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
|
||||
<link rel="me" href="https://techhub.social/@RadDevon" />
|
||||
<link rel="me" href="https://{{ metadata.author.mastodon.instance }}/@{{ metadata.author.mastodon.username }}" />
|
||||
<link rel="me" href="https://github.com/raddevon" />
|
||||
|
||||
<link rel="webmention" href="https://webmention.io/devon.lol/webmention" />
|
||||
|
|
|
|||
64
src/_includes/webmentions.njk
Normal file
64
src/_includes/webmentions.njk
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{% set pageAbsoluteUrl = page.url | absoluteUrl(metadata.url) %}
|
||||
{% set mentions = webmentions | webmentionsByUrl(pageAbsoluteUrl) %}
|
||||
{% set mastodonSourcePrefix = r/^https:\/\/brid\.gy\/\w+\/mastodon\// %}
|
||||
|
||||
{% if mentions['in-reply-to'] | length %}
|
||||
<h2 class="mention-heading">Replies</h2>
|
||||
<ol class="webmentions-replies">
|
||||
{%- for reply in mentions['in-reply-to'] %}
|
||||
<li class="{% if mastodonSourcePrefix.test(reply['wm-source']) %}from-mastodon{% endif %}">
|
||||
<a rel="noopener" href="{{ reply.author.url }}">
|
||||
<img
|
||||
src="{{ reply.author.photo }}"
|
||||
title="{{ reply.author.name }}"
|
||||
alt="{{ reply.author.name }}'s avatar"
|
||||
>
|
||||
</a>
|
||||
<div class="content">
|
||||
<a href="{{ reply.url }}">
|
||||
<time
|
||||
class="published-datetime"
|
||||
datetime="{{ reply.published }}"
|
||||
aria-label="Published on {{ reply.published | formatDatetimeForHumans }}"
|
||||
>{{ reply.published | formatDatetimeForHumans }}</time>
|
||||
</a>
|
||||
<p>{{ reply.content.html | safe }}</p>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
|
||||
{% if mentions['repost-of'] | length %}
|
||||
<h2 class="mention-heading">Boosts</h2>
|
||||
<ol class="webmentions-boosts">
|
||||
{%- for boost in mentions['repost-of'] %}
|
||||
<li class="{% if mastodonSourcePrefix.test(boost['wm-source']) %}from-mastodon{% endif %}">
|
||||
<a rel="noopener" href="{{boost.author.url}}">
|
||||
<img
|
||||
src="{{boost.author.photo}}"
|
||||
title="{{boost.author.name}}"
|
||||
alt="{{boost.author.name}}'s avatar"
|
||||
>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor%}
|
||||
</ol>
|
||||
{% endif %}
|
||||
|
||||
{% if mentions['like-of'] | length %}
|
||||
<h2 class="mention-heading">Likes</h2>
|
||||
<ol class="webmentions-likes">
|
||||
{%- for like in mentions['like-of'] %}
|
||||
<li class="{% if mastodonSourcePrefix.test(like['wm-source']) %}from-mastodon{% endif %}">
|
||||
<a rel="noopener" href="{{like.author.url}}">
|
||||
<img
|
||||
src="{{like.author.photo}}"
|
||||
title="{{like.author.name}}"
|
||||
alt="{{like.author.name}}'s avatar"
|
||||
>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor%}
|
||||
</ol>
|
||||
{% endif %}
|
||||
|
|
@ -2,14 +2,6 @@
|
|||
permalink: feed.xml
|
||||
eleventyExcludeFromCollections: true
|
||||
date: git Last Modified
|
||||
metadata:
|
||||
title: devon.lol
|
||||
description: I write essays to no one about video games, technology, the internet, and other things in the world.
|
||||
language: en-us
|
||||
url: https://devon.lol/
|
||||
author:
|
||||
name: Devon Campbell
|
||||
email: devon@devon.lol
|
||||
---
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ date: git Last Modified
|
|||
|
||||
**Last updated {{ page.date | formatDatetimeForHumans }}**
|
||||
|
||||
This site now both sends and receives WebMentions!
|
||||
|
||||
The Arc browser scares me. All the AI features and their insistence on referring to users as "members" just makes me feel like they're poised to make changes I couldn't live with. I decided to get ahead of them and [find an alternative browser](/blog/web-browser-review-06-2024).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue