Almost done - add replies, webmentions, finish rendering posts from wordpress
This commit is contained in:
parent
5b68b1278c
commit
b5d04b7360
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ node_modules
|
|||||||
src/css/custom-props.css
|
src/css/custom-props.css
|
||||||
_site
|
_site
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.cache
|
@ -4,4 +4,15 @@ module.exports = function(eleventyConfig) {
|
|||||||
eleventyConfig.addFilter('dateDisplay', date => new Date(date).toLocaleDateString('en-GB', {
|
eleventyConfig.addFilter('dateDisplay', date => new Date(date).toLocaleDateString('en-GB', {
|
||||||
"dateStyle": "short"
|
"dateStyle": "short"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
eleventyConfig.addFilter('dateTimeDisplay', date => new Date(date).toLocaleString('en-GB', {
|
||||||
|
'dateStyle': 'short',
|
||||||
|
'timeStyle': 'short',
|
||||||
|
}));
|
||||||
|
|
||||||
|
eleventyConfig.addFilter('timeDisplay', date => new Date(date).toLocaleTimeString('en-GB', {
|
||||||
|
'hour': '2-digit',
|
||||||
|
'minute': 'numeric',
|
||||||
|
'hour12': true
|
||||||
|
}));
|
||||||
}
|
}
|
26
package-lock.json
generated
26
package-lock.json
generated
@ -8,9 +8,6 @@
|
|||||||
"name": "whimsy",
|
"name": "whimsy",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
|
||||||
"html-entities": "^2.3.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^2.0.0",
|
"@11ty/eleventy": "^2.0.0",
|
||||||
"@11ty/eleventy-fetch": "^3.0.0",
|
"@11ty/eleventy-fetch": "^3.0.0",
|
||||||
@ -19,6 +16,7 @@
|
|||||||
"@11ty/eleventy-plugin-syntaxhighlight": "^4.2.0",
|
"@11ty/eleventy-plugin-syntaxhighlight": "^4.2.0",
|
||||||
"@toycode/markdown-it-class": "^1.2.4",
|
"@toycode/markdown-it-class": "^1.2.4",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"markdown-it-abbr": "^1.0.4",
|
"markdown-it-abbr": "^1.0.4",
|
||||||
"markdown-it-anchor": "^8.6.6",
|
"markdown-it-anchor": "^8.6.6",
|
||||||
"markdown-it-eleventy-img": "^0.9.0",
|
"markdown-it-eleventy-img": "^0.9.0",
|
||||||
@ -1998,11 +1996,6 @@
|
|||||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/html-entities": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
|
|
||||||
},
|
|
||||||
"node_modules/html-escaper": {
|
"node_modules/html-escaper": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
|
||||||
@ -2683,6 +2676,12 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/lodash.chunk": {
|
"node_modules/lodash.chunk": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
|
||||||
@ -6672,11 +6671,6 @@
|
|||||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"html-entities": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
|
|
||||||
},
|
|
||||||
"html-escaper": {
|
"html-escaper": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
|
||||||
@ -7160,6 +7154,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"lodash.chunk": {
|
"lodash.chunk": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"@11ty/eleventy-plugin-syntaxhighlight": "^4.2.0",
|
"@11ty/eleventy-plugin-syntaxhighlight": "^4.2.0",
|
||||||
"@toycode/markdown-it-class": "^1.2.4",
|
"@toycode/markdown-it-class": "^1.2.4",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"markdown-it-abbr": "^1.0.4",
|
"markdown-it-abbr": "^1.0.4",
|
||||||
"markdown-it-anchor": "^8.6.6",
|
"markdown-it-anchor": "^8.6.6",
|
||||||
"markdown-it-eleventy-img": "^0.9.0",
|
"markdown-it-eleventy-img": "^0.9.0",
|
||||||
@ -36,8 +37,5 @@
|
|||||||
"postcss-nested": "^6.0.0",
|
"postcss-nested": "^6.0.0",
|
||||||
"prettier": "^2.8.3",
|
"prettier": "^2.8.3",
|
||||||
"tailwindcss": "^3.2.4"
|
"tailwindcss": "^3.2.4"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"html-entities": "^2.3.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,88 @@
|
|||||||
|
const EleventyFetch = require('@11ty/eleventy-fetch');
|
||||||
|
const fs = require('fs');
|
||||||
|
const merge = require('lodash/merge')
|
||||||
|
|
||||||
const wordpressPassword = process.env.WORDPRESS_PASSWORD;
|
const wordpressPassword = process.env.WORDPRESS_PASSWORD;
|
||||||
const auth = Buffer.from(`lewis:${wordpressPassword}`).toString('base64');
|
const auth = Buffer.from(`lewis:${wordpressPassword}`).toString('base64');
|
||||||
|
|
||||||
|
const dateSort = (a, b) => new Date(b.date) - new Date(a.date);
|
||||||
|
|
||||||
|
class PostCache {
|
||||||
|
constructor() {
|
||||||
|
this.readCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
readCache() {
|
||||||
|
if (!fs.existsSync(".cache/wordpress_post_fetch.json")) {
|
||||||
|
this.last_read_date = null;
|
||||||
|
this.posts = {};
|
||||||
|
} else {
|
||||||
|
const data = JSON.parse(fs.readFileSync(".cache/wordpress_post_fetch.json"));
|
||||||
|
this.last_read_date = new Date(data.last_read_date);
|
||||||
|
this.posts = data.posts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCache() {
|
||||||
|
fs.writeFileSync(".cache/wordpress_post_fetch.json", JSON.stringify(this, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchCommentsByType(type = "comment") {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("per_page", 100);
|
||||||
|
params.set("type", type);
|
||||||
|
|
||||||
|
if (this.last_read_date) {
|
||||||
|
params.set("modified_after", this.last_read_date.toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`https://lewisdale.dev/wp-json/wp/v2/comments?${params.toString()}`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Basic ${auth}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return await response.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchComments() {
|
||||||
|
const comments = [...await this.fetchCommentsByType(), ...await this.fetchCommentsByType("webmention")];
|
||||||
|
|
||||||
|
comments.forEach(comment => {
|
||||||
|
if (this.posts[comment.post]) {
|
||||||
|
this.posts[comment.post].comments[comment.id] = comment;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchPosts() {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("per_page", 100);
|
||||||
|
|
||||||
|
if (this.last_read_date) {
|
||||||
|
params.set("modified_after", this.last_read_date.toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`https://lewisdale.dev/wp-json/wp/v2/posts?${params.toString()}`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Basic ${auth}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const posts = await response.json();
|
||||||
|
|
||||||
|
posts.forEach(post => {
|
||||||
|
this.posts[post.id] = merge({ comments: {} }, this.posts[post.id], post);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchLatest() {
|
||||||
|
await this.fetchPosts();
|
||||||
|
await this.fetchComments();
|
||||||
|
|
||||||
|
this.last_read_date = new Date();
|
||||||
|
this.writeCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mapComment = comment => ({
|
const mapComment = comment => ({
|
||||||
author: {
|
author: {
|
||||||
name: comment.author_name,
|
name: comment.author_name,
|
||||||
@ -9,46 +91,27 @@ const mapComment = comment => ({
|
|||||||
},
|
},
|
||||||
content: comment.content.rendered,
|
content: comment.content.rendered,
|
||||||
canonical: comment.meta.semantic_linkbacks_canonical,
|
canonical: comment.meta.semantic_linkbacks_canonical,
|
||||||
date: comment.data,
|
date: comment.date,
|
||||||
});
|
});
|
||||||
|
|
||||||
const comments = async pid => {
|
|
||||||
const response = await fetch(`https://lewisdale.dev/wp-json/wp/v2/comments?per_page=100&post=${pid}`);
|
|
||||||
return (await response.json()).map(mapComment);
|
|
||||||
}
|
|
||||||
|
|
||||||
const webmentions = async (pid) => {
|
|
||||||
const response = await fetch(`https://lewisdale.dev/wp-json/wp/v2/comments?per_page=100&post=${pid}&type=Webmention`, {
|
|
||||||
headers: {
|
|
||||||
"Authorization": `Basic ${auth}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (await response.json())
|
|
||||||
.reduce((comments, comment) => {
|
|
||||||
if (!comments[comment.meta.semantic_linkbacks_type]) {
|
|
||||||
comments[comment.meta.semantic_linkbacks_type] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
comments[comment.meta.semantic_linkbacks_type].push(mapComment(comment));
|
|
||||||
return comments;
|
|
||||||
}, { like: [], repost: [] });
|
|
||||||
}
|
|
||||||
|
|
||||||
const getComments = async pid => {
|
|
||||||
return {
|
|
||||||
comments: await comments(pid),
|
|
||||||
...(await webmentions(pid))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = async () => {
|
module.exports = async () => {
|
||||||
const response = await fetch("https://lewisdale.dev/wp-json/wp/v2/posts?per_page=100", {
|
const cache = new PostCache();
|
||||||
headers: {
|
await cache.fetchLatest();
|
||||||
"Authorization": `Basic ${auth}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const posts = (await response.json());
|
return Object.values(cache.posts)
|
||||||
return Promise.all(posts.map(async post => ({...post, comments: await getComments(post.id) })));
|
.sort(dateSort)
|
||||||
|
.map(post => ({
|
||||||
|
...post,
|
||||||
|
comments: Object.values(post.comments)
|
||||||
|
.sort(dateSort)
|
||||||
|
.reduce((comments, comment) => {
|
||||||
|
if (!comments[comment.meta.semantic_linkbacks_type]) {
|
||||||
|
comments[comment.meta.semantic_linkbacks_type] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
comments[comment.meta.semantic_linkbacks_type].push(mapComment(comment));
|
||||||
|
return comments;
|
||||||
|
}, { like: [], reply: [], repost: [] })
|
||||||
|
}));
|
||||||
};
|
};
|
@ -9,7 +9,13 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="/assets/css/prism.css" />
|
<link rel="stylesheet" type="text/css" href="/assets/css/prism.css" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/css/styles.css" />
|
<link rel="stylesheet" type="text/css" href="/assets/css/styles.css" />
|
||||||
|
|
||||||
|
<link rel="webmention" href="https://lewisdale.dev/wp-json/webmention/1.0/endpoint" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="generator" content="eleventy" />
|
||||||
|
|
||||||
|
<meta name="author" content="{{ metadata.author.name }}" />
|
||||||
|
<meta name="description" content="{{ description }}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="wrapper-lg row space-between">
|
<header class="wrapper-lg row space-between">
|
||||||
@ -20,7 +26,6 @@
|
|||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li><a href="/now">Now</a></li>
|
<li><a href="/now">Now</a></li>
|
||||||
<li><a href="/blog">Blog</a></li>
|
<li><a href="/blog">Blog</a></li>
|
||||||
<li><a href="/about">About me</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
@ -3,6 +3,7 @@ layout: base.njk
|
|||||||
includePrism: true
|
includePrism: true
|
||||||
eleventyComputed:
|
eleventyComputed:
|
||||||
title: "{{ post.title.rendered | safe }}"
|
title: "{{ post.title.rendered | safe }}"
|
||||||
|
description: "{{ post.yoast_head_json.description }}"
|
||||||
---
|
---
|
||||||
<main class="wrapper-md stack-lg">
|
<main class="wrapper-md stack-lg">
|
||||||
<article class="stack-md">
|
<article class="stack-md">
|
||||||
@ -10,9 +11,64 @@ eleventyComputed:
|
|||||||
{{ content | safe }}
|
{{ content | safe }}
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<div class="h-card">
|
<section class="stack-sm">
|
||||||
|
<h2>Responses</h2>
|
||||||
|
|
||||||
|
{% if post.comments.like | length %}
|
||||||
|
<div class="stack-xs">
|
||||||
|
<h3 class="block w-full">Likes</h3>
|
||||||
|
|
||||||
|
<div class="row overlaps">
|
||||||
|
{% for like in post.comments.like %}
|
||||||
|
<a href="{{ like.author.url }}">
|
||||||
|
<img src="{{ like.author.avatars["96"] }}" alt="{{ like.author.name }}" class="circle" />
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if post.comments.repost | length %}
|
||||||
|
<div class="stack-xs">
|
||||||
|
<h3 class="block w-full">Reposts</h3>
|
||||||
|
|
||||||
|
<div class="row overlaps">
|
||||||
|
{% for like in post.comments.repost %}
|
||||||
|
<a href="{{ like.author.url }}">
|
||||||
|
<img src="{{ like.author.avatars["96"] }}" alt="{{ like.author.name }}" class="circle" />
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if post.comments.reply | length %}
|
||||||
|
<div class="stack-xs">
|
||||||
|
<h3 class="block w-full">Replies</h3>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
{% for reply in post.comments.reply %}
|
||||||
|
|
||||||
|
<li class="row items-center">
|
||||||
|
<a href="{{ like.author.url }}">
|
||||||
|
<img src="{{ reply.author.avatars["96"] }}" alt="{{ reply.author.name }}" class="max-w-[4rem] box" />
|
||||||
|
</a>
|
||||||
|
<div class="stack-2xs">
|
||||||
|
{% set date = reply.date | parseDate %}
|
||||||
|
<h4>{{ reply.author.name }} <span class="font-normal">on</span> {{ date | dateDisplay }} <span class="font-normal">at</span> {{ date | timeDisplay }}</h4>
|
||||||
|
<a href="{{ reply.canonical }}">Original comment</a>
|
||||||
|
<p>{{ reply.content | safe }}</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</section>
|
||||||
|
<section class="h-card">
|
||||||
<h2>About Lewis</h2>
|
<h2>About Lewis</h2>
|
||||||
|
|
||||||
{% image metadata.author.avatar, "My face", "u-photo box circle", "150px", [150] %}
|
{% image metadata.author.avatar, "My face", "u-photo box circle", "150px", [150] %}
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
</main>
|
</main>
|
14
src/css/exceptions/overlaps.css
Normal file
14
src/css/exceptions/overlaps.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.overlaps {
|
||||||
|
gap: 0;
|
||||||
|
max-width: 30rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-inline-start: -1rem;
|
||||||
|
}
|
||||||
|
}
|
@ -24,8 +24,8 @@ layout: base.njk
|
|||||||
<h2>Recent Posts</h2>
|
<h2>Recent Posts</h2>
|
||||||
|
|
||||||
<ul class="stack-2xs">
|
<ul class="stack-2xs">
|
||||||
{% for post in collections.posts | reverse | take(3) %}
|
{% for post in posts | take(3) %}
|
||||||
<li><a href="{{ post.data.url }}">{{ post.data.title }}</a> <time>{{ post.date | dateDisplay }}</time></li>
|
<li><a href="/post/{{ post.slug }}">{{ post.title.rendered | safe }}</a> <time>{{ post.date | dateDisplay }}</time></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user