Skip to content

Commit

Permalink
feat: implement endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
CaliCastle committed Jun 4, 2023
1 parent 2d5612a commit 0046e86
Show file tree
Hide file tree
Showing 28 changed files with 1,160 additions and 191 deletions.
11 changes: 11 additions & 0 deletions app/(main)/(auth)/sign-up/[[...sign-up]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { SignUp } from '@clerk/nextjs'

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

export default function Page() {
return (
<Container className="mt-24 flex items-center justify-center">
<SignUp />
</Container>
)
}
8 changes: 4 additions & 4 deletions app/(main)/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -301,24 +301,24 @@ function UserInfo() {

return (
<AnimatePresence>
<SignedIn>
<SignedIn key="user-info">
<motion.div
className="pointer-events-auto flex h-10 items-center"
initial={{ opacity: 0, x: 25 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 25 }}
>
<UserButton
afterSignOutUrl={url('/').href}
afterSignOutUrl={url(pathname).href}
appearance={{
elements: {
avatarBox: 'w-10 h-10',
avatarBox: 'w-9 h-9 ring-2 ring-white/20',
},
}}
/>
</motion.div>
</SignedIn>
<SignedOut>
<SignedOut key="sign-in">
<motion.div
className="pointer-events-auto"
initial={{ opacity: 0, x: 25 }}
Expand Down
282 changes: 144 additions & 138 deletions app/(main)/blog/BlogPostPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Image from 'next/image'
import React from 'react'
import Balancer from 'react-wrap-balancer'

import { BlogPostStateLoader } from '~/app/(main)/blog/BlogPostStateLoader'
import { BlogReactions } from '~/app/(main)/blog/BlogReactions'
import {
CalendarIcon,
Expand All @@ -14,6 +15,7 @@ import {
ScriptIcon,
UTurnLeftIcon,
} from '~/assets'
import { ClientOnly } from '~/components/ClientOnly'
import { PostPortableText } from '~/components/PostPortableText'
import { Prose } from '~/components/Prose'
import { Button } from '~/components/ui/Button'
Expand All @@ -31,157 +33,161 @@ export function BlogPostPage({
reactions?: number[]
}) {
return (
<>
<Container className="mt-16 lg:mt-32">
<div className="xl:relative">
<div className="mx-auto max-w-2xl">
<Button
href="/blog"
variant="secondary"
aria-label="返回博客页面"
className="group mb-8 flex h-10 w-10 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 transition dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20 lg:absolute lg:-left-5 lg:-mt-2 lg:mb-0 xl:-top-1.5 xl:left-0 xl:mt-0"
>
<UTurnLeftIcon className="h-8 w-8 stroke-zinc-500 transition group-hover:stroke-zinc-700 dark:stroke-zinc-500 dark:group-hover:stroke-zinc-400" />
</Button>
<article>
<header className="relative flex flex-col items-center pb-5 after:absolute after:-bottom-1 after:block after:h-px after:w-full after:rounded after:bg-gradient-to-r after:from-zinc-400/20 after:via-zinc-200/10 after:to-transparent dark:after:from-zinc-600/20 dark:after:via-zinc-700/10">
<motion.div
className="relative mb-7 aspect-[240/135] w-full md:mb-12 md:w-[120%]"
initial={{ opacity: 0, scale: 0.96, y: 10 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{
duration: 0.35,
type: 'spring',
stiffness: 120,
damping: 20,
}}
>
<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=""
className="select-none"
unoptimized
fill
aria-hidden={true}
/>
</div>
<Container className="mt-16 lg:mt-32">
<div className="xl:relative">
<div className="mx-auto max-w-2xl">
<Button
href="/blog"
variant="secondary"
aria-label="返回博客页面"
className="group mb-8 flex h-10 w-10 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 transition dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20 lg:absolute lg:-left-5 lg:-mt-2 lg:mb-0 xl:-top-1.5 xl:left-0 xl:mt-0"
>
<UTurnLeftIcon className="h-8 w-8 stroke-zinc-500 transition group-hover:stroke-zinc-700 dark:stroke-zinc-500 dark:group-hover:stroke-zinc-400" />
</Button>
<article data-postid={post._id}>
<header className="relative flex flex-col items-center pb-5 after:absolute after:-bottom-1 after:block after:h-px after:w-full after:rounded after:bg-gradient-to-r after:from-zinc-400/20 after:via-zinc-200/10 after:to-transparent dark:after:from-zinc-600/20 dark:after:via-zinc-700/10">
<motion.div
className="relative mb-7 aspect-[240/135] w-full md:mb-12 md:w-[120%]"
initial={{ opacity: 0, scale: 0.96, y: 10 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{
duration: 0.35,
type: 'spring',
stiffness: 120,
damping: 20,
}}
>
<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={post.title}
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}
alt=""
className="select-none"
unoptimized
fill
aria-hidden={true}
/>
</motion.div>
<motion.div
className="flex w-full items-center space-x-4 text-sm font-medium text-zinc-600/80 dark:text-zinc-400/80"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.15,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.1,
}}
</div>
<Image
src={post.mainImage.asset.url}
alt={post.title}
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
fill
/>
</motion.div>
<motion.div
className="flex w-full items-center space-x-4 text-sm font-medium text-zinc-600/80 dark:text-zinc-400/80"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.15,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.1,
}}
>
<time
dateTime={post.publishedAt}
className="flex items-center space-x-1.5"
>
<time
dateTime={post.publishedAt}
className="flex items-center space-x-1.5"
>
<CalendarIcon />
<span>
{Intl.DateTimeFormat('zh').format(
new Date(post.publishedAt)
)}
</span>
</time>
<span className="inline-flex items-center space-x-1.5">
<ScriptIcon />
<span>{post.categories.join(', ')}</span>
<CalendarIcon />
<span>
{Intl.DateTimeFormat('zh').format(
new Date(post.publishedAt)
)}
</span>
</motion.div>
<motion.h1
className="mt-6 w-full text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.2,
type: 'spring',
stiffness: 150,
damping: 30,
delay: 0.2,
}}
>
<Balancer>{post.title}</Balancer>
</motion.h1>
<motion.p
className="my-5 w-full text-sm font-medium text-zinc-500"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.2,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.23,
}}
</time>
<span className="inline-flex items-center space-x-1.5">
<ScriptIcon />
<span>{post.categories.join(', ')}</span>
</span>
</motion.div>
<motion.h1
className="mt-6 w-full text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.2,
type: 'spring',
stiffness: 150,
damping: 30,
delay: 0.2,
}}
>
<Balancer>{post.title}</Balancer>
</motion.h1>
<motion.p
className="my-5 w-full text-sm font-medium text-zinc-500"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.2,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.23,
}}
>
<Balancer>{post.description}</Balancer>
</motion.p>
<motion.div
className="flex w-full items-center space-x-4 text-sm font-medium text-zinc-700/50 dark:text-zinc-300/50"
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.15,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.255,
}}
>
<span
className="inline-flex items-center space-x-1.5"
title={views?.toString()}
>
<Balancer>{post.description}</Balancer>
</motion.p>
<motion.div
className="flex w-full items-center space-x-4 text-sm font-medium text-zinc-700/50 dark:text-zinc-300/50"
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.15,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.255,
}}
>
<span
className="inline-flex items-center space-x-1.5"
title={views?.toString()}
>
<CursorClickIcon />
<span>{prettifyNumber(views ?? 0, true)}次点击</span>
</span>
<CursorClickIcon />
<span>{prettifyNumber(views ?? 0, true)}次点击</span>
</span>

