Skip to content

Commit

Permalink
perf(app): migrate client fetching to RSC to improve performance
Browse files Browse the repository at this point in the history
  • Loading branch information
AnnatarHe committed Jan 6, 2024
1 parent 3ad5e82 commit be37d06
Show file tree
Hide file tree
Showing 23 changed files with 235 additions and 140 deletions.
32 changes: 17 additions & 15 deletions src/app/dash/[userid]/book/[bookid]/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import { IN_APP_CHANNEL } from '../../../../../services/channel';
import OGWithBook from '../../../../../components/og/og-with-book';
import { useMasonaryColumnCount } from '../../../../../hooks/use-screen-size';
import { Masonry, useInfiniteLoader } from 'masonic';
import { Clipping, useBookQuery, useQueryMyIdByDomainQuery } from '../../../../../schema/generated';
import { BookDocument, BookQuery, Clipping, QueryMyIdByDomainDocument, QueryMyIdByDomainQuery, useBookQuery, useQueryMyIdByDomainQuery } from '../../../../../schema/generated';
import { useSingleBook } from '../../../../../hooks/book';
import BookPageSkeleton from './skeleton';
import { useSuspenseQuery } from '@apollo/experimental-nextjs-app-support/ssr';
import { skipToken } from '@apollo/client';

