From c37cce000f850fafb157dcb55980fa8dac7db1e1 Mon Sep 17 00:00:00 2001 From: zhsama Date: Tue, 11 Nov 2025 16:46:13 +0800 Subject: [PATCH] refactor: replace TRIGGER_NODE_TYPES with isTriggerNode utility for improved node type checks across workflow components --- .../workflow-header/features-trigger.tsx | 13 +++---- .../workflow/block-selector/main.tsx | 10 ++---- .../components/workflow/hooks/use-helpline.ts | 4 +-- .../workflow/hooks/use-nodes-interactions.ts | 8 ++--- .../panel-operator/change-block.tsx | 35 ++----------------- .../components/workflow/nodes/_base/node.tsx | 30 +++++++--------- 6 files changed, 27 insertions(+), 73 deletions(-) diff --git a/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx b/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx index c81f9aacbf..47ec60d7ad 100644 --- a/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx +++ b/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx @@ -23,10 +23,12 @@ import { useFeatures } from '@/app/components/base/features/hooks' import type { CommonEdgeType, CommonNodeType, + Node, } from '@/app/components/workflow/types' import { BlockEnum, InputVarType, + isTriggerNode, } from '@/app/components/workflow/types' import { useToastContext } from '@/app/components/base/toast' import { useInvalidateAppWorkflow, usePublishWorkflow, useResetWorkflowVersionHistory } from '@/service/use-workflow' @@ -36,15 +38,8 @@ import { fetchAppDetail } from '@/service/apps' import { useStore as useAppStore } from '@/app/components/app/store' import useTheme from '@/hooks/use-theme' import cn from '@/utils/classnames' -import { useIsChatMode } from '../../hooks' +import { useIsChatMode } from '@/app/components/workflow/hooks' import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' -import type { Node } from '@/app/components/workflow/types' - -const TRIGGER_NODE_TYPES: BlockEnum[] = [ - BlockEnum.TriggerSchedule, - BlockEnum.TriggerWebhook, - BlockEnum.TriggerPlugin, -] const FeaturesTrigger = () => { const { t } = useTranslation() @@ -97,7 +92,7 @@ const FeaturesTrigger = () => { }, [edges, startNodeIds]) // Track trigger presence so the publisher can adjust UI (e.g. hide missing start section). const hasTriggerNode = useMemo(() => ( - nodes.some(node => TRIGGER_NODE_TYPES.includes(node.data.type as BlockEnum)) + nodes.some(node => isTriggerNode(node.data.type as BlockEnum)) ), [nodes]) const resetWorkflowVersionHistory = useResetWorkflowVersionHistory() diff --git a/web/app/components/workflow/block-selector/main.tsx b/web/app/components/workflow/block-selector/main.tsx index 5f8480d9f5..3e13384785 100644 --- a/web/app/components/workflow/block-selector/main.tsx +++ b/web/app/components/workflow/block-selector/main.tsx @@ -20,7 +20,7 @@ import type { OnSelectBlock, ToolWithProvider, } from '../types' -import { BlockEnum } from '../types' +import { BlockEnum, isTriggerNode } from '../types' import Tabs from './tabs' import { TabsEnum } from './types' import { useTabs } from './hooks' @@ -35,12 +35,6 @@ import { } from '@/app/components/base/icons/src/vender/line/general' import SearchBox from '@/app/components/plugins/marketplace/search-box' -const TRIGGER_NODE_TYPES: BlockEnum[] = [ - BlockEnum.TriggerSchedule, - BlockEnum.TriggerWebhook, - BlockEnum.TriggerPlugin, -] - export type NodeSelectorProps = { open?: boolean onOpenChange?: (open: boolean) => void @@ -115,7 +109,7 @@ const NodeSelector: FC = ({ continue if (nodeType === BlockEnum.Start) result.hasUserInputNode = true - if (TRIGGER_NODE_TYPES.includes(nodeType)) + if (isTriggerNode(nodeType)) result.hasTriggerNode = true if (result.hasTriggerNode && result.hasUserInputNode) break diff --git a/web/app/components/workflow/hooks/use-helpline.ts b/web/app/components/workflow/hooks/use-helpline.ts index 96a85aabd0..55979904fb 100644 --- a/web/app/components/workflow/hooks/use-helpline.ts +++ b/web/app/components/workflow/hooks/use-helpline.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react' import { useStoreApi } from 'reactflow' import type { Node } from '../types' -import { BlockEnum, TRIGGER_NODE_TYPES } from '../types' +import { BlockEnum, isTriggerNode } from '../types' import { useWorkflowStore } from '../store' // Entry node (Start/Trigger) wrapper offsets @@ -18,7 +18,7 @@ export const useHelpline = () => { // Check if a node is an entry node (Start or Trigger) const isEntryNode = useCallback((node: Node): boolean => { - return TRIGGER_NODE_TYPES.includes(node.data.type as any) || node.data.type === BlockEnum.Start + return isTriggerNode(node.data.type as any) || node.data.type === BlockEnum.Start }, []) // Get the actual alignment position of a node (accounting for wrapper offset) diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index 2939d84c27..d4fa470fb8 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -18,7 +18,7 @@ import { } from 'reactflow' import type { PluginDefaultValue } from '../block-selector/types' import type { Edge, Node, OnNodeAdd } from '../types' -import { BlockEnum, TRIGGER_NODE_TYPES } from '../types' +import { BlockEnum, isTriggerNode } from '../types' import { useWorkflowStore } from '../store' import { CUSTOM_EDGE, @@ -148,12 +148,12 @@ export const useNodesInteractions = () => { const currentNode = draft.find(n => n.id === node.id)! // Check if current dragging node is an entry node - const isCurrentEntryNode = TRIGGER_NODE_TYPES.includes(node.data.type as any) || node.data.type === BlockEnum.Start + const isCurrentEntryNode = isTriggerNode(node.data.type as any) || node.data.type === BlockEnum.Start // X-axis alignment with offset consideration if (showVerticalHelpLineNodesLength > 0) { const targetNode = showVerticalHelpLineNodes[0] - const isTargetEntryNode = TRIGGER_NODE_TYPES.includes(targetNode.data.type as any) || targetNode.data.type === BlockEnum.Start + const isTargetEntryNode = isTriggerNode(targetNode.data.type as any) || targetNode.data.type === BlockEnum.Start // Calculate the wrapper position needed to align the inner nodes // Target inner position = target.position + target.offset @@ -177,7 +177,7 @@ export const useNodesInteractions = () => { // Y-axis alignment with offset consideration if (showHorizontalHelpLineNodesLength > 0) { const targetNode = showHorizontalHelpLineNodes[0] - const isTargetEntryNode = TRIGGER_NODE_TYPES.includes(targetNode.data.type as any) || targetNode.data.type === BlockEnum.Start + const isTargetEntryNode = isTriggerNode(targetNode.data.type as any) || targetNode.data.type === BlockEnum.Start const targetOffset = isTargetEntryNode ? ENTRY_NODE_WRAPPER_OFFSET.y : 0 const currentOffset = isCurrentEntryNode ? ENTRY_NODE_WRAPPER_OFFSET.y : 0 diff --git a/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx b/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx index 86c346f2ab..8b6d137127 100644 --- a/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx +++ b/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx @@ -5,7 +5,6 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import { intersection } from 'lodash-es' -import { useNodes } from 'reactflow' import BlockSelector from '@/app/components/workflow/block-selector' import { useAvailableBlocks, @@ -14,17 +13,11 @@ import { } from '@/app/components/workflow/hooks' import { useHooksStore } from '@/app/components/workflow/hooks-store' import type { - CommonNodeType, Node, OnSelectBlock, } from '@/app/components/workflow/types' -import { BlockEnum } from '@/app/components/workflow/types' +import { BlockEnum, isTriggerNode } from '@/app/components/workflow/types' -const TRIGGER_NODE_TYPES: BlockEnum[] = [ - BlockEnum.TriggerSchedule, - BlockEnum.TriggerWebhook, - BlockEnum.TriggerPlugin, -] import { FlowType } from '@/types/common' type ChangeBlockProps = { @@ -39,7 +32,6 @@ const ChangeBlock = ({ }: ChangeBlockProps) => { const { t } = useTranslation() const { handleNodeChange } = useNodesInteractions() - const nodes = useNodes() const { availablePrevBlocks, availableNextBlocks, @@ -47,30 +39,11 @@ const ChangeBlock = ({ const isChatMode = useIsChatMode() const flowType = useHooksStore(s => s.configsMap?.flowType) const showStartTab = flowType !== FlowType.ragPipeline && !isChatMode - // Count total trigger nodes - const totalTriggerNodes = useMemo(() => ( - nodes.filter(node => TRIGGER_NODE_TYPES.includes(node.data.type as BlockEnum)).length - ), [nodes]) - // Check if there is a User Input node - const hasUserInputNode = useMemo(() => ( - nodes.some(node => node.data.type === BlockEnum.Start) - ), [nodes]) - // Check if the current node is a trigger node - const isTriggerNode = TRIGGER_NODE_TYPES.includes(nodeData.type as BlockEnum) - // Force enabling Start tab regardless of existing trigger/user input nodes (e.g., when changing Start node type). - const forceEnableStartTab = isTriggerNode || nodeData.type === BlockEnum.Start - // Only allow converting a trigger into User Input when it's the sole trigger and no User Input exists yet. - const canChangeTriggerToUserInput = isTriggerNode && !hasUserInputNode && totalTriggerNodes === 1 - // Ignore current node when it's a trigger so the Start tab logic doesn't treat it as existing trigger. const ignoreNodeIds = useMemo(() => { - if (TRIGGER_NODE_TYPES.includes(nodeData.type as BlockEnum)) + if (isTriggerNode(nodeData.type as BlockEnum)) return [nodeId] return undefined }, [nodeData.type, nodeId]) - // Determine user input selection based on node type and trigger/user input node presence. - const allowUserInputSelection = forceEnableStartTab - ? (nodeData.type === BlockEnum.Start ? false : canChangeTriggerToUserInput) - : undefined const availableNodes = useMemo(() => { if (availablePrevBlocks.length && availableNextBlocks.length) @@ -106,9 +79,7 @@ const ChangeBlock = ({ availableBlocksTypes={availableNodes} showStartTab={showStartTab} ignoreNodeIds={ignoreNodeIds} - // When changing Start/Trigger nodes, force-enable Start tab to allow switching among entry nodes. - forceEnableStartTab={forceEnableStartTab} - allowUserInputSelection={allowUserInputSelection} + forceEnableStartTab={nodeData.type === BlockEnum.Start} /> ) } diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx index 57aea8a1df..73f78401ac 100644 --- a/web/app/components/workflow/nodes/_base/node.tsx +++ b/web/app/components/workflow/nodes/_base/node.tsx @@ -16,24 +16,18 @@ import { RiLoader2Line, } from '@remixicon/react' import { useTranslation } from 'react-i18next' -import type { NodeProps } from '../../types' +import type { NodeProps } from '@/app/components/workflow/types' import { BlockEnum, NodeRunningStatus, - TRIGGER_NODE_TYPES, -} from '../../types' -import { - useNodesReadOnly, - useToolIcon, -} from '../../hooks' -import { - hasErrorHandleNode, - hasRetryNode, -} from '../../utils' -import { useNodeIterationInteractions } from '../iteration/use-interactions' -import { useNodeLoopInteractions } from '../loop/use-interactions' -import type { IterationNodeType } from '../iteration/types' -import CopyID from '../tool/components/copy-id' + isTriggerNode, +} from '@/app/components/workflow/types' +import { useNodesReadOnly, useToolIcon } from '@/app/components/workflow/hooks' +import { hasErrorHandleNode, hasRetryNode } from '@/app/components/workflow/utils' +import { useNodeIterationInteractions } from '@/app/components/workflow/nodes/iteration/use-interactions' +import { useNodeLoopInteractions } from '@/app/components/workflow/nodes/loop/use-interactions' +import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types' +import CopyID from '@/app/components/workflow/nodes/tool/components/copy-id' import { NodeSourceHandle, NodeTargetHandle, @@ -47,8 +41,8 @@ import EntryNodeContainer, { StartNodeTypeEnum } from './components/entry-node-c import cn from '@/utils/classnames' import BlockIcon from '@/app/components/workflow/block-icon' import Tooltip from '@/app/components/base/tooltip' -import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' -import { ToolTypeEnum } from '../../block-selector/types' +import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' +import { ToolTypeEnum } from '@/app/components/workflow/block-selector/types' type NodeChildProps = { id: string @@ -354,7 +348,7 @@ const BaseNode: FC = ({ ) const isStartNode = data.type === BlockEnum.Start - const isEntryNode = TRIGGER_NODE_TYPES.includes(data.type as any) || isStartNode + const isEntryNode = isTriggerNode(data.type as any) || isStartNode return isEntryNode ? (