Skip to content

Commit

Permalink
feat: comment list
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Jun 25, 2023
1 parent 35b61db commit 98837de
Show file tree
Hide file tree
Showing 15 changed files with 467 additions and 100 deletions.
7 changes: 7 additions & 0 deletions src/app/(page-detail)/[slug]/Container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const Container: Component = ({ children }) => {
return (
<div className="container m-auto mt-[120px] max-w-6xl px-2 md:px-6 lg:p-0">
{children}
</div>
)
}
96 changes: 96 additions & 0 deletions src/app/(page-detail)/[slug]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React from 'react'
import { headers } from 'next/dist/client/components/headers'
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'

import { RequestError } from '@mx-space/api-client'

import { NotSupport } from '~/components/common/NotSupport'
import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView'
import { CommentRoot } from '~/components/widgets/comment/CommentRoot'
import { REQUEST_GEO } from '~/constants/system'
import { attachUA } from '~/lib/attach-ua'
import { getSummaryFromMd } from '~/lib/markdown'
import { CurrentPageDataProvider } from '~/providers/page/CurrentPageDataProvider'
import { LayoutRightSideProvider } from '~/providers/shared/LayoutRightSideProvider'
import { queries } from '~/queries/definition'
import { getQueryClient } from '~/utils/query-client.server'

import { Container } from './Container'

export const generateMetadata = async ({
params,
}: {
params: PageParams
}): Promise<Metadata> => {
const { slug } = params
try {
attachUA()
const data = await getQueryClient().fetchQuery(queries.page.bySlug(slug))
const { title, images, text } = data
const description = getSummaryFromMd(text ?? '')

const ogImage = images?.length
? {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
url: images[0].src!,
}
: undefined
return {
title,
description,
openGraph: {
title,
description,
images: ogImage,
type: 'article',
},
twitter: {
images: ogImage,
title,
description,
card: 'summary_large_image',
},
} satisfies Metadata
} catch {
return {}
}
}

interface PageParams {
slug: string
}

export default async (props: NextPageParams<PageParams>) => {
attachUA()
const {
params: { slug },
} = props
const query = queries.page.bySlug(slug)
// const queryKey = query.queryKey
const data = await getQueryClient()
.fetchQuery(query)
.catch((error) => {
if (error instanceof RequestError && error.status === 404) {
return notFound()
}
throw error
})
const header = headers()
const geo = header.get(REQUEST_GEO)

const isCN = geo === 'CN'
return (
<Container>
<CurrentPageDataProvider data={data} />
<div className="relative flex min-h-[120px]">
<BottomToUpTransitionView className="min-w-0">
{props.children}
</BottomToUpTransitionView>

<LayoutRightSideProvider className="relative hidden lg:block" />
</div>
{isCN ? <NotSupport /> : <CommentRoot refId={data.id} />}
</Container>
)
}
7 changes: 7 additions & 0 deletions src/app/(page-detail)/[slug]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Loading } from '~/components/ui/loading'

export default () => (
<div className="absolute inset-0 flex h-[120px] center">
<Loading useDefaultLoadingText />
</div>
)
96 changes: 96 additions & 0 deletions src/app/(page-detail)/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use client'

import { useEffect } from 'react'
import { Balancer } from 'react-wrap-balancer'
import type { Image } from '@mx-space/api-client'
import type { PropsWithChildren } from 'react'

import { useSetHeaderMetaInfo } from '~/components/layout/header/hooks'
import { Markdown } from '~/components/ui/markdown'
import { TocAside, TocAutoScroll } from '~/components/widgets/toc'
import { noopArr } from '~/lib/noop'
import { MarkdownImageRecordProvider } from '~/providers/article/MarkdownImageRecordProvider'
import { useCurrentPageDataSelector } from '~/providers/page/CurrentPageDataProvider'
import { LayoutRightSidePortal } from '~/providers/shared/LayoutRightSideProvider'
import { WrappedElementProvider } from '~/providers/shared/WrappedElementProvider'

import Loading from './loading'

const PageDetail = () => {
const id = useCurrentPageDataSelector((p) => p?.id)
const title = useCurrentPageDataSelector((p) => p?.title)
const subtitle = useCurrentPageDataSelector((p) => p?.subtitle)
if (!id) {
return <Loading />
}

return (
<div>
<HeaderMetaInfoSetting />
<article className="prose">
<header className="mb-8">
<h1 className="text-center lg:text-left">
<Balancer>{title}</Balancer>
</h1>

<desc className="text-center text-lg text-gray-600/70 dark:text-neutral-400 lg:text-left">
{subtitle}
</desc>
</header>
<WrappedElementProvider>
<MarkdownImageRecordProviderInternal>
<PostMarkdown />
</MarkdownImageRecordProviderInternal>

<LayoutRightSidePortal>
<TocAside
className="sticky top-[120px] ml-4 mt-[120px]"
treeClassName="max-h-[calc(100vh-6rem-4.5rem-300px)] h-[calc(100vh-6rem-4.5rem-300px)] min-h-[120px] relative"
/>
<TocAutoScroll />
</LayoutRightSidePortal>
</WrappedElementProvider>
</article>
</div>
)
}

