fix webmentions & css

This commit is contained in:
Kathleen Fitzpatrick
2024-11-29 11:14:42 -05:00
parent d3997cbfb2
commit 891ed350b9
12 changed files with 7431 additions and 82 deletions

BIN
_cache/.DS_Store vendored Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import sanitizeHtml from 'sanitize-html';
export default function(eleventyConfig) { export default function(eleventyConfig) {
eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => { eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => {
@@ -6,6 +7,10 @@ export default function(eleventyConfig) {
return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }).toFormat(format || "dd LLLL yyyy"); return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }).toFormat(format || "dd LLLL yyyy");
}); });
eleventyConfig.addFilter('dateFromTimestamp', (timestamp) => {
return DateTime.fromISO(timestamp, { zone: 'America/New_York' }).toJSDate()
})
eleventyConfig.addFilter("htmlDateString", (dateObj) => { eleventyConfig.addFilter("htmlDateString", (dateObj) => {
// dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string // dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat('yyyy-LL-dd'); return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat('yyyy-LL-dd');
@@ -36,7 +41,7 @@ export default function(eleventyConfig) {
const clean = (entry) => { const clean = (entry) => {
const { html, text } = entry.content const { html, text } = entry.content
if (html) { if (html) {
entry.content.value = sanitizeHTML(text, allowedHTML) entry.content.value = sanitizeHtml(text, allowedHTML)
}; };
return entry; return entry;
} }
@@ -82,6 +87,7 @@ export default function(eleventyConfig) {
return mentions.filter(entry => !!entry['mentionType']) return mentions.filter(entry => !!entry['mentionType'])
}); });
// Return the smallest number argument // Return the smallest number argument
eleventyConfig.addFilter("min", (...numbers) => { eleventyConfig.addFilter("min", (...numbers) => {
return Math.min.apply(null, numbers); return Math.min.apply(null, numbers);
@@ -97,3 +103,4 @@ export default function(eleventyConfig) {
}); });
}; };

View File

@@ -1,95 +1,91 @@
import "dotenv/config.js"; import Fetch from "@11ty/eleventy-fetch";
import 'dotenv/config';
import fs from "fs"; import fs from "fs";
import fetch from "node-fetch";
import unionBy from "lodash-es/unionBy.js"; import unionBy from "lodash-es/unionBy.js";
import domain from "./metadata.js"; import domain from "./metadata.js";
// Configuration Parameters // Configuration Parameters
const CACHE_DIR = '_cache'; const CACHE_DIR = '_cache';
const API_ORIGIN = 'https://webmention.io/api/mentions.jf2'; const API_ORIGIN = 'https://webmention.io/api/mentions.jf2';
const TOKEN = process.env.WEBMENTION_IO_TOKEN; const TOKEN = process.env.WEBMENTION_IO_TOKEN;
async function fetchWebmentions(since, perPage = 10000) { async function fetchWebmentions(since, perPage = 10000) {
// If we don't have a domain name or token, abort if (!domain || !TOKEN) {
if (!domain || !TOKEN) {
console.warn('>>> unable to fetch webmentions: missing domain or token'); console.warn('>>> unable to fetch webmentions: missing domain or token');
return false; return false;
} }
let url = `${API_ORIGIN}?token=${TOKEN}&per-page=${perPage}`;
if (since) url += `&since=${since}`; // only fetch new mentions
const response = await fetch(url);
if (response.ok) {
let feed = await response.json();
console.log(`>>> ${feed.children.length} new webmentions fetched from ${API_ORIGIN}`);
return feed;
}
let url = `${API_ORIGIN}?domain=${domain}&token=${TOKEN}&per-page=${perPage}`; return null;
if (since) url += `&since=${since}`; // only fetch new mentions
const response = await fetch(url);
if (response.ok) {
const feed = await response.json();
console.log(`>>> ${feed.children.length} new webmentions fetched from ${API_ORIGIN}`);
return feed;
}
return null;
} }
// Merge fresh webmentions with cached entries, unique per id // Merge fresh webmentions with cached entries, unique per id
function mergeWebmentions(a, b) { function mergeWebmentions(a, b) {
return unionBy(a.children, b.children, 'wm-id'); return unionBy(a.children, b.children, 'wm-id');
} }
// save combined webmentions in cache file // save combined webmentions in cache file
function writeToCache(data) { function writeToCache(data) {
const filePath = `${CACHE_DIR}/webmentions.json`; const filePath = `${CACHE_DIR}/webmentions.json`;
const fileContent = JSON.stringify(data, null, 2); const fileContent = JSON.stringify(data, null, 2);
// create cache folder if it doesnt exist already // create cache folder if it doesnt exist already
if (!fs.existsSync(CACHE_DIR)) { if (!fs.existsSync(CACHE_DIR)) {
fs.mkdirSync(CACHE_DIR); fs.mkdirSync(CACHE_DIR);
} }
// write data to cache json file // write data to cache json file
fs.writeFile(filePath, fileContent, err => { fs.writeFile(filePath, fileContent, err => {
if (err) throw err; if (err) throw err;
console.log(`>>> webmentions cached to ${filePath}`); console.log(`>>> webmentions cached to ${filePath}`);
}); });
} }
// get cache contents from json file // get cache contents from json file
function readFromCache() { function readFromCache() {
const filePath = `${CACHE_DIR}/webmentions.json`; const filePath = `${CACHE_DIR}/webmentions.json`;
if (fs.existsSync(filePath)) { if (fs.existsSync(filePath)) {
const cacheFile = fs.readFileSync(filePath); const cacheFile = fs.readFileSync(filePath);
return JSON.parse(cacheFile); return JSON.parse(cacheFile);
}; };
// no cache found // no cache found
return { return {
lastFetched: null, lastFetched: null,
children: [] children: []
}; };
} }
export default async function() { export default async function() {
console.log('>>> Reading webmentions from cache...'); console.log('>>> Reading webmentions from cache...');
const cache = readFromCache(); const cache = readFromCache();
if (cache.children.length) { if (cache.children.length) {
console.log(`>>> ${cache.children.length} webmentions loaded from cache`); console.log(`>>> ${cache.children.length} webmentions loaded from cache`);
}; };
// Only fetch new mentions in production // Only fetch new mentions in production
if (process.env.ELEVENTY_ENV === 'production') { if (process.env.ELEVENTY_ENV === 'production') {
console.log('>>> Checking for new webmentions...'); console.log('>>> Checking for new webmentions...');
const feed = await fetchWebmentions(cache.lastFetched); const feed = await fetchWebmentions(cache.lastFetched);
if (feed) { if (feed) {
const webmentions = { const webmentions = {
lastFetched: new Date().toISOString(), lastFetched: new Date().toISOString(),
children: mergeWebmentions(cache, feed) children: mergeWebmentions(cache, feed)
} }
writeToCache(webmentions); writeToCache(webmentions);
return webmentions; return webmentions;
}
} }
}
return cache; return cache;
} }

