Skip to content

Commit

Permalink
Merge pull request #836 from tszhong0411/app-18-enhance-the-comment-s…
Browse files Browse the repository at this point in the history
…ection

Enhance the comment section
  • Loading branch information
tszhong0411 authored Aug 19, 2024
2 parents fc9de7c + eb8765b commit 1b2e635
Show file tree
Hide file tree
Showing 44 changed files with 1,022 additions and 1,071 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-waves-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tszhong0411/tailwind-config': patch
---

update the style of ul, blockquote
5 changes: 5 additions & 0 deletions .changeset/forty-schools-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tszhong0411/ui': patch
---

figure className is now available in code block
5 changes: 5 additions & 0 deletions .changeset/pink-windows-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tszhong0411/tailwind-config': patch
---

set blockquote to normal font style
3 changes: 2 additions & 1 deletion .cspell/libraries.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ nanostores
nextauth
nextra
normy
nuqs
nuxt
paralleldrive
rehype
Expand All @@ -33,10 +34,10 @@ sonarjs
sonner
sqld
tinycolor2
tiptap
tsup
turso
tursodatabase
unifiedjs
usehooks
vfile
zustand
17 changes: 6 additions & 11 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,6 @@
"@tanstack/react-query-next-experimental": "^5.44.0",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@tiptap/core": "^2.4.0",
"@tiptap/extension-bold": "^2.4.0",
"@tiptap/extension-document": "^2.4.0",
"@tiptap/extension-italic": "^2.4.0",
"@tiptap/extension-paragraph": "^2.4.0",
"@tiptap/extension-placeholder": "^2.4.0",
"@tiptap/extension-strike": "^2.4.0",
"@tiptap/extension-text": "^2.4.0",
"@tiptap/html": "^2.4.0",
"@tiptap/pm": "^2.4.0",
"@tiptap/react": "^2.4.0",
"@trpc/client": "11.0.0-rc.373",
"@trpc/react-query": "11.0.0-rc.373",
"@trpc/server": "11.0.0-rc.373",
Expand All @@ -62,12 +51,16 @@
"jiti": "^1.21.6",
"js-sha512": "^0.9.0",
"lucide-react": "^0.394.0",
"markdown-to-jsx": "^7.4.7",
"nanostores": "^0.10.3",
"next": "14.2.3",
"next-auth": "5.0.0-beta.19",
"next-themes": "^0.3.0",
"nuqs": "^1.17.8",
"pluralize": "^8.0.0",
"react": "18.3.1",
"react-dom": "18.2.0",
"react-intersection-observer": "^9.13.0",
"react-medium-image-zoom": "^5.2.4",
"react-spring": "^9.7.3",
"resend": "^3.2.0",
Expand All @@ -76,6 +69,7 @@
"superjson": "^2.2.1",
"tinycolor2": "^1.6.0",
"use-debounce": "^10.0.1",
"usehooks-ts": "^3.1.0",
"zod": "^3.23.8"
},
"devDependencies": {
Expand All @@ -87,6 +81,7 @@
"@tszhong0411/tsconfig": "workspace:*",
"@types/canvas-confetti": "^1.6.4",
"@types/node": "^20.14.2",
"@types/pluralize": "^0.0.33",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@types/rss": "^0.0.32",
Expand Down
Binary file removed apps/web/public/images/email/logo.png
Binary file not shown.
8 changes: 4 additions & 4 deletions apps/web/src/app/blog/[slug]/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ const Header = () => {
onSettled: () => utils.views.get.invalidate()
})

