Skip to content

Commit

Permalink
Merge branch 'main' into feat/comments
Browse files Browse the repository at this point in the history
# Conflicts:
#	components/PostPortableText.tsx
  • Loading branch information
CaliCastle committed May 27, 2023
2 parents bbb9d7f + 0e70a1e commit 4bcbd11
Show file tree
Hide file tree
Showing 23 changed files with 1,238 additions and 1,240 deletions.
46 changes: 21 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## 我的个人网站

## Getting Started
目前还在开发中,可以通过 [https://cali.so/](https://cali.so/) 访问。

First, run the development server:
需要其他服务商的环境变量才能正常运行,所以如果你想要在本地运行,需要自己配置。

```bash
npm run dev
# or
yarn dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
### 技术栈

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
- [Next.js](https://nextjs.org/)
- [React](https://reactjs.org/)
- [TypeScript](https://www.typescriptlang.org/)
- [Tailwind CSS](https://tailwindcss.com/)
- [Framer Motion](https://www.framer.com/motion/)
- [Radix UI](https://www.radix-ui.com/)
- [Sanity](https://www.sanity.io/)

## Learn More
### 本地开发

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
```bash
# 安装依赖
pnpm install

## Deploy on Vercel
# 启动开发服务器
pnpm dev

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
# 构建
pnpm build
```

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
通过 [Vercel](https://vercel.com/) 一键部署。
2 changes: 0 additions & 2 deletions app/(main)/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,9 @@ export function Footer() {
<Container.Inner className="mt-6">
<div className="flex flex-col items-center justify-start gap-2 sm:flex-row">
<React.Suspense>
{/* @ts-expect-error Async Server Component */}
<TotalPageViews />
</React.Suspense>
<React.Suspense>
{/* @ts-expect-error Async Server Component */}
<LastVisitorInfo />
</React.Suspense>
</div>
Expand Down
1 change: 1 addition & 0 deletions app/(main)/Headline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export function Headline() {
aria-label="我的 Telegram"
platform="telegram"
/>
<SocialLink href="/feed.xml" platform="rss" aria-label="RSS 订阅" />
<SocialLink
href="mailto:[email protected]"
aria-label="我的邮箱"
Expand Down
4 changes: 2 additions & 2 deletions app/(main)/blog/BlogPostPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function BlogPostPage({
damping: 20,
}}
>
<div className="absolute z-0 aspect-[240/135] w-full blur-xl saturate-150 after:absolute after:inset-0 after:block after:bg-white/50 dark:after:bg-black/50">
<div className="absolute z-0 hidden aspect-[240/135] w-full blur-xl saturate-150 after:absolute after:inset-0 after:hidden after:bg-white/50 dark:after:bg-black/50 md:block md:after:block">
<Image
src={post.mainImage.asset.url}
alt=""
Expand All @@ -68,7 +68,7 @@ export function BlogPostPage({
<Image
src={post.mainImage.asset.url}
alt={post.title}
className="select-none rounded-3xl ring-1 ring-zinc-900/5 transition dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20"
className="select-none rounded-2xl ring-1 ring-zinc-900/5 transition dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20 md:rounded-3xl"
placeholder="blur"
blurDataURL={post.mainImage.asset.lqip}
unoptimized
Expand Down
9 changes: 8 additions & 1 deletion app/(main)/blog/BlogPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ import {
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'

export async function BlogPosts({ limit = 5 }) {
const posts = await getLatestBlogPosts(limit)
const postIdKeys = posts.map(({ _id }) => kvKeys.postViews(_id))
const views = await redis.mget<number[]>(...postIdKeys)

let views: number[] = []
if (env.VERCEL_ENV === 'development') {
views = posts.map(() => Math.floor(Math.random() * 1000))
} else {
views = await redis.mget<number[]>(...postIdKeys)
}

return (
<>
Expand Down
9 changes: 6 additions & 3 deletions app/(main)/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Balancer from 'react-wrap-balancer'

import { SocialLink } from '~/components/links/SocialLink'
import { Container } from '~/components/ui/Container'

import { BlogPosts } from './BlogPosts'
Expand Down Expand Up @@ -28,12 +29,14 @@ export default function BlogPage() {
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
欢迎光临我的博客
</h1>
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
<p className="my-6 text-base text-zinc-600 dark:text-zinc-400">
<Balancer>{description}</Balancer>
</p>
<p className="flex items-center">
<SocialLink href="/feed.xml" platform="rss" />
</p>
</header>
<div className="mt-16 grid grid-cols-1 gap-6 sm:mt-20 lg:grid-cols-2 lg:gap-8">
{/* @ts-expect-error Server Component */}
<div className="mt-12 grid grid-cols-1 gap-6 sm:mt-20 lg:grid-cols-2 lg:gap-8">
<BlogPosts limit={20} />
</div>
</Container>
Expand Down
33 changes: 14 additions & 19 deletions app/(main)/feed.xml/route.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import { Feed } from 'feed'
import RSS from 'rss'

import { seo } from '~/lib/seo'
import { getLatestBlogPosts } from '~/sanity/queries'

export const revalidate = 60 * 60 * 12 // 12 hours
export const revalidate = 60 * 60 // 1 hour

export async function GET() {
const feed = new Feed({
const feed = new RSS({
title: seo.title,
description: seo.description,
id: seo.url.href,
link: seo.url.href,
site_url: seo.url.href,
feed_url: `${seo.url.href}feed.xml`,
language: 'zh-CN',
image: `${seo.url.href}opengraph-image.png`,
favicon: `${seo.url.href}icon.png`,
copyright: '版权所有 2023, Cali Castle',
image_url: `${seo.url.href}opengraph-image.png`,
generator: 'PHP 9.0',
author: {
name: 'Cali Castle',
email: '[email protected]',
link: seo.url.href,
},
})

const data = await getLatestBlogPosts(999)
Expand All @@ -29,19 +22,21 @@ export async function GET() {
}

data.forEach((post) => {
feed.addItem({
feed.item({
title: post.title,
id: post._id,
link: `${seo.url.href}blog/${post.slug}`,
guid: post._id,
url: `${seo.url.href}blog/${post.slug}`,
description: post.description,
date: new Date(post.publishedAt),
image: post.mainImage.asset.url,
enclosure: {
url: post.mainImage.asset.url,
},
})
})

return new Response(feed.rss2(), {
return new Response(feed.xml(), {
headers: {
'content-type': 'application/xml; charset=utf-8',
'content-type': 'application/xml',
},
})
}
1 change: 0 additions & 1 deletion app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export default function BlogHomePage() {
<PencilSwooshIcon className="h-5 w-5 flex-none" />
<span className="ml-2">近期文章</span>
</h2>
{/* @ts-expect-error Server Component */}
<BlogPosts />
</div>
<aside className="space-y-10 lg:sticky lg:top-8 lg:h-fit lg:pl-16 xl:pl-20">
Expand Down
24 changes: 24 additions & 0 deletions app/api/tweet/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import cors from 'edge-cors'
import { NextResponse } from 'next/server'
import { getTweet } from 'react-tweet/api'

type RouteSegment = { params: { id: string } }

export async function GET(req: Request, { params }: RouteSegment) {
try {
const tweet = await getTweet(params.id)
return cors(
req,
NextResponse.json({ data: tweet ?? null }, { status: tweet ? 200 : 404 })
)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
return cors(
req,
NextResponse.json(
{ error: error.message ?? 'Bad request.' },
{ status: 400 }
)
)
}
}
12 changes: 12 additions & 0 deletions app/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { type Component, type ReactNode } from 'react'

type ReactJSXElementConstructor<Props> =
| ((props: Props) => ReactNode | Promise<ReactNode>)
| (new (props: Props) => Component<Props, any>)

declare global {
namespace React.JSX {
type ElementType = string | ReactJSXElementConstructor<any>
}
}
36 changes: 36 additions & 0 deletions assets/icons/AtomIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { type IconProps } from '~/assets'

export function AtomIcon(props: IconProps = {}) {
return (
<svg
width="1em"
height="1em"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M11 12C11 11.4477 11.4477 11 12 11C12.5523 11 13 11.4477 13 12C13 12.5523 12.5523 13 12 13C11.4477 13 11 12.5523 11 12Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M15.8685 8.1315C20.3552 12.6182 22.2604 17.9873 20.1238 20.1238C17.9873 22.2604 12.6182 20.3552 8.1315 15.8685C3.64482 11.3818 1.73964 6.01266 3.87615 3.87615C6.01266 1.73964 11.3818 3.64482 15.8685 8.1315Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.13151 8.1315C3.64483 12.6182 1.73965 17.9873 3.87616 20.1238C6.01267 22.2604 11.3818 20.3552 15.8685 15.8685C20.3552 11.3818 22.2604 6.01266 20.1239 3.87615C17.9873 1.73964 12.6182 3.64482 8.13151 8.1315Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
1 change: 1 addition & 0 deletions assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type React from 'react'

export type IconProps = React.SVGAttributes<SVGElement>

export { AtomIcon } from './icons/AtomIcon'
export { BilibiliIcon } from './icons/BilibiliIcon'
export { BriefcaseIcon } from './icons/BriefcaseIcon'
export { CalendarIcon } from './icons/CalendarIcon'
Expand Down
6 changes: 3 additions & 3 deletions components/BleedThroughImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export function BleedThroughImage({
...props
}: BleedThroughImageProps) {
return (
<div className="relative">
<div className="not-prose absolute z-10 h-full w-full opacity-80 blur-2xl saturate-150 after:absolute after:inset-0 after:block after:bg-white/50 dark:opacity-70 dark:after:bg-black/50">
<div className="group relative">
<div className="not-prose absolute z-10 hidden h-full w-full scale-[0.96] transform-gpu opacity-80 blur-2xl saturate-150 transition-transform after:absolute after:inset-0 after:block after:bg-white/50 group-hover:scale-105 dark:opacity-70 dark:after:bg-black/50 md:block">
<Image
width={dimensions.width}
height={dimensions.height}
Expand All @@ -32,7 +32,7 @@ export function BleedThroughImage({
unoptimized
placeholder={lqip ? 'blur' : 'empty'}
blurDataURL={lqip}
className={clsxm('relative z-20', className)}
className={clsxm('relative z-20 rounded-xl md:rounded-3xl', className)}
{...props}
alt={alt ?? ''}
/>
Expand Down
6 changes: 6 additions & 0 deletions components/PostPortableText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { PortableText, type PortableTextComponents } from '@portabletext/react'
import React from 'react'
import { Tweet } from 'react-tweet'

import { NewCommentIcon } from '~/assets'
import { BleedThroughImage } from '~/components/BleedThroughImage'
Expand Down Expand Up @@ -56,6 +57,11 @@ const components: PortableTextComponents = {
lqip={value.lqip}
/>
),
tweet: ({ value }) => (
<div className="flex justify-center">
<Tweet id={value.id} />
</div>
),
},

marks: {
Expand Down
3 changes: 3 additions & 0 deletions components/links/SocialLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Link, { type LinkProps } from 'next/link'
import React from 'react'

import {
AtomIcon,
BilibiliIcon,
GitHubIcon,
type IconProps,
Expand All @@ -23,6 +24,7 @@ type Platform =
| 'telegram'
| 'bilibili'
| 'mail'
| 'rss'
type PlatformInfo = {
icon: IconType
platform: Platform
Expand Down Expand Up @@ -51,6 +53,7 @@ const iconMapper: { [key: string]: PlatformInfo } = {
label: '哔哩哔哩',
},
'(?:mailto:)': { icon: MailIcon, platform: 'mail', label: '邮箱地址' },
'(?:feed.xml)': { icon: AtomIcon, platform: 'rss', label: 'RSS 订阅' },
}

function getIconForUrl(url: string): PlatformInfo | undefined {
Expand Down
Loading

0 comments on commit 4bcbd11

Please sign in to comment.