diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..b90a368f
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,2 @@
+node_modules
+.next
diff --git a/app/(main)/blog/BlogPostCard.tsx b/app/(main)/blog/BlogPostCard.tsx
new file mode 100644
index 00000000..1e0e20c4
--- /dev/null
+++ b/app/(main)/blog/BlogPostCard.tsx
@@ -0,0 +1,74 @@
+import Image from 'next/image'
+import Link from 'next/link'
+
+import {
+ CalendarIcon,
+ CursorClickIcon,
+ HourglassIcon,
+ ScriptIcon,
+} from '~/assets'
+import { prettifyNumber } from '~/lib/math'
+import { type Post } from '~/sanity/schemas/post'
+
+export function BlogPostCard({ post, views }: { post: Post; views: number }) {
+ const { title, slug, mainImage, publishedAt, categories, readingTime } = post
+
+ return (
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+ {Intl.DateTimeFormat('zh').format(new Date(publishedAt))}
+
+
+
+
+
+ {categories.join(', ')}
+
+
+
+
+
+ {prettifyNumber(views, true)}
+
+
+
+
+ {readingTime.toFixed(0)}分钟阅读
+
+
+
+
+
+ )
+}
diff --git a/app/(main)/blog/BlogPosts.tsx b/app/(main)/blog/BlogPosts.tsx
index 2ed83cd1..2ce58160 100644
--- a/app/(main)/blog/BlogPosts.tsx
+++ b/app/(main)/blog/BlogPosts.tsx
@@ -1,18 +1,10 @@
-import Image from 'next/image'
-import Link from 'next/link'
-
-import {
- CalendarIcon,
- CursorClickIcon,
- HourglassIcon,
- ScriptIcon,
-} from '~/assets'
import { kvKeys } from '~/config/kv'
import { env } from '~/env.mjs'
-import { prettifyNumber } from '~/lib/math'
import { redis } from '~/lib/redis'
import { getLatestBlogPosts } from '~/sanity/queries'
+import { BlogPostCard } from './BlogPostCard'
+
export async function BlogPosts({ limit = 5 }) {
const posts = await getLatestBlogPosts({ limit, forDisplay: true })
const postIdKeys = posts.map(({ _id }) => kvKeys.postViews(_id))
@@ -26,69 +18,9 @@ export async function BlogPosts({ limit = 5 }) {
return (
<>
- {posts.map(
- (
- { slug, title, mainImage, publishedAt, categories, readingTime },
- idx
- ) => (
-
-
-
-
-
-
- {title}
-
-
-
-
-
-
-
- {Intl.DateTimeFormat('zh').format(new Date(publishedAt))}
-
-
-
-
-
- {categories.join(', ')}
-
-
-
-
-
- {prettifyNumber(views[idx] ?? 0, true)}
-
-
-
-
- {readingTime.toFixed(0)}分钟阅读
-
-
-
-
-
- )
- )}
+ {posts.map((post, idx) => (
+
+ ))}
>
)
}
diff --git a/components/links/RichLink.tsx b/components/links/RichLink.tsx
index 4e815916..da93fba6 100644
--- a/components/links/RichLink.tsx
+++ b/components/links/RichLink.tsx
@@ -7,6 +7,8 @@ import React from 'react'
import { ExternalLinkIcon } from '~/assets'
+const hostsThatNeedInvertedFavicons = ['github.com']
+
type RichLinkProps = LinkProps &
React.ComponentPropsWithoutRef<'a'> & {
children: React.ReactNode
@@ -15,12 +17,10 @@ type RichLinkProps = LinkProps &
}
export const RichLink = React.forwardRef(
({ children, href, favicon = true, className, ...props }, ref) => {
+ const hrefHost = new URL(href).host
const faviconUrl = React.useMemo(
- () =>
- href.startsWith('http')
- ? `/api/favicon?url=${new URL(href).host}`
- : null,
- [href]
+ () => (href.startsWith('http') ? `/api/favicon?url=${hrefHost}` : null),
+ [hrefHost]
)
// if it's a relative link, use a fallback Link
@@ -45,7 +45,12 @@ export const RichLink = React.forwardRef(
{...props}
>
{favicon && faviconUrl && (
-
+
{
+ const {
+ subject = '测试主题',
+ body = `# 你好,世界
+ - 你好
+ - 世界
+ `,
+ } = props
+
+ return (
+
+ {subject}
+
+ {body && (
+
+ )}
+
+ )
+}
+
+export default NewslettersTemplate
diff --git a/next.config.mjs b/next.config.mjs
index 2dc6d3b0..172640be 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -8,6 +8,10 @@ import { get } from '@vercel/edge-config'
/** @type {import('next').NextConfig} */
const nextConfig = {
+ experimental: {
+ serverActions: true,
+ },
+
images: {
domains: ['cdn.sanity.io'],
},
@@ -33,9 +37,9 @@ const nextConfig = {
{
source: '/rss.xml',
destination: '/feed.xml',
- }
+ },
]
- }
+ },
}
export default nextConfig