feat: infinite query for manager archives tags pages
This commit is contained in:
parent
77de726e5d
commit
ae8e4d330d
|
|
@ -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>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue