feat: infinite query for manager archives tags pages

This commit is contained in:
DIYgod 2022-11-16 16:34:54 +00:00
parent 77de726e5d
commit ae8e4d330d
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
6 changed files with 182 additions and 107 deletions

View File

@ -68,7 +68,7 @@ export const PagesManager: React.FC<{
const pages = useGetPagesBySite({
type: isPost ? "post" : "page",
site: subdomain!,
take: 1000,
take: 100,
visibility,
render: true,
})
@ -320,6 +320,8 @@ export const PagesManager: React.FC<{
</>
)
let currentLength = 0
return (
<DashboardMain>
<header className="mb-8">
@ -371,82 +373,101 @@ export const PagesManager: React.FC<{
<Tabs items={tabItems} />
<div className="-mt-3">
{pages.data?.list.length === 0 && (
{!pages.data?.pages?.[0].total && (
<EmptyState resource={isPost ? "posts" : "pages"} />
)}
{pages.data?.list?.map((page) => {
return (
<Link
key={page.id}
href={getPageEditLink(page)}
className="group relative hover:bg-zinc-100 rounded-lg py-3 px-3 transition-colors -mx-3 flex justify-between"
>
<div className="min-w-0">
{page.title ? (
<div className="flex items-center">
<span>{page.title}</span>
</div>
) : (
<div className="text-zinc-500 text-xs mt-1 truncate">
<span>{page.summary?.content}</span>
</div>
)}
<div className="text-zinc-400 text-xs mt-1">
<span className="capitalize">
{getPageVisibility(page).toLowerCase()}
</span>
<span className="mx-2">·</span>
<span>
{getPageVisibility(page) === PageVisibilityEnum.Draft
? formatDate(page.date_updated)
: formatDate(page.date_published)}
</span>
</div>
</div>
<div className="w-10 flex-shrink-0">
<Menu>
{({ open }: { open: boolean }) => (
<>
<Menu.Button as={Fragment}>
<button
className={clsx(
`text-gray-400 relative z-50 w-8 h-8 rounded inline-flex invisible group-hover:visible justify-center items-center`,
open ? `bg-gray-200` : `hover:bg-gray-200`,
)}
onClick={(e) => {
e.stopPropagation()
}}
>
<EllipsisHorizontalIcon className="w-5 h-5" />
</button>
</Menu.Button>
<Menu.Items className="text-sm absolute z-20 right-0 bg-white shadow-modal rounded-lg overflow-hidden py-2 w-64">
{getPageMenuItems(page).map((item) => {
return (
<Menu.Item key={item.text}>
<button
type="button"
className="h-10 flex w-full space-x-2 items-center px-3 hover:bg-gray-100"
onClick={(e) => {
e.preventDefault()
item.onClick()
}}
>
<span className="inline-flex">{item.icon}</span>
<span>{item.text}</span>
</button>
</Menu.Item>
)
})}
</Menu.Items>
</>
{pages.data?.pages.map((page) =>
page.list?.map((page) => {
currentLength++
return (
<Link
key={page.id}
href={getPageEditLink(page)}
className="group relative hover:bg-zinc-100 rounded-lg py-3 px-3 transition-colors -mx-3 flex justify-between"
>
<div className="min-w-0">
{page.title ? (
<div className="flex items-center">
<span>{page.title}</span>
</div>
) : (
<div className="text-zinc-500 text-xs mt-1 truncate">
<span>{page.summary?.content}</span>
</div>
)}
</Menu>
</div>
</Link>
)
})}
<div className="text-zinc-400 text-xs mt-1">
<span className="capitalize">
{getPageVisibility(page).toLowerCase()}
</span>
<span className="mx-2">·</span>
<span>
{getPageVisibility(page) === PageVisibilityEnum.Draft
? formatDate(page.date_updated)
: formatDate(page.date_published)}
</span>
</div>
</div>
<div className="w-10 flex-shrink-0">
<Menu>
{({ open }: { open: boolean }) => (
<>
<Menu.Button as={Fragment}>
<button
className={clsx(
`text-gray-400 relative z-50 w-8 h-8 rounded inline-flex invisible group-hover:visible justify-center items-center`,
open ? `bg-gray-200` : `hover:bg-gray-200`,
)}
onClick={(e) => {
e.stopPropagation()
}}
>
<EllipsisHorizontalIcon className="w-5 h-5" />
</button>
</Menu.Button>
<Menu.Items className="text-sm absolute z-20 right-0 bg-white shadow-modal rounded-lg overflow-hidden py-2 w-64">
{getPageMenuItems(page).map((item) => {
return (
<Menu.Item key={item.text}>
<button
type="button"
className="h-10 flex w-full space-x-2 items-center px-3 hover:bg-gray-100"
onClick={(e) => {
e.preventDefault()
item.onClick()
}}
>
<span className="inline-flex">
{item.icon}
</span>
<span>{item.text}</span>
</button>
</Menu.Item>
)
})}
</Menu.Items>
</>
)}
</Menu>
</div>
</Link>
)
}),
)}
{pages.hasNextPage && (
<Button
className="w-full hover:bg-zinc-100 bg-zinc-50 rounded-lg transition-colors mt-4"
variant="text"
onClick={pages.fetchNextPage as () => void}
isLoading={pages.isFetchingNextPage}
>
There are {(pages.data?.pages?.[0].total || 0) - currentLength} more{" "}
{isPost ? "post" : "page"}
{(pages.data?.pages?.[0].total || 0) - currentLength > 1 ? "s" : ""}
, click to load more
</Button>
)}
</div>
</DashboardMain>
)

View File

@ -4,58 +4,75 @@ import { formatDate } from "~/lib/date"
import { EmptyState } from "../ui/EmptyState"
import { Notes, Note } from "~/lib/types"
import { UniLink } from "../ui/UniLink"
import { Button } from "~/components/ui/Button"
export const SiteArchives: React.FC<{
posts?: Notes
title?: string
showTags?: boolean
}> = ({ posts, title, showTags }) => {
postPages?: Notes[]
fetchNextPage: () => void
hasNextPage?: boolean
isFetchingNextPage?: boolean
}> = ({
title,
showTags,
postPages,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
}) => {
let currentLength = 0
const groupedByYear = useMemo<Map<string, Note[]>>(() => {
const map = new Map()
if (posts) {
for (const post of posts.list) {
const year = formatDate(post.date_published, "YYYY")
const items = map.get(year) || []
items.push(post)
map.set(year, items)
if (postPages?.length) {
for (const posts of postPages) {
for (const post of posts.list) {
const year = formatDate(post.date_published, "YYYY")
const items = map.get(year) || []
items.push(post)
map.set(year, items)
}
}
}
return map
}, [posts])
}, [postPages])
const tags = useMemo<Map<string, number>>(() => {
const result = new Map()
if (posts) {
for (const post of posts.list) {
post.tags?.forEach((tag) => {
if (tag !== "post" && tag !== "page") {
if (result.has(tag)) {
result.set(tag, result.get(tag) + 1)
} else {
result.set(tag, 1)
if (postPages?.length) {
for (const posts of postPages) {
for (const post of posts.list) {
post.tags?.forEach((tag) => {
if (tag !== "post" && tag !== "page") {
if (result.has(tag)) {
result.set(tag, result.get(tag) + 1)
} else {
result.set(tag, 1)
}
}
}
})
})
}
}
}
return result
}, [posts])
}, [postPages])
if (!posts) return null
if (!postPages?.length) return null
return (
<>
<h2 className="text-xl font-bold page-title">{title || "Archives"}</h2>
{posts.list.length === 0 && (
{!postPages[0].total && (
<div className="mt-5">
<EmptyState />
</div>
)}
{posts.list.length > 0 && (
{postPages[0].total && (
<>
{showTags && tags.size > 0 && (
<div className="mt-5">
@ -74,6 +91,7 @@ export const SiteArchives: React.FC<{
)}
<div className="mt-5 space-y-5">
{[...groupedByYear.keys()].map((year) => {
currentLength++
const posts = groupedByYear.get(year)!
return (
<div key={year}>
@ -97,6 +115,18 @@ export const SiteArchives: React.FC<{
</div>
)
})}
{hasNextPage && (
<Button
className="mt-8 w-full hover:bg-zinc-100 bg-zinc-50 transition-colors text-sm"
variant="text"
onClick={fetchNextPage}
isLoading={isFetchingNextPage}
>
There are {postPages[0].total - currentLength} more post
{postPages[0].total - currentLength > 1 ? "s" : ""}, click to
load more
</Button>
)}
</div>
</>
)}

View File

@ -90,9 +90,9 @@ export const SiteHome: React.FC<{
onClick={fetchNextPage}
isLoading={isFetchingNextPage}
>
There are {postPages[0].total - currentLength} more{" "}
{postPages[0].total - currentLength > 1 ? "posts" : "post"}, click to
load more
There are {postPages[0].total - currentLength} more post
{postPages[0].total - currentLength > 1 ? "s" : ""}, click to load
more
</Button>
)}
</>

View File

@ -12,6 +12,9 @@ import { notFound } from "~/lib/server-side-props"
export const getServerSideProps = async (
ctx: any,
queryClient: QueryClient,
options?: {
take?: number
},
) => {
const domainOrSubdomain = ctx.params!.site as string
const pageSlug = ctx.params!.page as string
@ -53,6 +56,7 @@ export const getServerSideProps = async (
await prefetchGetPagesBySite(
{
site: domainOrSubdomain,
...(options?.take && { take: options.take }),
type: "post",
visibility: PageVisibilityEnum.Published,
render: true,

View File

@ -13,11 +13,13 @@ export const getServerSideProps: GetServerSideProps = serverSidePropsHandler(
async (ctx) => {
const queryClient = new QueryClient()
const domainOrSubdomain = ctx.params!.site as string
await getLayoutServerSideProps(ctx, queryClient)
await getLayoutServerSideProps(ctx, queryClient, {
take: 100,
})
return {
props: {
dehydratedState: dehydrate(queryClient),
dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))),
domainOrSubdomain,
},
}
@ -33,13 +35,21 @@ function SiteArchivesPage({
}) {
const posts = useGetPagesBySite({
site: domainOrSubdomain,
take: 1000,
take: 100,
type: "post",
visibility: PageVisibilityEnum.Published,
render: true,
})
return <SiteArchives posts={posts.data} showTags={true} />
return (
<SiteArchives
postPages={posts.data?.pages}
fetchNextPage={posts.fetchNextPage}
hasNextPage={posts.hasNextPage}
isFetchingNextPage={posts.isFetchingNextPage}
showTags={true}
/>
)
}
SiteArchivesPage.getLayout = (page: ReactElement) => {

View File

@ -15,11 +15,13 @@ export const getServerSideProps: GetServerSideProps = serverSidePropsHandler(
const domainOrSubdomain = ctx.params!.site as string
const tag = ctx.params!.tag as string
await getLayoutServerSideProps(ctx, queryClient)
await getLayoutServerSideProps(ctx, queryClient, {
take: 100,
})
return {
props: {
dehydratedState: dehydrate(queryClient),
dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))),
domainOrSubdomain,
tag,
},
@ -38,14 +40,22 @@ function SiteTagPage({
}) {
const posts = useGetPagesBySite({
site: domainOrSubdomain,
take: 1000,
take: 100,
type: "post",
visibility: PageVisibilityEnum.Published,
render: true,
tags: [tag],
})
return <SiteArchives posts={posts.data} title={tag} />
return (
<SiteArchives
postPages={posts.data?.pages}
fetchNextPage={posts.fetchNextPage}
hasNextPage={posts.hasNextPage}
isFetchingNextPage={posts.isFetchingNextPage}
title={tag}
/>
)
}
SiteTagPage.getLayout = (page: ReactElement) => {