mirror of https://github.com/langgenius/dify.git
node data use crdt data
This commit is contained in:
parent
c3287755e3
commit
684f7df158
|
|
@ -202,29 +202,107 @@ export class CollaborationManager {
|
|||
}
|
||||
|
||||
private syncNodes(oldNodes: Node[], newNodes: Node[]): void {
|
||||
if (!this.nodesMap) return
|
||||
if (!this.nodesMap || !this.doc) return
|
||||
|
||||
const oldNodesMap = new Map(oldNodes.map(node => [node.id, node]))
|
||||
const newNodesMap = new Map(newNodes.map(node => [node.id, node]))
|
||||
|
||||
// Delete removed nodes
|
||||
oldNodes.forEach((oldNode) => {
|
||||
if (!newNodesMap.has(oldNode.id))
|
||||
this.nodesMap.delete(oldNode.id)
|
||||
})
|
||||
|
||||
// Add or update nodes with fine-grained sync for data properties
|
||||
newNodes.forEach((newNode) => {
|
||||
const oldNode = oldNodesMap.get(newNode.id)
|
||||
|
||||
if (!oldNode) {
|
||||
const persistentData = this.getPersistentNodeData(newNode)
|
||||
const clonedData = JSON.parse(JSON.stringify(persistentData))
|
||||
this.nodesMap.set(newNode.id, clonedData)
|
||||
// New node - create as nested structure
|
||||
const nodeData: any = {
|
||||
id: newNode.id,
|
||||
type: newNode.type,
|
||||
position: { ...newNode.position },
|
||||
width: newNode.width,
|
||||
height: newNode.height,
|
||||
sourcePosition: newNode.sourcePosition,
|
||||
targetPosition: newNode.targetPosition,
|
||||
data: {},
|
||||
}
|
||||
|
||||
// Clone data properties, excluding private ones
|
||||
Object.entries(newNode.data).forEach(([key, value]) => {
|
||||
if (!key.startsWith('_') && value !== undefined)
|
||||
nodeData.data[key] = JSON.parse(JSON.stringify(value))
|
||||
})
|
||||
|
||||
this.nodesMap.set(newNode.id, nodeData)
|
||||
}
|
||||
else {
|
||||
const oldPersistentData = this.getPersistentNodeData(oldNode)
|
||||
const newPersistentData = this.getPersistentNodeData(newNode)
|
||||
if (!isEqual(oldPersistentData, newPersistentData)) {
|
||||
const clonedData = JSON.parse(JSON.stringify(newPersistentData))
|
||||
this.nodesMap.set(newNode.id, clonedData)
|
||||
// Get existing node from CRDT
|
||||
const existingNode = this.nodesMap.get(newNode.id)
|
||||
|
||||
if (existingNode) {
|
||||
// Create a deep copy to modify
|
||||
const updatedNode = JSON.parse(JSON.stringify(existingNode))
|
||||
|
||||
// Update position only if changed
|
||||
if (oldNode.position.x !== newNode.position.x || oldNode.position.y !== newNode.position.y)
|
||||
updatedNode.position = { ...newNode.position }
|
||||
|
||||
// Update dimensions only if changed
|
||||
if (oldNode.width !== newNode.width)
|
||||
updatedNode.width = newNode.width
|
||||
|
||||
if (oldNode.height !== newNode.height)
|
||||
updatedNode.height = newNode.height
|
||||
|
||||
// Ensure data object exists
|
||||
if (!updatedNode.data)
|
||||
updatedNode.data = {}
|
||||
|
||||
// Fine-grained update of data properties
|
||||
const oldData = oldNode.data || {}
|
||||
const newData = newNode.data || {}
|
||||
|
||||
// Only update changed properties in data
|
||||
Object.entries(newData).forEach(([key, value]) => {
|
||||
if (!key.startsWith('_')) {
|
||||
const oldValue = (oldData as any)[key]
|
||||
if (!isEqual(oldValue, value))
|
||||
updatedNode.data[key] = JSON.parse(JSON.stringify(value))
|
||||
}
|
||||
})
|
||||
|
||||
// Remove deleted properties from data
|
||||
Object.keys(oldData).forEach((key) => {
|
||||
if (!key.startsWith('_') && !(key in newData))
|
||||
delete updatedNode.data[key]
|
||||
})
|
||||
|
||||
// Only update in CRDT if something actually changed
|
||||
if (!isEqual(existingNode, updatedNode))
|
||||
this.nodesMap.set(newNode.id, updatedNode)
|
||||
}
|
||||
else {
|
||||
// Node exists locally but not in CRDT yet
|
||||
const nodeData: any = {
|
||||
id: newNode.id,
|
||||
type: newNode.type,
|
||||
position: { ...newNode.position },
|
||||
width: newNode.width,
|
||||
height: newNode.height,
|
||||
sourcePosition: newNode.sourcePosition,
|
||||
targetPosition: newNode.targetPosition,
|
||||
data: {},
|
||||
}
|
||||
|
||||
Object.entries(newNode.data).forEach(([key, value]) => {
|
||||
if (!key.startsWith('_') && value !== undefined)
|
||||
nodeData.data[key] = JSON.parse(JSON.stringify(value))
|
||||
})
|
||||
|
||||
this.nodesMap.set(newNode.id, nodeData)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -254,14 +332,6 @@ export class CollaborationManager {
|
|||
})
|
||||
}
|
||||
|
||||
private getPersistentNodeData(node: Node): any {
|
||||
const { data, ...rest } = node
|
||||
const filteredData = Object.fromEntries(
|
||||
Object.entries(data).filter(([key]) => !key.startsWith('_')),
|
||||
)
|
||||
return { ...rest, data: filteredData }
|
||||
}
|
||||
|
||||
private setupSubscriptions(): void {
|
||||
this.nodesMap?.subscribe((event: any) => {
|
||||
if (event.by === 'import' && this.reactFlowStore) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import produce from 'immer'
|
|||
import { useStoreApi } from 'reactflow'
|
||||
import { useNodesSyncDraft } from './use-nodes-sync-draft'
|
||||
import { useNodesReadOnly } from './use-workflow'
|
||||
import { useCollaborativeWorkflow } from './use-collaborative-workflow'
|
||||
|
||||
type NodeDataUpdatePayload = {
|
||||
id: string
|
||||
|
|
@ -13,13 +14,11 @@ export const useNodeDataUpdate = () => {
|
|||
const store = useStoreApi()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const collaborativeWorkflow = useCollaborativeWorkflow()
|
||||
|
||||
const handleNodeDataUpdate = useCallback(({ id, data }: NodeDataUpdatePayload) => {
|
||||
const {
|
||||
getNodes,
|
||||
setNodes,
|
||||
} = store.getState()
|
||||
const newNodes = produce(getNodes(), (draft) => {
|
||||
const { nodes, setNodes } = collaborativeWorkflow.getState()
|
||||
const newNodes = produce(nodes, (draft) => {
|
||||
const currentNode = draft.find(node => node.id === id)!
|
||||
|
||||
if (currentNode)
|
||||
|
|
|
|||
Loading…
Reference in New Issue