mirror of
https://github.com/svemagie/blog-eleventy-indiekit.git
synced 2026-04-02 08:44:56 +02:00
feat: add digest templates and digestToHtml filter
- digest.njk: individual digest pages at /digest/YYYY/WNN/ - digest-index.njk: paginated index at /digest/ - digest-feed.njk: RSS feed at /digest/feed.xml - digestToHtml filter for RSS feed item descriptions
This commit is contained in:
31
digest-feed.njk
Normal file
31
digest-feed.njk
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
eleventyExcludeFromCollections: true
|
||||
eleventyImport:
|
||||
collections:
|
||||
- weeklyDigests
|
||||
permalink: /digest/feed.xml
|
||||
---
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ site.name }} — Weekly Digest</title>
|
||||
<link>{{ site.url }}/digest/</link>
|
||||
<description>Weekly summary of all posts on {{ site.name }}. One update per week.</description>
|
||||
<language>{{ site.locale | default('en') }}</language>
|
||||
<atom:link href="{{ site.url }}/digest/feed.xml" rel="self" type="application/rss+xml"/>
|
||||
<atom:link href="https://websubhub.com/hub" rel="hub"/>
|
||||
{%- set latestDigests = collections.weeklyDigests | head(20) %}
|
||||
{%- if latestDigests.length %}
|
||||
<lastBuildDate>{{ latestDigests[0].endDate | dateToRfc822 }}</lastBuildDate>
|
||||
{%- endif %}
|
||||
{%- for digest in latestDigests %}
|
||||
<item>
|
||||
<title>{{ digest.label }} ({{ digest.startDate | dateDisplay }} – {{ digest.endDate | dateDisplay }})</title>
|
||||
<link>{{ site.url }}/digest/{{ digest.slug }}/</link>
|
||||
<guid isPermaLink="true">{{ site.url }}/digest/{{ digest.slug }}/</guid>
|
||||
<pubDate>{{ digest.endDate | dateToRfc822 }}</pubDate>
|
||||
<description>{{ digest | digestToHtml(site.url) | escape }}</description>
|
||||
</item>
|
||||
{%- endfor %}
|
||||
</channel>
|
||||
</rss>
|
||||
83
digest-index.njk
Normal file
83
digest-index.njk
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: Weekly Digest
|
||||
withSidebar: true
|
||||
eleventyExcludeFromCollections: true
|
||||
eleventyImport:
|
||||
collections:
|
||||
- weeklyDigests
|
||||
pagination:
|
||||
data: collections.weeklyDigests
|
||||
size: 20
|
||||
alias: paginatedDigests
|
||||
permalink: "digest/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber + 1 }}/{% endif %}"
|
||||
---
|
||||
<div>
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-surface-900 dark:text-surface-100 mb-2">Weekly Digest</h1>
|
||||
<p class="text-surface-600 dark:text-surface-400 mb-6 sm:mb-8">
|
||||
A weekly summary of all posts. Subscribe via <a href="/digest/feed.xml" class="text-primary-600 dark:text-primary-400 hover:underline">RSS</a> for one update per week.
|
||||
</p>
|
||||
|
||||
{% if paginatedDigests.length > 0 %}
|
||||
<ul class="space-y-4">
|
||||
{% for d in paginatedDigests %}
|
||||
<li class="p-4 bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg hover:border-primary-300 dark:hover:border-primary-600 transition-colors">
|
||||
<a href="/digest/{{ d.slug }}/" class="block">
|
||||
<h2 class="font-semibold text-surface-900 dark:text-surface-100 hover:text-primary-600 dark:hover:text-primary-400">
|
||||
{{ d.label }}
|
||||
</h2>
|
||||
<p class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
{{ d.startDate | dateDisplay }} – {{ d.endDate | dateDisplay }}
|
||||
· {{ d.posts.length }} post{% if d.posts.length != 1 %}s{% endif %}
|
||||
</p>
|
||||
{% set typeLabels = [] %}
|
||||
{% for key, posts in d.byType %}
|
||||
{% set typeLabels = (typeLabels.push(key + " (" + posts.length + ")"), typeLabels) %}
|
||||
{% endfor %}
|
||||
{% if typeLabels.length %}
|
||||
<p class="text-xs text-surface-400 dark:text-surface-500 mt-1">
|
||||
{{ typeLabels | join(", ") }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% if pagination.pages.length > 1 %}
|
||||
<nav class="pagination mt-8" aria-label="Digest pagination">
|
||||
<div class="pagination-info">
|
||||
Page {{ pagination.pageNumber + 1 }} of {{ pagination.pages.length }}
|
||||
</div>
|
||||
<div class="pagination-links">
|
||||
{% if pagination.href.previous %}
|
||||
<a href="{{ pagination.href.previous }}" class="pagination-link" aria-label="Previous page">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
|
||||
Previous
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="pagination-link disabled">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
|
||||
Previous
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if pagination.href.next %}
|
||||
<a href="{{ pagination.href.next }}" class="pagination-link" aria-label="Next page">
|
||||
Next
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="pagination-link disabled">
|
||||
Next
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<p class="text-surface-600 dark:text-surface-400">No digests yet. Posts will be grouped into weekly digests automatically.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
168
digest.njk
Normal file
168
digest.njk
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
withSidebar: true
|
||||
eleventyExcludeFromCollections: true
|
||||
eleventyImport:
|
||||
collections:
|
||||
- weeklyDigests
|
||||
pagination:
|
||||
data: collections.weeklyDigests
|
||||
size: 1
|
||||
alias: digest
|
||||
eleventyComputed:
|
||||
title: "{{ digest.label }}"
|
||||
permalink: "digest/{{ digest.slug }}/"
|
||||
---
|
||||
<article class="h-feed">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-surface-900 dark:text-surface-100 mb-2">
|
||||
{{ digest.label }}
|
||||
</h1>
|
||||
<p class="text-surface-600 dark:text-surface-400 mb-6 sm:mb-8">
|
||||
{{ digest.startDate | dateDisplay }} – {{ digest.endDate | dateDisplay }}
|
||||
<span class="text-sm">({{ digest.posts.length }} post{% if digest.posts.length != 1 %}s{% endif %})</span>
|
||||
</p>
|
||||
|
||||
{# Type display order #}
|
||||
{% set typeOrder = [
|
||||
{ key: "articles", label: "Articles" },
|
||||
{ key: "notes", label: "Notes" },
|
||||
{ key: "photos", label: "Photos" },
|
||||
{ key: "bookmarks", label: "Bookmarks" },
|
||||
{ key: "likes", label: "Likes" },
|
||||
{ key: "reposts", label: "Reposts" }
|
||||
] %}
|
||||
|
||||
{% for typeInfo in typeOrder %}
|
||||
{% set typePosts = digest.byType[typeInfo.key] %}
|
||||
{% if typePosts and typePosts.length %}
|
||||
<section class="mb-8">
|
||||
<h2 class="text-lg sm:text-xl font-semibold text-surface-800 dark:text-surface-200 mb-4 border-b border-surface-200 dark:border-surface-700 pb-2">
|
||||
{{ typeInfo.label }}
|
||||
<span class="text-sm font-normal text-surface-500 dark:text-surface-400">({{ typePosts.length }})</span>
|
||||
</h2>
|
||||
<ul class="space-y-4">
|
||||
{% for post in typePosts %}
|
||||
<li class="h-entry">
|
||||
{% if typeInfo.key == "likes" %}
|
||||
{% set targetUrl = post.data.likeOf or post.data.like_of %}
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-red-500 flex-shrink-0">❤</span>
|
||||
<div>
|
||||
<a href="{{ targetUrl }}" class="text-primary-600 dark:text-primary-400 hover:underline break-all">{{ targetUrl }}</a>
|
||||
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
<time class="dt-published" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
|
||||
· <a href="{{ post.url }}" class="hover:underline">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% elif typeInfo.key == "bookmarks" %}
|
||||
{% set targetUrl = post.data.bookmarkOf or post.data.bookmark_of %}
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-amber-500 flex-shrink-0">🔖</span>
|
||||
<div>
|
||||
{% if post.data.title %}
|
||||
<a href="{{ post.url }}" class="font-medium text-surface-900 dark:text-surface-100 hover:text-primary-600 dark:hover:text-primary-400">{{ post.data.title }}</a>
|
||||
{% else %}
|
||||
<a href="{{ targetUrl }}" class="text-primary-600 dark:text-primary-400 hover:underline break-all">{{ targetUrl }}</a>
|
||||
{% endif %}
|
||||
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
<time class="dt-published" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
|
||||
· <a href="{{ post.url }}" class="hover:underline">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% elif typeInfo.key == "reposts" %}
|
||||
{% set targetUrl = post.data.repostOf or post.data.repost_of %}
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-500 flex-shrink-0">🔁</span>
|
||||
<div>
|
||||
<a href="{{ targetUrl }}" class="text-primary-600 dark:text-primary-400 hover:underline break-all">{{ targetUrl }}</a>
|
||||
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
<time class="dt-published" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
|
||||
· <a href="{{ post.url }}" class="hover:underline">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% elif typeInfo.key == "photos" %}
|
||||
<div>
|
||||
{% if post.data.photo and post.data.photo[0] %}
|
||||
{% set photoUrl = post.data.photo[0].url or post.data.photo[0] %}
|
||||
{% if photoUrl and photoUrl[0] != '/' and 'http' not in photoUrl %}
|
||||
{% set photoUrl = '/' + photoUrl %}
|
||||
{% endif %}
|
||||
<a href="{{ post.url }}" class="block mb-2">
|
||||
<img src="{{ photoUrl }}" alt="{{ post.data.photo[0].alt | default('Photo') }}" class="rounded max-h-48 object-cover" loading="lazy" eleventy:ignore>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if post.data.title %}
|
||||
<a href="{{ post.url }}" class="font-medium text-surface-900 dark:text-surface-100 hover:text-primary-600 dark:hover:text-primary-400">{{ post.data.title }}</a>
|
||||
{% elif post.templateContent %}
|
||||
<p class="text-surface-700 dark:text-surface-300 text-sm">{{ post.templateContent | striptags | truncate(120) }}</p>
|
||||
{% endif %}
|
||||
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
<time class="dt-published" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
|
||||
· <a href="{{ post.url }}" class="hover:underline">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% elif typeInfo.key == "articles" %}
|
||||
<div>
|
||||
<a href="{{ post.url }}" class="font-medium text-surface-900 dark:text-surface-100 hover:text-primary-600 dark:hover:text-primary-400">
|
||||
{{ post.data.title | default("Untitled") }}
|
||||
</a>
|
||||
{% if post.templateContent %}
|
||||
<p class="text-surface-700 dark:text-surface-300 text-sm mt-1">{{ post.templateContent | striptags | truncate(200) }}</p>
|
||||
{% endif %}
|
||||
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
<time class="dt-published" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
|
||||
· <a href="{{ post.url }}" class="hover:underline">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div>
|
||||
<p class="text-surface-700 dark:text-surface-300">{{ post.templateContent | striptags | truncate(200) }}</p>
|
||||
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
<time class="dt-published" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
|
||||
· <a href="{{ post.url }}" class="hover:underline">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# Previous/Next digest navigation #}
|
||||
{% set allDigests = collections.weeklyDigests %}
|
||||
{% set currentIndex = -1 %}
|
||||
{% for d in allDigests %}
|
||||
{% if d.slug == digest.slug %}
|
||||
{% set currentIndex = loop.index0 %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<nav class="flex justify-between items-center mt-8 pt-6 border-t border-surface-200 dark:border-surface-700" aria-label="Digest navigation">
|
||||
{% if currentIndex > 0 %}
|
||||
{% set newer = allDigests[currentIndex - 1] %}
|
||||
<a href="/digest/{{ newer.slug }}/" class="text-primary-600 dark:text-primary-400 hover:underline">
|
||||
← {{ newer.label }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span></span>
|
||||
{% endif %}
|
||||
{% if currentIndex < allDigests.length - 1 %}
|
||||
{% set older = allDigests[currentIndex + 1] %}
|
||||
<a href="/digest/{{ older.slug }}/" class="text-primary-600 dark:text-primary-400 hover:underline">
|
||||
{{ older.label }} →
|
||||
</a>
|
||||
{% else %}
|
||||
<span></span>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</article>
|
||||
Reference in New Issue
Block a user