From d0f357a690d8b910318577ca8fb8f7412cc030d2 Mon Sep 17 00:00:00 2001 From: zhsama Date: Fri, 17 Oct 2025 19:09:55 +0800 Subject: [PATCH] feat(workflow): enhance listening functionality with multiple trigger node support --- .../workflow-app/hooks/use-workflow-run.ts | 15 ++++ .../hooks/use-workflow-start-run.tsx | 18 +++++ .../workflow/store/workflow/workflow-slice.ts | 8 ++ .../workflow/variable-inspect/listening.tsx | 78 +++++++++++++++++-- 4 files changed, 114 insertions(+), 5 deletions(-) diff --git a/web/app/components/workflow-app/hooks/use-workflow-run.ts b/web/app/components/workflow-app/hooks/use-workflow-run.ts index 016769cf2b..2769321e28 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-run.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-run.ts @@ -241,12 +241,23 @@ export const useWorkflowRun = () => { setIsListening, setShowVariableInspectPanel, setListeningTriggerType, + setListeningTriggerNodeIds, + setListeningTriggerIsAll, setListeningTriggerNodeId, } = workflowStore.getState() if (runMode === TriggerType.Webhook || runMode === TriggerType.Plugin || runMode === TriggerType.All) { setIsListening(true) setShowVariableInspectPanel(true) + setListeningTriggerIsAll(runMode === TriggerType.All) + if (runMode === TriggerType.All) + setListeningTriggerNodeIds(options?.allNodeIds ?? []) + else if (runMode === TriggerType.Webhook && options?.webhookNodeId) + setListeningTriggerNodeIds([options.webhookNodeId]) + else if (runMode === TriggerType.Plugin && options?.pluginNodeId) + setListeningTriggerNodeIds([options.pluginNodeId]) + else + setListeningTriggerNodeIds([]) setWorkflowRunningData({ result: { status: WorkflowRunningStatus.Running, @@ -262,6 +273,8 @@ export const useWorkflowRun = () => { setIsListening(false) setListeningTriggerType(null) setListeningTriggerNodeId(null) + setListeningTriggerNodeIds([]) + setListeningTriggerIsAll(false) setWorkflowRunningData({ result: { status: WorkflowRunningStatus.Running, @@ -299,6 +312,8 @@ export const useWorkflowRun = () => { state.setIsListening(false) state.setListeningTriggerType(null) state.setListeningTriggerNodeId(null) + state.setListeningTriggerNodeIds([]) + state.setListeningTriggerIsAll(false) } const wrappedOnError = (params: any) => { diff --git a/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx b/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx index 8ccb08cae9..a5daab0525 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx +++ b/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx @@ -74,6 +74,8 @@ export const useWorkflowStartRun = () => { setShowEnvPanel, setListeningTriggerType, setListeningTriggerNodeId, + setListeningTriggerNodeIds, + setListeningTriggerIsAll, } = workflowStore.getState() if (workflowRunningData?.result.status === WorkflowRunningStatus.Running) @@ -97,6 +99,8 @@ export const useWorkflowStartRun = () => { setListeningTriggerType(BlockEnum.TriggerSchedule) setListeningTriggerNodeId(nodeId) + setListeningTriggerNodeIds([nodeId]) + setListeningTriggerIsAll(false) await doSyncWorkflowDraft() handleRun( @@ -123,6 +127,8 @@ export const useWorkflowStartRun = () => { setShowEnvPanel, setListeningTriggerType, setListeningTriggerNodeId, + setListeningTriggerNodeIds, + setListeningTriggerIsAll, } = workflowStore.getState() if (workflowRunningData?.result.status === WorkflowRunningStatus.Running) @@ -145,6 +151,8 @@ export const useWorkflowStartRun = () => { setShowInputsPanel(false) setListeningTriggerType(BlockEnum.TriggerWebhook) setListeningTriggerNodeId(nodeId) + setListeningTriggerNodeIds([nodeId]) + setListeningTriggerIsAll(false) await doSyncWorkflowDraft() handleRun( @@ -168,6 +176,8 @@ export const useWorkflowStartRun = () => { setShowEnvPanel, setListeningTriggerType, setListeningTriggerNodeId, + setListeningTriggerNodeIds, + setListeningTriggerIsAll, } = workflowStore.getState() if (workflowRunningData?.result.status === WorkflowRunningStatus.Running) @@ -190,6 +200,8 @@ export const useWorkflowStartRun = () => { setShowInputsPanel(false) setListeningTriggerType(BlockEnum.TriggerPlugin) setListeningTriggerNodeId(nodeId) + setListeningTriggerNodeIds([nodeId]) + setListeningTriggerIsAll(false) await doSyncWorkflowDraft() handleRun( @@ -211,6 +223,9 @@ export const useWorkflowStartRun = () => { setShowDebugAndPreviewPanel, setShowInputsPanel, setShowEnvPanel, + setListeningTriggerIsAll, + setListeningTriggerNodeIds, + setListeningTriggerNodeId, } = workflowStore.getState() if (workflowRunningData?.result.status === WorkflowRunningStatus.Running) @@ -218,6 +233,9 @@ export const useWorkflowStartRun = () => { setShowEnvPanel(false) setShowInputsPanel(false) + setListeningTriggerIsAll(true) + setListeningTriggerNodeIds(nodeIds) + setListeningTriggerNodeId(null) if (!showDebugAndPreviewPanel) setShowDebugAndPreviewPanel(true) diff --git a/web/app/components/workflow/store/workflow/workflow-slice.ts b/web/app/components/workflow/store/workflow/workflow-slice.ts index ba9bfb967c..35eeff07a7 100644 --- a/web/app/components/workflow/store/workflow/workflow-slice.ts +++ b/web/app/components/workflow/store/workflow/workflow-slice.ts @@ -20,6 +20,10 @@ export type WorkflowSliceShape = { setListeningTriggerType: (triggerType: TriggerNodeType | null) => void listeningTriggerNodeId: string | null setListeningTriggerNodeId: (nodeId: string | null) => void + listeningTriggerNodeIds: string[] + setListeningTriggerNodeIds: (nodeIds: string[]) => void + listeningTriggerIsAll: boolean + setListeningTriggerIsAll: (isAll: boolean) => void clipboardElements: Node[] setClipboardElements: (clipboardElements: Node[]) => void selection: null | { x1: number; y1: number; x2: number; y2: number } @@ -49,6 +53,10 @@ export const createWorkflowSlice: StateCreator = set => ({ setListeningTriggerType: triggerType => set(() => ({ listeningTriggerType: triggerType })), listeningTriggerNodeId: null, setListeningTriggerNodeId: nodeId => set(() => ({ listeningTriggerNodeId: nodeId })), + listeningTriggerNodeIds: [], + setListeningTriggerNodeIds: nodeIds => set(() => ({ listeningTriggerNodeIds: nodeIds })), + listeningTriggerIsAll: false, + setListeningTriggerIsAll: isAll => set(() => ({ listeningTriggerIsAll: isAll })), clipboardElements: [], setClipboardElements: clipboardElements => set(() => ({ clipboardElements })), selection: null, diff --git a/web/app/components/workflow/variable-inspect/listening.tsx b/web/app/components/workflow/variable-inspect/listening.tsx index f78e4a17a2..13a3ae6cdc 100644 --- a/web/app/components/workflow/variable-inspect/listening.tsx +++ b/web/app/components/workflow/variable-inspect/listening.tsx @@ -6,7 +6,7 @@ import BlockIcon from '@/app/components/workflow/block-icon' import { BlockEnum } from '@/app/components/workflow/types' import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { useStore } from '../store' -import { useToolIcon } from '@/app/components/workflow/hooks/use-tool-icon' +import { useGetToolIcon } from '@/app/components/workflow/hooks/use-tool-icon' import type { TFunction } from 'i18next' const resolveListeningDescription = ( @@ -24,6 +24,23 @@ const resolveListeningDescription = ( return t('workflow.debug.variableInspect.listening.tip') } +const resolveMultipleListeningDescription = ( + nodes: Node[], + t: TFunction, +): string => { + if (!nodes.length) + return t('workflow.debug.variableInspect.listening.tip') + + const titles = nodes + .map(node => (node.data as { title?: string })?.title) + .filter((title): title is string => Boolean(title)) + + if (titles.length) + return titles.join(', ') + + return t('workflow.debug.variableInspect.listening.tip') +} + export type ListeningProps = { onStop: () => void message?: string @@ -39,8 +56,12 @@ const Listening: FC = ({ // Get the current trigger type and node ID from store const listeningTriggerType = useStore(s => s.listeningTriggerType) const listeningTriggerNodeId = useStore(s => s.listeningTriggerNodeId) + const listeningTriggerNodeIds = useStore(s => s.listeningTriggerNodeIds) + const listeningTriggerIsAll = useStore(s => s.listeningTriggerIsAll) const triggerType = listeningTriggerType || BlockEnum.TriggerWebhook + const getToolIcon = useGetToolIcon() + // Get the trigger node data to extract icon information const { getNodes } = store.getState() const nodes = getNodes() @@ -48,13 +69,60 @@ const Listening: FC = ({ ? nodes.find(node => node.id === listeningTriggerNodeId) : undefined - // Use the useToolIcon hook to get the icon for plugin/datasource triggers - const toolIcon = useToolIcon(triggerNode?.data) - const description = resolveListeningDescription(message, triggerNode, t) + let displayNodes: Node[] = [] + + if (listeningTriggerIsAll) { + if (listeningTriggerNodeIds.length > 0) { + displayNodes = nodes.filter(node => listeningTriggerNodeIds.includes(node.id)) + } + else { + displayNodes = nodes.filter((node) => { + const nodeType = (node.data as { type?: BlockEnum })?.type + return nodeType === BlockEnum.TriggerSchedule + || nodeType === BlockEnum.TriggerWebhook + || nodeType === BlockEnum.TriggerPlugin + }) + } + } + else if (triggerNode) { + displayNodes = [triggerNode] + } + + const iconsToRender = displayNodes.map((node) => { + const blockType = (node.data as { type?: BlockEnum })?.type || BlockEnum.TriggerWebhook + const icon = getToolIcon(node.data as any) + return { + key: node.id, + type: blockType, + toolIcon: icon, + } + }) + + if (iconsToRender.length === 0) { + iconsToRender.push({ + key: 'default', + type: listeningTriggerIsAll ? BlockEnum.TriggerWebhook : triggerType, + toolIcon: !listeningTriggerIsAll && triggerNode ? getToolIcon(triggerNode.data as any) : undefined, + }) + } + + const description = listeningTriggerIsAll + ? resolveMultipleListeningDescription(displayNodes, t) + : resolveListeningDescription(message, triggerNode, t) return (
- +
+ {iconsToRender.map(icon => ( + + ))} +
{t('workflow.debug.variableInspect.listening.title')}
{description}