style: 迁移到 v2.
Some checks failed
🚀 Build and deploy by ftp / 🎉 Deploy (push) Failing after 8m21s

This commit is contained in:
2023-08-16 23:55:57 +08:00
parent de1da22508
commit 3932a2b612
45 changed files with 806 additions and 606 deletions

View File

@@ -1,10 +1,10 @@
import Link from '@/components/Link'
import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
import { formatDate } from 'pliny/utils/formatDate'
import NewsletterForm from 'pliny/ui/NewsletterForm'
import Link from '@/components/Link';
import Tag from '@/components/Tag';
import siteMetadata from '@/data/siteMetadata';
import { formatDate } from 'pliny/utils/formatDate';
import NewsletterForm from 'pliny/ui/NewsletterForm';
const MAX_DISPLAY = 5
const MAX_DISPLAY = 5;
export default function Home({ posts }) {
return (
@@ -21,7 +21,7 @@ export default function Home({ posts }) {
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
{!posts.length && 'No posts found.'}
{posts.slice(0, MAX_DISPLAY).map((post) => {
const { slug, date, title, summary, tags } = post
const { slug, date, title, summary, tags } = post;
return (
<li key={slug} className="py-12">
<article>
@@ -29,7 +29,9 @@ export default function Home({ posts }) {
<dl>
<dt className="sr-only">Published on</dt>
<dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400">
<time dateTime={date}>{formatDate(date, siteMetadata.locale)}</time>
<time dateTime={date}>
{formatDate(date, siteMetadata.locale)}
</time>
</dd>
</dl>
<div className="space-y-5 xl:col-span-3">
@@ -38,8 +40,7 @@ export default function Home({ posts }) {
<h2 className="text-2xl font-bold leading-8 tracking-tight">
<Link
href={`/blog/${slug}`}
className="text-gray-900 dark:text-gray-100"
>
className="text-gray-900 dark:text-gray-100">
{title}
</Link>
</h2>
@@ -57,8 +58,7 @@ export default function Home({ posts }) {
<Link
href={`/blog/${slug}`}
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
aria-label={`Read "${title}"`}
>
aria-label={`Read "${title}"`}>
Read more &rarr;
</Link>
</div>
@@ -66,7 +66,7 @@ export default function Home({ posts }) {
</div>
</article>
</li>
)
);
})}
</ul>
</div>
@@ -75,8 +75,7 @@ export default function Home({ posts }) {
<Link
href="/blog"
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
aria-label="All posts"
>
aria-label="All posts">
All Posts &rarr;
</Link>
</div>
@@ -87,5 +86,5 @@ export default function Home({ posts }) {
</div>
)}
</>
)
);
}

View File

@@ -1,14 +1,14 @@
import { Authors, allAuthors } from 'contentlayer/generated'
import { MDXLayoutRenderer } from 'pliny/mdx-components'
import AuthorLayout from '@/layouts/AuthorLayout'
import { coreContent } from 'pliny/utils/contentlayer'
import { genPageMetadata } from 'app/seo'
import { Authors, allAuthors } from 'contentlayer/generated';
import { MDXLayoutRenderer } from 'pliny/mdx-components';
import AuthorLayout from '@/layouts/AuthorLayout';
import { coreContent } from 'pliny/utils/contentlayer';
import { genPageMetadata } from 'app/seo';
export const metadata = genPageMetadata({ title: 'About' })
export const metadata = genPageMetadata({ title: 'About' });
export default function Page() {
const author = allAuthors.find((p) => p.slug === 'default') as Authors
const mainContent = coreContent(author)
const author = allAuthors.find((p) => p.slug === 'default') as Authors;
const mainContent = coreContent(author);
return (
<>
@@ -16,5 +16,5 @@ export default function Page() {
<MDXLayoutRenderer code={author.body.code} />
</AuthorLayout>
</>
)
);
}

View File

@@ -1,9 +1,9 @@
import { NewsletterAPI } from 'pliny/newsletter'
import siteMetadata from '@/data/siteMetadata'
import { NewsletterAPI } from 'pliny/newsletter';
import siteMetadata from '@/data/siteMetadata';
const handler = NewsletterAPI({
// @ts-ignore
provider: siteMetadata.newsletter.provider,
})
});
export { handler as GET, handler as POST }
export { handler as GET, handler as POST };

View File

@@ -1,54 +1,54 @@
import 'css/prism.css'
import 'katex/dist/katex.css'
import 'css/prism.css';
import 'katex/dist/katex.css';
import PageTitle from '@/components/PageTitle'
import { components } from '@/components/MDXComponents'
import { MDXLayoutRenderer } from 'pliny/mdx-components'
import { sortPosts, coreContent } from 'pliny/utils/contentlayer'
import { allBlogs, allAuthors } from 'contentlayer/generated'
import type { Authors, Blog } from 'contentlayer/generated'
import PostSimple from '@/layouts/PostSimple'
import PostLayout from '@/layouts/PostLayout'
import PostBanner from '@/layouts/PostBanner'
import { Metadata } from 'next'
import siteMetadata from '@/data/siteMetadata'
import PageTitle from '@/components/PageTitle';
import { components } from '@/components/MDXComponents';
import { MDXLayoutRenderer } from 'pliny/mdx-components';
import { sortPosts, coreContent } from 'pliny/utils/contentlayer';
import { allBlogs, allAuthors } from 'contentlayer/generated';
import type { Authors, Blog } from 'contentlayer/generated';
import PostSimple from '@/layouts/PostSimple';
import PostLayout from '@/layouts/PostLayout';
import PostBanner from '@/layouts/PostBanner';
import { Metadata } from 'next';
import siteMetadata from '@/data/siteMetadata';
const isProduction = process.env.NODE_ENV === 'production'
const defaultLayout = 'PostLayout'
const isProduction = process.env.NODE_ENV === 'production';
const defaultLayout = 'PostLayout';
const layouts = {
PostSimple,
PostLayout,
PostBanner,
}
};
export async function generateMetadata({
params,
}: {
params: { slug: string[] }
params: { slug: string[] };
}): Promise<Metadata | undefined> {
const slug = decodeURI(params.slug.join('/'))
const post = allBlogs.find((p) => p.slug === slug)
const authorList = post?.authors || ['default']
const slug = decodeURI(params.slug.join('/'));
const post = allBlogs.find((p) => p.slug === slug);
const authorList = post?.authors || ['default'];
const authorDetails = authorList.map((author) => {
const authorResults = allAuthors.find((p) => p.slug === author)
return coreContent(authorResults as Authors)
})
const authorResults = allAuthors.find((p) => p.slug === author);
return coreContent(authorResults as Authors);
});
if (!post) {
return
return;
}
const publishedAt = new Date(post.date).toISOString()
const modifiedAt = new Date(post.lastmod || post.date).toISOString()
const authors = authorDetails.map((author) => author.name)
let imageList = [siteMetadata.socialBanner]
const publishedAt = new Date(post.date).toISOString();
const modifiedAt = new Date(post.lastmod || post.date).toISOString();
const authors = authorDetails.map((author) => author.name);
let imageList = [siteMetadata.socialBanner];
if (post.images) {
imageList = typeof post.images === 'string' ? [post.images] : post.images
imageList = typeof post.images === 'string' ? [post.images] : post.images;
}
const ogImages = imageList.map((img) => {
return {
url: img.includes('http') ? img : siteMetadata.siteUrl + img,
}
})
};
});
return {
title: post.title,
@@ -71,37 +71,37 @@ export async function generateMetadata({
description: post.summary,
images: imageList,
},
}
};
}
export const generateStaticParams = async () => {
const paths = allBlogs.map((p) => ({ slug: p.slug.split('/') }))
const paths = allBlogs.map((p) => ({ slug: p.slug.split('/') }));
return paths
}
return paths;
};
export default async function Page({ params }: { params: { slug: string[] } }) {
const slug = decodeURI(params.slug.join('/'))
const sortedPosts = sortPosts(allBlogs) as Blog[]
const postIndex = sortedPosts.findIndex((p) => p.slug === slug)
const prev = coreContent(sortedPosts[postIndex + 1])
const next = coreContent(sortedPosts[postIndex - 1])
const post = sortedPosts.find((p) => p.slug === slug) as Blog
const authorList = post?.authors || ['default']
const slug = decodeURI(params.slug.join('/'));
const sortedPosts = sortPosts(allBlogs) as Blog[];
const postIndex = sortedPosts.findIndex((p) => p.slug === slug);
const prev = coreContent(sortedPosts[postIndex + 1]);
const next = coreContent(sortedPosts[postIndex - 1]);
const post = sortedPosts.find((p) => p.slug === slug) as Blog;
const authorList = post?.authors || ['default'];
const authorDetails = authorList.map((author) => {
const authorResults = allAuthors.find((p) => p.slug === author)
return coreContent(authorResults as Authors)
})
const mainContent = coreContent(post)
const jsonLd = post.structuredData
const authorResults = allAuthors.find((p) => p.slug === author);
return coreContent(authorResults as Authors);
});
const mainContent = coreContent(post);
const jsonLd = post.structuredData;
jsonLd['author'] = authorDetails.map((author) => {
return {
'@type': 'Person',
name: author.name,
}
})
};
});
const Layout = layouts[post.layout || defaultLayout]
const Layout = layouts[post.layout || defaultLayout];
return (
<>
@@ -120,11 +120,19 @@ export default async function Page({ params }: { params: { slug: string[] } }) {
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<Layout content={mainContent} authorDetails={authorDetails} next={next} prev={prev}>
<MDXLayoutRenderer code={post.body.code} components={components} toc={post.toc} />
<Layout
content={mainContent}
authorDetails={authorDetails}
next={next}
prev={prev}>
<MDXLayoutRenderer
code={post.body.code}
components={components}
toc={post.toc}
/>
</Layout>
</>
)}
</>
)
);
}

