use Next.js

This commit is contained in:
polv committed 2020-12-17 23:15:28 +07:00
1 parent cf94c66807
commit 20a00d5ab3
34 files changed
+7117 -1

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
import { THEME } from '~/assets/global'
import Layout from '~/components/Layout'
import Head from 'next/head'
// eslint-disable-next-line react/prop-types
const MyApp = ({ Component, pageProps }) => {
return (
<>
<Head>
<title>{THEME.title}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</Head>
<Layout>
<Component {...pageProps} />
</Layout>
</>
)
}
export default MyApp
+55
View File
@@ -0,0 +1,55 @@
import THEME from '~/build/theme.json'
import Document, { Head, Html, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps (ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render () {
return (
<Html lang="en">
<Head>
<meta httpEquiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="description" content={THEME.description} />
<link
rel="shortcut icon"
href="/media/favicon.ico"
type="image/x-icon"
/>
<link
rel="stylesheet"
href={
THEME.awsm
? `https://unpkg.com/awsm.css/dist/awsm_theme_${THEME.awsm}.min.css`
: 'https://unpkg.com/awsm.css/dist/awsm.min.css'
}
/>
{THEME.analytics?.plausible ? (
<script
src="https://plausible.io/js/plausible.js"
async
defer
data-domain={THEME.analytics.plausible}
/>
) : null}
<script
src="https://unpkg.com/@webcomponents/webcomponentsjs/webcomponents-loader.js"
defer
/>
<script type="module" src="/webcomponents.js"></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
+12
View File
@@ -0,0 +1,12 @@
import { doSearch } from '~/server'
import type { NextApiRequest, NextApiResponse } from 'next'
export default async (req: NextApiRequest, res: NextApiResponse) => {
const r = await doSearch({
q: req.query.q as string,
tag: req.query.tag as string,
page: parseInt(req.query.page as string) || 1
})
res.json(r)
}
+52
View File
@@ -0,0 +1,52 @@
import PostQuery from '~/components/PostQuery'
import { doSearch } from '~/server'
import { IPost, dbPost, initDatabase } from '~/server/db'
import { GetStaticPaths, GetStaticProps } from 'next'
const BlogPagePaged = ({
count,
result
}: {
count: number
result: IPost[]
}) => {
return (
<PostQuery
defaults={{
posts: result,
count
}}
></PostQuery>
)
}
export default BlogPagePaged
export const getStaticPaths: GetStaticPaths = async () => {
await initDatabase()
return {
paths: Array(
Math.ceil(
dbPost.find({
date: { $exists: true } as any
}).length / 5
) - 1
)
.fill(null)
.map((_, i) => ({
params: {
page: (i + 2).toString()
}
})),
fallback: false
}
}
export const getStaticProps: GetStaticProps = async (ctx) => {
return {
props: await doSearch({
page: parseInt(ctx.params.page as string)
})
}
}
+23
View File
@@ -0,0 +1,23 @@
import PostQuery from '~/components/PostQuery'
import { doSearch } from '~/server'
import { IPost } from '~/server/db'
import { GetStaticProps } from 'next'
const BlogPage = ({ count, result }: { count: number; result: IPost[] }) => {
return (
<PostQuery
defaults={{
posts: result,
count
}}
></PostQuery>
)
}
export default BlogPage
export const getStaticProps: GetStaticProps = async () => {
return {
props: await doSearch({})
}
}
+125
View File
@@ -0,0 +1,125 @@
import '~/assets/remark42'
import { THEME } from '~/assets/global'
import { formatDate } from '~/assets/util'
import { IPost, dbPost, initDatabase } from '~/server/db'
import styles from '~/styles/PostPage.module.css'
import { GetStaticPaths, GetStaticProps } from 'next'
import Head from 'next/head'
import Link from 'next/link'
import { createRef, useEffect } from 'react'
const PostPage = (post: IPost) => {
const remark42Ref = createRef<HTMLDivElement>()
useEffect(() => {
if (remark42Ref.current) {
if (window.remark42Instance) {
window.remark42Instance.destroy()
}
window.remark42Instance = window.REMARK42.createInstance({
node: remark42Ref.current,
host: THEME.comment.remark42.host,
site_id: THEME.comment.remark42.siteId
})
}
}, [remark42Ref.current])
return (
<section>
<Head>
<title>
{post.title} - {THEME.title}
</title>
<meta property="og:title" content={post.title} />
<meta property="twitter:title" content={post.title} />
<meta name="description" content={post.content.substr(0, 140)} />
<meta property="og:description" content={post.content.substr(0, 140)} />
<meta property="twitter:title" content={post.content.substr(0, 140)} />
{post.tag ? (
<meta name="keywords" content={post.tag.join(',')} />
) : null}
{post.image ? (
<>
<meta property="og:image" content={post.image} />
<meta property="twitter:title" content={post.image} />
</>
) : null}
</Head>
<section>
<h2>{post.title}</h2>
<small>
<span>
by{' '}
{THEME.author.url ? (
<a
href={THEME.author.url}
target="_blank"
rel="noopener noreferrer"
>
{THEME.author.name}
</a>
) : (
<span>{THEME.author.name}</span>
)}
</span>
{post.date ? (
<span style={{ float: 'right' }}>{formatDate(post.date)}</span>
) : null}
</small>
</section>
<section dangerouslySetInnerHTML={{ __html: post.contentHtml }} />
{post.tag ? (
<section className={styles['tags-container']}>
<span>Tags:</span>
{post.tag.map((t) => (
<span key={t}>
<Link href={`/tag/${t}`}>
<a>{t}</a>
</Link>
</span>
))}
</section>
) : null}
{THEME.comment?.remark42 ? (
<footer>
<div ref={remark42Ref}></div>
</footer>
) : null}
</section>
)
}
export default PostPage
export const getStaticPaths: GetStaticPaths = async () => {
await initDatabase()
return {
paths: dbPost
.find()
.map(({ path }) => ({
params: { path: path.split(/\//g).map(encodeURIComponent) }
})),
fallback: false
}
}
export const getStaticProps: GetStaticProps = async (ctx) => {
await initDatabase()
return {
props: dbPost.findOne({
path: (ctx.params.path as string[]).join('/')
})
}
}
+57
View File
@@ -0,0 +1,57 @@
import PostQuery from '~/components/PostQuery'
import { doSearch } from '~/server'
import { IPost, dbPost, initDatabase } from '~/server/db'
import { GetStaticPaths, GetStaticProps } from 'next'
const TagPagePaged = ({
count,
result
}: {
count: number
result: IPost[]
}) => {
return (
<PostQuery
defaults={{
posts: result,
count
}}
></PostQuery>
)
}
export default TagPagePaged
export const getStaticProps: GetStaticProps = async (ctx) => {
return {
props: await doSearch({
page: parseInt(ctx.params.page as string),
tag: ctx.params.tag as string
})
}
}
export const getStaticPaths: GetStaticPaths = async () => {
await initDatabase()
const tMap = new Map<string, number>()
dbPost.find().map(({ tag = [] }) => {
return tag.map((t) => {
return tMap.set(t, (tMap.get(t) || 0) + 1)
})
})
return {
paths: Array.from(tMap).flatMap(([t, n]) =>
Array(Math.ceil(n / 5) - 1)
.fill(null)
.map((_, i) => ({
params: {
tag: t,
page: (i + 2).toString()
}
}))
),
fallback: false
}
}
+45
View File
@@ -0,0 +1,45 @@
import PostQuery from '~/components/PostQuery'
import { doSearch } from '~/server'
import { IPost, dbPost, initDatabase } from '~/server/db'
import { GetStaticPaths, GetStaticProps } from 'next'
const TagPage = ({ count, result }: { count: number; result: IPost[] }) => {
return (
<PostQuery
defaults={{
posts: result,
count
}}
></PostQuery>
)
}
export default TagPage
export const getStaticPaths: GetStaticPaths = async () => {
await initDatabase()
const tMap = new Map<string, number>()
dbPost.find().map(({ tag = [] }) => {
return tag.map((t) => {
return tMap.set(t, (tMap.get(t) || 0) + 1)
})
})
return {
paths: Array.from(tMap).map(([t]) => ({
params: {
tag: t
}
})),
fallback: false
}
}
export const getStaticProps: GetStaticProps = async (ctx) => {
return {
props: await doSearch({
tag: ctx.params.tag as string
})
}
}