Skip to content

Commit 0f7dfe0

Browse files
fix(hydration): duplicate overlay after idle + subblocks race condition (#1246)
* fix(hydration): duplicate overlay after idle + subblocks race condition * remove random timeout * re-use correct helper * remove redundant check * add check * remove third init func
1 parent afc1632 commit 0f7dfe0

File tree

4 files changed

+29
-95
lines changed

4 files changed

+29
-95
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -887,14 +887,10 @@ const WorkflowContent = React.memo(() => {
887887
// 2. The workflow exists in the registry
888888
// 3. Workflows are not currently loading
889889
if (hasActiveWorkflow && hasWorkflowInRegistry && isNotLoading) {
890-
// Add a small delay to ensure blocks state has settled
891-
const timeoutId = setTimeout(() => {
892-
setIsWorkflowReady(true)
893-
}, 100)
894-
895-
return () => clearTimeout(timeoutId)
890+
setIsWorkflowReady(true)
891+
} else {
892+
setIsWorkflowReady(false)
896893
}
897-
setIsWorkflowReady(false)
898894
}, [activeWorkflowId, params.workflowId, workflows, isLoading])
899895

900896
// Init workflow

apps/sim/contexts/socket-context.tsx

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -376,38 +376,24 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
376376
)
377377
})
378378

379-
// Merge workflow store with server state (do not drop optimistic local state)
380-
const existing = useWorkflowStore.getState()
381-
const mergedBlocks = {
382-
...(existing.blocks || {}),
383-
...(workflowState.blocks || {}),
384-
}
385-
const edgeById = new Map<string, any>()
386-
;(existing.edges || []).forEach((e: any) => edgeById.set(e.id, e))
387-
;(workflowState.edges || []).forEach((e: any) => edgeById.set(e.id, e))
388-
const mergedEdges = Array.from(edgeById.values())
379+
// Replace local workflow store with authoritative server state
389380
useWorkflowStore.setState({
390-
blocks: mergedBlocks,
391-
edges: mergedEdges,
392-
loops: workflowState.loops || existing.loops || {},
393-
parallels: workflowState.parallels || existing.parallels || {},
394-
lastSaved: workflowState.lastSaved || existing.lastSaved || Date.now(),
395-
isDeployed: workflowState.isDeployed ?? existing.isDeployed ?? false,
396-
deployedAt: workflowState.deployedAt || existing.deployedAt,
397-
deploymentStatuses:
398-
workflowState.deploymentStatuses || existing.deploymentStatuses || {},
399-
hasActiveWebhook:
400-
workflowState.hasActiveWebhook ?? existing.hasActiveWebhook ?? false,
381+
blocks: workflowState.blocks || {},
382+
edges: workflowState.edges || [],
383+
loops: workflowState.loops || {},
384+
parallels: workflowState.parallels || {},
385+
lastSaved: workflowState.lastSaved || Date.now(),
386+
isDeployed: workflowState.isDeployed ?? false,
387+
deployedAt: workflowState.deployedAt,
388+
deploymentStatuses: workflowState.deploymentStatuses || {},
389+
hasActiveWebhook: workflowState.hasActiveWebhook ?? false,
401390
})
402391

403-
// Merge subblock store values per workflow
392+
// Replace subblock store values for this workflow
404393
useSubBlockStore.setState((state: any) => ({
405394
workflowValues: {
406395
...state.workflowValues,
407-
[data.workflowId]: {
408-
...(state.workflowValues?.[data.workflowId] || {}),
409-
...subblockValues,
410-
},
396+
[data.workflowId]: subblockValues,
411397
},
412398
}))
413399

@@ -518,36 +504,24 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
518504
})
519505
})
520506