View File

@@ -31,7 +31,10 @@
{#- Add an arbitrary string to the bundle #} {#- Add an arbitrary string to the bundle #}
{%- css %}/* This is an arbitrary CSS string added to the bundle */{% endcss %} {%- css %}/* This is an arbitrary CSS string added to the bundle */{% endcss %}
{#- Add the contents of a file to the bundle #} {#- Add the contents of a file to the bundle #}
{%- css %}{% include "public/css/index.css" %}{% endcss %} {% css %}{% include "public/css/index.css" %}{% endcss %}
{% css %}{% include "public/css/webmentions.css" %}{% endcss %}
{% css %}{% include "public/css/message-box.css" %}{% endcss %}
{% css %}{% include "public/css/prism-diff.css" %}{% endcss %}
{#- Or you can add from node_modules #} {#- Or you can add from node_modules #}
{# {%- css %}{% include "node_modules/prismjs/themes/prism-okaidia.css" %}{% endcss %} #} {# {%- css %}{% include "node_modules/prismjs/themes/prism-okaidia.css" %}{% endcss %} #}

View File

@@ -27,7 +27,8 @@ layout: layouts/base.njk
<hyvor-talk-comments website-id="9100" page-id="{{ permalink }}"></hyvor-talk-comments> <hyvor-talk-comments website-id="9100" page-id="{{ permalink }}"></hyvor-talk-comments>
{% include 'webmentionlist.njk' %} {% include 'webmentions.njk' %}
{%- endif %} {%- endif %}
{%- endif %} {%- endif %}

View File

@@ -5,37 +5,36 @@
<h4>{{ likes.length }} Like{% if likes.length != 1 %}s{% endif %}</h4> <h4>{{ likes.length }} Like{% if likes.length != 1 %}s{% endif %}</h4>
<div class="webmentions__facepile"> <div class="webmentions__facepile">
{% for webmention in likes %} {% for webmention in likes %}
{% if webmention.url != "" %} {% if webmention.url != "" %}
<a class="h-card u-url link-u-exempt" href="{{ webmention.url }}" target="_blank" rel="noopener noreferrer"> <a class="h-card u-url link-u-exempt" href="{{ webmention.url }}" target="_blank" rel="noopener noreferrer">
{% endif %} {% endif %}
{% if webmention.author.photo %} {% if webmention.author.photo %}
<img src="{{ webmention.author.photo }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" alt="" class="webmentions__face" loading="lazy" /> <img eleventy:ignore class="webmention__author__photo" src="{{ webmention.author.photo }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" class="webmentions__face" loading="lazy" />
{% else %} {% else %}
<img class="webmention__author__photo" src="{{ '/img/default_avatar.png' | url }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" alt="" class="webmentions__face" /> <img eleventy:ignore class="webmention__author__photo" src="{{ '/img/default_avatar.png' | url }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" class="webmentions__face" />
{% endif %} {% endif %}
{% if webmention.url != "" %} {% if webmention.url != "" %}
</a> </a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
{% if reposts.length > 0 %}
{% if reposts.length > 0 %}
<h4>{{ reposts.length }} Repost{% if reposts.length != 1 %}s{% endif %}</h4> <h4>{{ reposts.length }} Repost{% if reposts.length != 1 %}s{% endif %}</h4>
<div class="webmentions__facepile"> <div class="webmentions__facepile">
{% for webmention in reposts %} {% for webmention in reposts %}
{% if webmention.url != "" %} {% if webmention.url != "" %}
<a class="h-card u-url link-u-exempt" href="{{ webmention.url }}" target="_blank" rel="noopener noreferrer"> <a class="h-card u-url link-u-exempt" href="{{ webmention.url }}" target="_blank" rel="noopener noreferrer">
{% endif %} {% endif %}
{% if webmention.author.photo %} {% if webmention.author.photo %}
<img src="{{ webmention.author.photo }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" alt="" class="webmentions__face" loading="lazy" /> <img eleventy:ignore class="webmention__author__photo" src="{{ webmention.author.photo }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" class="webmentions__face" loading="lazy" />
{% else %} {% else %}
<img class="webmention__author__photo" src="{{ '/img/default_avatar.png' | url }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" alt="" class="webmentions__face" /> <img eleventy:ignore class="webmention__author__photo" src="{{ '/img/default_avatar.png' | url }}" alt="{{ webmention.author.name }}" title="{{ webmention.author.name }}" class="webmentions__face" />
{% endif %} {% endif %}
{% if webmention.url != "" %} {% if webmention.url != "" %}
</a> </a>

View File

@@ -3,19 +3,19 @@
{% if webmention.author %} {% if webmention.author %}
<a class="webmention__author p-author h-card u-url" href="{{ webmention.url }}" target="_blank" rel="noopener noreferrer"> <a class="webmention__author p-author h-card u-url" href="{{ webmention.url }}" target="_blank" rel="noopener noreferrer">
{% if webmention.author.photo %} {% if webmention.author.photo %}
<img class="webmention__author__photo u-photo" src="{{ webmention.author.photo }}" alt="{{ webmention.author.name }}"> <img eleventy:ignore class="webmention__author__photo u-photo" src="{{ webmention.author.photo }}" alt="{{ webmention.author.name }}">
{% else %} {% else %}
<img class="webmention__author__photo" src="{{ '/img/webmention-avatar-default.svg' | url }}" alt=""> <img eleventy:ignore class="webmention__author__photo" src="{{ '/blog/img/default_avatar.png' | url }}" alt="">
{% endif %} {% endif %}
<strong class="p-name">{{ webmention.author.name }}</strong> <strong class="p-name">{{ webmention.author.name }}</strong>
</a> </a>
{% else %} {% else %}
<span class="webmention__author"> <span class="webmention__author">
<img class="webmention__author__photo" src="{{ '/img/webmention-avatar-default.svg' | url }}" alt=""> <img class="webmention__author__photo" src="{{ '/blog/img/default_avatar.png' | url }}" alt="">
<strong>Anonymous</strong> <strong>Anonymous</strong>
</span> </span>
{% endif %} {% endif %}
{% if webmention.published %} {% if webmention.published %}
<time class="webmention__pubdate dt-published" datetime="{{ webmention.published }}">{{ webmention.published | dateFromTimestamp | readableDate("dd LLL yyyy - HH:mm") }}</time> <time class="webmention__pubdate dt-published" datetime="{{ webmention.published }}">{{ webmention.published | dateFromTimestamp | readableDate("dd LLL yyyy - HH:mm") }}</time>
{% endif %} {% endif %}
@@ -23,4 +23,4 @@
<div class="webmention__content p-content"> <div class="webmention__content p-content">
{{ webmention.content.text | safe }} {{ webmention.content.text | safe }}
</div> </div>
</article> </article>

View File

@@ -1,22 +1,22 @@
{%- set absoluteUrl -%}{{ page.url | url | absoluteUrl(metadata.url) }}{%- endset -%} {%- set absoluteUrl -%}{{ page.url | url | absoluteUrl(metadata.url) }}{%- endset -%}
{%- set mentions = webmentions.children | mentionsForUrl(absoluteUrl) -%} {%- set mentions = webmentions.children | mentionsForUrl(absoluteUrl) -%}
<div class="webmentions" id="webmentions"> <div class="webmentions" id="webmentions">
<h3>Webmentions</h3> <h3>Webmentions</h3>
{% if mentions | length %} {% if mentions | length %}
<h4>{{ mentions.length }} {% if mentions.length == "1" %}Reply{% else %}Replies{% endif %}</h4> <h4>{{ mentions.length }} {% if mentions.length == "1" %}Reply{% else %}Replies{% endif %}</h4>
<ol class="webmentions__list"> <ol class="webmentions__list">
{% for webmention in mentions | reverse %} {% for webmention in mentions | reverse %}
<li class="webmentions__item"> <li class="webmentions__item">
{% include 'webmention.njk' %} {% include 'webmention.njk' %}
</li> </li>
{% endfor %} {% endfor %}
</ol> </ol>
{% else %} {% else %}
<p>No replies yet.</p> <p>No replies yet.</p>
{% endif %} {% endif %}
{% include 'likes.njk' %} {% include 'likes.njk' %}
</div> </div>

View File

@@ -92,9 +92,9 @@ export default async function(eleventyConfig) {
extensions: "html", extensions: "html",
// Output formats for each image. // Output formats for each image.
formats: ["jpeg", "png"], formats: ["jpeg", "png", "webp"],
widths: ["600"], widths: ["auto", 400, 600],
urlPath: "/img/", urlPath: "/img/",
@@ -102,6 +102,7 @@ export default async function(eleventyConfig) {
// e.g. <img loading decoding> assigned on the HTML tag will override these values. // e.g. <img loading decoding> assigned on the HTML tag will override these values.
loading: "lazy", loading: "lazy",
decoding: "async", decoding: "async",
sizes: "100vw",
} }
}); });

