From f6ddbf8b288e6bb597c8b632cf1010b0360b8dad Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Sun, 16 Oct 2022 19:17:55 +0300 Subject: [PATCH] next/prev page --- assets/styles/components/_blog.scss | 31 ++++++++++ components/blog-post.vue | 20 +++++++ lib/blog/read-posts.ts | 91 ++++++++++++++++++++--------- lib/types/post.ts | 2 + server/api/blog/[slug].ts | 5 +- server/api/blog/index.ts | 4 +- server/routes/blog/atom.xml.ts | 4 +- 7 files changed, 121 insertions(+), 36 deletions(-) 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',