diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index 6a9b708eca..263f7f8e67 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -1471,11 +1471,10 @@ export const useNodesInteractions = () => { const { getNodes, - setNodes, edges, - setEdges, } = store.getState() const nodes = getNodes() + const { setNodes, setEdges } = useCollaborationStore.getState() const currentNode = nodes.find(node => node.id === nodeId)! const connectedEdges = getConnectedEdges([currentNode], edges) const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap( @@ -1497,18 +1496,6 @@ export const useNodesInteractions = () => { return draft.filter(edge => !connectedEdges.find(connectedEdge => connectedEdge.id === edge.id)) }) setEdges(newEdges) - - const { nodesMap, edgesMap, loroDoc } = useCollaborationStore.getState() - if (nodesMap && edgesMap && loroDoc) { - // newNodes.forEach((node) => { - // nodesMap.set(node.id, node) - // }) - connectedEdges.forEach((edge) => { - edgesMap.delete(edge.id) - }) - loroDoc.commit() - } - handleSyncWorkflowDraft() saveStateToHistory(WorkflowHistoryEvent.EdgeDelete) }, [store, getNodesReadOnly, handleSyncWorkflowDraft, saveStateToHistory]) diff --git a/web/app/components/workflow/store/collaboration-store.ts b/web/app/components/workflow/store/collaboration-store.ts index c9fe1f0e44..649ae6a008 100644 --- a/web/app/components/workflow/store/collaboration-store.ts +++ b/web/app/components/workflow/store/collaboration-store.ts @@ -1,6 +1,7 @@ import { create } from 'zustand' import { LoroDoc } from 'loro-crdt' +import { isEqual } from 'lodash-es' import type { Edge, Node } from '../types' import { useWebSocketStore } from './websocket-store' @@ -48,6 +49,9 @@ type CollaborationStore = { edges: Edge[] updateNodes?: () => void updateEdges?: () => void + + setNodes: (newNodes: Node[]) => void + setEdges: (newEdges: Edge[]) => void initCollaboration: (appId: string) => void destroyCollaboration: () => void } @@ -60,6 +64,78 @@ export const useCollaborationStore = create((set, get) => ({ nodes: [], edges: [], + setNodes: (newNodes: Node[]) => { + const { nodes: oldNodes, nodesMap, loroDoc } = get() + + const oldNodesMap = new Map(oldNodes.map(node => [node.id, node])) + const newNodesMap = new Map(newNodes.map(node => [node.id, node])) + + const getPersistentNodeData = (node: Node) => { + const { data, ...rest } = node + const filteredData = Object.fromEntries( + Object.entries(data).filter(([key]) => + !key.startsWith('_') && key !== 'selected', + ), + ) + + return { + ...rest, + data: filteredData, + } + } + + // delete + oldNodes.forEach((oldNode) => { + if (!newNodesMap.has(oldNode.id)) + nodesMap.delete(oldNode.id) + }) + + newNodes.forEach((newNode) => { + const oldNode = oldNodesMap.get(newNode.id) + if (!oldNode) { + // add + nodesMap.set(newNode.id, getPersistentNodeData(newNode)) + } + else { + const oldPersistentData = getPersistentNodeData(oldNode) + const newPersistentData = getPersistentNodeData(newNode) + + if (!isEqual(oldPersistentData, newPersistentData)) { + // update + nodesMap.set(newNode.id, newPersistentData) + } + } + }) + loroDoc.commit() + }, + + setEdges: (newEdges: Edge[]) => { + const { edges: oldEdges, edgesMap, loroDoc } = get() + + const oldEdgesMap = new Map(oldEdges.map(edge => [edge.id, edge])) + const newEdgesMap = new Map(newEdges.map(edge => [edge.id, edge])) + + // delete + oldEdges.forEach((oldEdge) => { + if (!newEdgesMap.has(oldEdge.id)) + edgesMap.delete(oldEdge.id) + }) + + newEdges.forEach((newEdge) => { + const oldEdge = oldEdgesMap.get(newEdge.id) + if (!oldEdge) { + // add + edgesMap.set(newEdge.id, newEdge) + } + else if (!isEqual(oldEdge, newEdge)) { + // update + edgesMap.set(newEdge.id, newEdge) + } + }) + + loroDoc.commit() + }, + initCollaboration: (appId: string) => { const { getSocket } = useWebSocketStore.getState() const socket = getSocket(appId) diff --git a/web/app/components/workflow/store/workflow/workflow-draft-slice.ts b/web/app/components/workflow/store/workflow/workflow-draft-slice.ts index ec28debee2..c6d62e4628 100644 --- a/web/app/components/workflow/store/workflow/workflow-draft-slice.ts +++ b/web/app/components/workflow/store/workflow/workflow-draft-slice.ts @@ -28,7 +28,7 @@ export const createWorkflowDraftSlice: StateCreator = s setBackupDraft: backupDraft => set(() => ({ backupDraft })), debouncedSyncWorkflowDraft: debounce((syncWorkflowDraft) => { syncWorkflowDraft() - }, 5000), + }, 500000), syncWorkflowDraftHash: '', setSyncWorkflowDraftHash: syncWorkflowDraftHash => set(() => ({ syncWorkflowDraftHash })), isSyncingWorkflowDraft: false,