feat: lazy infinite scroller for session history view (#4922)

This commit is contained in:
Jack Amadeo
2025-10-01 14:14:44 -04:00
committed by GitHub
parent b027dd180e
commit 5fa1efbddb
2 changed files with 58 additions and 5 deletions
@@ -185,6 +185,8 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
currentIndex: number;
} | null>(null);
const [visibleGroupsCount, setVisibleGroupsCount] = useState(15);
// Edit modal state
const [showEditModal, setShowEditModal] = useState(false);
const [editingSession, setEditingSession] = useState<Session | null>(null);
@@ -210,6 +212,33 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
}
};
const visibleDateGroups = useMemo(() => {
return dateGroups.slice(0, visibleGroupsCount);
}, [dateGroups, visibleGroupsCount]);
const handleScroll = useCallback(
(target: HTMLDivElement) => {
const { scrollTop, scrollHeight, clientHeight } = target;
const threshold = 200;
if (
scrollHeight - scrollTop - clientHeight < threshold &&
visibleGroupsCount < dateGroups.length
) {
setVisibleGroupsCount((prev) => Math.min(prev + 5, dateGroups.length));
}
},
[visibleGroupsCount, dateGroups.length]
);
useEffect(() => {
if (debouncedSearchTerm) {
setVisibleGroupsCount(dateGroups.length);
} else {
setVisibleGroupsCount(15);
}
}, [debouncedSearchTerm, dateGroups.length]);
const loadSessions = useCallback(async () => {
setIsLoading(true);
setShowSkeleton(true);
@@ -557,10 +586,9 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
);
}
// For regular rendering in grid layout
return (
<div className="space-y-8">
{dateGroups.map((group) => (
{visibleDateGroups.map((group) => (
<div key={group.label} className="space-y-4">
<div className="sticky top-0 z-10 bg-background-default/95 backdrop-blur-sm">
<h2 className="text-text-muted">{group.label}</h2>
@@ -577,6 +605,15 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
</div>
</div>
))}
{visibleGroupsCount < dateGroups.length && (
<div className="flex justify-center py-8">
<div className="flex items-center space-x-2 text-text-muted">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-text-muted"></div>
<span>Loading more sessions...</span>
</div>
</div>
)}
</div>
);
};
@@ -597,7 +634,7 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
</div>
<div className="flex-1 min-h-0 relative px-8">
<ScrollArea className="h-full" data-search-scroll-area>
<ScrollArea handleScroll={handleScroll} className="h-full" data-search-scroll-area>
<div ref={containerRef} className="h-full relative">
<SearchView
onSearch={handleSearch}
+18 -2
View File
@@ -15,10 +15,22 @@ interface ScrollAreaProps extends React.ComponentPropsWithoutRef<typeof ScrollAr
/* padding needs to be passed into the container inside ScrollArea to avoid pushing the scrollbar out */
paddingX?: number;
paddingY?: number;
handleScroll?: (viewport: HTMLDivElement) => void;
}
const ScrollArea = React.forwardRef<ScrollAreaHandle, ScrollAreaProps>(
({ className, children, autoScroll = false, paddingX, paddingY, ...props }, ref) => {
(
{
className,
children,
autoScroll = false,
paddingX,
paddingY,
handleScroll: handleScrollProp,
...props
},
ref
) => {
const rootRef = React.useRef<React.ElementRef<typeof ScrollAreaPrimitive.Root>>(null);
const viewportRef = React.useRef<HTMLDivElement>(null);
const viewportEndRef = React.useRef<HTMLDivElement>(null);
@@ -71,7 +83,11 @@ const ScrollArea = React.forwardRef<ScrollAreaHandle, ScrollAreaProps>(
setIsFollowing(isAtBottom);
setIsScrolled(scrollTop > 0);
}, []);
if (handleScrollProp) {
handleScrollProp(viewport);
}
}, [handleScrollProp]);
// Track previous scroll height to detect content changes
const prevScrollHeightRef = React.useRef<number>(0);