Compare commits

...

2 Commits

Author SHA1 Message Date
6d8981ff57 feat: finish post page 2022-07-24 05:35:23 +07:00
f1ed9a1609 feat: update not found handling 2022-07-24 05:04:56 +07:00
12 changed files with 159 additions and 146 deletions

View File

@ -1,23 +1,15 @@
FROM node:16 FROM node:16-alpine
# install dependencies # install dependencies
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json ./ RUN npm install -g pnpm
RUN pnpm install
# Copy all local files into the image.
COPY . . COPY . .
RUN pnpm install
RUN pnpm build RUN pnpm build
FROM node:16-alpine
###
# Only copy over the Node pieces we need
# ~> Saves 35MB
###
FROM node:16-slim
WORKDIR /app WORKDIR /app
COPY --from=0 /app/build . COPY --from=0 /app/build /app
COPY --from=0 /app/package.json /app
COPY --from=0 /app/node_modules /app/node_modules
EXPOSE 3000 EXPOSE 3000
CMD ["node", "./index.js"] CMD ["node","./index.js"]

View File

@ -39,7 +39,7 @@
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@damillora/plachta": "^1.0.0", "@damillora/plachta": "^1.2.0",
"@tryghost/content-api": "^1.11.0", "@tryghost/content-api": "^1.11.0",
"fitvids": "^2.1.1", "fitvids": "^2.1.1",
"svelte-material-icons": "^2.0.2", "svelte-material-icons": "^2.0.2",

View File

@ -1,7 +1,7 @@
lockfileVersion: 5.3 lockfileVersion: 5.3
specifiers: specifiers:
'@damillora/plachta': ^1.0.0 '@damillora/plachta': ^1.2.0
'@sveltejs/adapter-auto': next '@sveltejs/adapter-auto': next
'@sveltejs/adapter-node': ^1.0.0-next.83 '@sveltejs/adapter-node': ^1.0.0-next.83
'@sveltejs/kit': next '@sveltejs/kit': next
@ -30,7 +30,7 @@ specifiers:
vite: ^3.0.2 vite: ^3.0.2
dependencies: dependencies:
'@damillora/plachta': 1.0.0_svelte@3.49.0 '@damillora/plachta': 1.2.0_svelte@3.49.0
'@tryghost/content-api': 1.11.0 '@tryghost/content-api': 1.11.0
fitvids: 2.1.1 fitvids: 2.1.1
svelte-material-icons: 2.0.2_svelte@3.49.0 svelte-material-icons: 2.0.2_svelte@3.49.0
@ -66,8 +66,8 @@ packages:
resolution: {integrity: sha512-B1/plF62pt+H2IJHvApK8fdOJAVsvojvacuac8x8s+JIyqbropMyqNqHTKLm3YD8ZFLGwYeFTudU+PQ7vGvBdA==} resolution: {integrity: sha512-B1/plF62pt+H2IJHvApK8fdOJAVsvojvacuac8x8s+JIyqbropMyqNqHTKLm3YD8ZFLGwYeFTudU+PQ7vGvBdA==}
dev: true dev: true
/@damillora/plachta/1.0.0_svelte@3.49.0: /@damillora/plachta/1.2.0_svelte@3.49.0:
resolution: {integrity: sha512-ZOwMptEOhI0fBOe40p8PJVjV3b78yg6HoW3e/PaKND+fXtvOTbFnDZGfCPcMOD+pKRZm9G5X7AygqJ9atn0rfA==} resolution: {integrity: sha512-DDXojXmmyUS6tX4dgv8eirAU8R13fzHhcX/VoRRf8kfEpwPLqkAVto7qCdfveN0/Q3t/IqX0AKiFCdCfRI50wA==}
dependencies: dependencies:
svelte-material-icons: 2.0.2_svelte@3.49.0 svelte-material-icons: 2.0.2_svelte@3.49.0
svelte-themes: 0.0.98 svelte-themes: 0.0.98

View File

