diff --git a/web/app/components/workflow/header/__tests__/view-history.spec.tsx b/web/app/components/workflow/header/__tests__/view-history.spec.tsx index 93e0b56125..0b825a408c 100644 --- a/web/app/components/workflow/header/__tests__/view-history.spec.tsx +++ b/web/app/components/workflow/header/__tests__/view-history.spec.tsx @@ -127,8 +127,11 @@ describe('ViewHistory', () => { vi.clearAllMocks() mockIsChatMode = false mockUseWorkflowRunHistory.mockReturnValue({ - data: { data: [] } satisfies WorkflowRunHistoryResponse, + data: { pages: [{ data: [], has_more: false, limit: 20 }] satisfies WorkflowRunHistoryResponse[] }, isLoading: false, + hasNextPage: false, + isFetchingNextPage: false, + fetchNextPage: vi.fn(), }) }) @@ -152,8 +155,11 @@ describe('ViewHistory', () => { it('renders the icon trigger variant and loading state, and clears log modals on trigger click', () => { const onClearLogAndMessageModal = vi.fn() mockUseWorkflowRunHistory.mockReturnValue({ - data: { data: [] } satisfies WorkflowRunHistoryResponse, + data: { pages: [{ data: [], has_more: false, limit: 20 }] satisfies WorkflowRunHistoryResponse[] }, isLoading: true, + hasNextPage: false, + isFetchingNextPage: false, + fetchNextPage: vi.fn(), }) renderWorkflowComponent( @@ -196,9 +202,12 @@ describe('ViewHistory', () => { mockUseWorkflowRunHistory.mockReturnValue({ data: { - data: [pausedRun, failedRun, succeededRun], - } satisfies WorkflowRunHistoryResponse, + pages: [{ data: [pausedRun, failedRun, succeededRun], has_more: false, limit: 20 }] satisfies WorkflowRunHistoryResponse[], + }, isLoading: false, + hasNextPage: false, + isFetchingNextPage: false, + fetchNextPage: vi.fn(), }) const { store } = renderWorkflowComponent(, { @@ -237,9 +246,12 @@ describe('ViewHistory', () => { mockUseWorkflowRunHistory.mockReturnValue({ data: { - data: [chatRun], - } satisfies WorkflowRunHistoryResponse, + pages: [{ data: [chatRun], has_more: false, limit: 20 }] satisfies WorkflowRunHistoryResponse[], + }, isLoading: false, + hasNextPage: false, + isFetchingNextPage: false, + fetchNextPage: vi.fn(), }) renderWorkflowComponent(, { @@ -256,8 +268,11 @@ describe('ViewHistory', () => { it('closes the popup from the close button and clears log modals', () => { const onClearLogAndMessageModal = vi.fn() mockUseWorkflowRunHistory.mockReturnValue({ - data: { data: [] } satisfies WorkflowRunHistoryResponse, + data: { pages: [{ data: [], has_more: false, limit: 20 }] satisfies WorkflowRunHistoryResponse[] }, isLoading: false, + hasNextPage: false, + isFetchingNextPage: false, + fetchNextPage: vi.fn(), }) renderWorkflowComponent( diff --git a/web/app/components/workflow/header/view-history.tsx b/web/app/components/workflow/header/view-history.tsx index bde9f370c9..77164e1777 100644 --- a/web/app/components/workflow/header/view-history.tsx +++ b/web/app/components/workflow/header/view-history.tsx @@ -11,6 +11,8 @@ import { } from '@langgenius/dify-ui/tooltip' import { memo, + useEffect, + useRef, useState, } from 'react' import { useTranslation } from 'react-i18next' @@ -61,8 +63,26 @@ const ViewHistory = ({ const { data, isLoading, + hasNextPage, + isFetchingNextPage, + fetchNextPage, } = useWorkflowRunHistory(historyUrl, shouldFetchHistory) + const sentinelRef = useRef(null) + useEffect(() => { + const el = sentinelRef.current + if (!el) + return + const observer = new IntersectionObserver(([entry]) => { + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) + fetchNextPage() + }) + observer.observe(el) + return () => observer.disconnect() + }, [fetchNextPage, hasNextPage, isFetchingNextPage]) + + const allRuns = data?.pages.flatMap(page => page.data) ?? [] + return ( ( -
+
{t('common.runHistory', { ns: 'workflow' })}