feat: workflow remove preview mode (#3941)

This commit is contained in:
zxhlyh
2024-04-28 17:09:56 +08:00
committed by GitHub
parent 0940f01634
commit 8e4989ed03
33 changed files with 549 additions and 309 deletions

View File

@@ -7,3 +7,5 @@ export * from './use-workflow'
export * from './use-workflow-run'
export * from './use-workflow-template'
export * from './use-checklist'
export * from './use-workflow-mode'
export * from './use-workflow-interactions'

View File

@@ -201,6 +201,20 @@ export const useEdgesInteractions = () => {
setEdges(newEdges)
}, [store])
const handleEdgeCancelRunningStatus = useCallback(() => {
const {
edges,
setEdges,
} = store.getState()
const newEdges = produce(edges, (draft) => {
draft.forEach((edge) => {
edge.data._runned = false
})
})
setEdges(newEdges)
}, [store])
return {
handleEdgeEnter,
handleEdgeLeave,
@@ -208,5 +222,6 @@ export const useEdgesInteractions = () => {
handleEdgeDelete,
handleEdgesChange,
handleVariableAssignerEdgesChange,
handleEdgeCancelRunningStatus,
}
}

View File

@@ -243,9 +243,6 @@ export const useNodesInteractions = () => {
}, [store, getNodesReadOnly])
const handleNodeSelect = useCallback((nodeId: string, cancelSelection?: boolean) => {
if (getNodesReadOnly() && !workflowStore.getState().isRestoring)
return
const {
getNodes,
setNodes,
@@ -289,14 +286,11 @@ export const useNodesInteractions = () => {
setEdges(newEdges)
handleSyncWorkflowDraft()
}, [store, handleSyncWorkflowDraft, getNodesReadOnly, workflowStore])
}, [store, handleSyncWorkflowDraft])
const handleNodeClick = useCallback<NodeMouseHandler>((_, node) => {
if (getNodesReadOnly() && !workflowStore.getState().isRestoring)
return
handleNodeSelect(node.id)
}, [handleNodeSelect, getNodesReadOnly, workflowStore])
}, [handleNodeSelect])
const handleNodeConnect = useCallback<OnConnect>(({
source,
@@ -834,6 +828,36 @@ export const useNodesInteractions = () => {
handleNodeDelete(node.id)
}, [getNodesReadOnly, handleNodeDelete, store, workflowStore])
const handleNodeCancelRunningStatus = useCallback(() => {
const {
getNodes,
setNodes,
} = store.getState()
const nodes = getNodes()
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
node.data._runningStatus = undefined
})
})
setNodes(newNodes)
}, [store])
const handleNodesCancelSelected = useCallback(() => {
const {
getNodes,
setNodes,
} = store.getState()
const nodes = getNodes()
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
node.data.selected = false
})
})
setNodes(newNodes)
}, [store])
return {
handleNodeDragStart,
handleNodeDrag,
@@ -853,5 +877,7 @@ export const useNodesInteractions = () => {
handleNodeCut,
handleNodeDeleteSelected,
handleNodePaste,
handleNodeCancelRunningStatus,
handleNodesCancelSelected,
}
}

View File

