Sweeping changes and a (hopefully) proper feed page

This commit is contained in:
Gordon Pedersen 2022-12-08 13:19:45 +11:00
parent c920bc400f
commit 2e46a7f9c4
20 changed files with 4291 additions and 133 deletions

View file

@ -7,7 +7,7 @@ module.exports = function(eleventyConfig) {
else throw 'Unrecognized data format'
}
catch(e) {
console.log(`Could not convert "${value}"`, e)
console.error(`Could not convert "${value}"`, e)
return value;
}
});
@ -19,7 +19,7 @@ module.exports = function(eleventyConfig) {
else throw 'Unrecognized data format'
}
catch(e) {
console.log(`Could not convert "${value}"`, e)
console.error(`Could not convert "${value}"`, e)
return value;
}
});
@ -28,9 +28,66 @@ module.exports = function(eleventyConfig) {
return collectionApi.getFilteredByGlob("_content/notes/*.md").reverse();
});
eleventyConfig.addCollection("feed", function(collectionApi) {
return collectionApi.getAllSorted().reverse().filter(item => {
let showInFeed = false;
//if(!item.data.published) return false
if(item.filePathStem.startsWith('/notes/')){
if(item.data['like-of']) item.data.postType = "like"
else if(item.data['in-reply-to']) item.data.postType = "reply"
else if(item.data['repost-of']) item.data.postType = "boost"
else if(item.data['read-of']) item.data.postType = "read"
else if(item.data['watch-of']) item.data.postType = "watch"
else item.data.postType = "note";
showInFeed = true;
}
else if(item.filePathStem.startsWith('/articles/')){
item.data.postType = "article";
showInFeed = true;
}
else if (item.filePathStem.startsWith('/bookmarks/')){
item.data.postType = "bookmark";
showInFeed = true;
}
else if (item.filePathStem.startsWith('/likes/')){
item.data.postType = "like";
showInFeed = true;
}
else if (item.filePathStem.startsWith('/rsvp/')){
item.data.postType = "rsvp";
showInFeed = true;
}
else if (item.filePathStem.startsWith('/watched/')){
item.data.postType = "watched";
showInFeed = true;
}
// if(showInFeed) {
// item.data.author =
// }
return showInFeed;
});
});
eleventyConfig.addNunjucksShortcode("getVar", function(varString) {
return this.ctx[varString];
});
eleventyConfig.addShortcode('renderlayoutblock', function(name) {
return (this.page.layoutblock || {})[name] || '';
});
eleventyConfig.addPairedShortcode('layoutblock', function(content, name) {
if (!this.page.layoutblock) this.page.layoutblock = {};
this.page.layoutblock[name] = content;
return '';
});
eleventyConfig.addPassthroughCopy({"img":"assets/img"});
eleventyConfig.addPassthroughCopy({"js":"assets/js"});
eleventyConfig.addPassthroughCopy("css");
eleventyConfig.addPassthroughCopy("CNAME");
// Return your Object options:
return {
@ -38,6 +95,7 @@ module.exports = function(eleventyConfig) {
input: "_content",
output: "pub"
},
htmlTemplateEngine: "njk",
templateFormats: ["md","html"]
}
};

1
.gitignore vendored
View file

@ -1 +1,2 @@
node_modules
pub

57
_content/_data/author.js Normal file
View file

@ -0,0 +1,57 @@
const EleventyFetch = require("@11ty/eleventy-fetch");
module.exports = async function() {
// set these default options
let author = {
"handle": "@death.au@death.id.au",
"display_name": "death.au",
"url": "https://death.id.au", // this is the domain your webfinger is on
"avatar": "/assets/img/avatar-tt.svg" // avatar to use by default
}
try {
// options for the cached fetch request
const options = {
duration: "1d", // 1 day
type: "json", // also supports "text" or "buffer"
fetchOptions: {
headers: {
'Content-Type': 'application/activity+json'
}
}
}
// get the webfinger
handle = author.handle.startsWith('@') ? author.handle.substring(1) : author.handle;
let webfinger = await EleventyFetch(`${author.url}/.well-known/webfinger?resource=acct:${handle}`, options);
// get the canonical uri as well
let canonical_uri = webfinger?.links?.find(link => link.rel == "canonical_uri")
if(canonical_uri) author.canonical_uri = canonical_uri.href
// get the rel="self" link from the webfinger
let self = webfinger?.links?.find(link => link.rel == "self");
if(self) {
// fetch the profile information
if(self.type) options.fetchOptions.headers['Content-Type'] = self.type;
json = await EleventyFetch(self.href, options);
// apply the supplied author information to the result
json.handle = author.handle
json.avatar = author.avatar
json.display_name = author.display_name || json.preferredUsername
json.canonical_uri = author.canonical_uri || json.url[0]
// this is now the author object
author = json
}
else throw 'Could not find rel="self" link in webfinger'
return author
}
catch(e) {
console.error("Failed to get webfinger info ", e);
return author
}
};