const PostMarkdown = () => {
const text = useCurrentPageDataSelector((data) => data?.text)
if (!text) return null

return <Markdown value={text} as="main" className="min-w-0 overflow-hidden" />
}
const MarkdownImageRecordProviderInternal = (props: PropsWithChildren) => {
const images = useCurrentPageDataSelector(
(data) => data?.images || (noopArr as Image[]),
)
if (!images) return null

return (
<MarkdownImageRecordProvider images={images}>
{props.children}
</MarkdownImageRecordProvider>
)
}

const HeaderMetaInfoSetting = () => {
const setHeaderMetaInfo = useSetHeaderMetaInfo()
const meta = useCurrentPageDataSelector((data) => {
if (!data) return null

return {
title: data.title,
description: data.subtitle || '',
slug: `/${data.slug}`,
}
})

useEffect(() => {
if (meta) setHeaderMetaInfo(meta)
}, [meta])

return null
}

export default PageDetail
41 changes: 2 additions & 39 deletions src/components/layout/header/internal/HeaderContent.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
'use client'

import React, { memo, useMemo } from 'react'
import React, { memo } from 'react'
import clsx from 'clsx'
import { motion, useMotionValue } from 'framer-motion'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import type { IHeaderMenu } from '../config'

import { FloatPopover } from '~/components/ui/float-popover'
import { clsxm } from '~/utils/helper'

import { useHeaderConfig } from './HeaderDataConfigureProvider'
import { useMenuOpacity } from './hooks'
import { MenuPopover } from './MenuPopover'

export const HeaderContent = () => {
return (
Expand Down Expand Up @@ -120,43 +120,6 @@ const HeaderMenuItem = memo<{
})
HeaderMenuItem.displayName = 'HeaderMenuItem'

const MenuPopover: Component<{
subMenu: IHeaderMenu['subMenu']
}> = memo(({ children, subMenu }) => {
const TriggerComponent = useMemo(() => () => children, [children])
if (!subMenu) return children
return (
<FloatPopover
strategy="fixed"
headless
placement="bottom"
offset={10}
popoverWrapperClassNames="z-[19] relative"
popoverClassNames="rounded-xl !p-0"
TriggerComponent={TriggerComponent}
>
{!!subMenu.length && (
<div className="relative flex w-[130px] flex-col px-4">
{subMenu.map((m) => {
return (
<Link
key={m.title}
href={m.path}
className="flex w-full items-center justify-around space-x-2 py-3 duration-200 hover:text-accent"
role="button"
>
{!!m.icon && <span>{m.icon}</span>}
<span>{m.title}</span>
</Link>
)
})}
</div>
)}
</FloatPopover>
)
})
MenuPopover.displayName = 'MenuPopover'

function AnimatedItem({
href,
children,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const HeaderDataConfigureProvider: Component = ({ children }) => {
nextMenuConfig[homeIndex].subMenu = []
for (const page of pageMeta) {
nextMenuConfig[homeIndex].subMenu!.push({
path: page.slug,
path: `/${page.slug}`,
title: page.title,
})
}
Expand Down
44 changes: 44 additions & 0 deletions src/components/layout/header/internal/MenuPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client'

import React, { memo, useMemo } from 'react'
import Link from 'next/link'
import type { IHeaderMenu } from '../config'

import { FloatPopover } from '~/components/ui/float-popover'

export const MenuPopover: Component<{
subMenu: IHeaderMenu['subMenu']
}> = memo(({ children, subMenu }) => {
const TriggerComponent = useMemo(() => () => children, [children])
if (!subMenu) return children
return (
<FloatPopover
strategy="fixed"
headless
placement="bottom"
offset={10}
popoverWrapperClassNames="z-[19] relative"
popoverClassNames="rounded-xl !p-0"
TriggerComponent={TriggerComponent}
>
{!!subMenu.length && (
<div className="relative flex w-[130px] flex-col px-4">
{subMenu.map((m) => {
return (
<Link
key={m.title}
href={`${m.path}`}
className="flex w-full items-center justify-around space-x-2 py-3 duration-200 hover:text-accent"
role="button"
>
{!!m.icon && <span>{m.icon}</span>}
<span>{m.title}</span>
</Link>
)
})}
</div>
)}
</FloatPopover>
)
})
MenuPopover.displayName = 'MenuPopover'
16 changes: 11 additions & 5 deletions src/components/ui/link-card/LinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import type { FC } from 'react'

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

import { useIsClient } from '~/hooks/common/use-is-client'
import { LazyLoad } from '~/components/common/Lazyload'
import { useIsClientTransition } 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 @@ -23,12 +24,17 @@ export interface LinkCardProps {
}

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

return <LinkCardImpl {...props} />
if (!isClient) return null

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

Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/markdown/renderers/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { FC } from 'react'
export const MTableHead: FC<JSX.IntrinsicElements['thead']> = (props) => {
const { children, className, ...rest } = props
return (
<thead className={clsx(className, 'bg-accent/20')} {...rest}>
<thead className={clsx(className, 'bg-base-content/20')} {...rest}>
{children}
</thead>
)
Expand Down
Loading

1 comment on commit 98837de

@vercel
Copy link

@vercel vercel bot commented on 98837de 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.vercel.app
springtide-innei.vercel.app
innei.in
springtide-git-main-innei.vercel.app

Please sign in to comment.