@@ -81,6 +81,8 @@ export const useNodesSyncDraft = () => {
}, [store, featuresStore, workflowStore])
const syncWorkflowDraftWhenPageClose = useCallback(() => {
if (getNodesReadOnly())
return
const postParams = getPostParams()
if (postParams) {
@@ -89,16 +91,18 @@ export const useNodesSyncDraft = () => {
JSON.stringify(postParams.params),
)
}
}, [getPostParams, params.appId])
}, [getPostParams, params.appId, getNodesReadOnly])
const doSyncWorkflowDraft = useCallback(async (appId?: string) => {
if (getNodesReadOnly())
return
const postParams = getPostParams(appId)
if (postParams) {
const res = await syncWorkflowDraft(postParams)
workflowStore.getState().setDraftUpdatedAt(res.updated_at)
}
}, [workflowStore, getPostParams])
}, [workflowStore, getPostParams, getNodesReadOnly])
const handleSyncWorkflowDraft = useCallback((sync?: boolean, appId?: string) => {
if (getNodesReadOnly())

View File

@@ -0,0 +1,50 @@
import { useCallback } from 'react'
import { useReactFlow } from 'reactflow'
import { useWorkflowStore } from '../store'
import { WORKFLOW_DATA_UPDATE } from '../constants'
import type { WorkflowDataUpdator } from '../types'
import {
initialEdges,
initialNodes,
} from '../utils'
import { useEdgesInteractions } from './use-edges-interactions'
import { useNodesInteractions } from './use-nodes-interactions'
import { useEventEmitterContextContext } from '@/context/event-emitter'
export const useWorkflowInteractions = () => {
const reactflow = useReactFlow()
const workflowStore = useWorkflowStore()
const { handleNodeCancelRunningStatus } = useNodesInteractions()
const { handleEdgeCancelRunningStatus } = useEdgesInteractions()
const { eventEmitter } = useEventEmitterContextContext()
const handleCancelDebugAndPreviewPanel = useCallback(() => {
workflowStore.setState({
showDebugAndPreviewPanel: false,
})
handleNodeCancelRunningStatus()
handleEdgeCancelRunningStatus()
}, [workflowStore, handleNodeCancelRunningStatus, handleEdgeCancelRunningStatus])
const handleUpdateWorkflowCanvas = useCallback((payload: WorkflowDataUpdator) => {
const {
nodes,
edges,
viewport,
} = payload
const { setViewport } = reactflow
eventEmitter?.emit({
type: WORKFLOW_DATA_UPDATE,
payload: {
nodes: initialNodes(nodes, edges),
edges: initialEdges(edges, nodes),
},
} as any)
setViewport(viewport)
}, [eventEmitter, reactflow])
return {
handleCancelDebugAndPreviewPanel,
handleUpdateWorkflowCanvas,
}
}

View File

@@ -0,0 +1,14 @@
import { useMemo } from 'react'
import { useStore } from '../store'
export const useWorkflowMode = () => {
const historyWorkflowData = useStore(s => s.historyWorkflowData)
const isRestoring = useStore(s => s.isRestoring)
return useMemo(() => {
return {
normal: !historyWorkflowData && !isRestoring,
restoring: isRestoring,
viewHistory: !!historyWorkflowData,
}
}, [historyWorkflowData, isRestoring])
}

View File

@@ -5,11 +5,12 @@ import {
} from 'reactflow'
import produce from 'immer'
import { useWorkflowStore } from '../store'
import { useNodesSyncDraft } from '../hooks'
import {
NodeRunningStatus,
WorkflowRunningStatus,
} from '../types'
import { useWorkflow } from './use-workflow'
import { useWorkflowInteractions } from './use-workflow-interactions'
import { useStore as useAppStore } from '@/app/components/app/store'
import type { IOtherOptions } from '@/service/base'
import { ssePost } from '@/service/base'
@@ -24,7 +25,8 @@ export const useWorkflowRun = () => {
const workflowStore = useWorkflowStore()
const reactflow = useReactFlow()
const featuresStore = useFeaturesStore()
const { renderTreeFromRecord } = useWorkflow()
const { doSyncWorkflowDraft } = useNodesSyncDraft()
const { handleUpdateWorkflowCanvas } = useWorkflowInteractions()
const handleBackupDraft = useCallback(() => {
const {
@@ -45,15 +47,11 @@ export const useWorkflowRun = () => {
viewport: getViewport(),
features,
})
doSyncWorkflowDraft()
}
}, [reactflow, workflowStore, store, featuresStore])
}, [reactflow, workflowStore, store, featuresStore, doSyncWorkflowDraft])
const handleLoadBackupDraft = useCallback(() => {
const {
setNodes,
setEdges,
} = store.getState()
const { setViewport } = reactflow
const {
backupDraft,
setBackupDraft,
@@ -66,64 +64,32 @@ export const useWorkflowRun = () => {
viewport,
features,
} = backupDraft
setNodes(nodes)
setEdges(edges)
setViewport(viewport)
handleUpdateWorkflowCanvas({
nodes,
edges,
viewport,
})
featuresStore!.setState({ features })
setBackupDraft(undefined)
}
}, [store, reactflow, workflowStore, featuresStore])
}, [handleUpdateWorkflowCanvas, workflowStore, featuresStore])
const handleRunSetting = useCallback((shouldClear?: boolean) => {
if (shouldClear) {
workflowStore.setState({
workflowRunningData: undefined,
historyWorkflowData: undefined,
showInputsPanel: false,
})
}
else {
workflowStore.setState({
workflowRunningData: {
result: {
status: shouldClear ? '' : WorkflowRunningStatus.Waiting,
},
tracing: [],
},
})
}
const {
setNodes,
getNodes,
edges,
setEdges,
} = store.getState()
if (shouldClear) {
handleLoadBackupDraft()
}
else {
handleBackupDraft()
const newNodes = produce(getNodes(), (draft) => {
draft.forEach((node) => {
node.data._runningStatus = NodeRunningStatus.Waiting
})
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
draft.forEach((edge) => {
edge.data._runned = false
})
})
setEdges(newEdges)
}
}, [store, handleLoadBackupDraft, handleBackupDraft, workflowStore])
const handleRun = useCallback((
const handleRun = useCallback(async (
params: any,
callback?: IOtherOptions,
) => {
const {
getNodes,
setNodes,
} = store.getState()
const newNodes = produce(getNodes(), (draft) => {
draft.forEach((node) => {
node.data.selected = false
})
})
setNodes(newNodes)
await doSyncWorkflowDraft()
const {
onWorkflowStarted,
onWorkflowFinished,
@@ -151,15 +117,14 @@ export const useWorkflowRun = () => {
let prevNodeId = ''
const {
workflowRunningData,
setWorkflowRunningData,
} = workflowStore.getState()
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
draft.result = {
...draft?.result,
setWorkflowRunningData({
result: {
status: WorkflowRunningStatus.Running,
}
}))
},
tracing: [],
})
ssePost(
url,
@@ -174,8 +139,6 @@ export const useWorkflowRun = () => {
setWorkflowRunningData,
} = workflowStore.getState()
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
@@ -188,12 +151,6 @@ export const useWorkflowRun = () => {
}
}))
const newNodes = produce(getNodes(), (draft) => {
draft.forEach((node) => {
node.data._runningStatus = NodeRunningStatus.Waiting
})
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
draft.forEach((edge) => {
edge.data = {
@@ -253,6 +210,7 @@ export const useWorkflowRun = () => {
setNodes,
edges,
setEdges,
transform,
} = store.getState()
const nodes = getNodes()
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
@@ -268,12 +226,12 @@ export const useWorkflowRun = () => {
const currentNodeIndex = nodes.findIndex(node => node.id === data.node_id)
const currentNode = nodes[currentNodeIndex]
const position = currentNode.position
const zoom = 1
const zoom = transform[2]
setViewport({
x: (clientWidth - 400 - currentNode.width!) / 2 - position.x,
y: (clientHeight - currentNode.height!) / 2 - position.y,
zoom,
x: (clientWidth - 400 - currentNode.width! * zoom) / 2 - position.x * zoom,
y: (clientHeight - currentNode.height! * zoom) / 2 - position.y * zoom,
zoom: transform[2],
})
const newNodes = produce(nodes, (draft) => {
draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Running
@@ -329,7 +287,7 @@ export const useWorkflowRun = () => {
...restCallback,
},
)
}, [store, reactflow, workflowStore])
}, [store, reactflow, workflowStore, doSyncWorkflowDraft])
const handleStopRun = useCallback((taskId: string) => {
const appId = useAppStore.getState().appDetail?.id
@@ -344,18 +302,21 @@ export const useWorkflowRun = () => {
if (publishedWorkflow) {
const nodes = publishedWorkflow.graph.nodes
const edges = publishedWorkflow.graph.edges
const viewport = publishedWorkflow.graph.viewport
const viewport = publishedWorkflow.graph.viewport!
renderTreeFromRecord(nodes, edges, viewport)
handleUpdateWorkflowCanvas({
nodes,
edges,
viewport,
})
featuresStore?.setState({ features: publishedWorkflow.features })
workflowStore.getState().setPublishedAt(publishedWorkflow.created_at)
}
}, [featuresStore, workflowStore, renderTreeFromRecord])
}, [featuresStore, handleUpdateWorkflowCanvas, workflowStore])
return {
handleBackupDraft,
handleLoadBackupDraft,
handleRunSetting,
handleRun,
handleStopRun,
handleRestoreFromPublishedWorkflow,

View File

@@ -16,15 +16,11 @@ import {
} from 'reactflow'
import type {
Connection,
Viewport,
} from 'reactflow'
import {
getLayoutByDagre,
initialEdges,
initialNodes,
} from '../utils'
import type {
Edge,
Node,
ValueSelector,
} from '../types'
@@ -39,7 +35,6 @@ import {
import {
AUTO_LAYOUT_OFFSET,
SUPPORT_OUTPUT_VARS_NODE,
WORKFLOW_DATA_UPDATE,
} from '../constants'
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
import { useNodesExtraData } from './use-nodes-data'
@@ -58,7 +53,6 @@ import {
fetchAllCustomTools,
} from '@/service/tools'
import I18n from '@/context/i18n'
import { useEventEmitterContextContext } from '@/context/event-emitter'
export const useIsChatMode = () => {
const appDetail = useAppStore(s => s.appDetail)
@@ -73,7 +67,6 @@ export const useWorkflow = () => {
const workflowStore = useWorkflowStore()
const nodesExtraData = useNodesExtraData()
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { eventEmitter } = useEventEmitterContextContext()
const setPanelWidth = useCallback((width: number) => {
localStorage.setItem('workflow-node-panel-width', `${width}`)
@@ -323,23 +316,6 @@ export const useWorkflow = () => {
return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow()
}, [locale])
const renderTreeFromRecord = useCallback((nodes: Node[], edges: Edge[], viewport?: Viewport) => {
const { setViewport } = reactflow
const nodesMap = nodes.map(node => ({ ...node, data: { ...node.data, selected: false } }))
eventEmitter?.emit({
type: WORKFLOW_DATA_UPDATE,
payload: {
nodes: initialNodes(nodesMap, edges),
edges: initialEdges(edges, nodesMap),
},
} as any)
if (viewport)
setViewport(viewport)
}, [reactflow, eventEmitter])
const getNode = useCallback((nodeId?: string) => {
const { getNodes } = store.getState()
const nodes = getNodes()
@@ -369,7 +345,6 @@ export const useWorkflow = () => {
isNodeVarsUsedInNodes,
isValidConnection,
formatTimeFromNow,
renderTreeFromRecord,
getNode,
getBeforeNodeById,
enableShortcuts,
@@ -510,11 +485,11 @@ export const useNodesReadOnly = () => {
isRestoring,
} = workflowStore.getState()
return workflowRunningData || historyWorkflowData || isRestoring
return workflowRunningData?.result.status === WorkflowRunningStatus.Running || historyWorkflowData || isRestoring
}, [workflowStore])
return {
nodesReadOnly: !!(workflowRunningData || historyWorkflowData || isRestoring),
nodesReadOnly: !!(workflowRunningData?.result.status === WorkflowRunningStatus.Running || historyWorkflowData || isRestoring),
getNodesReadOnly,
}
}