<span className="inline-flex items-center space-x-1.5">
<HourglassIcon />
<span>{post.readingTime.toFixed(0)}分钟阅读</span>
</span>
</motion.div>
</header>
<Prose className="mt-8">
<PostPortableText value={post.body} />
</Prose>
</article>
</div>
<span className="inline-flex items-center space-x-1.5">
<HourglassIcon />
<span>{post.readingTime.toFixed(0)}分钟阅读</span>
</span>
</motion.div>
</header>
<Prose className="mt-8">
<PostPortableText value={post.body} />
</Prose>
</article>
</div>
</Container>
</div>

<Portal>
<div className="pointer-events-none fixed inset-0 z-50 h-full w-full sm:px-8">
<div className="mx-auto h-full max-w-7xl px-4 sm:px-8 lg:px-12">
<div className="mx-auto flex h-full max-w-2xl justify-between lg:max-w-5xl">
<div className=""></div>
<div className="hidden h-full flex-col items-center justify-center lg:flex">
<BlogReactions
_id={post._id}
mood={post.mood}
reactions={reactions}
/>
<ClientOnly>
<BlogPostStateLoader post={post} />
</ClientOnly>

<ClientOnly>
<Portal>
<div className="pointer-events-none fixed inset-0 z-50 h-full w-full sm:px-8">
<div className="mx-auto h-full max-w-7xl px-4 sm:px-8 lg:px-12">
<div className="mx-auto flex h-full max-w-2xl justify-between lg:max-w-5xl">
<div className=""></div>
<div className="hidden h-full flex-col items-center justify-center lg:flex">
<BlogReactions
_id={post._id}
mood={post.mood}
reactions={reactions}
/>
</div>
</div>
</div>
</div>
</div>
</Portal>
</>
</Portal>
</ClientOnly>
</Container>
)
}
33 changes: 33 additions & 0 deletions app/(main)/blog/BlogPostStateLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use client'

import React from 'react'
import { useQuery } from 'react-query'

import { addComment, blogPostState } from '~/app/(main)/blog/blog-post.state'
import { type PostIDLessCommentDto } from '~/db/dto/comment.dto'
import { type Post } from '~/sanity/schemas/post'

export function BlogPostStateLoader({ post }: { post: Post }) {
const { data: comments } = useQuery(
['comments', post._id],
async () => {
const res = await fetch(`/api/comments/${post._id}`)
const data = await res.json()
return data as PostIDLessCommentDto[]
},
{ initialData: [] }
)

React.useEffect(() => {
blogPostState.postId = post._id
}, [post._id])
React.useEffect(() => {
// only append new comments
comments?.forEach((comment) => {
if (blogPostState.comments.find((c) => c.id === comment.id)) return
addComment(comment)
})
}, [comments])

return null
}
Loading

0 comments on commit 0046e86

Please sign in to comment.