View File

@@ -1,23 +1,23 @@
import ListLayout from '@/layouts/ListLayoutWithTags'
import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer'
import { allBlogs } from 'contentlayer/generated'
import { genPageMetadata } from 'app/seo'
import ListLayout from '@/layouts/ListLayoutWithTags';
import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer';
import { allBlogs } from 'contentlayer/generated';
import { genPageMetadata } from 'app/seo';
const POSTS_PER_PAGE = 5
const POSTS_PER_PAGE = 5;
export const metadata = genPageMetadata({ title: 'Blog' })
export const metadata = genPageMetadata({ title: 'Blog' });
export default function BlogPage() {
const posts = allCoreContent(sortPosts(allBlogs))
const pageNumber = 1
const posts = allCoreContent(sortPosts(allBlogs));
const pageNumber = 1;
const initialDisplayPosts = posts.slice(
POSTS_PER_PAGE * (pageNumber - 1),
POSTS_PER_PAGE * pageNumber
)
POSTS_PER_PAGE * pageNumber,
);
const pagination = {
currentPage: pageNumber,
totalPages: Math.ceil(posts.length / POSTS_PER_PAGE),
}
};
return (
<ListLayout
@@ -26,5 +26,5 @@ export default function BlogPage() {
pagination={pagination}
title="All Posts"
/>
)
);
}

