initial commit
This commit is contained in:
31
pages/404.tsx
Normal file
31
pages/404.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { fontSypxzs } from "@/styles/font";
|
||||
import { TfiFaceSad } from "react-icons/tfi";
|
||||
|
||||
export default function NotFoundPage() {
|
||||
const goBack = () => {
|
||||
window.history.back();
|
||||
};
|
||||
return (
|
||||
<Page>
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<div className="flex flex-col justify-center">
|
||||
<TfiFaceSad className="mx-auto my-4" size={"6em"} />
|
||||
<p className="mx-auto my-3 text-center text-2xl font-bold">{"404 NOT FOUND"}</p>
|
||||
<p className={`${fontSypxzs.className} mx-auto my-3 text-center text-xl`}>
|
||||
{"This page does not exist for it might be removed or closed."}
|
||||
</p>
|
||||
<div className="my-5 flex justify-center">
|
||||
<button onClick={goBack} className="link text-xl font-bold">
|
||||
{"GO BACK"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
16
pages/_app.tsx
Normal file
16
pages/_app.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import "@/styles/globals.css";
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { SpeedInsights } from "@vercel/speed-insights/next";
|
||||
import "katex/dist/katex.min.css";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
import type { AppProps } from "next/app";
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<ThemeProvider attribute="class" enableColorScheme enableSystem={false}>
|
||||
<Analytics />
|
||||
<SpeedInsights />
|
||||
<Component {...pageProps} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
13
pages/_document.tsx
Normal file
13
pages/_document.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Head, Html, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
75
pages/about.tsx
Normal file
75
pages/about.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { SocialIcons } from "@/components/utils/SocialIcons";
|
||||
import { Config } from "@/data/config";
|
||||
import { fontFzxbs, fontSypxzs } from "@/styles/font";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title={`About Me - ${Config.AuthorName}`}
|
||||
description={"Type your brief self-introduction in a sentence here make SEO recognize it easily."}
|
||||
coverURL={Config.PageCovers.websiteCoverURL}
|
||||
/>
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<h2 className={`my-5 flex justify-around text-2xl font-bold ${fontFzxbs.className}`}>{"ABOUT ME"}</h2>
|
||||
<hr />
|
||||
<div className={`${fontSypxzs.className} my-5 justify-center md:flex md:space-x-10`}>
|
||||
<div className="my-auto flex md:w-1/3">
|
||||
<img alt="my-profile" className="mx-auto my-auto max-h-[23rem] rounded-lg" src="/images/profile.webp" />
|
||||
</div>
|
||||
<div className="my-auto md:w-1/3">
|
||||
<div className="mt-5 mb-3 text-3xl font-bold">Hi, there👋</div>
|
||||
I am a student / entrepreneur / engineer (Your profession) majoring in (Your Research Field) born in XXXX
|
||||
(Your birth year)
|
||||
<br />
|
||||
<br />
|
||||
My main research interests includes XXXX
|
||||
<br />
|
||||
<br />
|
||||
Additionally, I am also interested in XXXX.
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<SocialIcons />
|
||||
<hr />
|
||||
|
||||
<ul className="mx-auto my-10 md:w-2/3 list-disc">
|
||||
{Config.SocialLinks.github && (
|
||||
<li className="my-2">
|
||||
{"📕 Check out my github profile at "}
|
||||
<Link target="_blank" className="underline" href={`https://github.com/${Config.SocialLinks.github}`}>
|
||||
Github
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
<li className="my-2">🖥️ Programming stack: XXXX</li>
|
||||
<li className="my-2">🤝 I am looking to XXXXX</li>
|
||||
{Config.SocialLinks.twitter && (
|
||||
<li className="my-2">
|
||||
{"📫 How to reach me on Twitter: "}
|
||||
<Link target="_blank" className="link" href={`https://x.com/${Config.SocialLinks.twitter}`}>
|
||||
{Config.SocialLinks.twitter}
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
<li className="my-2">Language : 汉语普通话(First Language) / English / 한국어 / 日本語</li>
|
||||
<li className="my-2">Gender Identity : Male / Female / MTF / FTM / And Others </li>
|
||||
<li className="my-2">From : Your Country, State / Province</li>
|
||||
</ul>
|
||||
|
||||
<div className="mx-auto my-10 md:w-2/3 font-bold">
|
||||
{
|
||||
"** In addition to the above content, you can also add other customized components, content, etc. to this page. **"
|
||||
}
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
214
pages/blog/[id].tsx
Normal file
214
pages/blog/[id].tsx
Normal file
@@ -0,0 +1,214 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { MDXComponentsSet } from "@/components/mdx";
|
||||
import { PostComments } from "@/components/readerpage/PostComments";
|
||||
import { PostCover } from "@/components/readerpage/PostCover";
|
||||
import { ShareButtons } from "@/components/readerpage/ShareButtons";
|
||||
import { SideTOC } from "@/components/readerpage/SideTOC";
|
||||
import { TOC } from "@/components/readerpage/TOC";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { Config } from "@/data/config";
|
||||
import { normalizeDate } from "@/lib/date";
|
||||
import { getPostFileContent, sortedPosts } from "@/lib/post-process";
|
||||
import { getTOCTree } from "@/lib/toc";
|
||||
import { fontFzxbs, fontSypxzs } from "@/styles/font";
|
||||
import { TFrontmatter } from "@/types/frontmatter.type";
|
||||
import { TPostListItem } from "@/types/post-list";
|
||||
import { TTOCItem } from "@/types/toc.type";
|
||||
import { nanoid } from "nanoid";
|
||||
import { GetStaticPaths, GetStaticProps } from "next";
|
||||
import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote";
|
||||
import { serialize } from "next-mdx-remote/serialize";
|
||||
import Link from "next/link";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypePresetMinify from "rehype-preset-minify";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
import externalLinks from "remark-external-links";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkPrism from "remark-prism";
|
||||
|
||||
type ReaderPageProps = {
|
||||
source: MDXRemoteSerializeResult;
|
||||
tocList: TTOCItem[];
|
||||
frontMatter: TFrontmatter;
|
||||
postId: string;
|
||||
nextPostListItem: TPostListItem | null;
|
||||
prevPostListItem: TPostListItem | null;
|
||||
};
|
||||
|
||||
const ReaderPage = (props: ReaderPageProps) => {
|
||||
const source = props.source;
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title={`${props.frontMatter.title} - ${Config.SiteTitle}`}
|
||||
description={props.frontMatter.summary}
|
||||
coverURL={props.frontMatter.coverURL ?? Config.AvatarURL}
|
||||
/>
|
||||
<NavBar />
|
||||
<Toaster />
|
||||
<ContentContainer>
|
||||
<div className="my-5 justify-center md:flex">
|
||||
<div className="md:w-2/3">
|
||||
<div className="py-1">
|
||||
{props.frontMatter.coverURL && <PostCover coverURL={props.frontMatter.coverURL} />}
|
||||
<h2
|
||||
className={`${fontFzxbs.className} flex justify-center whitespace-normal break-words text-3xl font-bold capitalize`}
|
||||
>
|
||||
{props.frontMatter?.title}
|
||||
</h2>
|
||||
{props.frontMatter?.subtitle && (
|
||||
<div className={`${fontFzxbs.className} my-1 flex justify-center text-xl font-bold capitalize`}>
|
||||
{props.frontMatter.subtitle}
|
||||
</div>
|
||||
)}
|
||||
<div className="my-2 flex justify-center text-sm italic">{normalizeDate(props.frontMatter?.time)}</div>
|
||||
{props.frontMatter?.summary && (
|
||||
<p className={`${fontSypxzs.className} my-4 indent-8 text-gray-800 dark:text-gray-300`}>
|
||||
{props.frontMatter?.summary}
|
||||
</p>
|
||||
)}
|
||||
{props.frontMatter.tags && (
|
||||
<div className={`py-3 flex flex-wrap justify-start border-t border-b`}>
|
||||
<div className="font-bold mr-2 my-1">{"TAGS : "}</div>
|
||||
{props.frontMatter.tags.map((tagName) => (
|
||||
<Link
|
||||
href={`/tags/${tagName}`}
|
||||
className={`tag-link m-1 text-sm ${fontSypxzs.className}`}
|
||||
key={`tags-${nanoid()}`}
|
||||
>
|
||||
{tagName}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`typesetting ${fontSypxzs.className} flat-scrollbar-thin my-0 ${
|
||||
!props.frontMatter.allowShare && "select-none"
|
||||
}`}
|
||||
>
|
||||
{source && (
|
||||
<MDXRemote
|
||||
compiledSource={source.compiledSource}
|
||||
frontmatter={source.frontmatter}
|
||||
scope={source.scope}
|
||||
//@ts-ignore
|
||||
components={MDXComponentsSet}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<hr />
|
||||
<ShareButtons
|
||||
subtitle={props.frontMatter.subtitle}
|
||||
title={props.frontMatter.title}
|
||||
quote={props.frontMatter.summary}
|
||||
postId={props.postId}
|
||||
allowShare={props.frontMatter.allowShare}
|
||||
/>
|
||||
<hr />
|
||||
<ul className="my-2 px-5 flex flex-col justify-center list-disc">
|
||||
{props.prevPostListItem && (
|
||||
<li className="my-1">
|
||||
<Link
|
||||
className=" hover:text-sky-600 dark:hover:text-sky-500"
|
||||
href={`/blog/${props.prevPostListItem?.id}`}
|
||||
>
|
||||
{props.prevPostListItem?.frontMatter.title}
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
{props.nextPostListItem && (
|
||||
<li className="my-1">
|
||||
<Link
|
||||
className=" hover:text-sky-600 dark:hover:text-sky-500"
|
||||
href={`/blog/${props.nextPostListItem?.id}`}
|
||||
>
|
||||
{props.nextPostListItem?.frontMatter.title}
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<PostComments postId={props.postId} />
|
||||
</div>
|
||||
{props.tocList.length > 2 && (
|
||||
<div className="hidden md:block md:w-1/3">
|
||||
<TOC data={props.tocList} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{props.tocList.length > 2 && (
|
||||
<div className="md:hidden">
|
||||
<SideTOC data={props.tocList} />
|
||||
</div>
|
||||
)}
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticPaths: GetStaticPaths<{ id: string }> = async () => {
|
||||
const allPaths = sortedPosts.allPostList.map((item) => ({
|
||||
params: { id: item.id },
|
||||
}));
|
||||
return {
|
||||
paths: allPaths,
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<ReaderPageProps> = async (context) => {
|
||||
const postId = context.params?.id;
|
||||
|
||||
if (postId == null || Array.isArray(postId)) {
|
||||
return { notFound: true };
|
||||
}
|
||||
|
||||
const source = getPostFileContent(postId);
|
||||
|
||||
if (source == null) {
|
||||
return { notFound: true };
|
||||
}
|
||||
|
||||
const mdxSource = await serialize(source, {
|
||||
parseFrontmatter: true,
|
||||
mdxOptions: {
|
||||
remarkPlugins: [remarkPrism, externalLinks, remarkMath, remarkGfm],
|
||||
rehypePlugins: [rehypeKatex as any, rehypeAutolinkHeadings, rehypeSlug, rehypePresetMinify],
|
||||
format: "md",
|
||||
},
|
||||
});
|
||||
|
||||
const tocList = getTOCTree(renderToString(<MDXRemote {...mdxSource} />));
|
||||
|
||||
const postIndexInAllPosts = sortedPosts.allPostList.findIndex((item) => item.id === postId);
|
||||
|
||||
const frontMatter: TFrontmatter = sortedPosts.allPostList[postIndexInAllPosts].frontMatter;
|
||||
|
||||
const nextPostListItem =
|
||||
postIndexInAllPosts !== sortedPosts.allPostList.length - 1
|
||||
? sortedPosts.allPostList[postIndexInAllPosts + 1]
|
||||
: null;
|
||||
|
||||
const prevPostListItem = postIndexInAllPosts !== 0 ? sortedPosts.allPostList[postIndexInAllPosts - 1] : null;
|
||||
|
||||
return {
|
||||
props: {
|
||||
source: mdxSource,
|
||||
tocList: tocList,
|
||||
frontMatter: frontMatter,
|
||||
postId: postId,
|
||||
nextPostListItem: nextPostListItem,
|
||||
prevPostListItem: prevPostListItem,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default ReaderPage;
|
||||
39
pages/friends.tsx
Normal file
39
pages/friends.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { Config } from "@/data/config";
|
||||
import { FriendsList } from "@/data/friends";
|
||||
import { fontFzxbs, fontSypxzs } from "@/styles/font";
|
||||
import { nanoid } from "nanoid";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function FriendsPage() {
|
||||
return (
|
||||
<Page>
|
||||
<SEO title={`${Config.SiteTitle} - Friends`} description={"My Friend Links"} />
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<h2 className={`my-5 flex justify-center text-2xl font-bold ${fontFzxbs.className}`}>{"FRIENDS"}</h2>
|
||||
<hr />
|
||||
<div className={`my-5 flex flex-wrap justify-center text-2xl ${fontSypxzs.className}`}>
|
||||
{FriendsList.map((item) => (
|
||||
<Link className="mx-3 p-2 underline" href={item.url} key={nanoid()}>
|
||||
{item.title}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
<div className="my-2 text-base flex-col flex justify-start">
|
||||
<div className="mx-auto">
|
||||
{"Welcome to exchange our friend links and every high-quality blog websites are welcomed. "}
|
||||
<Link className="underline" href={`mailto:${Config.SocialLinks.email}`}>
|
||||
{"Email me please"}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
87
pages/index.tsx
Normal file
87
pages/index.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { HomeCover } from "@/components/homepage/HomeCover";
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { PostList } from "@/components/utils/PostList";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { LatestPostCountInHomePage } from "@/consts/consts";
|
||||
import { Config } from "@/data/config";
|
||||
import { sortedPosts } from "@/lib/post-process";
|
||||
import { generateRSSFeed } from "@/lib/rss";
|
||||
import { fontFzxbs } from "@/styles/font";
|
||||
import { TPostListItem } from "@/types/post-list";
|
||||
import { GetStaticProps } from "next";
|
||||
import Link from "next/link";
|
||||
import { LuPenTool } from "react-icons/lu";
|
||||
import { RiStarFill } from "react-icons/ri";
|
||||
|
||||
type HomePageProps = {
|
||||
pinnedPostList: TPostListItem[];
|
||||
latestPostList: TPostListItem[];
|
||||
};
|
||||
|
||||
export default function Home(props: HomePageProps) {
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title={`${Config.SiteTitle} - The personal blog for ${Config.Nickname}`}
|
||||
description={`Welcome to the ${Config.Nickname}'s blog website. It's the website for recording thoughts for technology, life experience and so on.`}
|
||||
coverURL={Config.PageCovers.websiteCoverURL}
|
||||
/>
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<HomeCover />
|
||||
{props.pinnedPostList.length !== 0 && (
|
||||
<div>
|
||||
<hr />
|
||||
<h2 className={`my-5 flex justify-center text-2xl font-bold ${fontFzxbs.className}`}>
|
||||
<RiStarFill className="mx-2 my-auto" />
|
||||
{"PINNED POSTS"}
|
||||
</h2>
|
||||
<hr />
|
||||
<PostList data={props.pinnedPostList} />
|
||||
</div>
|
||||
)}
|
||||
<hr />
|
||||
{props.latestPostList.length !== 0 && (
|
||||
<div>
|
||||
<h2 className={`my-5 flex justify-center text-2xl font-bold ${fontFzxbs.className}`}>
|
||||
<LuPenTool className="mx-2 my-auto" />
|
||||
{"LATEST POSTS"}
|
||||
</h2>
|
||||
<hr />
|
||||
<PostList data={props.latestPostList} />
|
||||
<div className="my-2 flex justify-end">
|
||||
<Link href="/posts" className="link-button font-bold text-base">
|
||||
{"MORE POSTS >"}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<HomePageProps> = async () => {
|
||||
const pinnedPostList = sortedPosts.pinnedPostList;
|
||||
const latestPostList = [];
|
||||
|
||||
for (let i = 0, j = 0; j < LatestPostCountInHomePage && i < sortedPosts.allPostList.length; i++) {
|
||||
const postListItem = sortedPosts.allPostList[i];
|
||||
if (!postListItem.frontMatter.noPrompt) {
|
||||
latestPostList.push(postListItem);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
await generateRSSFeed();
|
||||
|
||||
return {
|
||||
props: {
|
||||
pinnedPostList: pinnedPostList,
|
||||
latestPostList: latestPostList,
|
||||
},
|
||||
};
|
||||
};
|
||||
116
pages/posts/[[...slug]].tsx
Normal file
116
pages/posts/[[...slug]].tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { PostList } from "@/components/utils/PostList";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { PostCountPerPagination } from "@/consts/consts";
|
||||
import { Config } from "@/data/config";
|
||||
import { sortedPosts } from "@/lib/post-process";
|
||||
import { paginateArray } from "@/lib/utils";
|
||||
import { fontFzxbs } from "@/styles/font";
|
||||
import { TPostListItem } from "@/types/post-list";
|
||||
import { GetStaticPaths, GetStaticProps } from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ChangeEvent, KeyboardEvent, useEffect, useState } from "react";
|
||||
import { LuPenTool } from "react-icons/lu";
|
||||
|
||||
type PostsPageProps = {
|
||||
pageAmount: number;
|
||||
pageNumber: number;
|
||||
postList: TPostListItem[];
|
||||
};
|
||||
|
||||
export default function PostsPage(props: PostsPageProps) {
|
||||
const router = useRouter();
|
||||
const [pageNumber, setPageNumber] = useState<string>(props.pageNumber.toString());
|
||||
|
||||
const handleEnterKeyJump = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
setPageNumber(pageNumber.replace(/[^\d]/g, ""));
|
||||
if (parseInt(pageNumber) > 0 && parseInt(pageNumber) < props.pageAmount + 1) {
|
||||
(event.key === "Go" || event.key === "Enter") && router.push(`/posts/${pageNumber}`);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangePageNumber = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setPageNumber(event.target.value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPageNumber(props.pageNumber.toString().replace(/[^\d]/g, ""));
|
||||
}, [props.pageNumber]);
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title={`${Config.SiteTitle} - All published posts`}
|
||||
description={"Here is the list page for all published posts. Click here for more details."}
|
||||
coverURL={Config.PageCovers.websiteCoverURL}
|
||||
/>
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<h2 className={`my-5 flex justify-center text-2xl ${fontFzxbs.className} font-bold`}>
|
||||
<LuPenTool className="mx-2 my-auto" />
|
||||
{"ALL POSTS"}
|
||||
</h2>
|
||||
<hr />
|
||||
<PostList data={props.postList} />
|
||||
<div className="my-5 flex justify-between text-base font-bold">
|
||||
{props.pageNumber !== 1 && (
|
||||
<Link href={`/posts/${props.pageNumber - 1}`} className="link-button my-auto">
|
||||
{"< PREV"}
|
||||
</Link>
|
||||
)}
|
||||
<div className="my-auto font-bold flex justify-center">
|
||||
<Input
|
||||
onKeyDown={handleEnterKeyJump}
|
||||
onChange={handleChangePageNumber}
|
||||
className="my-auto mx-2 w-11 h-6"
|
||||
value={pageNumber}
|
||||
/>
|
||||
<div className="my-auto">{` / ${props.pageAmount}`}</div>
|
||||
</div>
|
||||
{props.pageNumber !== props.pageAmount && (
|
||||
<Link href={`/posts/${props.pageNumber + 1}`} className="link-button my-auto">
|
||||
{"NEXT >"}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = () => {
|
||||
const allPaths: { params: { slug?: string[] } }[] = [{ params: { slug: [] } }];
|
||||
|
||||
const pageAmount = Math.ceil(sortedPosts.allPostList.length / PostCountPerPagination);
|
||||
|
||||
for (let i = 0; i < pageAmount; i++) {
|
||||
allPaths.push({ params: { slug: [(i + 1).toString()] } });
|
||||
}
|
||||
|
||||
return { paths: allPaths, fallback: false };
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<PostsPageProps> = async (context) => {
|
||||
const params = (context.params?.slug as string[]) ?? [];
|
||||
|
||||
const pageNumber = params[0] ? parseInt(params[0]) : 1;
|
||||
let postList: TPostListItem[] = [];
|
||||
|
||||
postList = paginateArray(sortedPosts.allPostList, PostCountPerPagination, pageNumber);
|
||||
|
||||
const pageAmount = Math.ceil(sortedPosts.allPostList.length / PostCountPerPagination);
|
||||
|
||||
return {
|
||||
props: {
|
||||
pageAmount: pageAmount,
|
||||
pageNumber: pageNumber,
|
||||
postList: postList,
|
||||
},
|
||||
};
|
||||
};
|
||||
97
pages/sponsor.tsx
Normal file
97
pages/sponsor.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { Config } from "@/data/config";
|
||||
import { isEmptyString } from "@/lib/utils";
|
||||
import { fontFzxbs, fontSypxzs } from "@/styles/font";
|
||||
import Link from "next/link";
|
||||
import { QRCodeSVG } from "qrcode.react";
|
||||
import { FaCcPaypal } from "react-icons/fa";
|
||||
import { GoHeartFill } from "react-icons/go";
|
||||
import { SiAlipay, SiWechat } from "react-icons/si";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title={`${Config.SiteTitle} - Sponsor Me`}
|
||||
description={
|
||||
"If you like my works, I would deeply appreciate your support as a patron. Your contribution not only fuels my creative journey but also allows me to delve deeper into my passion."
|
||||
}
|
||||
/>
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<div className="md:flex">
|
||||
<div className="flex flex-col justify-center md:w-1/2">
|
||||
<h2 className={`my-5 flex justify-center text-2xl font-bold text-red-500 ${fontFzxbs.className}`}>
|
||||
<GoHeartFill className="mx-2 my-auto" />
|
||||
{"SPONSOR"}
|
||||
</h2>
|
||||
<p className={`${fontSypxzs.className} break-words text-lg`}>
|
||||
{
|
||||
"If you like my works, I would deeply appreciate your support as a patron. Your contribution not only fuels my creative journey but also allows me to delve deeper into my passion. Your support plays a vital role in making this vision a reality. Thank you for considering becoming a patron and being an integral part of this work endeavor."
|
||||
}
|
||||
<br />
|
||||
<br />
|
||||
{"Here are the ways you can become a patron. Thank you for your support!"}
|
||||
<br />
|
||||
<br />
|
||||
{`Yours, ${Config.AuthorName}`}
|
||||
</p>
|
||||
</div>
|
||||
<div className="md:px-15 md:w-1/2">
|
||||
<div className="mx-2 my-10 flex flex-col justify-around font-bold">
|
||||
{!isEmptyString(Config.SponsorLink?.wechatPay) && (
|
||||
<div className="my-3 flex justify-between">
|
||||
<div className="my-auto flex">
|
||||
<SiWechat className="mx-3 my-auto text-5xl text-green-500" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm">{"WECHAT-PAY"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2 bg-white p-1">
|
||||
<QRCodeSVG width={120} value={Config.SponsorLink?.wechatPay!} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<hr />
|
||||
{!isEmptyString(Config.SponsorLink?.alipay) && (
|
||||
<div className="my-6 flex justify-between">
|
||||
<div className="my-auto flex">
|
||||
<SiAlipay className="mx-3 my-auto text-5xl text-blue-500" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm">{"ALIPAY"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<Link className="link-button my-auto text-2xl" target="_blank" href={Config.SponsorLink?.alipay!}>
|
||||
{"DONATE"}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<hr />
|
||||
{!isEmptyString(Config.SponsorLink?.paypal) && (
|
||||
<div className="my-6 flex justify-between">
|
||||
<div className="my-auto flex">
|
||||
<FaCcPaypal className="mx-3 my-auto text-5xl text-blue-600" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm">{"PAYPAL"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<Link className="link-button my-auto text-2xl" target="_blank" href={Config.SponsorLink?.paypal!}>
|
||||
{"DONATE"}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
123
pages/tags/[...slug].tsx
Normal file
123
pages/tags/[...slug].tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { PostList } from "@/components/utils/PostList";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { PostCountPerPagination } from "@/consts/consts";
|
||||
import { Config } from "@/data/config";
|
||||
import { sortedPosts } from "@/lib/post-process";
|
||||
import { paginateArray } from "@/lib/utils";
|
||||
import { fontFzxbs } from "@/styles/font";
|
||||
import { TPostListItem } from "@/types/post-list";
|
||||
import { GetStaticPaths, GetStaticProps } from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { ChangeEvent, KeyboardEvent, useEffect, useState } from "react";
|
||||
|
||||
type TagsContentPageProps = {
|
||||
tagName: string | null;
|
||||
postList: TPostListItem[];
|
||||
pageAmount: number;
|
||||
pageNumber: number;
|
||||
};
|
||||
|
||||
export default function TagsContentPage(props: TagsContentPageProps) {
|
||||
const router = useRouter();
|
||||
const [pageNumber, setPageNumber] = useState<string>(props.pageNumber.toString());
|
||||
|
||||
const handleEnterKeyJump = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
setPageNumber(pageNumber.replace(/[^\d]/g, ""));
|
||||
if (parseInt(pageNumber) > 0 && parseInt(pageNumber) < props.pageAmount + 1) {
|
||||
(event.key === "Go" || event.key === "Enter") && router.push(`/tags/${props.tagName}/${pageNumber}`);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangePageNumber = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setPageNumber(event.target.value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPageNumber(props.pageNumber.toString());
|
||||
}, [props.pageNumber]);
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title={`Tag - ${props.tagName}`}
|
||||
description={`Here are posts under the tag ${props.tagName}.`}
|
||||
coverURL={Config.PageCovers.websiteCoverURL}
|
||||
/>
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<h2 className={`my-5 flex flex-col justify-center text-center text-3xl font-bold ${fontFzxbs.className}`}>
|
||||
{`Posts of ${props.tagName}`}
|
||||
</h2>
|
||||
<hr />
|
||||
<PostList data={props.postList} />
|
||||
<div className="my-5 flex justify-between text-base font-bold">
|
||||
{props.pageNumber !== 1 && (
|
||||
<Link href={`/tags/${props.tagName}/${props.pageNumber - 1}/`} className="link-button my-auto">
|
||||
{"< PREV"}
|
||||
</Link>
|
||||
)}
|
||||
<div className="my-auto font-bold flex justify-center">
|
||||
<Input
|
||||
onKeyDown={handleEnterKeyJump}
|
||||
onChange={handleChangePageNumber}
|
||||
className="my-auto mx-2 w-11 h-6"
|
||||
value={pageNumber}
|
||||
/>
|
||||
<div className="my-auto">{` / ${props.pageAmount}`}</div>
|
||||
</div>
|
||||
{props.pageNumber !== props.pageAmount && (
|
||||
<Link href={`/tags/${props.tagName}/${props.pageNumber + 1}/`} className="link-button my-auto">
|
||||
{"NEXT >"}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = () => {
|
||||
const allPaths: { params: { slug: string[] } }[] = [];
|
||||
|
||||
const allTags = Object.keys(sortedPosts.tagSubPostSet).map((tagName) => ({
|
||||
name: tagName,
|
||||
count: sortedPosts.tagSubPostSet[tagName].length,
|
||||
}));
|
||||
|
||||
for (let i = 0; i < allTags.length; i++) {
|
||||
allPaths.push({ params: { slug: [allTags[i].name] } });
|
||||
for (let j = 0; j < allTags[i].count; j++) {
|
||||
allPaths.push({ params: { slug: [allTags[i].name, (j + 1).toString()] } });
|
||||
}
|
||||
}
|
||||
|
||||
return { paths: allPaths, fallback: false };
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<TagsContentPageProps> = async (context) => {
|
||||
const params = (context.params?.slug as string[]) ?? [];
|
||||
|
||||
const tagName = params[0] ?? null;
|
||||
const pageNumber = params[1] ? parseInt(params[1]) : 1;
|
||||
let postList: TPostListItem[] = [];
|
||||
|
||||
postList = paginateArray(sortedPosts.tagSubPostSet[tagName], PostCountPerPagination, pageNumber);
|
||||
|
||||
const pageAmount = Math.ceil(sortedPosts.tagSubPostSet[tagName].length / PostCountPerPagination);
|
||||
|
||||
return {
|
||||
props: {
|
||||
tagName: tagName,
|
||||
pageAmount: pageAmount,
|
||||
pageNumber: pageNumber,
|
||||
postList: postList,
|
||||
},
|
||||
};
|
||||
};
|
||||
58
pages/tags/index.tsx
Normal file
58
pages/tags/index.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { ContentContainer, Page } from "@/components/layouts/layouts";
|
||||
import { Footer } from "@/components/utils/Footer";
|
||||
import { NavBar } from "@/components/utils/NavBar";
|
||||
import { SEO } from "@/components/utils/SEO";
|
||||
import { Config } from "@/data/config";
|
||||
import { sortedPosts } from "@/lib/post-process";
|
||||
import { fontFzxbs, fontSypxzs } from "@/styles/font";
|
||||
import { nanoid } from "nanoid";
|
||||
import { GetStaticProps } from "next";
|
||||
import Link from "next/link";
|
||||
import { AiOutlineTags } from "react-icons/ai";
|
||||
|
||||
type TagsIndexPageProps = {
|
||||
tagList: { name: string; count: number }[];
|
||||
};
|
||||
|
||||
export default function TagsIndexPage(props: TagsIndexPageProps) {
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title={`${Config.SiteTitle} - All tags`}
|
||||
description={"Here is the list page for all tags which sorts all posts to every catagories."}
|
||||
coverURL={Config.PageCovers.websiteCoverURL}
|
||||
/>
|
||||
<NavBar />
|
||||
<ContentContainer>
|
||||
<h2 className={`my-5 flex justify-center text-2xl font-bold ${fontFzxbs.className}`}>
|
||||
<AiOutlineTags className="mx-2 my-auto" />
|
||||
{"ALL TAGS"}
|
||||
</h2>
|
||||
<div className={`my-5 flex flex-wrap justify-center px-2 ${fontSypxzs.className}`}>
|
||||
{props.tagList.map((item) => (
|
||||
<Link key={`tag-link-${nanoid()}`} href={`/tags/${item.name}`} className="tag-link m-2 text-base">
|
||||
{`${item.name} (${item.count})`}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<Footer />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<TagsIndexPageProps> = async (context) => {
|
||||
const tagList: {
|
||||
name: string;
|
||||
count: number;
|
||||
}[] = Object.keys(sortedPosts.tagSubPostSet).map((tagName) => ({
|
||||
name: tagName,
|
||||
count: sortedPosts.tagSubPostSet[tagName].length,
|
||||
}));
|
||||
|
||||
return {
|
||||
props: {
|
||||
tagList: tagList,
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user