diff --git a/assets/styles/components/_blog.scss b/assets/styles/components/_blog.scss
index 74e5051..43e30e2 100644
--- a/assets/styles/components/_blog.scss
+++ b/assets/styles/components/_blog.scss
@@ -201,6 +201,37 @@
color: #999;
}
}
+
+ &__page {
+ display: flex;
+ margin-top: 1.2rem;
+ justify-content: space-between;
+
+ &-link {
+ display: flex;
+ flex-direction: column;
+
+ &--newer {
+ margin-left: auto;
+ text-align: right;
+ }
+
+ &:hover {
+ text-decoration: none;
+
+ .blog-post__page-name {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ &-title {
+ color: #999;
+ line-height: 1em;
+ text-shadow: 0 1px #fff;
+ font-weight: bold;
+ }
+ }
}
&__sidebar {
diff --git a/components/blog-post.vue b/components/blog-post.vue
index f128e34..1272fd9 100644
--- a/components/blog-post.vue
+++ b/components/blog-post.vue
@@ -30,6 +30,26 @@
+
+
+
+ Older
+ {{ post.prev.title }}
+
+
+
+ Newer
+ {{ post.next.title }}
+
+
diff --git a/lib/blog/read-posts.ts b/lib/blog/read-posts.ts
index 1a52b88..e43fdaf 100644
--- a/lib/blog/read-posts.ts
+++ b/lib/blog/read-posts.ts
@@ -23,38 +23,27 @@ marked.use({
},
});
-export async function readBlogPosts(
- condition?: (body: BlogPost) => boolean,
- render = true,
- includeContent = true
-) {
+async function readBlogPosts() {
const files = await fs.readdir(dir);
const readMD = files.filter((file) => file.endsWith('.md'));
const readFiles = [];
+
for (const file of readMD) {
- const post = await readBlogPost(
- file.replace('.md', ''),
- condition,
- render,
- includeContent
- );
+ const post = await readBlogPost(file.replace('.md', ''));
if (!post) continue;
readFiles.push(post);
}
+
readFiles.sort((a, b) =>
new Date(b.date)
.toISOString()
.localeCompare(new Date(a.date).toISOString(), 'en', { numeric: true })
);
+
return readFiles;
}
-export async function readBlogPost(
- slug: string,
- condition?: (body: BlogPost) => boolean,
- render = true,
- includeContent = true
-): Promise {
+async function readBlogPost(slug: string): Promise {
const decoded = decodeURIComponent(slug);
if (!slug || decoded.includes('/') || decoded.includes('.'))
throw new Error('Invalid post slug');
@@ -64,30 +53,74 @@ export async function readBlogPost(
const read = await fs.readFile(mdpath, { encoding: 'utf-8' });
const { header: parsedHeader, length: headerLength } = await readHeader(read);
- const renderedMd = render
- ? await marked(read.substring(headerLength), { async: true })
- : undefined;
+ const renderedMd = await marked(read.substring(headerLength), {
+ async: true,
+ });
+
const { year, month, day } = getDateObject(parsedHeader);
const content = {
...parsedHeader,
file,
slug,
fullSlug: `${year}/${month}/${day}/${slug}`,
- markdown: includeContent ? read.substring(headerLength) : undefined,
+ markdown: read.substring(headerLength),
html: renderedMd,
};
- if (condition) {
- if (!condition(content)) {
- return;
- }
- }
-
return content;
}
+let blogPostCache: BlogPost[] = [];
+export const blogPostList = async () => {
+ if (!blogPostCache.length) {
+ blogPostCache = await readBlogPosts();
+ }
+ return blogPostCache;
+};
+
+export const getFilteredBlogPosts = async (
+ condition?: (body: BlogPost) => boolean,
+ htmlContent = true,
+ includeMarkdown = true
+): Promise => {
+ const posts = await blogPostList();
+ return posts
+ .filter((item) => (condition ? condition(item) : true))
+ .map((item) => ({
+ ...item,
+ html: htmlContent ? item.html : undefined,
+ markdown: includeMarkdown ? item.markdown : undefined,
+ }));
+};
+
+export const getBlogPost = async (
+ slug: string,
+ htmlContent = true,
+ includeMarkdown = true
+): Promise => {
+ const posts = await blogPostList();
+ const item = posts.find((item) => item.slug === slug);
+ if (!item) return;
+
+ const index = posts.indexOf(item);
+ const next = index > 0 ? posts[index - 1] : undefined;
+ const prev = posts[index + 1];
+
+ return {
+ ...item,
+ html: htmlContent ? item.html : undefined,
+ markdown: includeMarkdown ? item.markdown : undefined,
+ next: next
+ ? { title: next.title, slug: next.slug, fullSlug: next.fullSlug }
+ : undefined,
+ prev: prev
+ ? { title: prev.title, slug: prev.slug, fullSlug: prev.fullSlug }
+ : undefined,
+ };
+};
+
export async function getTags(): Promise {
- const posts = await readBlogPosts(undefined, false, false);
+ const posts = await getFilteredBlogPosts(undefined, false, false);
const obj: BlogPostTag[] = [];
for (const post of posts) {
@@ -113,7 +146,7 @@ export async function getTags(): Promise {
}
export async function getArchiveTree(condition?: (body: BlogPost) => boolean) {
- const posts = await readBlogPosts(condition, false, false);
+ const posts = await getFilteredBlogPosts(condition, false, false);
const obj = {};
for (const post of posts) {
diff --git a/lib/types/post.ts b/lib/types/post.ts
index 2c5adf0..1a3ce98 100644
--- a/lib/types/post.ts
+++ b/lib/types/post.ts
@@ -7,6 +7,8 @@ export interface BlogPost {
fullSlug: string;
markdown: string;
html: string;
+ next?: Partial;
+ prev?: Partial;
}
export interface BlogPostTag {
diff --git a/server/api/blog/[slug].ts b/server/api/blog/[slug].ts
index 3326355..7078a9b 100644
--- a/server/api/blog/[slug].ts
+++ b/server/api/blog/[slug].ts
@@ -1,10 +1,9 @@
-import { H3Error } from 'h3';
-import { readBlogPost } from '~~/lib/blog/read-posts';
+import { getBlogPost } from '~~/lib/blog/read-posts';
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug');
try {
- const post = await readBlogPost(slug);
+ const post = await getBlogPost(slug);
return post;
} catch (e) {}
});
diff --git a/server/api/blog/index.ts b/server/api/blog/index.ts
index 217fde6..9be5cc0 100644
--- a/server/api/blog/index.ts
+++ b/server/api/blog/index.ts
@@ -1,4 +1,4 @@
-import { readBlogPosts } from '~~/lib/blog/read-posts';
+import { getFilteredBlogPosts } from '~~/lib/blog/read-posts';
export default defineEventHandler(async (event) => {
const query = getQuery(event);
@@ -32,7 +32,7 @@ export default defineEventHandler(async (event) => {
return true;
};
- const posts = await readBlogPosts(
+ const posts = await getFilteredBlogPosts(
include,
query.render !== 'false',
query.body === 'true'
diff --git a/server/routes/blog/atom.xml.ts b/server/routes/blog/atom.xml.ts
index f7b27c6..9463330 100644
--- a/server/routes/blog/atom.xml.ts
+++ b/server/routes/blog/atom.xml.ts
@@ -1,10 +1,10 @@
import { Feed } from 'feed';
-import { readBlogPosts } from '~~/lib/blog/read-posts';
+import { getFilteredBlogPosts } from '~~/lib/blog/read-posts';
const BASE_URL = 'https://lunasqu.ee/blog';
export default defineEventHandler(async (event) => {
- const posts = await readBlogPosts(undefined, true, false);
+ const posts = await getFilteredBlogPosts(undefined, true, false);
const feed = new Feed({
title: "Evert's Blog",
description: 'Projects and Tutorials',