Skip to content

Commit

Permalink
feat: comment ui
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Jun 25, 2023
1 parent f8902a6 commit 35b61db
Show file tree
Hide file tree
Showing 16 changed files with 282 additions and 12 deletions.
13 changes: 9 additions & 4 deletions src/app/notes/[id]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { headers } from 'next/dist/client/components/headers'
import type { Metadata } from 'next'

import { NotSupport } from '~/components/common/NotSupport'
import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView'
import { Comments } from '~/components/widgets/comment/Comments'
import { CommentRoot } from '~/components/widgets/comment/CommentRoot'
import { NoteMainContainer } from '~/components/widgets/note/NoteMainContainer'
import { REQUEST_QUERY } from '~/constants/system'
import { REQUEST_GEO, REQUEST_QUERY } from '~/constants/system'
import { attachUA } from '~/lib/attach-ua'
import { getSummaryFromMd } from '~/lib/markdown'
import {
Expand Down Expand Up @@ -65,13 +66,17 @@ export default async (
}>,
) => {
attachUA()
const searchParams = new URLSearchParams(headers().get(REQUEST_QUERY) || '')
const header = headers()
const searchParams = new URLSearchParams(header.get(REQUEST_QUERY) || '')
const id = props.params.id
const query = queries.note.byNid(
id,
searchParams.get('password') || undefined,
)
const data = await getQueryClient().fetchQuery(query)
const geo = header.get(REQUEST_GEO)

const isCN = geo === 'CN'

return (
<>
Expand All @@ -81,7 +86,7 @@ export default async (

<BottomToUpTransitionView className="min-w-0">
<Paper as={NoteMainContainer}>{props.children}</Paper>
<Comments refId={id} />
{isCN ? <NotSupport /> : <CommentRoot refId={data.data.id} />}
</BottomToUpTransitionView>
</>
)
Expand Down
2 changes: 2 additions & 0 deletions src/components/common/Lazyload.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import React, { useEffect } from 'react'
import { useInView } from 'react-intersection-observer'
import type { FC, PropsWithChildren } from 'react'
Expand Down
7 changes: 7 additions & 0 deletions src/components/common/NotSupport.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const NotSupport = () => {
return (
<div className="flex h-[100px] items-center justify-center text-lg font-medium">
您当前所在地区暂不支持此功能
</div>
)
}
2 changes: 2 additions & 0 deletions src/components/ui/link-card/LinkCard.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
}

.skeleton {
@apply !cursor-auto;

& .title,
& .desc {
border-radius: 99px;
Expand Down
22 changes: 21 additions & 1 deletion src/components/ui/link-card/LinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { FC } from 'react'

import { simpleCamelcaseKeys as camelcaseKeys } from '@mx-space/api-client'

import { useIsClient } from '~/hooks/common/use-is-client'
import { useIsUnMounted } from '~/hooks/common/use-is-unmounted'
import { useSafeSetState } from '~/hooks/common/use-safe-setState'
import { apiClient } from '~/utils/request'
Expand All @@ -20,7 +21,14 @@ export interface LinkCardProps {
source?: LinkCardSource
className?: string
}
export const LinkCard: FC<LinkCardProps> = (props) => {

export const LinkCard = (props: LinkCardProps) => {
const isClient = useIsClient()
if (!isClient) return <LinkCardSkeleton />

return <LinkCardImpl {...props} />
}
export const LinkCardImpl: FC<LinkCardProps> = (props) => {
const { id, source = 'self', className } = props
const isUnMounted = useIsUnMounted()

Expand Down Expand Up @@ -181,3 +189,15 @@ export const LinkCard: FC<LinkCardProps> = (props) => {
</LinkComponent>
)
}

const LinkCardSkeleton = () => {
return (
<span className={clsx(styles['card-grid'], styles['skeleton'])}>
<span className={styles['contents']}>
<span className={styles['title']} />
<span className={styles['desc']} />
</span>
<span className={styles['image']} />
</span>
)
}
13 changes: 13 additions & 0 deletions src/components/ui/skeleton/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { clsxm } from '~/utils/helper'

export const Skeleton: Component = ({ className }) => {
return (
<div className={clsxm('flex animate-pulse flex-col gap-3', className)}>
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<span className="sr-only">Loading...</span>
</div>
)
}
1 change: 1 addition & 0 deletions src/components/ui/skeleton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Skeleton'
78 changes: 78 additions & 0 deletions src/components/widgets/comment/Comment.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
.comment__message {
* {
@apply leading-6;
}

h1,
h2,
h3,
h4,
h5,
h6 {
@apply font-semibold tracking-tight;
}

h1 {
@apply text-lg font-bold;
}

h2 {
font-size: 1.065rem;
line-height: 1.75rem;
@apply font-bold;
}

hr {
@apply my-1.5 border-zinc-400 opacity-20;
}

ul {
@apply list-disc pl-4;
}

ol {
@apply list-decimal pl-4;
}

blockquote {
@apply my-1 border-l-4 border-zinc-400 pl-2;
}

img,
video {
@apply rounded-md;
max-height: 350px;
}

pre {
@apply my-1.5 whitespace-break-spaces;
}

pre,
code:not([class^='language-']) {
@apply rounded bg-zinc-700/10 px-1 py-0.5 text-zinc-900;
}

pre > code {
@apply bg-transparent px-0 py-0 !important;
}
}

.dark .comment__message {
hr {
@apply border-zinc-100 opacity-20;
}

blockquote {
@apply border-zinc-50/50;
}

pre,
code:not([class^='language-']) {
@apply bg-zinc-200/20 text-zinc-50;
}

pre > code {
@apply bg-transparent !important;
}
}
54 changes: 54 additions & 0 deletions src/components/widgets/comment/Comment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import clsx from 'clsx'
import Markdown from 'markdown-to-jsx'
import Image from 'next/image'
import type { CommentModel } from '@mx-space/api-client'

import { RelativeTime } from '~/components/ui/relative-time'

import styles from './Comment.module.css'

export const Comment: Component<{ comment: CommentModel }> = (props) => {
const { comment, className } = props
const { id: cid, avatar, author, text } = comment
const parentId =
typeof comment.parent === 'string' ? comment.parent : comment.parent?.id
return (
<li data-comment-id={cid} data-parent-id={parentId} className={className}>
<div className="flex w-full items-stretch gap-2">
<div className="flex w-9 shrink-0 items-end">
<Image
src={avatar}
alt=""
className="h-9 w-9 select-none rounded-full"
width={24}
height={24}
unoptimized
/>
</div>
<div className={clsx('flex flex-1 flex-col', 'items-start')}>
<span
className={clsx(
'flex items-center gap-2 font-semibold text-zinc-800 dark:text-zinc-200',
'mb-2',
)}
>
<span className="ml-2">{author}</span>
<span className="inline-flex select-none text-[10px] font-medium opacity-40">
<RelativeTime date={comment.created} />
</span>
</span>

<div
className={clsx(
styles['comment__message'],
'group relative inline-block rounded-xl px-2 py-1 text-zinc-800 dark:text-zinc-200',
'rounded-bl-sm bg-zinc-600/5 dark:bg-zinc-500/20',
)}
>
<Markdown>{text}</Markdown>
</div>
</div>
</div>
</li>
)
}
6 changes: 6 additions & 0 deletions src/components/widgets/comment/CommentBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { FC } from 'react'
import type { CommentBaseProps } from './types'

export const CommentBox: FC<CommentBaseProps> = (props) => {
return <div>CommentBox WIP, RefId: {props.refId}</div>
}
22 changes: 22 additions & 0 deletions src/components/widgets/comment/CommentRoot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { FC } from 'react'
import type { CommentBaseProps } from './types'

import { LazyLoad } from '~/components/common/Lazyload'
import { Loading } from '~/components/ui/loading'

import { CommentBox } from './CommentBox'
import { Comments } from './Comments'

const LoadingElement = <Loading loadingText="评论区加载中..." />
export const CommentRoot: FC<CommentBaseProps> = (props) => {
return (
<LazyLoad placeholder={LoadingElement}>
<div className="mt-12">
<CommentBox refId={props.refId} />

<div className="h-12" />
<Comments refId={props.refId} />
</div>
</LazyLoad>
)
}
13 changes: 13 additions & 0 deletions src/components/widgets/comment/CommentSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { clsxm } from '~/utils/helper'

export const CommentSkeleton: Component = ({ className }) => {
return (
<div className={clsxm('flex animate-pulse flex-col gap-3', className)}>
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<div className="h-6 w-full rounded-lg bg-gray-200 dark:bg-zinc-800/80" />
<span className="sr-only">Loading...</span>
</div>
)
}
45 changes: 39 additions & 6 deletions src/components/widgets/comment/Comments.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
'use client'

import { useQuery } from '@tanstack/react-query'
import { useState } from 'react'
import type { FC } from 'react'
import type { CommentBaseProps } from './types'

import { apiClient } from '~/utils/request'

import { Comment } from './Comment'
import { CommentSkeleton } from './CommentSkeleton'

export const Comments: FC<{
refId: string
}> = ({ refId }) => {
export const Comments: FC<CommentBaseProps> = ({ refId }) => {
const [page, setPage] = useState(1)

const { data, isLoading } = useQuery(
['comments', refId],
async ({ queryKey, meta }) => {
const { page } = meta as { page: number }
const [, refId] = queryKey as [string, string]
const data = await apiClient.comment.getByRefId(refId, {
page,
})
return data.$serialized
},

{
meta: {
page,
},
},
)
if (isLoading) {
return <CommentSkeleton />
}
if (!data) return null
return (
<div className="relative mb-[60px] mt-[120px] min-h-[100px]">
Comments WIP, RefId: {refId}
</div>
<ul className="list-none space-y-4">
{data.data.map((comment) => {
return <Comment comment={comment} key={comment.id} />
})}
</ul>
)
}
3 changes: 3 additions & 0 deletions src/components/widgets/comment/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface CommentBaseProps {
refId: string
}
7 changes: 6 additions & 1 deletion src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import type { NextRequest } from 'next/server'
import countries from '~/data/countries.json'
import { kvKeys, redis } from '~/lib/redis.server'

import { REQUEST_PATHNAME, REQUEST_QUERY } from './constants/system'
import {
REQUEST_GEO,
REQUEST_PATHNAME,
REQUEST_QUERY,
} from './constants/system'

export default async function middleware(req: NextRequest) {
const { pathname, search } = req.nextUrl
Expand All @@ -26,6 +30,7 @@ export default async function middleware(req: NextRequest) {
const requestHeaders = new Headers(req.headers)
requestHeaders.set(REQUEST_PATHNAME, pathname)
requestHeaders.set(REQUEST_QUERY, search)
requestHeaders.set(REQUEST_GEO, geo?.country || 'unknown')

const isApi = pathname.startsWith('/api/')

Expand Down
6 changes: 6 additions & 0 deletions src/providers/internal/createDataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ export const createDataProvider = <Model,>() => {
jotaiStore.set(currentDataAtom, data)
}, [data])

useEffect(() => {
return () => {
jotaiStore.set(currentDataAtom, null)
}
}, [])

return children
})
const useCurrentDataSelector = <T,>(
Expand Down

1 comment on commit 35b61db

@vercel
Copy link

@vercel vercel bot commented on 35b61db Jun 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

springtide – ./

springtide-innei.vercel.app
springtide.vercel.app
springtide-git-main-innei.vercel.app
innei.in

Please sign in to comment.