From cac8e531f49b720b7e2b99cb760e89f547caa421 Mon Sep 17 00:00:00 2001 From: Devv Date: Wed, 8 Apr 2026 23:09:57 -0700 Subject: [PATCH] fix(issues): add infinite scroll pagination for done issues in list view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit List view was missing pagination for done issues — only showing 50 items with no way to load more, while board view had full infinite scroll support. Reuse useLoadMoreDoneIssues hook and add InfiniteScrollSentinel to the done group. Display doneTotal instead of loaded count in the status header. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../features/issues/components/list-view.tsx | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/apps/web/features/issues/components/list-view.tsx b/apps/web/features/issues/components/list-view.tsx index bc0ef4368..dc40f17ac 100644 --- a/apps/web/features/issues/components/list-view.tsx +++ b/apps/web/features/issues/components/list-view.tsx @@ -1,7 +1,7 @@ "use client"; -import { useMemo } from "react"; -import { ChevronRight, Plus } from "lucide-react"; +import { useMemo, useEffect, useRef } from "react"; +import { ChevronRight, Loader2, Plus } from "lucide-react"; import { Accordion } from "@base-ui/react/accordion"; import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; import { Button } from "@/components/ui/button"; @@ -10,10 +10,35 @@ import { STATUS_CONFIG } from "@/features/issues/config"; import { useModalStore } from "@/features/modals"; import { useViewStore } from "@/features/issues/stores/view-store-context"; import { useIssueSelectionStore } from "@/features/issues/stores/selection-store"; +import { useLoadMoreDoneIssues } from "@core/issues/mutations"; import { sortIssues } from "@/features/issues/utils/sort"; import { StatusIcon } from "./status-icon"; import { ListRow } from "./list-row"; +/** Sentinel that triggers `onVisible` when scrolled into view. */ +function InfiniteScrollSentinel({ onVisible, loading }: { onVisible: () => void; loading: boolean }) { + const sentinelRef = useRef(null); + const onVisibleRef = useRef(onVisible); + onVisibleRef.current = onVisible; + + useEffect(() => { + const node = sentinelRef.current; + if (!node) return; + const observer = new IntersectionObserver( + ([entry]) => { if (entry?.isIntersecting) onVisibleRef.current(); }, + { rootMargin: "100px" }, + ); + observer.observe(node); + return () => observer.disconnect(); + }, []); + + return ( +
+ {loading && } +
+ ); +} + export function ListView({ issues, visibleStatuses, @@ -32,6 +57,7 @@ export function ListView({ const selectedIds = useIssueSelectionStore((s) => s.selectedIds); const select = useIssueSelectionStore((s) => s.select); const deselect = useIssueSelectionStore((s) => s.deselect); + const { loadMore, hasMore, isLoading: loadingMore, doneTotal } = useLoadMoreDoneIssues(); const issuesByStatus = useMemo(() => { const map = new Map(); @@ -101,7 +127,7 @@ export function ListView({ {cfg.label} - {statusIssues.length} + {status === "done" ? doneTotal : statusIssues.length}
@@ -128,9 +154,14 @@ export function ListView({ {statusIssues.length > 0 ? ( - statusIssues.map((issue) => ( - - )) + <> + {statusIssues.map((issue) => ( + + ))} + {status === "done" && hasMore && ( + + )} + ) : (

No issues