fix webmentions & css
This commit is contained in:
BIN
_cache/.DS_Store
vendored
Normal file
BIN
_cache/.DS_Store
vendored
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -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) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 %} #}
|
||||||
|
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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",
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,4 +50,4 @@
|
|||||||
}
|
}
|
||||||
.webmention__pubdate {
|
.webmention__pubdate {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user