@ -16,7 +16,7 @@
"url": "https://blog.nanao.moe/", "url": "https://blog.nanao.moe/",
"image": { "image": {
"@type": "ImageObject", "@type": "ImageObject",
"url": "https://blog.nanao.moe/images/default-header.jpg" "url": "https://blog.nanao.moe/images/default-feature.jpg"
}, },
"mainEntityOfPage": { "mainEntityOfPage": {
"@type": "WebPage", "@type": "WebPage",
@ -34,13 +34,13 @@
<meta property="og:title" content="Damillora&#x27;s Virtual Memoir" /> <meta property="og:title" content="Damillora&#x27;s Virtual Memoir" />
<meta property="og:description" content="I talk about code, games, and music." /> <meta property="og:description" content="I talk about code, games, and music." />
<meta property="og:url" content="https://blog.nanao.moe/" /> <meta property="og:url" content="https://blog.nanao.moe/" />
<meta property="og:image" content="https://blog.nanao.moe/images/default-header.jpg" /> <meta property="og:image" content="https://blog.nanao.moe/images/default-feature.jpg" />
<meta property="article:publisher" content="https://nanao.moe" /> <meta property="article:publisher" content="https://nanao.moe" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Damillora&#x27;s Virtual Memoir" /> <meta name="twitter:title" content="Damillora&#x27;s Virtual Memoir" />
<meta name="twitter:description" content="I talk about code, games, and music." /> <meta name="twitter:description" content="I talk about code, games, and music." />
<meta name="twitter:url" content="https://blog.nanao.moe/" /> <meta name="twitter:url" content="https://blog.nanao.moe/" />
<meta name="twitter:image" content="https://blog.nanao.moe/images/default-header.jpg" /> <meta name="twitter:image" content="https://blog.nanao.moe/images/default-feature.jpg" />
<meta name="twitter:site" content="@Damillora" /> <meta name="twitter:site" content="@Damillora" />
{@html schemaOrg} {@html schemaOrg}

View File

@ -1,7 +1,4 @@
<script lang="ts"> <script lang="ts">
import Post from "../../../../../Plachta/package/components/PageTypes/Post.svelte";
export let post: any; export let post: any;
let schemaOrg = ` let schemaOrg = `
@ -24,7 +21,10 @@ import Post from "../../../../../Plachta/package/components/PageTypes/Post.svelt
"name": "${post.primary_author.name}", "name": "${post.primary_author.name}",
"image": { "image": {
"@type": "ImageObject", "@type": "ImageObject",
"url": "${post.primary_author.profile_image ?? 'https://blog.nanao.moe/images/default-feature.jpg'}" "url": "${
post.primary_author.profile_image ??
'https://blog.nanao.moe/images/default-feature.jpg'
}"
}, },
"url": "${post.primary_author.url}/" "url": "${post.primary_author.url}/"
}, },
@ -48,28 +48,34 @@ import Post from "../../../../../Plachta/package/components/PageTypes/Post.svelt
</script> </script>
<svelte:head> <svelte:head>
<meta property="og:site_name" content="Damillora&#x27;s Virtual Memoir" /> <meta property="og:site_name" content="Damillora&#x27;s Virtual Memoir" />
<meta property="og:type" content="article" /> <meta property="og:type" content="article" />
<meta property="og:title" content={post.title} /> <meta property="og:title" content={post.title} />
<meta property="og:description" content={post.excerpt}/> <meta property="og:description" content={post.excerpt} />
<meta property="og:url" content={post.url} /> <meta property="og:url" content={post.url} />
<meta property="og:image" content={post.feature_image ?? 'https://blog.nanao.moe/images/default-feature.jpg'} /> <meta
<meta property="article:published_time" content="2022-07-22T12:42:23.000Z" /> property="og:image"
<meta property="article:modified_time" content="2022-07-22T12:47:19.000Z" /> content={post.feature_image ?? 'https://blog.nanao.moe/images/default-feature.jpg'}
<meta property="article:tag" content={post.primary_tag.name} /> />
<meta property="article:published_time" content="2022-07-22T12:42:23.000Z" />
<meta property="article:publisher" content="https://www.facebook.com/Damillora" /> <meta property="article:modified_time" content="2022-07-22T12:47:19.000Z" />
<meta name="twitter:card" content="summary_large_image" /> <meta property="article:tag" content={post.primary_tag.name} />
<meta name="twitter:title" content={post.title} />
<meta name="twitter:description" content={post.excerpt} /> <meta property="article:publisher" content="https://www.facebook.com/Damillora" />
<meta name="twitter:url" content={post.url} /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={post.feature_image ?? 'https://blog.nanao.moe/images/default-feature.jpg'} /> <meta name="twitter:title" content={post.title} />
<meta name="twitter:label1" content="Written by" /> <meta name="twitter:description" content={post.excerpt} />
<meta name="twitter:data1" content={post.primary_author.name} /> <meta name="twitter:url" content={post.url} />
<meta name="twitter:label2" content="Filed under" /> <meta
<meta name="twitter:data2" content={post.primary_tag.name} /> name="twitter:image"
<meta name="twitter:site" content="@Damillora" /> content={post.feature_image ?? 'https://blog.nanao.moe/images/default-feature.jpg'}
<meta name="twitter:creator" content={post.primary_author.twitter} /> />
<meta name="twitter:label1" content="Written by" />
<meta name="twitter:data1" content={post.primary_author.name} />
<meta name="twitter:label2" content="Filed under" />
<meta name="twitter:data2" content={post.primary_tag.name} />
<meta name="twitter:site" content="@Damillora" />
<meta name="twitter:creator" content={post.primary_author.twitter} />
{@html schemaOrg} {@html schemaOrg}
</svelte:head> </svelte:head>

