From 56e6efc795777f5c74768886553091fb6c2efabe Mon Sep 17 00:00:00 2001
From: Leni Aniva <v@leni.sh>
Date: Sat, 25 Jan 2025 15:06:09 -0800
Subject: [PATCH] feat: Implement series

---
 src/lib/posts.ts                    |  2 +-
 src/routes/post/[slug]/+page.svelte | 39 ++++++++++++++++++++++++++-
 src/routes/post/[slug]/+page.ts     | 42 +++++++++++++++++++++++++++--
 3 files changed, 79 insertions(+), 4 deletions(-)

diff --git a/src/lib/posts.ts b/src/lib/posts.ts
index 9984e6d..ecfc429 100644
--- a/src/lib/posts.ts
+++ b/src/lib/posts.ts
@@ -15,7 +15,7 @@ export function transformPostMeta(metadata: PostMetadata, slug: string) : Post {
 	};
 }
 
-// Generates a list of all posts filtering by tags and series
+// Generates a list of all posts filtering by tags and series, reverse chronologically ordered.
 export async function getPosts(filter: { tag?: string, series?: string }) : Promise<Post[]> {
 	const allPostFiles = import.meta.glob('$content/post/*.md');
 	const iterablePostFiles = Object.entries(allPostFiles);
diff --git a/src/routes/post/[slug]/+page.svelte b/src/routes/post/[slug]/+page.svelte
index 22fcab7..f7609be 100644
--- a/src/routes/post/[slug]/+page.svelte
+++ b/src/routes/post/[slug]/+page.svelte
@@ -1,7 +1,8 @@
 <script lang="ts">
+	import { TrackPrevious, TrackNext } from 'svelte-radix';
 	import type { PageData } from './$types';
 	export let data: PageData;
-	const { post, Content } = data;
+	const { post, Content, seriesNeighbours } = data;
 	import siteMetadata from '$content/metadata.json';
 	import PostHeader from '$lib/components/PostHeader.svelte';
 </script>
@@ -17,3 +18,39 @@
 	<Content />
 </article>
 <hr />
+<ul id="series">
+	{#each seriesNeighbours as { name, prevSlug, prevTitle, nextSlug, nextTitle }}
+		<li class="series-neighbour">
+			{#if prevTitle}
+				<a href="/post/{prevSlug}">{prevTitle}</a>
+				<p class="dummy"><TrackPrevious /></p>
+			{/if}
+			<p class="series-tag">{name}</p>
+			{#if nextTitle}
+				<p class="dummy"><TrackNext /></p>
+				<a href="/post/{nextSlug}">{nextTitle}</a>
+			{/if}
+		</li>
+	{/each}
+</ul>
+
+<style>
+	#series {
+		text-align: center;
+	}
+	.series-tag {
+		color: var(--series);
+		display: inline-block;
+	}
+	.dummy {
+		display: inline-block;
+		color: rgba(128,128,128, .6);
+		vertical-align: middle;
+		padding-left: 1em;
+		padding-right: 1em;
+	}
+	.series-neighbour {
+		padding-left: 2em;
+		padding-right: 2em;
+	}
+</style>
diff --git a/src/routes/post/[slug]/+page.ts b/src/routes/post/[slug]/+page.ts
index 974b02e..a974f60 100644
--- a/src/routes/post/[slug]/+page.ts
+++ b/src/routes/post/[slug]/+page.ts
@@ -1,16 +1,54 @@
 import { error } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
-import { transformPostMeta } from '$lib/posts';
+import type { Post } from '$lib/types'
+import { getPosts, transformPostMeta } from '$lib/posts';
 
 export const load: PageLoad = async ({ params }) => {
 	try {
 		const post = await import(`$content/post/${params.slug}.md`);
 		 if (!post) throw error(404);
+		const metadata: Post = transformPostMeta(post.metadata, params.slug);
 		const Content = post.default;
 
+		const seriesNeighbours : {
+			name: string,
+			prevSlug?: string,
+			prevTitle?: string,
+			nextSlug?: string,
+			nextTitle?: string,
+		}[] = await Promise.all(post.metadata.series.map(async (series: string) => {
+			const postInSeries: Post[] = await getPosts({ series });
+
+			let prevSlug = null;
+			let prevTitle = null;
+			let nextSlug = null;
+			let nextTitle = null;
+
+			for (const p of postInSeries) {
+				if (p.date > metadata.date) {
+					nextSlug = p.slug;
+					nextTitle = p.title;
+				}
+				if (p.date < metadata.date) {
+					prevSlug = p.slug;
+					prevTitle = p.title;
+					break;
+				}
+			}
+
+			return {
+				name: series,
+				prevSlug,
+				prevTitle,
+				nextSlug,
+				nextTitle,
+			};
+		}));
+
 		return {
-			post: transformPostMeta(post.metadata, params.slug),
+			post: metadata,
 			Content,
+			seriesNeighbours,
 		};
 	}
 	catch {