mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
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
This commit is contained in:
committed by
GitHub
parent
afc1632830
commit
0f7dfe084a
@@ -887,14 +887,10 @@ const WorkflowContent = React.memo(() => {
|
||||
// 2. The workflow exists in the registry
|
||||
// 3. Workflows are not currently loading
|
||||
if (hasActiveWorkflow && hasWorkflowInRegistry && isNotLoading) {
|
||||
// Add a small delay to ensure blocks state has settled
|
||||
const timeoutId = setTimeout(() => {
|
||||
setIsWorkflowReady(true)
|
||||
}, 100)
|
||||
|
||||
return () => clearTimeout(timeoutId)
|
||||
setIsWorkflowReady(true)
|
||||
} else {
|
||||
setIsWorkflowReady(false)
|
||||
}
|
||||
setIsWorkflowReady(false)
|
||||
}, [activeWorkflowId, params.workflowId, workflows, isLoading])
|
||||
|
||||
// Init workflow
|
||||
|
||||
@@ -376,38 +376,24 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
|
||||
)
|
||||
})
|
||||
|
||||
// Merge workflow store with server state (do not drop optimistic local state)
|
||||
const existing = useWorkflowStore.getState()
|
||||
const mergedBlocks = {
|
||||
...(existing.blocks || {}),
|
||||
...(workflowState.blocks || {}),
|
||||
}
|
||||
const edgeById = new Map<string, any>()
|
||||
;(existing.edges || []).forEach((e: any) => edgeById.set(e.id, e))
|
||||
;(workflowState.edges || []).forEach((e: any) => edgeById.set(e.id, e))
|
||||
const mergedEdges = Array.from(edgeById.values())
|
||||
// Replace local workflow store with authoritative server state
|
||||
useWorkflowStore.setState({
|
||||
blocks: mergedBlocks,
|
||||
edges: mergedEdges,
|
||||
loops: workflowState.loops || existing.loops || {},
|
||||
parallels: workflowState.parallels || existing.parallels || {},
|
||||
lastSaved: workflowState.lastSaved || existing.lastSaved || Date.now(),
|
||||
isDeployed: workflowState.isDeployed ?? existing.isDeployed ?? false,
|
||||
deployedAt: workflowState.deployedAt || existing.deployedAt,
|
||||
deploymentStatuses:
|
||||
workflowState.deploymentStatuses || existing.deploymentStatuses || {},
|
||||
hasActiveWebhook:
|
||||
workflowState.hasActiveWebhook ?? existing.hasActiveWebhook ?? false,
|
||||
blocks: workflowState.blocks || {},
|
||||
edges: workflowState.edges || [],
|
||||
loops: workflowState.loops || {},
|
||||
parallels: workflowState.parallels || {},
|
||||
lastSaved: workflowState.lastSaved || Date.now(),
|
||||
isDeployed: workflowState.isDeployed ?? false,
|
||||
deployedAt: workflowState.deployedAt,
|
||||
deploymentStatuses: workflowState.deploymentStatuses || {},
|
||||
hasActiveWebhook: workflowState.hasActiveWebhook ?? false,
|
||||
})
|
||||
|
||||
// Merge subblock store values per workflow
|
||||
// Replace subblock store values for this workflow
|
||||
useSubBlockStore.setState((state: any) => ({
|
||||
workflowValues: {
|
||||
...state.workflowValues,
|
||||
[data.workflowId]: {
|
||||
...(state.workflowValues?.[data.workflowId] || {}),
|
||||
...subblockValues,
|
||||
},
|
||||
[data.workflowId]: subblockValues,
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -518,36 +504,24 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
|
||||
})
|
||||
})
|
||||
|
||||
const existing = useWorkflowStore.getState()
|
||||
const mergedBlocks = {
|
||||
...(existing.blocks || {}),
|
||||
...(workflowState.blocks || {}),
|
||||
}
|
||||
const edgeById = new Map<string, any>()
|
||||
;(existing.edges || []).forEach((e: any) => edgeById.set(e.id, e))
|
||||
;(workflowState.edges || []).forEach((e: any) => edgeById.set(e.id, e))
|
||||
const mergedEdges = Array.from(edgeById.values())
|
||||
// Replace local workflow store with authoritative server state
|
||||
useWorkflowStore.setState({
|
||||
blocks: mergedBlocks,
|
||||
edges: mergedEdges,
|
||||
loops: workflowState.loops || existing.loops || {},
|
||||
parallels: workflowState.parallels || existing.parallels || {},
|
||||
lastSaved: workflowState.lastSaved || existing.lastSaved || Date.now(),
|
||||
isDeployed: workflowState.isDeployed ?? existing.isDeployed ?? false,
|
||||
deployedAt: workflowState.deployedAt || existing.deployedAt,
|
||||
deploymentStatuses:
|
||||
workflowState.deploymentStatuses || existing.deploymentStatuses || {},
|
||||
hasActiveWebhook:
|
||||
workflowState.hasActiveWebhook ?? existing.hasActiveWebhook ?? false,
|
||||
blocks: workflowState.blocks || {},
|
||||
edges: workflowState.edges || [],
|
||||
loops: workflowState.loops || {},
|
||||
parallels: workflowState.parallels || {},
|
||||
lastSaved: workflowState.lastSaved || Date.now(),
|
||||
isDeployed: workflowState.isDeployed ?? false,
|
||||
deployedAt: workflowState.deployedAt,
|
||||
deploymentStatuses: workflowState.deploymentStatuses || {},
|
||||
hasActiveWebhook: workflowState.hasActiveWebhook ?? false,
|
||||
})
|
||||
|
||||
// Replace subblock store values for this workflow
|
||||
useSubBlockStore.setState((state: any) => ({
|
||||
workflowValues: {
|
||||
...state.workflowValues,
|
||||
[workflowData.id]: {
|
||||
...(state.workflowValues?.[workflowData.id] || {}),
|
||||
...subblockValues,
|
||||
},
|
||||
[workflowData.id]: subblockValues,
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
@@ -124,27 +124,6 @@ async function fetchWorkflowsFromDB(workspaceId?: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize subblock values
|
||||
const subblockValues: Record<string, Record<string, any>> = {}
|
||||
if (state?.blocks) {
|
||||
Object.entries(state.blocks).forEach(([blockId, block]) => {
|
||||
const blockState = block as BlockState
|
||||
subblockValues[blockId] = {}
|
||||
|
||||
Object.entries(blockState.subBlocks || {}).forEach(([subblockId, subblock]) => {
|
||||
subblockValues[blockId][subblockId] = subblock.value
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Update subblock store
|
||||
useSubBlockStore.setState((state) => ({
|
||||
workflowValues: {
|
||||
...state.workflowValues,
|
||||
[id]: subblockValues,
|
||||
},
|
||||
}))
|
||||
|
||||
if (variables && typeof variables === 'object') {
|
||||
useVariablesStore.setState((state) => {
|
||||
const withoutWorkflow = Object.fromEntries(
|
||||
@@ -506,22 +485,7 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
|
||||
},
|
||||
}
|
||||
|
||||
// Extract and update subblock values
|
||||
const subblockValues: Record<string, Record<string, any>> = {}
|
||||
Object.entries(workflowState.blocks).forEach(([blockId, block]) => {
|
||||
const blockState = block as any
|
||||
subblockValues[blockId] = {}
|
||||
Object.entries(blockState.subBlocks || {}).forEach(([subblockId, subblock]) => {
|
||||
subblockValues[blockId][subblockId] = (subblock as any).value
|
||||
})
|
||||
})
|
||||
|
||||
useSubBlockStore.setState((state) => ({
|
||||
workflowValues: {
|
||||
...state.workflowValues,
|
||||
[id]: subblockValues,
|
||||
},
|
||||
}))
|
||||
// Subblock values will be initialized by initializeFromWorkflow below
|
||||
} else {
|
||||
// If no state in DB, use empty state - server should have created start block
|
||||
workflowState = {
|
||||
|
||||
@@ -103,7 +103,7 @@ export const useSubBlockStore = create<SubBlockStore>()(
|
||||
const values: Record<string, Record<string, any>> = {}
|
||||
Object.entries(blocks).forEach(([blockId, block]) => {
|
||||
values[blockId] = {}
|
||||
Object.entries(block.subBlocks).forEach(([subBlockId, subBlock]) => {
|
||||
Object.entries(block.subBlocks || {}).forEach(([subBlockId, subBlock]) => {
|
||||
values[blockId][subBlockId] = (subBlock as SubBlockConfig).value
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user