Skip to content

Commit 914f1cd

Browse files
improvement(sockets): duplicate op should let addBlock take subblock values instead of separate looped op (#836)
* improvement(sockets): addBlock can accept subblock values * cleanup unused code
1 parent fb6f555 commit 914f1cd

File tree

4 files changed

+35
-301
lines changed

4 files changed

+35
-301
lines changed

apps/sim/contexts/socket-context.tsx

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,13 @@ interface SocketContextType {
5050
value: any,
5151
operationId?: string
5252
) => void
53-
emitBatchSubblockUpdate: (
54-
blockId: string,
55-
subblockValues: Record<string, any>,
56-
operationId?: string
57-
) => void
53+
5854
emitCursorUpdate: (cursor: { x: number; y: number }) => void
5955
emitSelectionUpdate: (selection: { type: 'block' | 'edge' | 'none'; id?: string }) => void
6056
// Event handlers for receiving real-time updates
6157
onWorkflowOperation: (handler: (data: any) => void) => void
6258
onSubblockUpdate: (handler: (data: any) => void) => void
63-
onBatchSubblockUpdate: (handler: (data: any) => void) => void
59+
6460
onCursorUpdate: (handler: (data: any) => void) => void
6561
onSelectionUpdate: (handler: (data: any) => void) => void
6662
onUserJoined: (handler: (data: any) => void) => void
@@ -81,12 +77,10 @@ const SocketContext = createContext<SocketContextType>({
8177
leaveWorkflow: () => {},
8278
emitWorkflowOperation: () => {},
8379
emitSubblockUpdate: () => {},
84-
emitBatchSubblockUpdate: () => {},
8580
emitCursorUpdate: () => {},
8681
emitSelectionUpdate: () => {},
8782
onWorkflowOperation: () => {},
8883
onSubblockUpdate: () => {},
89-
onBatchSubblockUpdate: () => {},
9084
onCursorUpdate: () => {},
9185
onSelectionUpdate: () => {},
9286
onUserJoined: () => {},
@@ -119,7 +113,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
119113
const eventHandlers = useRef<{
120114
workflowOperation?: (data: any) => void
121115
subblockUpdate?: (data: any) => void
122-
batchSubblockUpdate?: (data: any) => void
116+
123117
cursorUpdate?: (data: any) => void
124118
selectionUpdate?: (data: any) => void
125119
userJoined?: (data: any) => void
@@ -298,11 +292,6 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
298292
eventHandlers.current.subblockUpdate?.(data)
299293
})
300294

301-
// Batch subblock update events
302-
socketInstance.on('batch-subblock-update', (data) => {
303-
eventHandlers.current.batchSubblockUpdate?.(data)
304-
})
305-
306295
// Workflow deletion events
307296
socketInstance.on('workflow-deleted', (data) => {
308297
logger.warn(`Workflow ${data.workflowId} has been deleted`)
@@ -708,29 +697,6 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
708697
[socket, currentWorkflowId]
709698
)
710699

711-
// Emit batch subblock value updates
712-
const emitBatchSubblockUpdate = useCallback(
713-
(blockId: string, subblockValues: Record<string, any>, operationId?: string) => {
714-
// Only emit if socket is connected and we're in a valid workflow room
715-
if (socket && currentWorkflowId) {
716-
socket.emit('batch-subblock-update', {
717-
blockId,
718-
subblockValues,
719-
timestamp: Date.now(),
720-
operationId, // Include operation ID for queue tracking
721-
})
722-
} else {
723-
logger.warn('Cannot emit batch subblock update: no socket connection or workflow room', {
724-
hasSocket: !!socket,
725-
currentWorkflowId,
726-
blockId,
727-
subblockCount: Object.keys(subblockValues).length,
728-
})
729-
}
730-
},
731-
[socket, currentWorkflowId]
732-
)
733-
734700
// Cursor throttling optimized for database connection health
735701
const lastCursorEmit = useRef(0)
736702
const emitCursorUpdate = useCallback(
@@ -766,10 +732,6 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
766732
eventHandlers.current.subblockUpdate = handler
767733
}, [])
768734

769-
const onBatchSubblockUpdate = useCallback((handler: (data: any) => void) => {
770-
eventHandlers.current.batchSubblockUpdate = handler
771-
}, [])
772-
773735
const onCursorUpdate = useCallback((handler: (data: any) => void) => {
774736
eventHandlers.current.cursorUpdate = handler
775737
}, [])
@@ -814,12 +776,12 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
814776
leaveWorkflow,
815777
emitWorkflowOperation,
816778
emitSubblockUpdate,
817-
emitBatchSubblockUpdate,
779+
818780
emitCursorUpdate,
819781
emitSelectionUpdate,
820782
onWorkflowOperation,
821783
onSubblockUpdate,
822-
onBatchSubblockUpdate,
784+
823785
onCursorUpdate,
824786
onSelectionUpdate,
825787
onUserJoined,

apps/sim/hooks/use-collaborative-workflow.ts

Lines changed: 30 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@ export function useCollaborativeWorkflow() {
2222
leaveWorkflow,
2323
emitWorkflowOperation,
2424
emitSubblockUpdate,
25-
emitBatchSubblockUpdate,
2625
onWorkflowOperation,
2726
onSubblockUpdate,
28-
onBatchSubblockUpdate,
2927
onUserJoined,
3028
onUserLeft,
3129
onWorkflowDeleted,
@@ -73,13 +71,8 @@ export function useCollaborativeWorkflow() {
7371

7472
// Register emit functions with operation queue store
7573
useEffect(() => {
76-
registerEmitFunctions(
77-
emitWorkflowOperation,
78-
emitSubblockUpdate,
79-
emitBatchSubblockUpdate,
80-
currentWorkflowId
81-
)
82-
}, [emitWorkflowOperation, emitSubblockUpdate, emitBatchSubblockUpdate, currentWorkflowId])
74+
registerEmitFunctions(emitWorkflowOperation, emitSubblockUpdate, currentWorkflowId)
75+
}, [emitWorkflowOperation, emitSubblockUpdate, currentWorkflowId])
8376

8477
useEffect(() => {
8578
const handleWorkflowOperation = (data: any) => {
@@ -245,29 +238,6 @@ export function useCollaborativeWorkflow() {
245238
}
246239
}
247240

248-
const handleBatchSubblockUpdate = (data: any) => {
249-
const { blockId, subblockValues, userId } = data
250-
251-
if (isApplyingRemoteChange.current) return
252-
253-
logger.info(
254-
`Received batch subblock update from user ${userId}: ${blockId} (${Object.keys(subblockValues).length} subblocks)`
255-
)
256-
257-
isApplyingRemoteChange.current = true
258-
259-
try {
260-
// Apply all subblock values in batch
261-
Object.entries(subblockValues).forEach(([subblockId, value]) => {
262-
subBlockStore.setValue(blockId, subblockId, value)
263-
})
264-
} catch (error) {
265-
logger.error('Error applying remote batch subblock update:', error)
266-
} finally {
267-
isApplyingRemoteChange.current = false
268-
}
269-
}
270-
271241
const handleUserJoined = (data: any) => {
272242
logger.info(`User joined: ${data.userName}`)
273243
}
@@ -373,7 +343,6 @@ export function useCollaborativeWorkflow() {
373343
// Register event handlers
374344
onWorkflowOperation(handleWorkflowOperation)
375345
onSubblockUpdate(handleSubblockUpdate)
376-
onBatchSubblockUpdate(handleBatchSubblockUpdate)
377346
onUserJoined(handleUserJoined)
378347
onUserLeft(handleUserLeft)
379348
onWorkflowDeleted(handleWorkflowDeleted)
@@ -387,7 +356,6 @@ export function useCollaborativeWorkflow() {
387356
}, [
388357
onWorkflowOperation,
389358
onSubblockUpdate,
390-
onBatchSubblockUpdate,
391359
onUserJoined,
392360
onUserLeft,
393361
onWorkflowDeleted,
@@ -755,43 +723,6 @@ export function useCollaborativeWorkflow() {
755723
]
756724
)
757725

758-
const collaborativeBatchSetSubblockValues = useCallback(
759-
(blockId: string, subblockValues: Record<string, any>) => {
760-
if (isApplyingRemoteChange.current) return
761-
762-
if (!currentWorkflowId || activeWorkflowId !== currentWorkflowId) {
763-
logger.debug('Skipping batch subblock update - not in active workflow', {
764-
currentWorkflowId,
765-
activeWorkflowId,
766-
blockId,
767-
subblockCount: Object.keys(subblockValues).length,
768-
})
769-
return
770-
}
771-
772-
// Generate operation ID for queue tracking
773-
const operationId = crypto.randomUUID()
774-
775-
// Add to queue for retry mechanism
776-
addToQueue({
777-
id: operationId,
778-
operation: {
779-
operation: 'batch-subblock-update',
780-
target: 'block',
781-
payload: { blockId, subblockValues },
782-
},
783-
workflowId: activeWorkflowId || '',
784-
userId: session?.user?.id || 'unknown',
785-
})
786-
787-
// Apply locally first (immediate UI feedback)
788-
Object.entries(subblockValues).forEach(([subblockId, value]) => {
789-
subBlockStore.setValue(blockId, subblockId, value)
790-
})
791-
},
792-
[subBlockStore, currentWorkflowId, activeWorkflowId, addToQueue, session?.user?.id]
793-
)
794-
795726
const collaborativeDuplicateBlock = useCallback(
796727
(sourceId: string) => {
797728
const sourceBlock = workflowStore.blocks[sourceId]
@@ -809,6 +740,26 @@ export function useCollaborativeWorkflow() {
809740
? `${match[1]}${Number.parseInt(match[2]) + 1}`
810741
: `${sourceBlock.name} 1`
811742

743+
// Get subblock values from the store
744+
const subBlockValues = subBlockStore.workflowValues[activeWorkflowId || '']?.[sourceId] || {}
745+
746+
// Merge subblock structure with actual values
747+
const mergedSubBlocks = sourceBlock.subBlocks
748+
? JSON.parse(JSON.stringify(sourceBlock.subBlocks))
749+
: {}
750+
Object.entries(subBlockValues).forEach(([subblockId, value]) => {
751+
if (mergedSubBlocks[subblockId]) {
752+
mergedSubBlocks[subblockId].value = value
753+
} else {
754+
// Create subblock if it doesn't exist in structure
755+
mergedSubBlocks[subblockId] = {
756+
id: subblockId,
757+
type: 'unknown',
758+
value: value,
759+
}
760+
}
761+
})
762+
812763
// Create the complete block data for the socket operation
813764
const duplicatedBlockData = {
814765
sourceId,
@@ -817,7 +768,7 @@ export function useCollaborativeWorkflow() {
817768
name: newName,
818769
position: offsetPosition,
819770
data: sourceBlock.data ? JSON.parse(JSON.stringify(sourceBlock.data)) : {},
820-
subBlocks: sourceBlock.subBlocks ? JSON.parse(JSON.stringify(sourceBlock.subBlocks)) : {},
771+
subBlocks: mergedSubBlocks,
821772
outputs: sourceBlock.outputs ? JSON.parse(JSON.stringify(sourceBlock.outputs)) : {},
822773
parentId: sourceBlock.data?.parentId || null,
823774
extent: sourceBlock.data?.extent || null,
@@ -837,21 +788,6 @@ export function useCollaborativeWorkflow() {
837788
sourceBlock.data?.extent
838789
)
839790

840-
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
841-
if (activeWorkflowId) {
842-
const subBlockValues =
843-
useSubBlockStore.getState().workflowValues[activeWorkflowId]?.[sourceId] || {}
844-
useSubBlockStore.setState((state) => ({
845-
workflowValues: {
846-
...state.workflowValues,
847-
[activeWorkflowId]: {
848-
...state.workflowValues[activeWorkflowId],
849-
[newId]: JSON.parse(JSON.stringify(subBlockValues)),
850-
},
851-
},
852-
}))
853-
}
854-
855791
executeQueuedOperation('duplicate', 'block', duplicatedBlockData, () => {
856792
workflowStore.addBlock(
857793
newId,
@@ -861,19 +797,16 @@ export function useCollaborativeWorkflow() {
861797
sourceBlock.data ? JSON.parse(JSON.stringify(sourceBlock.data)) : {}
862798
)
863799

864-
const subBlockValues = subBlockStore.workflowValues[activeWorkflowId || '']?.[sourceId]
865-
if (subBlockValues && activeWorkflowId) {
866-
collaborativeBatchSetSubblockValues(newId, subBlockValues)
800+
// Apply subblock values locally for immediate UI feedback
801+
// The server will persist these values as part of the block creation
802+
if (activeWorkflowId && Object.keys(subBlockValues).length > 0) {
803+
Object.entries(subBlockValues).forEach(([subblockId, value]) => {
804+
subBlockStore.setValue(newId, subblockId, value)
805+
})
867806
}
868807
})
869808
},
870-
[
871-
executeQueuedOperation,
872-
workflowStore,
873-
subBlockStore,
874-
activeWorkflowId,
875-
collaborativeBatchSetSubblockValues,
876-
]
809+
[executeQueuedOperation, workflowStore, subBlockStore, activeWorkflowId]
877810
)
878811

879812
const collaborativeUpdateLoopCount = useCallback(

0 commit comments

Comments
 (0)