diff --git a/apps/sim/hooks/use-undo-redo.ts b/apps/sim/hooks/use-undo-redo.ts index aa40c9c70..536941c72 100644 --- a/apps/sim/hooks/use-undo-redo.ts +++ b/apps/sim/hooks/use-undo-redo.ts @@ -6,6 +6,7 @@ import { enqueueReplaceWorkflowState } from '@/lib/workflows/operations/socket-o import { useOperationQueue } from '@/stores/operation-queue/store' import { type BatchAddBlocksOperation, + type BatchAddEdgesOperation, type BatchMoveBlocksOperation, type BatchRemoveBlocksOperation, type BatchRemoveEdgesOperation, @@ -141,7 +142,6 @@ export function useUndoRedo() { data: { edgeId }, } - // Inverse is batch-remove-edges with a single edge const inverse: BatchRemoveEdgesOperation = { id: crypto.randomUUID(), type: 'batch-remove-edges', @@ -176,10 +176,9 @@ export function useUndoRedo() { }, } - // Inverse is batch-add-edges (using the snapshots to restore) - const inverse: BatchRemoveEdgesOperation = { + const inverse: BatchAddEdgesOperation = { id: crypto.randomUUID(), - type: 'batch-remove-edges', + type: 'batch-add-edges', timestamp: Date.now(), workflowId: activeWorkflowId, userId, diff --git a/apps/sim/socket/database/operations.ts b/apps/sim/socket/database/operations.ts index 865e51c6b..cdd4ff187 100644 --- a/apps/sim/socket/database/operations.ts +++ b/apps/sim/socket/database/operations.ts @@ -703,25 +703,22 @@ async function handleBlocksOperationTx( `Batch toggling enabled state for ${blockIds.length} blocks in workflow ${workflowId}` ) - for (const blockId of blockIds) { - const block = await tx - .select({ enabled: workflowBlocks.enabled }) - .from(workflowBlocks) - .where(and(eq(workflowBlocks.id, blockId), eq(workflowBlocks.workflowId, workflowId))) - .limit(1) + const blocks = await tx + .select({ id: workflowBlocks.id, enabled: workflowBlocks.enabled }) + .from(workflowBlocks) + .where(and(eq(workflowBlocks.workflowId, workflowId), inArray(workflowBlocks.id, blockIds))) - if (block.length > 0) { - await tx - .update(workflowBlocks) - .set({ - enabled: !block[0].enabled, - updatedAt: new Date(), - }) - .where(and(eq(workflowBlocks.id, blockId), eq(workflowBlocks.workflowId, workflowId))) - } + for (const block of blocks) { + await tx + .update(workflowBlocks) + .set({ + enabled: !block.enabled, + updatedAt: new Date(), + }) + .where(and(eq(workflowBlocks.id, block.id), eq(workflowBlocks.workflowId, workflowId))) } - logger.debug(`Batch toggled enabled state for ${blockIds.length} blocks`) + logger.debug(`Batch toggled enabled state for ${blocks.length} blocks`) break } @@ -733,25 +730,22 @@ async function handleBlocksOperationTx( logger.info(`Batch toggling handles for ${blockIds.length} blocks in workflow ${workflowId}`) - for (const blockId of blockIds) { - const block = await tx - .select({ horizontalHandles: workflowBlocks.horizontalHandles }) - .from(workflowBlocks) - .where(and(eq(workflowBlocks.id, blockId), eq(workflowBlocks.workflowId, workflowId))) - .limit(1) + const blocks = await tx + .select({ id: workflowBlocks.id, horizontalHandles: workflowBlocks.horizontalHandles }) + .from(workflowBlocks) + .where(and(eq(workflowBlocks.workflowId, workflowId), inArray(workflowBlocks.id, blockIds))) - if (block.length > 0) { - await tx - .update(workflowBlocks) - .set({ - horizontalHandles: !block[0].horizontalHandles, - updatedAt: new Date(), - }) - .where(and(eq(workflowBlocks.id, blockId), eq(workflowBlocks.workflowId, workflowId))) - } + for (const block of blocks) { + await tx + .update(workflowBlocks) + .set({ + horizontalHandles: !block.horizontalHandles, + updatedAt: new Date(), + }) + .where(and(eq(workflowBlocks.id, block.id), eq(workflowBlocks.workflowId, workflowId))) } - logger.debug(`Batch toggled handles for ${blockIds.length} blocks`) + logger.debug(`Batch toggled handles for ${blocks.length} blocks`) break } diff --git a/apps/sim/stores/undo-redo/types.ts b/apps/sim/stores/undo-redo/types.ts index 910a3f6e8..7973f4858 100644 --- a/apps/sim/stores/undo-redo/types.ts +++ b/apps/sim/stores/undo-redo/types.ts @@ -5,6 +5,7 @@ export type OperationType = | 'batch-add-blocks' | 'batch-remove-blocks' | 'add-edge' + | 'batch-add-edges' | 'batch-remove-edges' | 'add-subflow' | 'remove-subflow' @@ -50,6 +51,13 @@ export interface AddEdgeOperation extends BaseOperation { } } +export interface BatchAddEdgesOperation extends BaseOperation { + type: 'batch-add-edges' + data: { + edgeSnapshots: Edge[] + } +} + export interface BatchRemoveEdgesOperation extends BaseOperation { type: 'batch-remove-edges' data: { @@ -159,6 +167,7 @@ export type Operation = | BatchAddBlocksOperation | BatchRemoveBlocksOperation | AddEdgeOperation + | BatchAddEdgesOperation | BatchRemoveEdgesOperation | AddSubflowOperation | RemoveSubflowOperation diff --git a/apps/sim/stores/undo-redo/utils.ts b/apps/sim/stores/undo-redo/utils.ts index c36ed933d..d2d5e4087 100644 --- a/apps/sim/stores/undo-redo/utils.ts +++ b/apps/sim/stores/undo-redo/utils.ts @@ -1,5 +1,6 @@ import type { BatchAddBlocksOperation, + BatchAddEdgesOperation, BatchMoveBlocksOperation, BatchRemoveBlocksOperation, BatchRemoveEdgesOperation, @@ -56,8 +57,8 @@ export function createInverseOperation(operation: Operation): Operation { }, } as BatchRemoveEdgesOperation - case 'batch-remove-edges': { - const op = operation as BatchRemoveEdgesOperation + case 'batch-add-edges': { + const op = operation as BatchAddEdgesOperation return { ...operation, type: 'batch-remove-edges', @@ -67,6 +68,17 @@ export function createInverseOperation(operation: Operation): Operation { } as BatchRemoveEdgesOperation } + case 'batch-remove-edges': { + const op = operation as BatchRemoveEdgesOperation + return { + ...operation, + type: 'batch-add-edges', + data: { + edgeSnapshots: op.data.edgeSnapshots, + }, + } as BatchAddEdgesOperation + } + case 'add-subflow': return { ...operation, @@ -218,6 +230,23 @@ export function operationToCollaborativePayload(operation: Operation): { payload: { id: operation.data.edgeId }, } + case 'batch-add-edges': { + const op = operation as BatchAddEdgesOperation + return { + operation: 'batch-add-edges', + target: 'edges', + payload: { + edges: op.data.edgeSnapshots.map((e) => ({ + id: e.id, + source: e.source, + target: e.target, + sourceHandle: e.sourceHandle ?? null, + targetHandle: e.targetHandle ?? null, + })), + }, + } + } + case 'batch-remove-edges': { const op = operation as BatchRemoveEdgesOperation return {