fix(editor): Fix node deletion undo/redo in new canvas (no-changelog) (#10935)

This commit is contained in:
Alex Grozav 2024-09-25 12:59:54 +03:00 committed by GitHub
parent cf153ea085
commit dd3b2cb62d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 148 additions and 8 deletions

View file

@ -616,7 +616,6 @@ describe('useCanvasOperations', () => {
deleteNode(id, { trackHistory: true }); deleteNode(id, { trackHistory: true });
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeConnectionsById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id);
expect(historyStore.pushCommandToUndo).toHaveBeenCalledWith(new RemoveNodeCommand(node)); expect(historyStore.pushCommandToUndo).toHaveBeenCalledWith(new RemoveNodeCommand(node));
}); });
@ -644,7 +643,6 @@ describe('useCanvasOperations', () => {
deleteNode(id, { trackHistory: false }); deleteNode(id, { trackHistory: false });
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeConnectionsById).toHaveBeenCalledWith(id);
expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id); expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(id);
expect(historyStore.pushCommandToUndo).not.toHaveBeenCalled(); expect(historyStore.pushCommandToUndo).not.toHaveBeenCalled();
}); });
@ -714,7 +712,6 @@ describe('useCanvasOperations', () => {
deleteNode(nodes[1].id); deleteNode(nodes[1].id);
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id);
expect(workflowsStore.removeNodeConnectionsById).toHaveBeenCalledWith(nodes[1].id);
expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(nodes[1].id); expect(workflowsStore.removeNodeExecutionDataById).toHaveBeenCalledWith(nodes[1].id);
expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id); expect(workflowsStore.removeNodeById).toHaveBeenCalledWith(nodes[1].id);
}); });
@ -1356,6 +1353,62 @@ describe('useCanvasOperations', () => {
}); });
}); });
describe('deleteConnectionsByNodeId', () => {
it('should delete all connections for a given node ID', () => {
const workflowsStore = mockedStore(useWorkflowsStore);
const { deleteConnectionsByNodeId } = useCanvasOperations({ router });
const node1 = createTestNode({ id: 'node1', name: 'Node 1' });
const node2 = createTestNode({ id: 'node2', name: 'Node 1' });
workflowsStore.workflow.connections = {
[node1.name]: {
[NodeConnectionType.Main]: [
[{ node: node2.name, type: NodeConnectionType.Main, index: 0 }],
],
},
node2: {
[NodeConnectionType.Main]: [
[{ node: node1.name, type: NodeConnectionType.Main, index: 0 }],
],
},
};
workflowsStore.getNodeById.mockReturnValue(node1);
workflowsStore.getNodeByName.mockReturnValueOnce(node1).mockReturnValueOnce(node2);
deleteConnectionsByNodeId(node1.id);
expect(workflowsStore.removeConnection).toHaveBeenCalledWith({
connection: [
{ node: node1.name, type: NodeConnectionType.Main, index: 0 },
{ node: node2.name, type: NodeConnectionType.Main, index: 0 },
],
});
expect(workflowsStore.removeConnection).toHaveBeenCalledWith({
connection: [
{ node: node2.name, type: NodeConnectionType.Main, index: 0 },
{ node: node1.name, type: NodeConnectionType.Main, index: 0 },
],
});
expect(workflowsStore.workflow.connections[node1.name]).toBeUndefined();
});
it('should not delete connections if node ID does not exist', () => {
const workflowsStore = mockedStore(useWorkflowsStore);
const { deleteConnectionsByNodeId } = useCanvasOperations({ router });
const nodeId = 'nonexistent';
workflowsStore.getNodeById.mockReturnValue(undefined);
deleteConnectionsByNodeId(nodeId);
expect(workflowsStore.removeConnection).not.toHaveBeenCalled();
});
});
describe('duplicateNodes', () => { describe('duplicateNodes', () => {
it('should duplicate nodes', async () => { it('should duplicate nodes', async () => {
const workflowsStore = mockedStore(useWorkflowsStore); const workflowsStore = mockedStore(useWorkflowsStore);

View file

@ -236,7 +236,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
await renameNode(currentName, previousName); await renameNode(currentName, previousName);
} }
function connectAdjacentNodes(id: string) { function connectAdjacentNodes(id: string, { trackHistory = false } = {}) {
const node = workflowsStore.getNodeById(id); const node = workflowsStore.getNodeById(id);
if (!node) { if (!node) {
@ -262,6 +262,23 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
if (!outgoingNodeId) continue; if (!outgoingNodeId) continue;
if (trackHistory) {
historyStore.pushCommandToUndo(
new AddConnectionCommand([
{
node: incomingConnection.node,
type,
index: 0,
},
{
node: outgoingConnection.node,
type,
index: 0,
},
]),
);
}
createConnection({ createConnection({
source: incomingNodeId, source: incomingNodeId,
sourceHandle: createCanvasConnectionHandleString({ sourceHandle: createCanvasConnectionHandleString({
@ -289,8 +306,13 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
historyStore.startRecordingUndo(); historyStore.startRecordingUndo();
} }
connectAdjacentNodes(id); if (uiStore.lastInteractedWithNodeId === id) {
workflowsStore.removeNodeConnectionsById(id); uiStore.lastInteractedWithNodeId = null;
}
connectAdjacentNodes(id, { trackHistory });
deleteConnectionsByNodeId(id, { trackHistory, trackBulk: false });
workflowsStore.removeNodeExecutionDataById(id); workflowsStore.removeNodeExecutionDataById(id);
workflowsStore.removeNodeById(id); workflowsStore.removeNodeById(id);
@ -1135,6 +1157,72 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
deleteConnection(mapLegacyConnectionToCanvasConnection(sourceNode, targetNode, connection)); deleteConnection(mapLegacyConnectionToCanvasConnection(sourceNode, targetNode, connection));
} }
function deleteConnectionsByNodeId(
targetNodeId: string,
{ trackHistory = false, trackBulk = true } = {},
) {
const targetNode = workflowsStore.getNodeById(targetNodeId);
if (!targetNode) {
return;
}
if (trackHistory && trackBulk) {
historyStore.startRecordingUndo();
}
const connections = workflowsStore.workflow.connections;
for (const nodeName of Object.keys(connections)) {
const node = workflowsStore.getNodeByName(nodeName);
if (!node) {
continue;
}
for (const type of Object.keys(connections[nodeName])) {
for (const index of Object.keys(connections[nodeName][type])) {
for (const connectionIndex of Object.keys(
connections[nodeName][type][parseInt(index, 10)],
)) {
const connectionData =
connections[nodeName][type][parseInt(index, 10)][parseInt(connectionIndex, 10)];
if (!connectionData) {
continue;
}
const connectionDataNode = workflowsStore.getNodeByName(connectionData.node);
if (
connectionDataNode &&
(connectionDataNode.id === targetNode.id || node.name === targetNode.name)
) {
deleteConnection(
{
source: node.id,
sourceHandle: createCanvasConnectionHandleString({
mode: CanvasConnectionMode.Output,
type: type as NodeConnectionType,
index: parseInt(index, 10),
}),
target: connectionDataNode.id,
targetHandle: createCanvasConnectionHandleString({
mode: CanvasConnectionMode.Input,
type: connectionData.type as NodeConnectionType,
index: connectionData.index,
}),
},
{ trackHistory, trackBulk: false },
);
}
}
}
}
}
delete workflowsStore.workflow.connections[targetNode.name];
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}
function deleteConnection( function deleteConnection(
connection: Connection, connection: Connection,
{ trackHistory = false, trackBulk = true } = {}, { trackHistory = false, trackBulk = true } = {},
@ -1777,6 +1865,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
revertCreateConnection, revertCreateConnection,
deleteConnection, deleteConnection,
revertDeleteConnection, revertDeleteConnection,
deleteConnectionsByNodeId,
isConnectionAllowed, isConnectionAllowed,
importWorkflowData, importWorkflowData,
fetchWorkflowDataFromUrl, fetchWorkflowDataFromUrl,

View file

@ -1525,8 +1525,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
} }
removeNode(node); removeNode(node);
// @TODO When removing node connected between two nodes, create a connection between them
} }
function removeNodeConnectionsById(nodeId: string): void { function removeNodeConnectionsById(nodeId: string): void {