521-
const existing = useWorkflowStore.getState()
522-
const mergedBlocks = {
523-
...(existing.blocks || {}),
524-
...(workflowState.blocks || {}),
525-
}
526-
const edgeById = new Map<string, any>()
527-
;(existing.edges || []).forEach((e: any) => edgeById.set(e.id, e))
528-
;(workflowState.edges || []).forEach((e: any) => edgeById.set(e.id, e))
529-
const mergedEdges = Array.from(edgeById.values())
507+
// Replace local workflow store with authoritative server state
530508
useWorkflowStore.setState({
531-
blocks: mergedBlocks,
532-
edges: mergedEdges,
533-
loops: workflowState.loops || existing.loops || {},
534-
parallels: workflowState.parallels || existing.parallels || {},
535-
lastSaved: workflowState.lastSaved || existing.lastSaved || Date.now(),
536-
isDeployed: workflowState.isDeployed ?? existing.isDeployed ?? false,
537-
deployedAt: workflowState.deployedAt || existing.deployedAt,
538-
deploymentStatuses:
539-
workflowState.deploymentStatuses || existing.deploymentStatuses || {},
540-
hasActiveWebhook:
541-
workflowState.hasActiveWebhook ?? existing.hasActiveWebhook ?? false,
509+
blocks: workflowState.blocks || {},
510+
edges: workflowState.edges || [],
511+
loops: workflowState.loops || {},
512+
parallels: workflowState.parallels || {},
513+
lastSaved: workflowState.lastSaved || Date.now(),
514+
isDeployed: workflowState.isDeployed ?? false,
515+
deployedAt: workflowState.deployedAt,
516+
deploymentStatuses: workflowState.deploymentStatuses || {},
517+
hasActiveWebhook: workflowState.hasActiveWebhook ?? false,
542518
})
543519

520+
// Replace subblock store values for this workflow
544521
useSubBlockStore.setState((state: any) => ({
545522
workflowValues: {
546523
...state.workflowValues,
547-
[workflowData.id]: {
548-
...(state.workflowValues?.[workflowData.id] || {}),
549-
...subblockValues,
550-
},
524+
[workflowData.id]: subblockValues,
551525
},
552526
}))
553527

apps/sim/stores/workflows/registry/store.ts

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -124,27 +124,6 @@ async function fetchWorkflowsFromDB(workspaceId?: string): Promise<void> {
124124
}
125125
}
126126

127-
// Initialize subblock values
128-
const subblockValues: Record<string, Record<string, any>> = {}
129-
if (state?.blocks) {
130-
Object.entries(state.blocks).forEach(([blockId, block]) => {
131-
const blockState = block as BlockState
132-
subblockValues[blockId] = {}
133-
134-
Object.entries(blockState.subBlocks || {}).forEach(([subblockId, subblock]) => {
135-
subblockValues[blockId][subblockId] = subblock.value
136-
})
137-
})
138-
}
139-
140-
// Update subblock store
141-
useSubBlockStore.setState((state) => ({
142-
workflowValues: {
143-
...state.workflowValues,
144-
[id]: subblockValues,
145-
},
146-
}))
147-
148127
if (variables && typeof variables === 'object') {
149128
useVariablesStore.setState((state) => {
150129
const withoutWorkflow = Object.fromEntries(
@@ -506,22 +485,7 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
506485
},
507486
}
508487

509-
// Extract and update subblock values
510-
const subblockValues: Record<string, Record<string, any>> = {}
511-
Object.entries(workflowState.blocks).forEach(([blockId, block]) => {
512-
const blockState = block as any
513-
subblockValues[blockId] = {}
514-
Object.entries(blockState.subBlocks || {}).forEach(([subblockId, subblock]) => {
515-
subblockValues[blockId][subblockId] = (subblock as any).value
516-
})
517-
})
518-
519-
useSubBlockStore.setState((state) => ({
520-
workflowValues: {
521-
...state.workflowValues,
522-
[id]: subblockValues,
523-
},
524-
}))
488+
// Subblock values will be initialized by initializeFromWorkflow below
525489
} else {
526490
// If no state in DB, use empty state - server should have created start block
527491
workflowState = {

apps/sim/stores/workflows/subblock/store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export const useSubBlockStore = create<SubBlockStore>()(
103103
const values: Record<string, Record<string, any>> = {}
104104
Object.entries(blocks).forEach(([blockId, block]) => {
105105
values[blockId] = {}
106-
Object.entries(block.subBlocks).forEach(([subBlockId, subBlock]) => {
106+
Object.entries(block.subBlocks || {}).forEach(([subBlockId, subBlock]) => {
107107
values[blockId][subBlockId] = (subBlock as SubBlockConfig).value
108108
})
109109
})

0 commit comments

Comments
 (0)