View File

@@ -4,7 +4,7 @@
"description": "A starter repository for a blog web site using the Eleventy site generator.", "description": "A starter repository for a blog web site using the Eleventy site generator.",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "npx @11ty/eleventy --incremental", "build": "npx @11ty/eleventy --incremental --quiet",
"build-full": "npx @11ty/eleventy", "build-full": "npx @11ty/eleventy",
"build-nocolor": "cross-env NODE_DISABLE_COLORS=1 npx @11ty/eleventy", "build-nocolor": "cross-env NODE_DISABLE_COLORS=1 npx @11ty/eleventy",
"build-ghpages": "npx @11ty/eleventy --pathprefix=/eleventy-base-blog/", "build-ghpages": "npx @11ty/eleventy --pathprefix=/eleventy-base-blog/",
@@ -51,6 +51,7 @@
"@fontsource/atkinson-hyperlegible": "^5.0.3", "@fontsource/atkinson-hyperlegible": "^5.0.3",
"@zachleat/heading-anchors": "^1.0.1", "@zachleat/heading-anchors": "^1.0.1",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"lodash-es": "^4.17.21" "lodash-es": "^4.17.21",
"sanitize-html": "^2.13.1"
} }
} }

View File

@@ -50,4 +50,4 @@
} }
.webmention__pubdate { .webmention__pubdate {
font-style: italic; font-style: italic;
} }