This commit is contained in:
Benjamin 2026-05-09 07:51:36 +08:00 committed by GitHub
commit f441c9a4f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 66 additions and 12 deletions

View File

@ -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(<ViewHistory historyUrl="/history" withText />, {
@ -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(<ViewHistory historyUrl="/history" withText />, {
@ -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(

View File

@ -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<HTMLDivElement>(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 (
(
<Popover
@ -125,7 +145,7 @@ const ViewHistory = ({
maxHeight: 'calc(2 / 3 * 100vh)',
}}
>
<div className="sticky top-0 flex items-center justify-between bg-components-panel-bg px-4 pt-3 text-base font-semibold text-text-primary">
<div className="sticky top-0 z-10 flex items-center justify-between bg-components-panel-bg px-4 pt-3 text-base font-semibold text-text-primary">
<div className="grow">{t('common.runHistory', { ns: 'workflow' })}</div>
<button
type="button"
@ -150,7 +170,7 @@ const ViewHistory = ({
!isLoading && (
<div className="p-2">
{
!data?.data.length && (
!allRuns.length && (
<div className="py-12">
<span className="mx-auto mb-2 i-custom-vender-line-time-clock-play-slim h-8 w-8 text-text-quaternary" />
<div className="text-center text-[13px] text-text-quaternary">
@ -160,7 +180,7 @@ const ViewHistory = ({
)
}
{
data?.data.map(item => (
allRuns.map(item => (
<div
key={item.id}
className={cn(
@ -215,6 +235,11 @@ const ViewHistory = ({
</div>
))
}
{hasNextPage && (
<div ref={sentinelRef} className="flex h-8 items-center justify-center">
{isFetchingNextPage && <Loading />}
</div>
)}
</div>
)
}

View File

@ -27,13 +27,25 @@ export const useAppWorkflow = (appID: string) => {
}
const WorkflowRunHistoryKey = [NAME_SPACE, 'runHistory']
const WORKFLOW_RUN_HISTORY_LIMIT = 20
export const useWorkflowRunHistory = (url?: string, enabled = true) => {
return useQuery<WorkflowRunHistoryResponse>({
return useInfiniteQuery<WorkflowRunHistoryResponse>({
queryKey: [...WorkflowRunHistoryKey, url],
queryFn: () => get<WorkflowRunHistoryResponse>(url as string),
queryFn: ({ pageParam }) => get<WorkflowRunHistoryResponse>(url as string, {
params: {
limit: WORKFLOW_RUN_HISTORY_LIMIT,
...(pageParam ? { last_id: pageParam } : {}),
},
}),
enabled: !!url && enabled,
staleTime: 0,
initialPageParam: undefined as string | undefined,
getNextPageParam: (lastPage) => {
if (!lastPage.has_more || !lastPage.data.length)
return undefined
return lastPage.data[lastPage.data.length - 1].id
},
})
}

View File

@ -398,6 +398,8 @@ export type WorkflowRunHistory = {
}
export type WorkflowRunHistoryResponse = {
data: WorkflowRunHistory[]
has_more: boolean
limit: number
}
export type NodesDefaultConfigsResponse = {