diff --git a/apps/web/package.json b/apps/web/package.json index 9bd16e2..7f55925 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -69,7 +69,6 @@ "react-dom": "~19.1.1", "react-hook-form": "~7.57.0", "react-virtuoso": "~4.13.0", - "react-window": "^2.1.1", "remark-gfm": "~4.0.1", "remove-markdown": "~0.6.2", "sonner": "~2.0.7", diff --git a/apps/web/src/ai/tools/create-page.client.ts b/apps/web/src/ai/tools/create-page.client.ts index 5e497f3..aeb2c63 100644 --- a/apps/web/src/ai/tools/create-page.client.ts +++ b/apps/web/src/ai/tools/create-page.client.ts @@ -1,8 +1,8 @@ "use client"; -import type { Page } from "@acme/db/schema"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; +import { infinitePagesQueryOptions } from "~/app/api/trpc/options/pages-query-options"; import { useTRPC } from "~/trpc/react"; import { useAppEventEmitter } from "../../components/events/app-event-context"; import { PageCreatedEvent } from "../../events/page-created-event"; @@ -35,11 +35,34 @@ export function useCreatePageTool() { }); }, onSuccess: (newPage) => { + // Optimistically update the pages list queryClient.setQueryData( - trpc.pages.getByUser.queryOptions().queryKey, - (oldPages: Page[] | undefined) => { - if (!oldPages) return [newPage]; - return [newPage, ...oldPages]; + trpc.pages.getPaginated.infiniteQueryOptions( + infinitePagesQueryOptions, + ).queryKey, + (old) => { + if (!old) + return { + pageParams: [], + pages: [ + { + items: [newPage], + nextCursor: undefined, + }, + ], + }; + const [first, ...rest] = old.pages; + return { + ...old, + pages: [ + { + ...first, + items: [newPage, ...(first?.items ?? [])], + nextCursor: first?.nextCursor, + }, + ...rest, + ], + }; }, ); diff --git a/apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-page-item-skeleton.tsx b/apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-page-item-skeleton.tsx new file mode 100644 index 0000000..b6fc870 --- /dev/null +++ b/apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-page-item-skeleton.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { + SidebarMenuSubButton, + SidebarMenuSubItem, +} from "~/components/ui/sidebar"; +import { Skeleton } from "~/components/ui/skeleton"; + +type AppSidebarPageItemSkeletonProps = { + className?: string; +}; + +export function AppSidebarPageItemSkeleton({ + className, +}: AppSidebarPageItemSkeletonProps) { + return ( + + +
+ +
+
+
+ ); +} diff --git a/apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-page-item.tsx b/apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-page-item.tsx index 7feae4e..faa1cf8 100644 --- a/apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-page-item.tsx +++ b/apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-page-item.tsx @@ -12,17 +12,19 @@ import { DeletePageButton } from "./delete-page-button"; type AppSidebarPageItemProps = { page: Page; + className?: string; }; -export function AppSidebarPageItem(props: AppSidebarPageItemProps) { - const { page } = props; - +export function AppSidebarPageItem({ + page, + className, +}: AppSidebarPageItemProps) { const pathname = usePathname(); const isActive = pathname.includes(page?.id ?? ""); return ( - +
{ const trpc = useTRPC(); const { state, setOpen } = useSidebar(); - const queryOptions = trpc.pages.getInfinite.infiniteQueryOptions( + const queryOptions = trpc.pages.getPaginated.infiniteQueryOptions( infinitePagesQueryOptions, ); - const { data, fetchNextPage, isFetchingNextPage, hasNextPage } = - useInfiniteQuery({ - ...queryOptions, - getNextPageParam: ({ nextCursor }) => { - return nextCursor; - }, - }); + const { status, data, fetchNextPage, hasNextPage } = useInfiniteQuery({ + ...queryOptions, + getNextPageParam: ({ nextCursor }) => { + return nextCursor; + }, + }); const pages = data?.pages?.flatMap((page) => page.items) ?? []; @@ -72,7 +72,7 @@ export const AppSidebarPages = ({ tooltip="Pages" onClick={handlePagesClick} > - {isFetchingNextPage ? ( + {status === "pending" ? ( ) : ( @@ -82,47 +82,35 @@ export const AppSidebarPages = ({ - - - - { - // Fetch next page when user scrolls near the end - if ( - stopIndex >= pages.length - 5 && - !isFetchingNextPage && - hasNextPage - ) { + + + + ( + + )} + endReached={() => { + if (status === "success" && hasNextPage) { fetchNextPage(); } }} + components={{ + Footer: () => + status === "pending" ? ( + + ) : null, + }} /> ); }; - -const PageRow = ({ - index, - style, - pages, -}: { - index: number; - style: React.CSSProperties; -} & { pages: Page[] }) => { - const page = pages?.[index]; - if (!page) return null; - return ( -
- -
- ); -}; diff --git a/apps/web/src/app/(app)/@appSidebar/_components/create-page-button.tsx b/apps/web/src/app/(app)/@appSidebar/_components/create-page-button.tsx index cfcc098..74c607a 100644 --- a/apps/web/src/app/(app)/@appSidebar/_components/create-page-button.tsx +++ b/apps/web/src/app/(app)/@appSidebar/_components/create-page-button.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Page } from "@acme/db/schema"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { Plus } from "lucide-react"; import { useRouter } from "next/navigation"; @@ -11,8 +10,13 @@ import { SidebarMenuSubItem, } from "~/components/ui/sidebar"; import { useTRPC } from "~/trpc/react"; +import { infinitePagesQueryOptions } from "../../../api/trpc/options/pages-query-options"; -export function CreatePageButton() { +type CreatePageButtonProps = { + className?: string; +}; + +export function CreatePageButton({ className }: CreatePageButtonProps) { const router = useRouter(); const trpc = useTRPC(); const queryClient = useQueryClient(); @@ -35,10 +39,32 @@ export function CreatePageButton() { onSuccess: (newPage) => { // Optimistically update the pages list queryClient.setQueryData( - trpc.pages.getByUser.queryOptions().queryKey, - (oldPages: Page[] | undefined) => { - if (!oldPages) return [newPage]; - return [newPage, ...oldPages]; + trpc.pages.getPaginated.infiniteQueryOptions( + infinitePagesQueryOptions, + ).queryKey, + (old) => { + if (!old) + return { + pageParams: [], + pages: [ + { + items: [newPage], + nextCursor: undefined, + }, + ], + }; + const [first, ...rest] = old.pages; + return { + ...old, + pages: [ + { + ...first, + items: [newPage, ...(first?.items ?? [])], + nextCursor: first?.nextCursor, + }, + ...rest, + ], + }; }, ); @@ -53,7 +79,7 @@ export function CreatePageButton() { const showLoading = isPending || isCreating; return ( - +