next/prev page
This commit is contained in:
parent
aceb33fe6a
commit
f6ddbf8b28
@ -201,6 +201,37 @@
|
|||||||
color: #999;
|
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 {
|
&__sidebar {
|
||||||
|
@ -30,6 +30,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="detail" class="blog-post__page">
|
||||||
|
<NuxtLink
|
||||||
|
v-if="post.prev"
|
||||||
|
:to="'/blog/' + post.prev.fullSlug"
|
||||||
|
class="blog-post__page-link blog-post__page-link--older"
|
||||||
|
>
|
||||||
|
<span class="blog-post__page-title">Older</span>
|
||||||
|
<span class="blog-post__page-name">{{ post.prev.title }}</span>
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<NuxtLink
|
||||||
|
v-if="post.next"
|
||||||
|
:to="'/blog/' + post.next.fullSlug"
|
||||||
|
class="blog-post__page-link blog-post__page-link--newer"
|
||||||
|
>
|
||||||
|
<span class="blog-post__page-title">Newer</span>
|
||||||
|
<span class="blog-post__page-name">{{ post.next.title }}</span>
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -23,38 +23,27 @@ marked.use({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function readBlogPosts(
|
async function readBlogPosts() {
|
||||||
condition?: (body: BlogPost) => boolean,
|
|
||||||
render = true,
|
|
||||||
includeContent = true
|
|
||||||
) {
|
|
||||||
const files = await fs.readdir(dir);
|
const files = await fs.readdir(dir);
|
||||||
const readMD = files.filter((file) => file.endsWith('.md'));
|
const readMD = files.filter((file) => file.endsWith('.md'));
|
||||||
const readFiles = [];
|
const readFiles = [];
|
||||||
|
|
||||||
for (const file of readMD) {
|
for (const file of readMD) {
|
||||||
const post = await readBlogPost(
|
const post = await readBlogPost(file.replace('.md', ''));
|
||||||
file.replace('.md', ''),
|
|
||||||
condition,
|
|
||||||
render,
|
|
||||||
includeContent
|
|
||||||
);
|
|
||||||
if (!post) continue;
|
if (!post) continue;
|
||||||
readFiles.push(post);
|
readFiles.push(post);
|
||||||
}
|
}
|
||||||
|
|
||||||
readFiles.sort((a, b) =>
|
readFiles.sort((a, b) =>
|
||||||
new Date(b.date)
|
new Date(b.date)
|
||||||
.toISOString()
|
.toISOString()
|
||||||
.localeCompare(new Date(a.date).toISOString(), 'en', { numeric: true })
|
.localeCompare(new Date(a.date).toISOString(), 'en', { numeric: true })
|
||||||
);
|
);
|
||||||
|
|
||||||
return readFiles;
|
return readFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readBlogPost(
|
async function readBlogPost(slug: string): Promise<BlogPost[]> {
|
||||||
slug: string,
|
|
||||||
condition?: (body: BlogPost) => boolean,
|
|
||||||
render = true,
|
|
||||||
includeContent = true
|
|
||||||
): Promise<BlogPost[]> {
|
|
||||||
const decoded = decodeURIComponent(slug);
|
const decoded = decodeURIComponent(slug);
|
||||||
if (!slug || decoded.includes('/') || decoded.includes('.'))
|
if (!slug || decoded.includes('/') || decoded.includes('.'))
|
||||||
throw new Error('Invalid post slug');
|
throw new Error('Invalid post slug');
|
||||||
@ -64,30 +53,74 @@ export async function readBlogPost(
|
|||||||
const read = await fs.readFile(mdpath, { encoding: 'utf-8' });
|
const read = await fs.readFile(mdpath, { encoding: 'utf-8' });
|
||||||
|
|
||||||
const { header: parsedHeader, length: headerLength } = await readHeader(read);
|
const { header: parsedHeader, length: headerLength } = await readHeader(read);
|
||||||
const renderedMd = render
|
const renderedMd = await marked(read.substring(headerLength), {
|
||||||
? await marked(read.substring(headerLength), { async: true })
|
async: true,
|
||||||
: undefined;
|
});
|
||||||
|
|
||||||
const { year, month, day } = getDateObject(parsedHeader);
|
const { year, month, day } = getDateObject(parsedHeader);
|
||||||
const content = {
|
const content = {
|
||||||
...parsedHeader,
|
...parsedHeader,
|
||||||
file,
|
file,
|
||||||
slug,
|
slug,
|
||||||
fullSlug: `${year}/${month}/${day}/${slug}`,
|
fullSlug: `${year}/${month}/${day}/${slug}`,
|
||||||
markdown: includeContent ? read.substring(headerLength) : undefined,
|
markdown: read.substring(headerLength),
|
||||||
html: renderedMd,
|
html: renderedMd,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (condition) {
|
|
||||||
if (!condition(content)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
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<BlogPost[]> => {
|
||||||
|
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<BlogPost> => {
|
||||||
|
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<BlogPostTag[]> {
|
export async function getTags(): Promise<BlogPostTag[]> {
|
||||||
const posts = await readBlogPosts(undefined, false, false);
|
const posts = await getFilteredBlogPosts(undefined, false, false);
|
||||||
const obj: BlogPostTag[] = [];
|
const obj: BlogPostTag[] = [];
|
||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
@ -113,7 +146,7 @@ export async function getTags(): Promise<BlogPostTag[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getArchiveTree(condition?: (body: BlogPost) => boolean) {
|
export async function getArchiveTree(condition?: (body: BlogPost) => boolean) {
|
||||||
const posts = await readBlogPosts(condition, false, false);
|
const posts = await getFilteredBlogPosts(condition, false, false);
|
||||||
const obj = {};
|
const obj = {};
|
||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
|
@ -7,6 +7,8 @@ export interface BlogPost {
|
|||||||
fullSlug: string;
|
fullSlug: string;
|
||||||
markdown: string;
|
markdown: string;
|
||||||
html: string;
|
html: string;
|
||||||
|
next?: Partial<BlogPost>;
|
||||||
|
prev?: Partial<BlogPost>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlogPostTag {
|
export interface BlogPostTag {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { H3Error } from 'h3';
|
import { getBlogPost } from '~~/lib/blog/read-posts';
|
||||||
import { readBlogPost } from '~~/lib/blog/read-posts';
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const slug = getRouterParam(event, 'slug');
|
const slug = getRouterParam(event, 'slug');
|
||||||
try {
|
try {
|
||||||
const post = await readBlogPost(slug);
|
const post = await getBlogPost(slug);
|
||||||
return post;
|
return post;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { readBlogPosts } from '~~/lib/blog/read-posts';
|
import { getFilteredBlogPosts } from '~~/lib/blog/read-posts';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
@ -32,7 +32,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const posts = await readBlogPosts(
|
const posts = await getFilteredBlogPosts(
|
||||||
include,
|
include,
|
||||||
query.render !== 'false',
|
query.render !== 'false',
|
||||||
query.body === 'true'
|
query.body === 'true'
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Feed } from 'feed';
|
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';
|
const BASE_URL = 'https://lunasqu.ee/blog';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const posts = await readBlogPosts(undefined, true, false);
|
const posts = await getFilteredBlogPosts(undefined, true, false);
|
||||||
const feed = new Feed({
|
const feed = new Feed({
|
||||||
title: "Evert's Blog",
|
title: "Evert's Blog",
|
||||||
description: 'Projects and Tutorials',
|
description: 'Projects and Tutorials',
|
||||||
|
Loading…
Reference in New Issue
Block a user