fix: workflow restore (#3711)

This commit is contained in:
zxhlyh 2024-04-23 17:02:23 +08:00 committed by GitHub
parent 96160837d2
commit 83caffe000
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 654 additions and 318 deletions

View File

@ -402,3 +402,5 @@ export const TOOL_OUTPUT_STRUCT: Var[] = [
type: VarType.arrayFile, type: VarType.arrayFile,
}, },
] ]
export const WORKFLOW_DATA_UPDATE = 'WORKFLOW_DATA_UPDATE'

View File

@ -73,6 +73,7 @@ const Header: FC = () => {
const handleRestore = useCallback(() => { const handleRestore = useCallback(() => {
workflowStore.setState({ isRestoring: false }) workflowStore.setState({ isRestoring: false })
workflowStore.setState({ backupDraft: undefined })
handleSyncWorkflowDraft(true) handleSyncWorkflowDraft(true)
}, [handleSyncWorkflowDraft, workflowStore]) }, [handleSyncWorkflowDraft, workflowStore])

View File

@ -19,6 +19,7 @@ import type {
Viewport, Viewport,
} from 'reactflow' } from 'reactflow'
import { import {
changeNodesAndEdgesId,
getLayoutByDagre, getLayoutByDagre,
initialEdges, initialEdges,
initialNodes, initialNodes,
@ -39,6 +40,7 @@ import {
import { import {
AUTO_LAYOUT_OFFSET, AUTO_LAYOUT_OFFSET,
SUPPORT_OUTPUT_VARS_NODE, SUPPORT_OUTPUT_VARS_NODE,
WORKFLOW_DATA_UPDATE,
} from '../constants' } from '../constants'
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils' import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
import { useNodesExtraData } from './use-nodes-data' import { useNodesExtraData } from './use-nodes-data'
@ -56,6 +58,8 @@ import {
fetchAllCustomTools, fetchAllCustomTools,
} from '@/service/tools' } from '@/service/tools'
import I18n from '@/context/i18n' import I18n from '@/context/i18n'
import { useEventEmitterContextContext } from '@/context/event-emitter'
export const useIsChatMode = () => { export const useIsChatMode = () => {
const appDetail = useAppStore(s => s.appDetail) const appDetail = useAppStore(s => s.appDetail)
@ -69,6 +73,7 @@ export const useWorkflow = () => {
const workflowStore = useWorkflowStore() const workflowStore = useWorkflowStore()
const nodesExtraData = useNodesExtraData() const nodesExtraData = useNodesExtraData()
const { handleSyncWorkflowDraft } = useNodesSyncDraft() const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { eventEmitter } = useEventEmitterContextContext()
const handleLayout = useCallback(async () => { const handleLayout = useCallback(async () => {
workflowStore.setState({ nodeAnimation: true }) workflowStore.setState({ nodeAnimation: true })
@ -314,15 +319,21 @@ export const useWorkflow = () => {
}, [locale]) }, [locale])
const renderTreeFromRecord = useCallback((nodes: Node[], edges: Edge[], viewport?: Viewport) => { const renderTreeFromRecord = useCallback((nodes: Node[], edges: Edge[], viewport?: Viewport) => {
const { setNodes } = store.getState() const { setViewport } = reactflow
const { setViewport, setEdges } = reactflow
setNodes(initialNodes(nodes, edges)) const [newNodes, newEdges] = changeNodesAndEdgesId(nodes, edges)
setEdges(initialEdges(edges, nodes))
eventEmitter?.emit({
type: WORKFLOW_DATA_UPDATE,
payload: {
nodes: initialNodes(newNodes, newEdges),
edges: initialEdges(newEdges, newNodes),
},
} as any)
if (viewport) if (viewport)
setViewport(viewport) setViewport(viewport)
}, [store, reactflow]) }, [reactflow, eventEmitter])
const getNode = useCallback((nodeId?: string) => { const getNode = useCallback((nodeId?: string) => {
const { getNodes } = store.getState() const { getNodes } = store.getState()

View File

@ -14,6 +14,8 @@ import {
import ReactFlow, { import ReactFlow, {
Background, Background,
ReactFlowProvider, ReactFlowProvider,
useEdgesState,
useNodesState,
useOnViewportChange, useOnViewportChange,
} from 'reactflow' } from 'reactflow'
import type { Viewport } from 'reactflow' import type { Viewport } from 'reactflow'
@ -46,9 +48,11 @@ import {
initialEdges, initialEdges,
initialNodes, initialNodes,
} from './utils' } from './utils'
import { WORKFLOW_DATA_UPDATE } from './constants'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import { FeaturesProvider } from '@/app/components/base/features' import { FeaturesProvider } from '@/app/components/base/features'
import type { Features as FeaturesData } from '@/app/components/base/features/types' import type { Features as FeaturesData } from '@/app/components/base/features/types'
import { useEventEmitterContextContext } from '@/context/event-emitter'
const nodeTypes = { const nodeTypes = {
custom: CustomNode, custom: CustomNode,
@ -63,10 +67,12 @@ type WorkflowProps = {
viewport?: Viewport viewport?: Viewport
} }
const Workflow: FC<WorkflowProps> = memo(({ const Workflow: FC<WorkflowProps> = memo(({
nodes, nodes: originalNodes,
edges, edges: originalEdges,
viewport, viewport,
}) => { }) => {
const [nodes, setNodes] = useNodesState(originalNodes)
const [edges, setEdges] = useEdgesState(originalEdges)
const showFeaturesPanel = useStore(state => state.showFeaturesPanel) const showFeaturesPanel = useStore(state => state.showFeaturesPanel)
const nodeAnimation = useStore(s => s.nodeAnimation) const nodeAnimation = useStore(s => s.nodeAnimation)
const { const {
@ -76,6 +82,15 @@ const Workflow: FC<WorkflowProps> = memo(({
const { workflowReadOnly } = useWorkflowReadOnly() const { workflowReadOnly } = useWorkflowReadOnly()
const { nodesReadOnly } = useNodesReadOnly() const { nodesReadOnly } = useNodesReadOnly()
const { eventEmitter } = useEventEmitterContextContext()
eventEmitter?.useSubscription((v: any) => {
if (v.type === WORKFLOW_DATA_UPDATE) {
setNodes(v.payload.nodes)
setEdges(v.payload.edges)
}
})
useEffect(() => { useEffect(() => {
setAutoFreeze(false) setAutoFreeze(false)

View File

@ -16,6 +16,7 @@ import type { ToolDefaultValue } from '../../../block-selector/types'
import { import {
useNodesExtraData, useNodesExtraData,
useNodesInteractions, useNodesInteractions,
useNodesReadOnly,
} from '../../../hooks' } from '../../../hooks'
import { useStore } from '../../../store' import { useStore } from '../../../store'
@ -35,6 +36,7 @@ export const NodeTargetHandle = memo(({
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const { handleNodeAdd } = useNodesInteractions() const { handleNodeAdd } = useNodesInteractions()
const nodesExtraData = useNodesExtraData() const nodesExtraData = useNodesExtraData()
const { getNodesReadOnly } = useNodesReadOnly()
const connected = data._connectedTargetHandleIds?.includes(handleId) const connected = data._connectedTargetHandleIds?.includes(handleId)
const availablePrevNodes = nodesExtraData[data.type].availablePrevNodes const availablePrevNodes = nodesExtraData[data.type].availablePrevNodes
const isConnectable = !!availablePrevNodes.length const isConnectable = !!availablePrevNodes.length
@ -78,7 +80,7 @@ export const NodeTargetHandle = memo(({
onClick={handleHandleClick} onClick={handleHandleClick}
> >
{ {
!connected && isConnectable && !data._isInvalidConnection && ( !connected && isConnectable && !data._isInvalidConnection && !getNodesReadOnly() && (
<BlockSelector <BlockSelector
open={open} open={open}
onOpenChange={handleOpenChange} onOpenChange={handleOpenChange}
@ -113,6 +115,7 @@ export const NodeSourceHandle = memo(({
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const { handleNodeAdd } = useNodesInteractions() const { handleNodeAdd } = useNodesInteractions()
const nodesExtraData = useNodesExtraData() const nodesExtraData = useNodesExtraData()
const { getNodesReadOnly } = useNodesReadOnly()
const availableNextNodes = nodesExtraData[data.type].availableNextNodes const availableNextNodes = nodesExtraData[data.type].availableNextNodes
const isConnectable = !!availableNextNodes.length const isConnectable = !!availableNextNodes.length
const connected = data._connectedSourceHandleIds?.includes(handleId) const connected = data._connectedSourceHandleIds?.includes(handleId)
@ -159,7 +162,7 @@ export const NodeSourceHandle = memo(({
onClick={handleHandleClick} onClick={handleHandleClick}
> >
{ {
!connected && isConnectable && !data._isInvalidConnection && ( !connected && isConnectable && !data._isInvalidConnection && !getNodesReadOnly() && (
<BlockSelector <BlockSelector
open={open} open={open}
onOpenChange={handleOpenChange} onOpenChange={handleOpenChange}

View File

@ -58,7 +58,7 @@ const BaseNode: FC<BaseNodeProps> = ({
`} `}
> >
{ {
data.type !== BlockEnum.VariableAssigner && !data._runningStatus && !nodesReadOnly && ( data.type !== BlockEnum.VariableAssigner && !data._runningStatus && (
<NodeTargetHandle <NodeTargetHandle
id={id} id={id}
data={data} data={data}
@ -68,7 +68,7 @@ const BaseNode: FC<BaseNodeProps> = ({
) )
} }
{ {
data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && !data._runningStatus && !nodesReadOnly && ( data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && !data._runningStatus && (
<NodeSourceHandle <NodeSourceHandle
id={id} id={id}
data={data} data={data}

View File

@ -4,6 +4,7 @@ import {
getOutgoers, getOutgoers,
} from 'reactflow' } from 'reactflow'
import dagre from 'dagre' import dagre from 'dagre'
import { v4 as uuid4 } from 'uuid'
import { import {
cloneDeep, cloneDeep,
uniqBy, uniqBy,
@ -331,3 +332,28 @@ export const getToolCheckParams = (
language, language,
} }
} }
export const changeNodesAndEdgesId = (nodes: Node[], edges: Edge[]) => {
const idMap = nodes.reduce((acc, node) => {
acc[node.id] = uuid4()
return acc
}, {} as Record<string, string>)
const newNodes = nodes.map((node) => {
return {
...node,
id: idMap[node.id],
}
})
const newEdges = edges.map((edge) => {
return {
...edge,
source: idMap[edge.source],
target: idMap[edge.target],
}
})
return [newNodes, newEdges] as [Node[], Edge[]]
}

View File

@ -83,6 +83,7 @@
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"swr": "^2.1.0", "swr": "^2.1.0",
"use-context-selector": "^1.4.1", "use-context-selector": "^1.4.1",
"uuid": "^9.0.1",
"zustand": "^4.5.1" "zustand": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
@ -104,6 +105,7 @@
"@types/react-window-infinite-loader": "^1.0.6", "@types/react-window-infinite-loader": "^1.0.6",
"@types/recordrtc": "^5.6.11", "@types/recordrtc": "^5.6.11",
"@types/sortablejs": "^1.15.1", "@types/sortablejs": "^1.15.1",
"@types/uuid": "^9.0.8",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.36.0", "eslint": "^8.36.0",

File diff suppressed because it is too large Load Diff