type BookPageContentProps = {
userid: string
Expand All @@ -30,7 +32,7 @@ function BookPageContent(props: BookPageContentProps) {
const bookData = useSingleBook(bookid)

const hasMore = useRef(true)
const { data: clippingsData, fetchMore, loading } = useBookQuery({
const { data: clippingsData, fetchMore } = useSuspenseQuery<BookQuery>(BookDocument, {
variables: {
id: ~~bookid,
pagination: {
Expand All @@ -39,7 +41,6 @@ function BookPageContent(props: BookPageContentProps) {
}
},
})

useEffect(() => {
if (!bookData) {
return
Expand All @@ -50,21 +51,22 @@ function BookPageContent(props: BookPageContentProps) {
const { t } = useTranslation()

const duration = useMemo(() => {
if (!clippingsData?.book.startReadingAt || !clippingsData.book.lastReadingAt) {
if (!clippingsData.book.startReadingAt || !clippingsData.book.lastReadingAt) {
return undefined
}
const result = dayjs(clippingsData?.book.lastReadingAt)
.diff(dayjs(clippingsData?.book.startReadingAt), 'd', false)
const result = dayjs(clippingsData.book.lastReadingAt)
.diff(dayjs(clippingsData.book.startReadingAt), 'd', false)
return result || undefined
}, [clippingsData?.book.startReadingAt, clippingsData?.book.lastReadingAt])
}, [clippingsData.book.startReadingAt, clippingsData.book.lastReadingAt])

const mappedMyData = useQueryMyIdByDomainQuery({
variables: {
domain
},
const mappedMyData = useSuspenseQuery<QueryMyIdByDomainQuery>(
QueryMyIdByDomainDocument,
// 是 NaN 就是 domain 啦~
skip: !Number.isNaN(parseInt(domain))
})
Number.isNaN(parseInt(domain)) ? {
variables: {
domain
},
} : skipToken)

const masonaryColumnCount = useMasonaryColumnCount()
const maybeLoadMore = useInfiniteLoader((startIndex, stopIndex, currentItems) => {
Expand All @@ -83,7 +85,7 @@ function BookPageContent(props: BookPageContentProps) {
}, {
isItemLoaded: (index, items) => !!items[index],
threshold: 3,
totalItems: clippingsData?.book.clippingsCount
totalItems: clippingsData.book.clippingsCount
})

if (!bookData || !clippingsData) {
Expand All @@ -96,7 +98,7 @@ function BookPageContent(props: BookPageContentProps) {
book={bookData}
uid={mappedMyData.data?.me.id ?? (~~domain)}
duration={duration}
isLastReadingBook={clippingsData?.book.isLastReadingBook}
isLastReadingBook={clippingsData.book.isLastReadingBook}
/>
<Divider title={t('app.book.title')} />
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ArrowDownIcon, ArrowUpIcon, SparklesIcon } from '@heroicons/react/24/so
import { Clipping, User, useToggleClippingVisibleMutation } from '@/schema/generated'
import { Button, NavLink, Tooltip } from '@mantine/core'
import ClippingAISummaryModal from '@/components/clipping-item/aiSummary'
import { toastPromiseDefaultOption } from '../../../../../services/misc'
import { toastPromiseDefaultOption } from '@/services/misc'

type ClippingSidebarProps = {
clipping?: Pick<Clipping, 'id' | 'visible' | 'content' | 'title' | 'createdAt' | 'nextClipping' | 'prevClipping'> & { creator: Pick<User, 'id' | 'name' | 'domain'> }
Expand Down
9 changes: 5 additions & 4 deletions src/app/dash/[userid]/clippings/[clippingid]/comment.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Avatar from '../../../../../components/avatar/avatar'
import { Comment as CommentData, User } from '../../../../../schema/generated'
import Avatar from '@/components/avatar/avatar'
import { Comment as CommentData, User } from '@/schema/generated'
import { useAutoAnimate } from '@formkit/auto-animate/react'
import { Button } from '@mantine/core'
import { ArrowDownIcon } from '@heroicons/react/24/solid'
import { ArrowUpIcon } from '@heroicons/react/24/outline'
import MarkdownPreview from '../../../../../components/markdown-editor/md-preview'
import MarkdownPreview from '@/components/markdown-editor/md-preview'

type CommentProps = {
comment: Pick<CommentData, 'id' | 'content'> & { creator: Pick<User, 'name' | 'avatar'> }
Expand All @@ -32,7 +32,8 @@ function Comment(props: CommentProps) {
<div className='flex container mb-6 flex-col lg:flex-row bg-gray-100 bg-opacity-25 lg:p-8 p-6 rounded'>
<div className='flex flex-row lg:flex-col lg:mt-4 items-center'>
<Avatar
img={creator.avatar} name={creator.name}
img={creator.avatar}
name={creator.name}
className='w-12 h-12 lg:h-24 lg:w-24'
/>
<h5 className='w-24 lg:mt-4 text-center'>{creator.name}</h5>
Expand Down
19 changes: 8 additions & 11 deletions src/app/dash/[userid]/clippings/[clippingid]/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,24 @@ import { CDN_DEFAULT_DOMAIN } from '@/constants/config'
import { useAutoAnimate } from '@formkit/auto-animate/react'
import { useSetAtom } from 'jotai'
import { appBackgroundAtom } from '@/store/global'
import { FetchClippingQuery, useFetchClippingQuery } from '@/schema/generated'
import { FetchClippingDocument, FetchClippingQuery, useFetchClippingQuery, useFetchClippingSuspenseQuery } from '@/schema/generated'
import styles from './clipping.module.css'
import { toast } from 'react-hot-toast';
import { useSuspenseQuery } from '@apollo/experimental-nextjs-app-support/ssr';

type ClippingPageProps = {
cid: number
clipping: FetchClippingQuery
iac: string
}

function ClippingPageContent(props: ClippingPageProps) {
const { cid, clipping: clippingServerData, iac } = props

const { data: clippingClientData } = useFetchClippingQuery({
const { cid, iac } = props
const { data: clipping } = useSuspenseQuery<FetchClippingQuery>(FetchClippingDocument, {
variables: {
id: cid
id: ~~cid,
},
})

const clipping = clippingClientData || clippingServerData

const me = useSelector<TGlobalStore, UserContent>(s => s.user.profile)
const [sharePreviewVisible, setSharePreviewVisible] = useState(false)

Expand Down Expand Up @@ -120,9 +117,9 @@ function ClippingPageContent(props: ClippingPageProps) {
<>
<h3 className='text-2xl lg:text-4xl font-light lg:mb-4'>{t('app.clipping.comments.title')}</h3>
<ul ref={commentListRef}>
{clipping?.clipping.comments.map(m => (
<Comment key={m.id} comment={m} />
))}
{clipping?.clipping.comments.map(m => (
<Comment key={m.id} comment={m} />
))}
</ul>
{clipping && me && (
<CommentBox me={me} book={book} clipping={clipping?.clipping} />
Expand Down
7 changes: 3 additions & 4 deletions src/app/dash/[userid]/clippings/[clippingid]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import React from 'react'
import { duration3Days } from '@/hooks/book'
import { FetchClippingQuery, FetchClippingQueryVariables, FetchClippingDocument } from '@/schema/generated'
import { FetchClippingQuery, FetchClippingQueryVariables, FetchClippingDocument, useFetchClippingSuspenseQuery } from '@/schema/generated'
import { getReactQueryClient } from '@/services/ajax'
import { WenquBook, wenquRequest, WenquSearchResponse } from '@/services/wenqu'
import { HydrationBoundary, dehydrate } from '@tanstack/react-query'
import ClippingPageContent from './content'
import { generateMetadata as clippingGenerateMetadata } from '@/components/og/og-with-clipping'
import { Metadata } from 'next'
import { getApolloServerClient } from '@/services/apollo.server'
import { cookies } from 'next/headers'

type PageProps = {
params: { clippingid: string, userid: string }
searchParams: { iac: string }
}
export async function generateMetadata(props: PageProps): Promise<Metadata> {
const { clippingid, userid } = props.params
const { clippingid } = props.params
const cid = ~~clippingid

const client = getApolloServerClient()
Expand Down Expand Up @@ -45,7 +46,6 @@ export async function generateMetadata(props: PageProps): Promise<Metadata> {
})
}


async function Page(props: PageProps) {
const { clippingid, userid } = props.params
const cid = ~~clippingid
Expand Down Expand Up @@ -74,7 +74,6 @@ async function Page(props: PageProps) {
<HydrationBoundary state={d}>
<ClippingPageContent
cid={cid}
clipping={clippingsResponse.data}
iac={props.searchParams.iac}
/>
</HydrationBoundary>
Expand Down
6 changes: 3 additions & 3 deletions src/app/dash/[userid]/clippings/[clippingid]/reactions.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useCallback, useMemo, useState } from 'react'
import { useSelector } from 'react-redux'
import { TGlobalStore } from '../../../../../store'
import { FetchClippingQuery } from '../../../../../schema/generated'
import ReactionCell, { SymbolGroupedData } from '../../../../../components/reaction/reaction-cell'
import { TGlobalStore } from '@/store'
import { FetchClippingQuery } from '@/schema/generated'
import ReactionCell, { SymbolGroupedData } from '@/components/reaction/reaction-cell'

const availableReactions = ["👍", "❤️", "⭐️", "🐶", "😱"]

Expand Down
57 changes: 36 additions & 21 deletions src/app/dash/[userid]/home/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { UserContent } from '@/store/user/type';
import { useSyncClippingsToServer } from '@/hooks/my-file'
import { useRouter } from 'next/navigation';
import HomePageSkeleton, { BooksSkeleton } from './skeleton';
import { useBooksQuery } from '@/schema/generated';
import { BooksDocument, BooksQuery, BooksQueryVariables, ProfileDocument, ProfileQuery, ProfileQueryVariables } from '@/schema/generated';
import { useSuspenseQuery } from '@apollo/experimental-nextjs-app-support/ssr';

const STEP = 10

function useUserNewbie(userProfile: UserContent, onNewbie: () => void) {
function useUserNewbie(userProfile: UserContent | null, onNewbie: () => void) {
useEffect(() => {
if (!userProfile || userProfile.id === 0) {
return
Expand All @@ -39,42 +40,56 @@ function useUserNewbie(userProfile: UserContent, onNewbie: () => void) {

type HomePageContentProps = {
userid: string
myUid?: number
}

function HomePageContent(props: HomePageContentProps) {
const { userid: userDomain } = props
const userProfile = useSelector<TGlobalStore, UserContent>(s => s.user.profile)
const uid = userProfile.id
const { userid: userDomain, myUid } = props
// const userProfile = useSelector<TGlobalStore, UserContent>(s => s.user.profile)
// const uid = userProfile.id

const isTypeUid = !Number.isNaN(parseInt(userDomain))
const { data: targetProfileData } = useSuspenseQuery<ProfileQuery, ProfileQueryVariables>(ProfileDocument, {
variables: {
id: isTypeUid ? ~~userDomain : undefined,
domain: isTypeUid ? undefined : userDomain
}
})

const { data: myProfile } = useSuspenseQuery<ProfileQuery, ProfileQueryVariables>(ProfileDocument, {
variables: {
id: myUid
}
})

const userProfile = targetProfileData.me
const uid = myProfile.me.id

const { push: navigate } = useRouter()

useUserNewbie(userProfile, () => {
// only works for me(logged user is same with the accessing user)
useUserNewbie(userProfile.id === uid ? userProfile : null, () => {
// is new bie
navigate(`/dash/${uid}/profile?with_profile_editor=1`)
})
useSyncClippingsToServer()
useSyncClippingsToServer(uid)

const [reachEnd, setReachEnd] = useState(false)
const { data, fetchMore, loading, called } = useBooksQuery({
const { data, fetchMore } = useSuspenseQuery<BooksQuery, BooksQueryVariables>(BooksDocument, {
variables: {
id: uid,
id: targetProfileData.me.id,
pagination: {
limit: STEP,
offset: 0
},
},
skip: !uid,
})

const bls = data?.books.map(x => x.doubanId) ?? []
const bls = data.books.map(x => x.doubanId) ?? []

const { t } = useTranslation()
const books = useMultipBook(bls)

if (!data) {
return (<HomePageSkeleton />)
}

const recents = data.me.recents

return (
Expand All @@ -98,13 +113,13 @@ function HomePageContent(props: HomePageContentProps) {
</header>

<div className='flex flex-wrap items-center justify-center'>
{loading && data.books.length === 0 && !called && (
{/* {loading && data.books.length === 0 && !called && (
<HomePageSkeleton />
)}
{data.books.length === 0 && called && (
)} */}
{data.books.length === 0 && (
<NoContentAlert domain={userDomain} />
)}
{(books.books.length > 0 && called) &&
{(books.books.length > 0) &&
books.books.map((item, index) => (
<BookCover
book={item}
Expand All @@ -121,7 +136,7 @@ function HomePageContent(props: HomePageContentProps) {
</div>
}
loadMoreFn={() => {
if (loading || !called || books.loading) {
if (books.loading) {
return
}
fetchMore({
Expand All @@ -133,7 +148,7 @@ function HomePageContent(props: HomePageContentProps) {
}
},
}).then(res => {
if (res.data.books.length === 0) {
if ((res.data?.books.length ?? 0) === 0) {
setReachEnd(true)
return
}
Expand Down
6 changes: 5 additions & 1 deletion src/app/dash/[userid]/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { generateMetadata as profileGenerateMetadata } from '../../../../compone
import { Metadata } from 'next'
import { ProfileQuery, ProfileQueryVariables, ProfileDocument } from '../../../../schema/generated'
import { getApolloServerClient } from '../../../../services/apollo.server'
import { cookies } from 'next/headers'

type PageProps = {
params: { userid: string }
Expand All @@ -28,8 +29,11 @@ export async function generateMetadata(props: PageProps): Promise<Metadata> {

async function Page(props: PageProps) {
const { userid } = props.params
const cs = cookies()
const myUid = cs.get('uid')?.value

return (
<HomePageContent userid={userid} />
<HomePageContent userid={userid} myUid={myUid ? parseInt(myUid) : undefined} />
)
}

Expand Down
5 changes: 4 additions & 1 deletion src/app/dash/[userid]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import DashboardContainer from '../../../components/dashboard-container/container'
import { Metadata } from 'next'
import { cookies } from 'next/headers'

type LayoutProps = {
children: React.ReactNode
Expand All @@ -14,8 +15,10 @@ export const metadata: Metadata = {
}

const Layout = (props: LayoutProps) => {
const cs = cookies()
const myUid = cs.get('uid')?.value
return (
<DashboardContainer>
<DashboardContainer uidOrDomain={myUid}>
{props.children}
</DashboardContainer>
)
Expand Down
Loading

0 comments on commit be37d06

Please sign in to comment.