View File

@@ -1,27 +1,29 @@
import ListLayout from '@/layouts/ListLayoutWithTags'
import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer'
import { allBlogs } from 'contentlayer/generated'
import ListLayout from '@/layouts/ListLayoutWithTags';
import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer';
import { allBlogs } from 'contentlayer/generated';
const POSTS_PER_PAGE = 5
const POSTS_PER_PAGE = 5;
export const generateStaticParams = async () => {
const totalPages = Math.ceil(allBlogs.length / POSTS_PER_PAGE)
const paths = Array.from({ length: totalPages }, (_, i) => ({ page: (i + 1).toString() }))
const totalPages = Math.ceil(allBlogs.length / POSTS_PER_PAGE);
const paths = Array.from({ length: totalPages }, (_, i) => ({
page: (i + 1).toString(),
}));
return paths
}
return paths;
};
export default function Page({ params }: { params: { page: string } }) {
const posts = allCoreContent(sortPosts(allBlogs))
const pageNumber = parseInt(params.page as string)
const posts = allCoreContent(sortPosts(allBlogs));
const pageNumber = parseInt(params.page as string);
const initialDisplayPosts = posts.slice(
POSTS_PER_PAGE * (pageNumber - 1),
POSTS_PER_PAGE * pageNumber
)
POSTS_PER_PAGE * pageNumber,
);
const pagination = {
currentPage: pageNumber,
totalPages: Math.ceil(posts.length / POSTS_PER_PAGE),
}
};
return (
<ListLayout
@@ -30,5 +32,5 @@ export default function Page({ params }: { params: { page: string } }) {
pagination={pagination}
title="All Posts"
/>
)
);
}