1
_content/_data/layout.js Normal file
View file

@ -0,0 +1 @@
module.exports = "layout-default.njk"

View file

@ -0,0 +1,13 @@
---js
{
layout: "layout-main.njk",
ctx: function() { return this.ctx }
}
---
{% from "macro-entry.njk" import entryMacro %}
{{ entryMacro(ctx(), url, content) }}
{% layoutblock 'foot' %}
<script src="/assets/js/relative-time.js"></script>
{% endlayoutblock %}

View file

@ -0,0 +1,15 @@
---
title: Feed
layout: layout-main.njk
scripts_foot: '<script src="/assets/js/relative-time.js"></script>'
---
<div class="h-feed">
<h2 class="p-name">{{ title }}</h2>
{{ content | safe }}
</div>
{% include 'partial-pagination.njk' %}
{% layoutblock 'foot' %}
<script src="/assets/js/relative-time.js"></script>
{% endlayoutblock %}

View file

@ -18,16 +18,16 @@ title: Mon Repos (Death's Domain)
<link rel="micropub" href="https://micropub.death.id.au/.netlify/functions/micropub">
<link rel="micropub_media" href="https://micropub.death.id.au/.netlify/functions/media">
<link rel="stylesheet" href="/css/styles.css">
<script src="https://kit.fontawesome.com/ebe14e90c3.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon/build/global/luxon.min.js" crossorigin="anonymous"></script>
<title>{{ title }}</title>
{% renderlayoutblock 'head' %}
</head>
<body>
<article class="{{ type }}">
<time class="dt-published" datetime="{{ date }}">{{ date | formatDate }}</time>
<div class="e-content">
<main>
{{ content | safe }}
</div>
</article>
</main>
</body>
{% renderlayoutblock 'foot' %}
</html>

View file

@ -0,0 +1,9 @@
{% macro authorMacro(author) %}
<a class="p-author h-card u-url" href="{{ author.canonical_uri if author.canonical_uri else author.url }}">
<img class="u-photo small-avatar" alt="{{ author.icon.name if author.icon.name else "Avatar for " + author.display_name }}" src="{{ author.icon.url if author.icon.url else author.avatar }}"/>
<span class="display-name">
<span class="p-name">{{ author.display_name }}</span>
<span class="p-nickname">{{ author.handle }}</span>
</span>
</a>
{% endmacro %}

View file

@ -0,0 +1,9 @@
{% from "macro-author.njk" import authorMacro %}
{% macro cardHeadMacro(author, date, url) %}
<div class="card-head">
<a class="u-url permalink" rel="bookmark" href="{{ url }}" >
<time class="dt-published" datetime="{{ date | dateISOString }}">{{ date | formatDate }}</time>
</a>
{{ authorMacro(author) }}
</div>
{% endmacro %}

View file

@ -0,0 +1,95 @@
{% from "macro-card-head.njk" import cardHeadMacro %}
{% macro entryMacro(item, url, content, summaryOnly=false) %}
<div class="h-entry type-{{ item.postType }}">
{{ cardHeadMacro(item.author, item.date, url) }}
<div class="card-body">
{% switch item.postType %}
{% case "article" %} {# article summary: #}
<h2 class="p-name"><a class="u-url" rel="bookmark" title="{{ item.name if item.name else item.title }}" href="{{ url }}">
{{ item.name if item.name else item.title }}
</a></h2>
{% if item.summary %}
<p class="p-summary">{{ item.summary | safe }}</p>
{% endif %}
{% case "reply" %} {# reply summary: #}
<p class="p-summary"><i class="fa-solid fa-reply"></i> Reply to <a class="u-in-reply-to" href="{{ item["in-reply-to"] }}">{{ item["in-reply-to"] }}</a></p>
{% case "like" %} {# like summary: #}
<p class="p-summary">Favourited <i class="fa-solid fa-star"></i> <a class="u-like-of" href="{{ item['like-of'] }}">{{ item['like-of'] }}</a></p>
{% case "boost" %} {# boost summary: #}
<p class="p-summary"></p>Boosted <i class="fa-solid fa-retweet"></i> <a class="u-repost-of" href="{{ item["repost-of"] }}">{{ item["repost-of"] }}</a></p>
{% case "bookmark" %} {# bookmark summary: #}
<p class="p-summary">Bookmarked <i class="fa-solid fa-bookmark"></i> <a class="u-bookmark-of" href="{{ item["bookmark-of"] }}">{{ item["bookmark-of"] }}</a></p>
{% case "read" %} {# read summary: #}
<p class="p-summary">
{% if item["read-status"].toLowerCase() == "to-read" %}
<data class="p-x-read-status p-read-status" value="to-read">To Read: </data>
<i class="fa-solid fa-book"></i>
{% elseif item["read-status"].toLowerCase() == "reading" %}
<data class="p-x-read-status p-read-status" value="reading">Currently Reading: </data>
<i class="fa-solid fa-book-open"></i>
{% elseif item["read-status"].toLowerCase() == "finished" %}
<data class="p-x-read-status p-read-status" value="finished">Finished Reading: </data>
<i class="fa-solid fa-book-bookmark"></i>
{% endif %}
{% if item["read-of"].startsWith("http") %}
<a class="u-read-of" href="{{ item["read-of"] }}">{{ item["read-of"] }}</a>
{% else %}
<strong class="p-read-of">{{ item["read-of"] }}</strong>
{% endif %}
</p>
{% case "watch" %} {# watch summary: #}
<p class="p-summary">
{% if item["watch-status"].toLowerCase() == "to-watch" %}
<data class="p-x-watch-status p-watch-status" value="to-watch">To Watch: </data>
{% elseif item["watch-status"].toLowerCase() == "watching" %}
<data class="p-x-watch-status p-watch-status" value="watching">Currently Watching: </data>
{% elseif item["watch-status"].toLowerCase() == "watched" or item["watch-status"].toLowerCase() == "finished" %}
<data class="p-x-watch-status p-watch-status" value="finished">Finished watching: </data>
{% else %}
<data class="p-x-watch-status p-watch-status" value="finished">Watched: </data>
{% endif %}
<i class="fa-solid fa-clapperboard"></i>
{% if item["watch-of"].startsWith("http") %}
<a class="u-watch-of" href="{{ item["watch-of"] }}">{{ item["watch-of"] }}</a>
{% else %}
<strong class="p-watch-of">{{ item["watch-of"] }}</strong>
{% endif %}
</p>
{% case "rsvp" %} {# rsvp summary: #}
<p class="p-summary">
{% if item.rsvp.toLowerCase() == "yes" %}
<i class="fa-regular fa-calendar-check"></i>
<data class="p-rsvp" value="yes">Will attend</data>
{% elseif item.rsvp.toLowerCase() == "maybe" %}
<i class="fa-regular fa-calendar-minus"></i>
<data class="p-rsvp" value="maybe">Might attend</data>
{% elseif item.rsvp.toLowerCase() == "no" %}
<i class="fa-regular fa-calendar-xmark"></i>
<data class="p-rsvp" value="no">Won't attend</data>
{% elseif item.rsvp.toLowerCase() == "interested" %}
<i class="fa-regular fa-calendar-plus"></i>
<data class="p-rsvp" value="interested">Interested in</data>
{% endif %}
<a class="u-in-reply-to" href="{{ item["in-reply-to"] }}">{{ item["in-reply-to"] }}</a>
</p>
{% endswitch %}
{% if item.postType == 'article' and summaryOnly %}
{% elseif content %}
<div class="e-content">{{ content | safe }}</div>
{% endif %}
</div>
</div>
{% endmacro %}

View file

@ -0,0 +1,15 @@
{% if pagination %}
<nav>
<ul class="pagination" role="list">
<li><a href="{{ pagination.href.first }}" rel="first" title="First Page">«</a></li>
<li><a href="{{ pagination.href.previous if pagination.href.previous else "#" }}" class="{{ '' if pagination.href.previous else 'disabled' }}" rel="prev" title="Previous Page"></a></li>
{%- for pageEntry in pagination.pages %}
<li><a href="{{ pagination.hrefs[ loop.index0 ] }}"{% if page.url == pagination.hrefs[ loop.index0 ] %} aria-current="page" class="active" title="Current Page" {% else %} title="Jump to Page {{ loop.index }}" {% endif %}>{{ loop.index }}</a></li>
{%- endfor %}
<li><a href="{{ pagination.href.next if pagination.href.next else "#" }}" class="{{ '' if pagination.href.next else 'disabled' }}" rel="next" title="Next Page"></a></li>
<li><a href="{{ pagination.href.last }}" rel="last" title="Last Page">»</a></li>
</ul>
</nav>
{% endif %}

View file

@ -0,0 +1,6 @@
<h2 class="p-name"><a class="u-url" rel="bookmark" title="{{ item.name if item.name else item.title }}" href="{{ url }}">
{{ item.name if item.name else item.title }}
</a></h2>
{% if item.summary %}
<p class="p-summary">{{ item.summary | safe }}</p>
{% endif %}

View file

17
_content/feed/index.html Normal file
View file

@ -0,0 +1,17 @@
---
title: My Feed
layout: layout-feed.njk
pagination:
data: collections.feed
size: 20
---
{% from "macro-entry.njk" import entryMacro %}
<ul>
{% for item in pagination.items %}
<li>
{{ entryMacro(item.data, item.url, item.templateContent) }}
</li>
{% endfor %}
</ul>

View file

@ -1,26 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="/assets/img/avatar-tt.svg">
<link rel="icon" type="image/png" href="/assets/img/avatar-tiny.png">
<link rel="authorization_endpoint" href="https://indieauth.com/auth">
<link rel="token_endpoint" href="https://tokens.indieauth.com/token">
<link rel="webmention" href="https://webmention.io/death.id.au/webmention" />
<link rel="pingback" href="https://webmention.io/death.id.au/xmlrpc" />
<link rel="microsub" href="https://aperture.p3k.io/microsub/807">
<link rel="micropub" href="https://micropub.death.id.au/.netlify/functions/micropub">
<link rel="micropub_media" href="https://micropub.death.id.au/.netlify/functions/media">
<link rel="stylesheet" href="/css/styles.css">
<script src="https://kit.fontawesome.com/ebe14e90c3.js" crossorigin="anonymous"></script>
<title>Mon Repos (Death's Domain)</title>
</head>
<body>
<!-- Reference for representative h-card properties: https://microformats.org/wiki/h-card -->
<main class="h-card" rel="author">
---
layout: layout-main.njk
---
<!-- Reference for representative h-card properties: https://microformats.org/wiki/h-card -->
<div class="h-card" rel="author">
<img class="u-featured" src="/assets/img/banner-1500x500.jpg" alt="A grim reaper in a decorated cubicle, writing and daydreaming, surrounded by many other grim reapers in bland, grey, cubicles" />
<img class="u-photo" alt="My profile photo — a pixelated version of me" src="/assets/img/avatar-tt.svg" />
<h1>
@ -46,8 +28,10 @@
<li><i class="fa-brands fa-twitter"></i> <a class="u-url" href="https://twitter.com/death_au" rel="me">@death_au</a></li>
<li><i class="fa-brands fa-linkedin"></i> <a class="u-url" href="https://www.linkedin.com/in/gordon-pedersen/">Gordon Pedersen</a></li>
</ul>
</main>
<script src="/assets/js/follow.js"></script>
</div>
{% layoutblock 'foot' %}
<script src="/assets/js/follow.js"></script>
<script src="/assets/js/button-input.js"></script>
<script>
window.addEventListener('unhandledrejection', function(event) {
@ -70,6 +54,4 @@
}
}
</script>
</body>
</html>
{% endlayoutblock %}

View file

@ -1,42 +0,0 @@
---
title: Mon Repos (Death's Domain)
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="/assets/img/avatar-tt.svg">
<link rel="icon" type="image/png" href="/assets/img/avatar-tiny.png">
<link rel="authorization_endpoint" href="https://indieauth.com/auth">
<link rel="token_endpoint" href="https://tokens.indieauth.com/token">
<link rel="webmention" href="https://webmention.io/death.id.au/webmention" />
<link rel="pingback" href="https://webmention.io/death.id.au/xmlrpc" />
<link rel="microsub" href="https://aperture.p3k.io/microsub/807">
<link rel="micropub" href="https://micropub.death.id.au/.netlify/functions/micropub">
<link rel="micropub_media" href="https://micropub.death.id.au/.netlify/functions/media">
<link rel="stylesheet" href="/css/styles.css">
<script src="https://cdn.jsdelivr.net/npm/luxon/build/global/luxon.min.js" crossorigin="anonymous"></script>
<title>{{ title }}</title>
</head>
<body>
<main>
<ol class="h-notes">
{%- for note in collections.notes -%}
<li class="{{ note.data.type }}" role="article">
<time class="dt-published" datetime="{{ note.data.date }}">{{ note.data.date | formatDate }}</time>
<div class="e-content">
{{ note.templateContent }}
</div>
</li>
{%- endfor -%}
</ol>
</main>
</body>
<script src="/assets/js/relative-time.js"></script>
</html>

View file

@ -1 +0,0 @@
{ "layout": "layout.njk" }

View file

@ -1,7 +1,50 @@
@media (prefers-color-scheme: dark) {
html { background-color: #111111; color: #fbfbfb; }
a { color: #ca8465; }
:root {
--bg-rgb: 238, 238, 238;
--on-bg-rgb: 4, 4, 4;
--primary-rgb: 160, 116, 196;
--on-primary-rgb: 238, 238, 238;
--bg: rgb(var(--bg-rgb));
--on-bg: rgb(var(--on-bg-rgb));
--primary: rgb(var(--primary-rgb));
--on-primary: rgb(var(--on-primary-rgb));
--fade-alpha: 0.06;
--mute-alpha: 0.3;
--bg-fade: rgba(var(--bg-rgb), var(--fade-alpha));
--on-bg-fade: rgba(var(--on-bg-rgb), var(--fade-alpha));
--primary-fade: rgba(var(--primary-rgb), var(--fade-alpha));
--on-primary-fade: rgba(var(--on-primary-rgb), var(--fade-alpha));
--bg-muted: rgba(var(--bg-rgb), var(--mute-alpha));
--on-bg-muted: rgba(var(--on-bg-rgb), var(--mute-alpha));
--primary-muted: rgba(var(--primary-rgb), var(--mute-alpha));
--on-primary-muted: rgba(var(--on-primary-rgb), var(--mute-alpha));
}
@media (prefers-color-scheme: dark) {
:root{
--bg-rgb: 17, 17, 17;
--on-bg-rgb: 251, 251, 251;
--on-primary-rgb: 251, 251, 251;
--fade-alpha: 0.16;
}
}
html {
background-color: var(--bg);
color: var(--on-bg);
}
a {
color: var(--primary);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
body {
line-height: 1.6;
font-family: system-ui, -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;
@ -14,7 +57,16 @@ body {
align-items: center;
overflow-x: hidden;
}
p:last-child {
margin-block-end: 0;
}
img.u-photo { border-radius: 50%; max-height:200px; }
/* .p-author img.u-photo{
max-height: 48px;
} */
ul { padding: 0; list-style: none; }
img.u-featured {
display:block;
@ -61,3 +113,99 @@ i>img{
img.tiny-avatar {
max-height: 17px;
}
img.small-avatar {
max-height: 40px;
}
/* Pagination */
.pagination {
margin: 50px auto;
}
.pagination li {
display: inline-block;
}
.pagination a {
padding: 8px 16px;
text-decoration: none;
border-radius: 5px;
border: 1px solid var(--on-bg-fade);
}
.pagination a.active {
background-color: var(--primary);
color: var(--on-primary);
}
.pagination a:hover:not(.active) { background-color: var(--on-bg-fade); }
.pagination a.disabled {
color: var(--primary-fade);
pointer-events: none;
}
/* End Pagination */
/* Feed Entries */
.h-entry {
max-width: 500px;
min-width: 320px;
background-color: var(--primary-fade);
padding: 20px;
margin: 0;
border: 1px solid var(--on-bg-fade);
text-align: left;
}
.h-entry:not(:last-child) {
border-bottom: none;
}
.h-entry .p-author {
max-width: calc(100% - 80px);
display: flex;
align-items: center;
gap: 10px;
overflow: hidden;
}
.h-entry .u-photo {
vertical-align: baseline;
}
.h-entry .display-name {
display: block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.h-entry .display-name .p-name {
white-space: nowrap;
display: block;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 700;
}
.h-entry .display-name .p-nickname {
white-space: nowrap;
display: block;
overflow: hidden;
text-overflow: ellipsis;
color: var(--on-primary-muted);
}
.h-entry .permalink {
float:right;
text-align: right;
font-size: 0.8em;
color: var(--on-primary-muted);
display: block;
overflow: hidden;
text-overflow: ellipsis;
max-width:80px;
}
/* End Feed Entries */

3817
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,8 @@
"description": "This repository provides minimal HTML for starting a new web site!",
"main": "index.js",
"scripts": {
"build": "npx @11ty/eleventy"
"build": "npx @11ty/eleventy",
"serve": "npx @11ty/eleventy --serve"
},
"repository": {
"type": "git",
@ -19,5 +20,8 @@
"homepage": "https://github.com/deathau/deathau.github.io#readme",
"devDependencies": {
"@11ty/eleventy": "^1.0.2"
},
"dependencies": {
"@11ty/eleventy-fetch": "^3.0.0"
}
}