const viewsQuery = api.views.get.useQuery({
const viewsCountQuery = api.views.get.useQuery({
slug
})

const commentsQuery = api.comments.getCount.useQuery({
const commentsCountQuery = api.comments.getTotalCommentsCount.useQuery({
slug
})

Expand Down Expand Up @@ -63,11 +63,11 @@ const Header = () => {
</div>
<div className='space-y-1 md:mx-auto'>
<div className='text-muted-foreground'>Views</div>
{viewsQuery.isLoading ? '--' : <div>{viewsQuery.data?.views}</div>}
{viewsCountQuery.isLoading ? '--' : <div>{viewsCountQuery.data?.views}</div>}
</div>
<div className='space-y-1 md:mx-auto'>
<div className='text-muted-foreground'>Comments</div>
{commentsQuery.isLoading ? '--' : <div>{commentsQuery.data?.value}</div>}
{commentsCountQuery.isLoading ? '--' : <div>{commentsCountQuery.data?.value}</div>}
</div>
</div>
</div>
Expand Down
15 changes: 8 additions & 7 deletions apps/web/src/app/blog/[slug]/like-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ const LikeButton = (props: LikeButtonProps) => {
const buttonRef = useRef<HTMLButtonElement>(null)
const utils = api.useUtils()

const likesQuery = api.likes.get.useQuery({ slug })
const queryKey = { slug }

const likesQuery = api.likes.get.useQuery(queryKey)
const likesMutation = api.likes.patch.useMutation({
onMutate: (newData) => {
void utils.likes.get.cancel({ slug })
onMutate: async (newData) => {
await utils.likes.get.cancel(queryKey)

const previousData = utils.likes.get.getData({ slug })
const previousData = utils.likes.get.getData(queryKey)

utils.likes.get.setData({ slug }, (old) => {
utils.likes.get.setData(queryKey, (old) => {
if (!old) return old

return {
Expand All @@ -41,7 +43,7 @@ const LikeButton = (props: LikeButtonProps) => {
},
onError: (_, __, ctx) => {
if (ctx?.previousData) {
utils.likes.get.setData({ slug }, ctx.previousData)
utils.likes.get.setData(queryKey, ctx.previousData)
}
},
onSettled: () => utils.likes.get.invalidate()
Expand Down Expand Up @@ -139,7 +141,6 @@ const LikeButton = (props: LikeButtonProps) => {
</g>
</svg>
Like
{likesQuery.data && likesQuery.data.likes + cacheCount === 1 ? '' : 's'}
<Separator orientation='vertical' className='bg-zinc-700' />
{likesQuery.isLoading ? <div> -- </div> : <div>{likesQuery.data!.likes + cacheCount}</div>}
</button>
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getTOC } from '@tszhong0411/mdx'
import { allBlogPosts } from 'mdx/generated'
import type { Metadata, ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'
import { Suspense } from 'react'
import { type Article, type WithContext } from 'schema-dts'

import Comments from '@/components/comments'
Expand Down Expand Up @@ -154,7 +155,11 @@ const Page = async (props: PageProps) => {
<Footer />
</Providers>

{flags.comment ? <Comments slug={slug} /> : null}
{flags.comment ? (
<Suspense>
<Comments slug={slug} />
</Suspense>
) : null}
</>
)
}
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/app/guestbook/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const Menu = () => {
const utils = api.useUtils()

const guestbookMutation = api.guestbook.delete.useMutation({
onSuccess: () => toast.success('Delete message successfully'),
onSettled: () => utils.guestbook.get.invalidate(),
onError: (error) => toast.error(error.message)
})
Expand Down
132 changes: 70 additions & 62 deletions apps/web/src/components/comments/comment-actions.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { UseTRPCMutationOptions } from '@trpc/react-query/shared'
import { Button, buttonVariants } from '@tszhong0411/ui'
import { Button, buttonVariants, toast } from '@tszhong0411/ui'
import { cva } from 'class-variance-authority'
import { ThumbsDownIcon, ThumbsUpIcon } from 'lucide-react'
import { useSession } from 'next-auth/react'

import { useCommentContext } from '@/contexts/comment'
import { useCommentsContext } from '@/contexts/comments'
import { useRatesContext } from '@/contexts/rates'
import { api, type RouterInputs } from '@/trpc/react'
import type { CommentsOutput } from '@/trpc/routers/comments'
import { useCommentParams } from '@/hooks/use-comment-params'
import { api } from '@/trpc/react'
import type { CommentsInput } from '@/trpc/routers/comments'

const rateVariants = cva(
buttonVariants({
Expand All @@ -28,100 +28,109 @@ const rateVariants = cva(
const CommentActions = () => {
const { comment, setIsReplying } = useCommentContext()
const { increment, decrement, getCount } = useRatesContext()
const { slug } = useCommentsContext()
const { slug, sort } = useCommentsContext()
const { status } = useSession()
const utils = api.useUtils()
const [params] = useCommentParams()

const queryKey: CommentsInput = {
slug,
...(comment.parentId
? {
parentId: comment.parentId,
sort: 'oldest',
type: 'replies',
...(params.reply ? { highlightedCommentId: params.reply } : {})
}
: { sort, ...(params.comment ? { highlightedCommentId: params.comment } : {}) })
}

const mutationOptions: UseTRPCMutationOptions<
RouterInputs['rates']['set'] | RouterInputs['rates']['delete'],
unknown,
void,
{
previousData: CommentsOutput | undefined
}
> = {
onMutate: (newData) => {
const ratesSetMutation = api.rates.set.useMutation({
onMutate: async (newData) => {
increment()
void utils.comments.get.cancel()

const target = {
slug,
sort: 'newest',
...(comment.parentId ? { parentId: comment.parentId } : {})
} as const

const previousData = utils.comments.get.getData(target)

utils.comments.get.setData(target, (oldData) => {
if (!oldData) return oldData

return oldData.map((c) => {
if (c.id === newData.id) {
const hasLike = 'like' in newData

let likes: number = c.likes
let dislikes: number = c.dislikes
await utils.comments.getInfiniteComments.cancel(queryKey)

if (c.liked === true) likes--
if (c.liked === false) dislikes--
const previousData = utils.comments.getInfiniteComments.getInfiniteData(queryKey)

if (hasLike && newData.like) likes++
if (hasLike && !newData.like) dislikes++
utils.comments.getInfiniteComments.setInfiniteData(queryKey, (oldData) => {
if (!oldData) {
return {
pages: [],
pageParams: []
}
}

return {
...oldData,
pages: oldData.pages.map((page) => {
return {
...c,
likes,
dislikes,
liked: hasLike ? newData.like : undefined
...page,
comments: page.comments.map((c) => {
if (c.id === newData.id) {
let likes: number = c.likes
let dislikes: number = c.dislikes

if (c.liked === true) likes--
if (c.liked === false) dislikes--

if (newData.like === true) likes++
if (newData.like === false) dislikes++

return {
...c,
likes,
dislikes,
liked: newData.like
}
}

return c
})
}
}

return c
})
})
}
})

return { previousData }
},
onError: (_, __, ctx) => {
onError: (error, _, ctx) => {
if (ctx?.previousData) {
utils.comments.get.setData({ slug }, ctx.previousData)
utils.comments.getInfiniteComments.setInfiniteData(queryKey, ctx.previousData)
}
toast.error(error.message)
},
onSettled: () => {
decrement()

if (getCount() === 0) {
void utils.comments.get.invalidate()
void utils.comments.getInfiniteComments.invalidate()
}
}
}

const ratesSetMutation = api.rates.set.useMutation(mutationOptions)
const ratesDeleteMutation = api.rates.delete.useMutation(mutationOptions)
})

const isAuthenticated = status === 'authenticated'

const rateHandler = (like: boolean) => {
if (like === comment.liked) {
ratesDeleteMutation.mutate({ id: comment.id })
} else {
ratesSetMutation.mutate({ id: comment.id, like })
if (!isAuthenticated) {
toast.error('You need to be logged in to rate comments')
return
}
ratesSetMutation.mutate({ id: comment.id, like: like === comment.liked ? null : like })
}

return (
<div className='flex gap-1'>
<div className='flex gap-1 pl-10'>
<Button
type='button'
variant='secondary'
onClick={() => {
rateHandler(true)
}}
className={rateVariants({
active: comment.liked === true || !isAuthenticated
active: comment.liked === true
})}
aria-label='Like'
disabled={!isAuthenticated}
>
<ThumbsUpIcon className='size-4' />
{comment.likes}
Expand All @@ -133,15 +142,14 @@ const CommentActions = () => {
rateHandler(false)
}}
className={rateVariants({
active: comment.liked === false || !isAuthenticated
active: comment.liked === false
})}
aria-label='Dislike'
disabled={!isAuthenticated}
>
<ThumbsDownIcon className='size-4' />
{comment.dislikes}
</Button>
{!comment.parentId && isAuthenticated ? (
{comment.parentId ? null : (
<Button
type='button'
variant='secondary'
Expand All @@ -152,7 +160,7 @@ const CommentActions = () => {
>
Reply
</Button>
) : null}
)}
</div>
)
}
Expand Down
Loading

0 comments on commit 1b2e635

Please sign in to comment.