View File

@@ -1,16 +1,42 @@
export default function Head() {
return (
<>
<link rel="apple-touch-icon" sizes="76x76" href="/static/favicons/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicons/favicon-16x16.png" />
<link
rel="apple-touch-icon"
sizes="76x76"
href="/static/favicons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/static/favicons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/static/favicons/favicon-16x16.png"
/>
<link rel="manifest" href="/static/favicons/site.webmanifest" />
<link rel="mask-icon" href="/static/favicons/safari-pinned-tab.svg" color="#5bbad5" />
<link
rel="mask-icon"
href="/static/favicons/safari-pinned-tab.svg"
color="#5bbad5"
/>
<meta name="msapplication-TileColor" content="#000000" />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#fff" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000" />
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="#fff"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#000"
/>
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
</>
)
);
}

View File

@@ -1,21 +1,21 @@
import 'css/tailwind.css'
import 'pliny/search/algolia.css'
import 'css/tailwind.css';
import 'pliny/search/algolia.css';
import { Space_Grotesk } from 'next/font/google'
import { Analytics, AnalyticsConfig } from 'pliny/analytics'
import { SearchProvider, SearchConfig } from 'pliny/search'
import Header from '@/components/Header'
import SectionContainer from '@/components/SectionContainer'
import Footer from '@/components/Footer'
import siteMetadata from '@/data/siteMetadata'
import { ThemeProviders } from './theme-providers'
import { Metadata } from 'next'
import { Space_Grotesk } from 'next/font/google';
import { Analytics, AnalyticsConfig } from 'pliny/analytics';
import { SearchProvider, SearchConfig } from 'pliny/search';
import Header from '@/components/Header';
import SectionContainer from '@/components/SectionContainer';
import Footer from '@/components/Footer';
import siteMetadata from '@/data/siteMetadata';
import { ThemeProviders } from './theme-providers';
import { Metadata } from 'next';
const space_grotesk = Space_Grotesk({
subsets: ['latin'],
display: 'swap',
variable: '--font-space-grotesk',
})
});
export const metadata: Metadata = {
metadataBase: new URL(siteMetadata.siteUrl),
@@ -55,30 +55,62 @@ export const metadata: Metadata = {
card: 'summary_large_image',
images: [siteMetadata.socialBanner],
},
}
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html
lang={siteMetadata.language}
className={`${space_grotesk.variable} scroll-smooth`}
suppressHydrationWarning
>
<link rel="apple-touch-icon" sizes="76x76" href="/static/favicons/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicons/favicon-16x16.png" />
suppressHydrationWarning>
<link
rel="apple-touch-icon"
sizes="76x76"
href="/static/favicons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/static/favicons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/static/favicons/favicon-16x16.png"
/>
<link rel="manifest" href="/static/favicons/site.webmanifest" />
<link rel="mask-icon" href="/static/favicons/safari-pinned-tab.svg" color="#5bbad5" />
<link
rel="mask-icon"
href="/static/favicons/safari-pinned-tab.svg"
color="#5bbad5"
/>
<meta name="msapplication-TileColor" content="#000000" />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#fff" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000" />
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="#fff"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#000"
/>
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
<body className="bg-white text-black antialiased dark:bg-gray-950 dark:text-white">
<ThemeProviders>
<Analytics analyticsConfig={siteMetadata.analytics as AnalyticsConfig} />
<Analytics
analyticsConfig={siteMetadata.analytics as AnalyticsConfig}
/>
<SectionContainer>
<div className="flex h-screen flex-col justify-between font-sans">
<SearchProvider searchConfig={siteMetadata.search as SearchConfig}>
<SearchProvider
searchConfig={siteMetadata.search as SearchConfig}>
<Header />
<main className="mb-auto">{children}</main>
</SearchProvider>
@@ -88,5 +120,5 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</ThemeProviders>
</body>
</html>
)
);
}

View File

