style: auto fix.
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import Link from '@/components/Link'
|
||||
import Link from '@/components/Link';
|
||||
|
||||
export default function FourZeroFour() {
|
||||
return (
|
||||
@ -12,7 +12,9 @@ export default function FourZeroFour() {
|
||||
<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="/">
|
||||
<button 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
|
||||
@ -20,5 +22,5 @@ export default function FourZeroFour() {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
import '@/css/tailwind.css'
|
||||
import '@/css/prism.css'
|
||||
import 'katex/dist/katex.css'
|
||||
import '@/css/tailwind.css';
|
||||
import '@/css/prism.css';
|
||||
import 'katex/dist/katex.css';
|
||||
|
||||
import '@fontsource/inter/variable.css'
|
||||
import '@fontsource/inter/variable.css';
|
||||
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
import type { AppProps } from 'next/app'
|
||||
import Head from 'next/head'
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import type { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import Analytics from '@/components/analytics'
|
||||
import LayoutWrapper from '@/components/LayoutWrapper'
|
||||
import { ClientReload } from '@/components/ClientReload'
|
||||
import siteMetadata from '@/data/siteMetadata';
|
||||
import Analytics from '@/components/analytics';
|
||||
import LayoutWrapper from '@/components/LayoutWrapper';
|
||||
import { ClientReload } from '@/components/ClientReload';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === 'development'
|
||||
const isSocket = process.env.SOCKET
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
const isSocket = process.env.SOCKET;
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
@ -28,5 +28,5 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
<Component {...pageProps} />
|
||||
</LayoutWrapper>
|
||||
</ThemeProvider>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
|
||||
class MyDocument extends Document {
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en" className="scroll-smooth">
|
||||
<Head>
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/static/favicons/apple-touch-icon.png" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="76x76"
|
||||
href="/static/favicons/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
@ -19,10 +23,22 @@ class MyDocument extends Document {
|
||||
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" />
|
||||
</Head>
|
||||
<body className="bg-white text-black antialiased dark:bg-gray-900 dark:text-white">
|
||||
@ -30,8 +46,8 @@ class MyDocument extends Document {
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
||||
export default MyDocument;
|
||||
|
@ -1,21 +1,25 @@
|
||||
import { MDXLayoutRenderer } from '@/components/MDXComponents'
|
||||
import { getFileBySlug } from '@/lib/mdx'
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import { AuthorFrontMatter } from 'types/AuthorFrontMatter'
|
||||
import { MDXLayoutRenderer } from '@/components/MDXComponents';
|
||||
import { getFileBySlug } from '@/lib/mdx';
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
import { AuthorFrontMatter } from 'types/AuthorFrontMatter';
|
||||
|
||||
const DEFAULT_LAYOUT = 'AuthorLayout'
|
||||
const DEFAULT_LAYOUT = 'AuthorLayout';
|
||||
|
||||
// @ts-ignore
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
authorDetails: { mdxSource: string; frontMatter: AuthorFrontMatter }
|
||||
authorDetails: { mdxSource: string; frontMatter: AuthorFrontMatter };
|
||||
}> = async () => {
|
||||
const authorDetails = await getFileBySlug<AuthorFrontMatter>('authors', ['default'])
|
||||
const { mdxSource, frontMatter } = authorDetails
|
||||
return { props: { authorDetails: { mdxSource, frontMatter } } }
|
||||
}
|
||||
const authorDetails = await getFileBySlug<AuthorFrontMatter>('authors', [
|
||||
'default',
|
||||
]);
|
||||
const { mdxSource, frontMatter } = authorDetails;
|
||||
return { props: { authorDetails: { mdxSource, frontMatter } } };
|
||||
};
|
||||
|
||||
export default function About({ authorDetails }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const { mdxSource, frontMatter } = authorDetails
|
||||
export default function About({
|
||||
authorDetails,
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const { mdxSource, frontMatter } = authorDetails;
|
||||
|
||||
return (
|
||||
<MDXLayoutRenderer
|
||||
@ -23,5 +27,5 @@ export default function About({ authorDetails }: InferGetStaticPropsType<typeof
|
||||
mdxSource={mdxSource}
|
||||
frontMatter={frontMatter}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
// eslint-disable-next-line import/no-anonymous-default-export
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { email } = req.body
|
||||
const { email } = req.body;
|
||||
if (!email) {
|
||||
return res.status(400).json({ error: 'Email is required' })
|
||||
return res.status(400).json({ error: 'Email is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const API_KEY = process.env.BUTTONDOWN_API_KEY
|
||||
const buttondownRoute = `${process.env.BUTTONDOWN_API_URL}subscribers`
|
||||
const API_KEY = process.env.BUTTONDOWN_API_KEY;
|
||||
const buttondownRoute = `${process.env.BUTTONDOWN_API_URL}subscribers`;
|
||||
const response = await fetch(buttondownRoute, {
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
@ -19,14 +19,16 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
});
|
||||
|
||||
if (response.status >= 400) {
|
||||
return res.status(500).json({ error: `There was an error subscribing to the list.` })
|
||||
return res
|
||||
.status(500)
|
||||
.json({ error: `There was an error subscribing to the list.` });
|
||||
}
|
||||
|
||||
return res.status(201).json({ error: '' })
|
||||
return res.status(201).json({ error: '' });
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error.message || error.toString() })
|
||||
return res.status(500).json({ error: error.message || error.toString() });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
/* eslint-disable import/no-anonymous-default-export */
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { email } = req.body
|
||||
const { email } = req.body;
|
||||
|
||||
if (!email) {
|
||||
return res.status(400).json({ error: 'Email is required' })
|
||||
return res.status(400).json({ error: 'Email is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const FORM_ID = process.env.CONVERTKIT_FORM_ID
|
||||
const API_KEY = process.env.CONVERTKIT_API_KEY
|
||||
const API_URL = process.env.CONVERTKIT_API_URL
|
||||
const FORM_ID = process.env.CONVERTKIT_FORM_ID;
|
||||
const API_KEY = process.env.CONVERTKIT_API_KEY;
|
||||
const API_URL = process.env.CONVERTKIT_API_URL;
|
||||
|
||||
// Send request to ConvertKit
|
||||
const data = { email, api_key: API_KEY }
|
||||
const data = { email, api_key: API_KEY };
|
||||
|
||||
const response = await fetch(`${API_URL}forms/${FORM_ID}/subscribe`, {
|
||||
body: JSON.stringify(data),
|
||||
@ -22,16 +22,16 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
});
|
||||
|
||||
if (response.status >= 400) {
|
||||
return res.status(400).json({
|
||||
error: `There was an error subscribing to the list.`,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(201).json({ error: '' })
|
||||
return res.status(201).json({ error: '' });
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error.message || error.toString() })
|
||||
return res.status(500).json({ error: error.message || error.toString() });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
/* eslint-disable import/no-anonymous-default-export */
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { email } = req.body
|
||||
const { email } = req.body;
|
||||
if (!email) {
|
||||
return res.status(400).json({ error: 'Email is required' })
|
||||
return res.status(400).json({ error: 'Email is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const API_KEY = process.env.KLAVIYO_API_KEY
|
||||
const LIST_ID = process.env.KLAVIYO_LIST_ID
|
||||
const API_KEY = process.env.KLAVIYO_API_KEY;
|
||||
const LIST_ID = process.env.KLAVIYO_LIST_ID;
|
||||
const response = await fetch(
|
||||
`https://a.klaviyo.com/api/v2/list/${LIST_ID}/subscribe?api_key=${API_KEY}`,
|
||||
{
|
||||
@ -24,14 +24,14 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
profiles: [{ email: email }],
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
if (response.status >= 400) {
|
||||
return res.status(400).json({
|
||||
error: `There was an error subscribing to the list.`,
|
||||
})
|
||||
});
|
||||
}
|
||||
return res.status(201).json({ error: '' })
|
||||
return res.status(201).json({ error: '' });
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error.message || error.toString() })
|
||||
return res.status(500).json({ error: error.message || error.toString() });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,26 +1,26 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import mailchimp from '@mailchimp/mailchimp_marketing'
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import mailchimp from '@mailchimp/mailchimp_marketing';
|
||||
|
||||
mailchimp.setConfig({
|
||||
apiKey: process.env.MAILCHIMP_API_KEY,
|
||||
server: process.env.MAILCHIMP_API_SERVER, // E.g. us1
|
||||
})
|
||||
});
|
||||
|
||||
// eslint-disable-next-line import/no-anonymous-default-export
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { email } = req.body
|
||||
const { email } = req.body;
|
||||
|
||||
if (!email) {
|
||||
return res.status(400).json({ error: 'Email is required' })
|
||||
return res.status(400).json({ error: 'Email is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
await mailchimp.lists.addListMember(process.env.MAILCHIMP_AUDIENCE_ID, {
|
||||
email_address: email,
|
||||
status: 'subscribed',
|
||||
})
|
||||
return res.status(201).json({ error: '' })
|
||||
});
|
||||
return res.status(201).json({ error: '' });
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error.message || error.toString() })
|
||||
return res.status(500).json({ error: error.message || error.toString() });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,26 +1,26 @@
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import ListLayout from '@/layouts/ListLayout'
|
||||
import { PageSEO } from '@/components/SEO'
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import { ComponentProps } from 'react'
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx';
|
||||
import siteMetadata from '@/data/siteMetadata';
|
||||
import ListLayout from '@/layouts/ListLayout';
|
||||
import { PageSEO } from '@/components/SEO';
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
import { ComponentProps } from 'react';
|
||||
|
||||
export const POSTS_PER_PAGE = 5
|
||||
export const POSTS_PER_PAGE = 5;
|
||||
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
posts: ComponentProps<typeof ListLayout>['posts']
|
||||
initialDisplayPosts: ComponentProps<typeof ListLayout>['initialDisplayPosts']
|
||||
pagination: ComponentProps<typeof ListLayout>['pagination']
|
||||
posts: ComponentProps<typeof ListLayout>['posts'];
|
||||
initialDisplayPosts: ComponentProps<typeof ListLayout>['initialDisplayPosts'];
|
||||
pagination: ComponentProps<typeof ListLayout>['pagination'];
|
||||
}> = async () => {
|
||||
const posts = await getAllFilesFrontMatter('blog')
|
||||
const initialDisplayPosts = posts.slice(0, POSTS_PER_PAGE)
|
||||
const posts = await getAllFilesFrontMatter('blog');
|
||||
const initialDisplayPosts = posts.slice(0, POSTS_PER_PAGE);
|
||||
const pagination = {
|
||||
currentPage: 1,
|
||||
totalPages: Math.ceil(posts.length / POSTS_PER_PAGE),
|
||||
}
|
||||
};
|
||||
|
||||
return { props: { initialDisplayPosts, posts, pagination } }
|
||||
}
|
||||
return { props: { initialDisplayPosts, posts, pagination } };
|
||||
};
|
||||
|
||||
export default function Blog({
|
||||
posts,
|
||||
@ -29,7 +29,10 @@ export default function Blog({
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
return (
|
||||
<>
|
||||
<PageSEO title={`Blog - ${siteMetadata.author}`} description={siteMetadata.description} />
|
||||
<PageSEO
|
||||
title={`Blog - ${siteMetadata.author}`}
|
||||
description={siteMetadata.description}
|
||||
/>
|
||||
<ListLayout
|
||||
posts={posts}
|
||||
initialDisplayPosts={initialDisplayPosts}
|
||||
@ -37,5 +40,5 @@ export default function Blog({
|
||||
title="All Posts"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,17 +1,22 @@
|
||||
import fs from 'fs'
|
||||
import PageTitle from '@/components/PageTitle'
|
||||
import generateRss from '@/lib/generate-rss'
|
||||
import { MDXLayoutRenderer } from '@/components/MDXComponents'
|
||||
import { formatSlug, getAllFilesFrontMatter, getFileBySlug, getFiles } from '@/lib/mdx'
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import { AuthorFrontMatter } from 'types/AuthorFrontMatter'
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter'
|
||||
import { Toc } from 'types/Toc'
|
||||
import fs from 'fs';
|
||||
import PageTitle from '@/components/PageTitle';
|
||||
import generateRss from '@/lib/generate-rss';
|
||||
import { MDXLayoutRenderer } from '@/components/MDXComponents';
|
||||
import {
|
||||
formatSlug,
|
||||
getAllFilesFrontMatter,
|
||||
getFileBySlug,
|
||||
getFiles,
|
||||
} from '@/lib/mdx';
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
import { AuthorFrontMatter } from 'types/AuthorFrontMatter';
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter';
|
||||
import { Toc } from 'types/Toc';
|
||||
|
||||
const DEFAULT_LAYOUT = 'PostLayout'
|
||||
const DEFAULT_LAYOUT = 'PostLayout';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = getFiles('blog')
|
||||
const posts = getFiles('blog');
|
||||
return {
|
||||
paths: posts.map((p) => ({
|
||||
params: {
|
||||
@ -19,34 +24,38 @@ export async function getStaticPaths() {
|
||||
},
|
||||
})),
|
||||
fallback: false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
post: { mdxSource: string; toc: Toc; frontMatter: PostFrontMatter }
|
||||
authorDetails: AuthorFrontMatter[]
|
||||
prev?: { slug: string; title: string }
|
||||
next?: { slug: string; title: string }
|
||||
post: { mdxSource: string; toc: Toc; frontMatter: PostFrontMatter };
|
||||
authorDetails: AuthorFrontMatter[];
|
||||
prev?: { slug: string; title: string };
|
||||
next?: { slug: string; title: string };
|
||||
}> = async ({ params }) => {
|
||||
const slug = (params.slug as string[]).join('/')
|
||||
const allPosts = await getAllFilesFrontMatter('blog')
|
||||
const postIndex = allPosts.findIndex((post) => formatSlug(post.slug) === slug)
|
||||
const prev: { slug: string; title: string } = allPosts[postIndex + 1] || null
|
||||
const next: { slug: string; title: string } = allPosts[postIndex - 1] || null
|
||||
const post = await getFileBySlug<PostFrontMatter>('blog', slug)
|
||||
const slug = (params.slug as string[]).join('/');
|
||||
const allPosts = await getAllFilesFrontMatter('blog');
|
||||
const postIndex = allPosts.findIndex(
|
||||
(post) => formatSlug(post.slug) === slug
|
||||
);
|
||||
const prev: { slug: string; title: string } = allPosts[postIndex + 1] || null;
|
||||
const next: { slug: string; title: string } = allPosts[postIndex - 1] || null;
|
||||
const post = await getFileBySlug<PostFrontMatter>('blog', slug);
|
||||
// @ts-ignore
|
||||
const authorList = post.frontMatter.authors || ['default']
|
||||
const authorList = post.frontMatter.authors || ['default'];
|
||||
const authorPromise = authorList.map(async (author) => {
|
||||
const authorResults = await getFileBySlug<AuthorFrontMatter>('authors', [author])
|
||||
return authorResults.frontMatter
|
||||
})
|
||||
const authorDetails = await Promise.all(authorPromise)
|
||||
const authorResults = await getFileBySlug<AuthorFrontMatter>('authors', [
|
||||
author,
|
||||
]);
|
||||
return authorResults.frontMatter;
|
||||
});
|
||||
const authorDetails = await Promise.all(authorPromise);
|
||||
|
||||
// rss
|
||||
if (allPosts.length > 0) {
|
||||
const rss = generateRss(allPosts)
|
||||
fs.writeFileSync('./public/feed.xml', rss)
|
||||
const rss = generateRss(allPosts);
|
||||
fs.writeFileSync('./public/feed.xml', rss);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -56,8 +65,8 @@ export const getStaticProps: GetStaticProps<{
|
||||
prev,
|
||||
next,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default function Blog({
|
||||
post,
|
||||
@ -65,7 +74,7 @@ export default function Blog({
|
||||
prev,
|
||||
next,
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const { mdxSource, toc, frontMatter } = post
|
||||
const { mdxSource, toc, frontMatter } = post;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -90,5 +99,5 @@ export default function Blog({
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,42 +1,42 @@
|
||||
import { PageSEO } from '@/components/SEO'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx'
|
||||
import ListLayout from '@/layouts/ListLayout'
|
||||
import { POSTS_PER_PAGE } from '../../blog'
|
||||
import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter'
|
||||
import { PageSEO } from '@/components/SEO';
|
||||
import siteMetadata from '@/data/siteMetadata';
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx';
|
||||
import ListLayout from '@/layouts/ListLayout';
|
||||
import { POSTS_PER_PAGE } from '../../blog';
|
||||
import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter';
|
||||
|
||||
export const getStaticPaths: GetStaticPaths<{ page: string }> = async () => {
|
||||
const totalPosts = await getAllFilesFrontMatter('blog')
|
||||
const totalPages = Math.ceil(totalPosts.length / POSTS_PER_PAGE)
|
||||
const totalPosts = await getAllFilesFrontMatter('blog');
|
||||
const totalPages = Math.ceil(totalPosts.length / POSTS_PER_PAGE);
|
||||
const paths = Array.from({ length: totalPages }, (_, i) => ({
|
||||
params: { page: (i + 1).toString() },
|
||||
}))
|
||||
}));
|
||||
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
posts: PostFrontMatter[]
|
||||
initialDisplayPosts: PostFrontMatter[]
|
||||
pagination: { currentPage: number; totalPages: number }
|
||||
posts: PostFrontMatter[];
|
||||
initialDisplayPosts: PostFrontMatter[];
|
||||
pagination: { currentPage: number; totalPages: number };
|
||||
}> = async (context) => {
|
||||
const {
|
||||
params: { page },
|
||||
} = context
|
||||
const posts = await getAllFilesFrontMatter('blog')
|
||||
const pageNumber = parseInt(page as string)
|
||||
} = context;
|
||||
const posts = await getAllFilesFrontMatter('blog');
|
||||
const pageNumber = parseInt(page as string);
|
||||
const initialDisplayPosts = posts.slice(
|
||||
POSTS_PER_PAGE * (pageNumber - 1),
|
||||
POSTS_PER_PAGE * pageNumber
|
||||
)
|
||||
);
|
||||
const pagination = {
|
||||
currentPage: pageNumber,
|
||||
totalPages: Math.ceil(posts.length / POSTS_PER_PAGE),
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
props: {
|
||||
@ -44,8 +44,8 @@ export const getStaticProps: GetStaticProps<{
|
||||
initialDisplayPosts,
|
||||
pagination,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default function PostPage({
|
||||
posts,
|
||||
@ -54,7 +54,10 @@ export default function PostPage({
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
return (
|
||||
<>
|
||||
<PageSEO title={siteMetadata.title} description={siteMetadata.description} />
|
||||
<PageSEO
|
||||
title={siteMetadata.title}
|
||||
description={siteMetadata.description}
|
||||
/>
|
||||
<ListLayout
|
||||
posts={posts}
|
||||
initialDisplayPosts={initialDisplayPosts}
|
||||
@ -62,5 +65,5 @@ export default function PostPage({
|
||||
title="All Posts"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,25 +1,32 @@
|
||||
import Link from '@/components/Link'
|
||||
import { PageSEO } from '@/components/SEO'
|
||||
import Tag from '@/components/Tag'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx'
|
||||
import formatDate from '@/lib/utils/formatDate'
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter'
|
||||
import NewsletterForm from '@/components/NewsletterForm'
|
||||
import Link from '@/components/Link';
|
||||
import { PageSEO } from '@/components/SEO';
|
||||
import Tag from '@/components/Tag';
|
||||
import siteMetadata from '@/data/siteMetadata';
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx';
|
||||
import formatDate from '@/lib/utils/formatDate';
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter';
|
||||
import NewsletterForm from '@/components/NewsletterForm';
|
||||
|
||||
const MAX_DISPLAY = 5
|
||||
const MAX_DISPLAY = 5;
|
||||
|
||||
export const getStaticProps: GetStaticProps<{ posts: PostFrontMatter[] }> = async () => {
|
||||
const posts = await getAllFilesFrontMatter('blog')
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
posts: PostFrontMatter[];
|
||||
}> = async () => {
|
||||
const posts = await getAllFilesFrontMatter('blog');
|
||||
|
||||
return { props: { posts } }
|
||||
}
|
||||
return { props: { posts } };
|
||||
};
|
||||
|
||||
export default function Home({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
export default function Home({
|
||||
posts,
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
return (
|
||||
<>
|
||||
<PageSEO title={siteMetadata.title} description={siteMetadata.description} />
|
||||
<PageSEO
|
||||
title={siteMetadata.title}
|
||||
description={siteMetadata.description}
|
||||
/>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
|
||||
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
|
||||
@ -32,7 +39,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
|
||||
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{!posts.length && '没有找到文章。 😭'}
|
||||
{posts.slice(0, MAX_DISPLAY).map((frontMatter) => {
|
||||
const { slug, date, title, summary, tags } = frontMatter
|
||||
const { slug, date, title, summary, tags } = frontMatter;
|
||||
return (
|
||||
<li key={slug} className="py-12">
|
||||
<article>
|
||||
@ -49,8 +56,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
|
||||
<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>
|
||||
@ -68,8 +74,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
|
||||
<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 →
|
||||
</Link>
|
||||
</div>
|
||||
@ -77,7 +82,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
|
||||
</div>
|
||||
</article>
|
||||
</li>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
@ -86,8 +91,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
|
||||
<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 →
|
||||
</Link>
|
||||
</div>
|
||||
@ -98,5 +102,5 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import projectsData from '@/data/projectsData'
|
||||
import Card from '@/components/Card'
|
||||
import { PageSEO } from '@/components/SEO'
|
||||
import siteMetadata from '@/data/siteMetadata';
|
||||
import projectsData from '@/data/projectsData';
|
||||
import Card from '@/components/Card';
|
||||
import { PageSEO } from '@/components/SEO';
|
||||
|
||||
export default function Projects() {
|
||||
return (
|
||||
<>
|
||||
<PageSEO title={`Projects - ${siteMetadata.author}`} description={siteMetadata.description} />
|
||||
<PageSEO
|
||||
title={`Projects - ${siteMetadata.author}`}
|
||||
description={siteMetadata.description}
|
||||
/>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
|
||||
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
|
||||
@ -31,5 +34,5 @@ export default function Projects() {
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,22 +1,29 @@
|
||||
import Link from '@/components/Link'
|
||||
import { PageSEO } from '@/components/SEO'
|
||||
import Tag from '@/components/Tag'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import { getAllTags } from '@/lib/tags'
|
||||
import kebabCase from '@/lib/utils/kebabCase'
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import Link from '@/components/Link';
|
||||
import { PageSEO } from '@/components/SEO';
|
||||
import Tag from '@/components/Tag';
|
||||
import siteMetadata from '@/data/siteMetadata';
|
||||
import { getAllTags } from '@/lib/tags';
|
||||
import kebabCase from '@/lib/utils/kebabCase';
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
|
||||
export const getStaticProps: GetStaticProps<{ tags: Record<string, number> }> = async () => {
|
||||
const tags = await getAllTags('blog')
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
tags: Record<string, number>;
|
||||
}> = async () => {
|
||||
const tags = await getAllTags('blog');
|
||||
|
||||
return { props: { tags } }
|
||||
}
|
||||
return { props: { tags } };
|
||||
};
|
||||
|
||||
export default function Tags({ tags }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const sortedTags = Object.keys(tags).sort((a, b) => tags[b] - tags[a])
|
||||
export default function Tags({
|
||||
tags,
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const sortedTags = Object.keys(tags).sort((a, b) => tags[b] - tags[a]);
|
||||
return (
|
||||
<>
|
||||
<PageSEO title={`Tags - ${siteMetadata.author}`} description="Things I blog about" />
|
||||
<PageSEO
|
||||
title={`Tags - ${siteMetadata.author}`}
|
||||
description="Things I blog about"
|
||||
/>
|
||||
<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">
|
||||
<div className="space-x-2 pt-6 pb-8 md:space-y-5">
|
||||
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:border-r-2 md:px-6 md:text-6xl md:leading-14">
|
||||
@ -31,15 +38,14 @@ export default function Tags({ tags }: InferGetStaticPropsType<typeof getStaticP
|
||||
<Tag text={t} />
|
||||
<Link
|
||||
href={`/tags/${kebabCase(t)}`}
|
||||
className="-ml-2 text-sm font-semibold uppercase text-gray-600 dark:text-gray-300"
|
||||
>
|
||||
className="-ml-2 text-sm font-semibold uppercase text-gray-600 dark:text-gray-300">
|
||||
{` (${tags[t]})`}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { TagSEO } from '@/components/SEO'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import ListLayout from '@/layouts/ListLayout'
|
||||
import generateRss from '@/lib/generate-rss'
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx'
|
||||
import { getAllTags } from '@/lib/tags'
|
||||
import kebabCase from '@/lib/utils/kebabCase'
|
||||
import fs from 'fs'
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import path from 'path'
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter'
|
||||
import { TagSEO } from '@/components/SEO';
|
||||
import siteMetadata from '@/data/siteMetadata';
|
||||
import ListLayout from '@/layouts/ListLayout';
|
||||
import generateRss from '@/lib/generate-rss';
|
||||
import { getAllFilesFrontMatter } from '@/lib/mdx';
|
||||
import { getAllTags } from '@/lib/tags';
|
||||
import kebabCase from '@/lib/utils/kebabCase';
|
||||
import fs from 'fs';
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
import path from 'path';
|
||||
import { PostFrontMatter } from 'types/PostFrontMatter';
|
||||
|
||||
const root = process.cwd()
|
||||
const root = process.cwd();
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const tags = await getAllTags('blog')
|
||||
const tags = await getAllTags('blog');
|
||||
|
||||
return {
|
||||
paths: Object.keys(tags).map((tag) => ({
|
||||
@ -22,32 +22,37 @@ export async function getStaticPaths() {
|
||||
},
|
||||
})),
|
||||
fallback: false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<{ posts: PostFrontMatter[]; tag: string }> = async (
|
||||
context
|
||||
) => {
|
||||
const tag = context.params.tag as string
|
||||
const allPosts = await getAllFilesFrontMatter('blog')
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
posts: PostFrontMatter[];
|
||||
tag: string;
|
||||
}> = async (context) => {
|
||||
const tag = context.params.tag as string;
|
||||
const allPosts = await getAllFilesFrontMatter('blog');
|
||||
const filteredPosts = allPosts.filter(
|
||||
(post) => post.draft !== true && post.tags.map((t) => kebabCase(t)).includes(tag)
|
||||
)
|
||||
(post) =>
|
||||
post.draft !== true && post.tags.map((t) => kebabCase(t)).includes(tag)
|
||||
);
|
||||
|
||||
// rss
|
||||
if (filteredPosts.length > 0) {
|
||||
const rss = generateRss(filteredPosts, `tags/${tag}/feed.xml`)
|
||||
const rssPath = path.join(root, 'public', 'tags', tag)
|
||||
fs.mkdirSync(rssPath, { recursive: true })
|
||||
fs.writeFileSync(path.join(rssPath, 'feed.xml'), rss)
|
||||
const rss = generateRss(filteredPosts, `tags/${tag}/feed.xml`);
|
||||
const rssPath = path.join(root, 'public', 'tags', tag);
|
||||
fs.mkdirSync(rssPath, { recursive: true });
|
||||
fs.writeFileSync(path.join(rssPath, 'feed.xml'), rss);
|
||||
}
|
||||
|
||||
return { props: { posts: filteredPosts, tag } }
|
||||
}
|
||||
return { props: { posts: filteredPosts, tag } };
|
||||
};
|
||||
|
||||
export default function Tag({ posts, tag }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
export default function Tag({
|
||||
posts,
|
||||
tag,
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
// 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);
|
||||
return (
|
||||
<>
|
||||
<TagSEO
|
||||
@ -56,5 +61,5 @@ export default function Tag({ posts, tag }: InferGetStaticPropsType<typeof getSt
|
||||
/>
|
||||
<ListLayout posts={posts} title={title} />
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user