View File

@ -16,6 +16,28 @@ export const browsePost = async (page = 1) => {
return posts return posts
} }
export const browseNextPost = async (id: string) => {
const posts = await api.posts.browse({ limit: 1, filter: [`id:>${id}`], order: 'id ASC' });
if (posts.length > 0) {
return posts[0];
}
return null;
}
export const browsePrevPost = async (id: string) => {
const posts = await api.posts.browse({ limit: 1, filter: [`id:<${id}`], order: 'id DESC' });
if (posts.length > 0) {
return posts[0];
}
return null;
}
export const browseRelatedPost = async (tag: string | undefined, id: string) => {
const tagCondition = tag ? [`tag:${tag}`] : []
const posts = await api.posts.browse({ limit: 3, filter: [...tagCondition, ...[`id:-${id}`]], order: 'published_at DESC', include: ['tags', 'authors'] });
return posts;
}
export const browseAllPost = async (page = 1) => { export const browseAllPost = async (page = 1) => {
const posts = await api.posts.browse({ limit: 'all' }); const posts = await api.posts.browse({ limit: 'all' });
@ -32,21 +54,32 @@ export const browsePostWithAuthor = async (slug: string, page = 1) => {
return posts return posts
} }
export const readPost = async (slug: string) => { export const readPost = async (slug: string) => {
const post = await api.posts.read({ slug }, { include: ['tags', 'authors'] }); try {
const post = await api.posts.read({ slug }, { include: ['tags', 'authors'] });
return post; return post;
} catch (e) {
return null;
}
} }
export const readTag = async (slug: string) => { export const readTag = async (slug: string) => {
const tag = await api.tags.read({ slug }); try {
const tag = await api.tags.read({ slug });
return tag; return tag;
} catch (e) {
return null;
}
} }
export const readAuthor = async (slug: string) => { export const readAuthor = async (slug: string) => {
const author = await api.authors.read({ slug }); try {
const author = await api.authors.read({ slug });
return author; return author;
} catch (e) {
return null;
}
} }
export const browseLatestPost = async () => { export const browseLatestPost = async () => {

View File

@ -2,10 +2,21 @@
export const load: Load = async ({ params }) => { export const load: Load = async ({ params }) => {
const postSlug = params.slug; const postSlug = params.slug;
const post = await readPost(postSlug); const post = await readPost(postSlug);
if (!post) {
return {
status: 404,
error: new Error('Post not found')
};
}
const prevPost = await browsePrevPost(post.id);
const nextPost = await browseNextPost(post.id);
const relatedPost = await browseRelatedPost(post.primary_tag?.slug, post.id);
return { return {
props: { props: {
post: post post: post,
prevPost: prevPost,
nextPost: nextPost,
relatedPost: relatedPost
} }
}; };
}; };
@ -24,22 +35,33 @@
import PostRelated from '@damillora/plachta/components/Post/PostRelated.svelte'; import PostRelated from '@damillora/plachta/components/Post/PostRelated.svelte';
import PostCard from '@damillora/plachta/components/PostCard/PostCard.svelte'; import PostCard from '@damillora/plachta/components/PostCard/PostCard.svelte';
import type { Load } from '@sveltejs/kit'; import type { Load } from '@sveltejs/kit';
import { readPost } from '$lib/content/contentApi'; import {
browseNextPost,
browsePrevPost,
browseRelatedPost,
readPost
} from '$lib/content/contentApi';
import { browser } from '$app/env'; import { browser } from '$app/env';
import PostSeo from '$lib/components/SEO/PostSEO.svelte'; import PostSeo from '$lib/components/SEO/PostSEO.svelte';
import { onMount } from 'svelte';
export let post: any; export let post: any;
export let prevPost: any;
export let nextPost: any;
export let relatedPost: any[];
if (browser) { onMount(() => {
fitvids(); if (browser) {
} fitvids();
}
});
</script> </script>
<svelte:head> <svelte:head>
<title>{post.title}</title> <title>{post.title}</title>
</svelte:head> </svelte:head>
<PostSeo post={post} /> <PostSeo {post} />
<Hero background={post.feature_image ?? `/images/default-feature.jpg`} /> <Hero background={post.feature_image ?? `/images/default-feature.jpg`} />
<Container> <Container>
@ -55,78 +77,21 @@ import PostSeo from '$lib/components/SEO/PostSEO.svelte';
{@html post.html} {@html post.html}
<svelte:fragment slot="comments" /> <svelte:fragment slot="comments" />
</PostMain> </PostMain>
<PostNavigator <PostNavigator prev_post={prevPost} next_post={nextPost} />
prev_post={{
url: '/examples/post-page',
title: 'Sophie!'
}}
next_post={{
url: '/examples/post-page',
title: 'Sophie!'
}}
/>
</Post> </Post>
<PostRelated name="Sophie" url="https://google.com" accent_color="#3b34dc"> <PostRelated name={post.primary_tag.name} url={post.primary_tag.utl} accent_color={post.primary_tag.accent_color}>
<PostCard {#each relatedPost as post}
authors={[ <PostCard
{ title={post.title}
profile_image: 'https://images.nanao.moe/r/GDuirR.jpg', authors={post.authors}
url: '/examples/post-page', primary_tag={post.primary_tag}
name: 'Sophie Neuenmuller' date={dayjs(post.published_at).format('DD MMM YYYY')}
} reading_time={`${post.reading_time} min read`}
]} excerpt={post.excerpt}
date="2022 November 2020" feature_image={post.feature_image ?? '/images/default-feature.jpg'}
excerpt="Sophie!" url={post.url}
feature_image="https://images.nanao.moe/r/7jETH3.png" />
primary_tag={{ {/each}
accent_color: '#3b7b9c',
name: 'Sound Voltex',
url: '/examples/post-page'
}}
reading_time="3 min"
title="Sophie!"
url="/examples/post-page"
/>
<PostCard
authors={[
{
profile_image: 'https://images.nanao.moe/r/GDuirR.jpg',
url: '/examples/post-page',
name: 'Sophie Neuenmuller'
}
]}
date="2022 November 2020"
excerpt="Sophie!"
feature_image="https://images.nanao.moe/r/7jETH3.png"
primary_tag={{
accent_color: '#3b7b9c',
name: 'Sound Voltex',
url: '/examples/post-page'
}}
reading_time="3 min"
title="Sophie!"
url="/examples/post-page"
/>
<PostCard
authors={[
{
profile_image: 'https://images.nanao.moe/r/GDuirR.jpg',
url: '/examples/post-page',
name: 'Sophie Neuenmuller'
}
]}
date="2022 November 2020"
excerpt="Sophie!"
feature_image="https://images.nanao.moe/r/7jETH3.png"
primary_tag={{
accent_color: '#3b7b9c',
name: 'Sound Voltex',
url: '/examples/post-page'
}}
reading_time="3 min"
title="Sophie!"
url="/examples/post-page"
/>
</PostRelated> </PostRelated>
</Container> </Container>

View File

@ -1,15 +1,22 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
export const load: Load = async ({ params }) => { export const load: Load = async ({ params }) => {
const tagSlug = params.tag; const tagSlug = params.tag;
const tagObj = await readTag(tagSlug); const tagObj = await readTag(tagSlug);
const posts = await browsePostWithTag(tagSlug); if (!tagObj) {
return {
status: 404,
error: new Error('Tag not found')
};
}
const posts = await browsePostWithTag(tagSlug);
return { return {
props: { props: {
tag: tagObj, tag: tagObj,
posts: posts posts: posts
} }
}; };
}; };
</script> </script>

View File

@ -44,7 +44,7 @@
<a href="/"> <strong>Damillora</strong>'s Virtual Memoir </a> <a href="/"> <strong>Damillora</strong>'s Virtual Memoir </a>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="nav"> <svelte:fragment slot="nav">
<NavMenu label="Home" url="/" /> <NavMenu label="nanao.moe" url="https://nanao.moe" />
<NavDarkMode /> <NavDarkMode />
<NavSearch on:search={doSearch} /> <NavSearch on:search={doSearch} />
</svelte:fragment> </svelte:fragment>

View File

@ -2,6 +2,14 @@
export const load: Load = async ({ params }) => { export const load: Load = async ({ params }) => {
const authorSlug = params.author; const authorSlug = params.author;
const authorObj = await readAuthor(authorSlug); const authorObj = await readAuthor(authorSlug);
if (!authorObj) {
return {
status: 404,
error: new Error('Author not found')
};
}
const posts = await browsePostWithAuthor(authorSlug); const posts = await browsePostWithAuthor(authorSlug);
return { return {

View File

@ -8,7 +8,7 @@ const config = {
preprocess: preprocess(), preprocess: preprocess(),
kit: { kit: {
adapter: adapter() adapter: adapter(),
} }
}; };

View File

@ -5,6 +5,8 @@ import preprocess from 'svelte-preprocess';
const config = { const config = {
plugins: [sveltekit({ plugins: [sveltekit({
useVitePreprocess: true, useVitePreprocess: true,
noExternal: ['dayjs']
})], })],
}; };