@@ -1,4 +1,4 @@
import Link from '@/components/Link'
import Link from '@/components/Link';
export default function NotFound() {
return (
@@ -12,14 +12,15 @@ export default function NotFound() {
<p className="mb-4 text-xl font-bold leading-normal md:text-2xl">
Sorry we couldn't find this page.
</p>
<p className="mb-8">But dont worry, you can find plenty of other things on our homepage.</p>
<p className="mb-8">
But dont worry, you can find plenty of other things on our homepage.
</p>
<Link
href="/"
className="focus:shadow-outline-blue inline rounded-lg border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium leading-5 text-white shadow transition-colors duration-150 hover:bg-blue-700 focus:outline-none dark:hover:bg-blue-500"
>
className="focus:shadow-outline-blue inline rounded-lg border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium leading-5 text-white shadow transition-colors duration-150 hover:bg-blue-700 focus:outline-none dark:hover:bg-blue-500">
Back to homepage
</Link>
</div>
</div>
)
);
}

View File

@@ -1,9 +1,9 @@
import { sortPosts, allCoreContent } from 'pliny/utils/contentlayer'
import { allBlogs } from 'contentlayer/generated'
import Main from './Main'
import { sortPosts, allCoreContent } from 'pliny/utils/contentlayer';
import { allBlogs } from 'contentlayer/generated';
import Main from './Main';
export default async function Page() {
const sortedPosts = sortPosts(allBlogs)
const posts = allCoreContent(sortedPosts)
return <Main posts={posts} />
const sortedPosts = sortPosts(allBlogs);
const posts = allCoreContent(sortedPosts);
return <Main posts={posts} />;
}

View File

@@ -1,8 +1,8 @@
import projectsData from '@/data/projectsData'
import Card from '@/components/Card'
import { genPageMetadata } from 'app/seo'
import projectsData from '@/data/projectsData';
import Card from '@/components/Card';
import { genPageMetadata } from 'app/seo';
export const metadata = genPageMetadata({ title: 'Projects' })
export const metadata = genPageMetadata({ title: 'Projects' });
export default function Projects() {
return (
@@ -31,5 +31,5 @@ export default function Projects() {
</div>
</div>
</>
)
);
}

View File

@@ -1,5 +1,5 @@
import { MetadataRoute } from 'next'
import siteMetadata from '@/data/siteMetadata'
import { MetadataRoute } from 'next';
import siteMetadata from '@/data/siteMetadata';
export default function robots(): MetadataRoute.Robots {
return {
@@ -9,5 +9,5 @@ export default function robots(): MetadataRoute.Robots {
},
sitemap: `${siteMetadata.siteUrl}/sitemap.xml`,
host: siteMetadata.siteUrl,
}
};
}

View File

@@ -1,15 +1,20 @@
import { Metadata } from 'next'
import siteMetadata from '@/data/siteMetadata'
import { Metadata } from 'next';
import siteMetadata from '@/data/siteMetadata';
interface PageSEOProps {
title: string
description?: string
image?: string
title: string;
description?: string;
image?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any
[key: string]: any;
}
export function genPageMetadata({ title, description, image, ...rest }: PageSEOProps): Metadata {
export function genPageMetadata({
title,
description,
image,
...rest
}: PageSEOProps): Metadata {
return {
title,
openGraph: {
@@ -27,5 +32,5 @@ export function genPageMetadata({ title, description, image, ...rest }: PageSEOP
images: image ? [image] : [siteMetadata.socialBanner],
},
...rest,
}
};
}

View File

@@ -1,18 +1,18 @@
import { MetadataRoute } from 'next'
import { allBlogs } from 'contentlayer/generated'
import siteMetadata from '@/data/siteMetadata'
import { MetadataRoute } from 'next';
import { allBlogs } from 'contentlayer/generated';
import siteMetadata from '@/data/siteMetadata';
export default function sitemap(): MetadataRoute.Sitemap {
const siteUrl = siteMetadata.siteUrl
const siteUrl = siteMetadata.siteUrl;
const blogRoutes = allBlogs.map((post) => ({
url: `${siteUrl}/${post.path}`,
lastModified: post.lastmod || post.date,
}))
}));
const routes = ['', 'blog', 'projects', 'tags'].map((route) => ({
url: `${siteUrl}/${route}`,
lastModified: new Date().toISOString().split('T')[0],
}))
}));
return [...routes, ...blogRoutes]
return [...routes, ...blogRoutes];
}

View File

@@ -1,14 +1,18 @@
import { slug } from 'github-slugger'
import { allCoreContent } from 'pliny/utils/contentlayer'
import siteMetadata from '@/data/siteMetadata'
import ListLayout from '@/layouts/ListLayoutWithTags'
import { allBlogs } from 'contentlayer/generated'
import tagData from 'app/tag-data.json'
import { genPageMetadata } from 'app/seo'
import { Metadata } from 'next'
import { slug } from 'github-slugger';
import { allCoreContent } from 'pliny/utils/contentlayer';
import siteMetadata from '@/data/siteMetadata';
import ListLayout from '@/layouts/ListLayoutWithTags';
import { allBlogs } from 'contentlayer/generated';
import tagData from 'app/tag-data.json';
import { genPageMetadata } from 'app/seo';
import { Metadata } from 'next';
export async function generateMetadata({ params }: { params: { tag: string } }): Promise<Metadata> {
const tag = params.tag
export async function generateMetadata({
params,
}: {
params: { tag: string };
}): Promise<Metadata> {
const tag = params.tag;
return genPageMetadata({
title: tag,
description: `${siteMetadata.title} ${tag} tagged content`,
@@ -18,26 +22,29 @@ export async function generateMetadata({ params }: { params: { tag: string } }):
'application/rss+xml': `${siteMetadata.siteUrl}/tags/${tag}/feed.xml`,
},
},
})
});
}
export const generateStaticParams = async () => {
const tagCounts = tagData as Record<string, number>
const tagKeys = Object.keys(tagCounts)
const tagCounts = tagData as Record<string, number>;
const tagKeys = Object.keys(tagCounts);
const paths = tagKeys.map((tag) => ({
tag: tag,
}))
return paths
}
}));
return paths;
};
export default function TagPage({ params }: { params: { tag: string } }) {
const { tag } = params
const { tag } = params;
// Capitalize first letter and convert space to dash
const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1)
const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1);
const filteredPosts = allCoreContent(
allBlogs.filter(
(post) => post.draft !== true && post.tags && post.tags.map((t) => slug(t)).includes(tag)
)
)
return <ListLayout posts={filteredPosts} title={title} />
(post) =>
post.draft !== true &&
post.tags &&
post.tags.map((t) => slug(t)).includes(tag),
),
);
return <ListLayout posts={filteredPosts} title={title} />;
}

View File

@@ -1,15 +1,18 @@
import Link from '@/components/Link'
import Tag from '@/components/Tag'
import { slug } from 'github-slugger'
import tagData from 'app/tag-data.json'
import { genPageMetadata } from 'app/seo'
import Link from '@/components/Link';
import Tag from '@/components/Tag';
import { slug } from 'github-slugger';
import tagData from 'app/tag-data.json';
import { genPageMetadata } from 'app/seo';
export const metadata = genPageMetadata({ title: 'Tags', description: 'Things I blog about' })
export const metadata = genPageMetadata({
title: 'Tags',
description: 'Things I blog about',
});
export default async function Page() {
const tagCounts = tagData as Record<string, number>
const tagKeys = Object.keys(tagCounts)
const sortedTags = tagKeys.sort((a, b) => tagCounts[b] - tagCounts[a])
const tagCounts = tagData as Record<string, number>;
const tagKeys = Object.keys(tagCounts);
const sortedTags = tagKeys.sort((a, b) => tagCounts[b] - tagCounts[a]);
return (
<>
<div className="flex flex-col items-start justify-start divide-y divide-gray-200 dark:divide-gray-700 md:mt-24 md:flex-row md:items-center md:justify-center md:space-x-6 md:divide-y-0">
@@ -27,15 +30,14 @@ export default async function Page() {
<Link
href={`/tags/${slug(t)}`}
className="-ml-2 text-sm font-semibold uppercase text-gray-600 dark:text-gray-300"
aria-label={`View posts tagged ${t}`}
>
aria-label={`View posts tagged ${t}`}>
{` (${tagCounts[t]})`}
</Link>
</div>
)
);
})}
</div>
</div>
</>
)
);
}

View File

@@ -1,12 +1,15 @@
'use client'
'use client';
import { ThemeProvider } from 'next-themes'
import siteMetadata from '@/data/siteMetadata'
import { ThemeProvider } from 'next-themes';
import siteMetadata from '@/data/siteMetadata';
export function ThemeProviders({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider attribute="class" defaultTheme={siteMetadata.theme} enableSystem>
<ThemeProvider
attribute="class"
defaultTheme={siteMetadata.theme}
enableSystem>
{children}
</ThemeProvider>
)
);
}