fix(condition-block): edges not following blocks, duplicate issues (#1146)

* fix(condition-block): edges not following blocks, duplicate issues

* add subblock update to setActiveWorkflow

* Update apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/condition-input.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Vikhyath Mondreti
2025-08-26 19:51:55 -07:00
committed by GitHub
parent 9c3b43325b
commit b177b291cf
3 changed files with 62 additions and 19 deletions

View File

@@ -72,6 +72,7 @@ export function ConditionInput({
const hasInitializedRef = useRef(false)
// Track previous blockId to detect workflow changes
const previousBlockIdRef = useRef<string>(blockId)
const shouldPersistRef = useRef<boolean>(false)
// Create default blocks with stable IDs
const createDefaultBlocks = (): ConditionalBlock[] => [
@@ -150,35 +151,30 @@ export function ConditionInput({
// If effective value is null, and we've already initialized, keep current state
if (effectiveValueStr === null) {
if (hasInitializedRef.current) {
// We already have blocks, just mark as ready if not already
if (!isReady) setIsReady(true)
isSyncingFromStoreRef.current = false
return
}
// If we haven't initialized yet, set default blocks
setConditionalBlocks(createDefaultBlocks())
hasInitializedRef.current = true
setIsReady(true)
shouldPersistRef.current = false
isSyncingFromStoreRef.current = false
return
}
// Skip if the effective value hasn't changed and we're already initialized
if (effectiveValueStr === prevStoreValueRef.current && hasInitializedRef.current) {
if (!isReady) setIsReady(true)
isSyncingFromStoreRef.current = false
return
}
// Update the previous store value ref
prevStoreValueRef.current = effectiveValueStr
// Parse the effective value
const parsedBlocks = safeParseJSON(effectiveValueStr)
if (parsedBlocks) {
// Use the parsed blocks, but ensure titles are correct based on position
const blocksWithCorrectTitles = parsedBlocks.map((block, index) => ({
...block,
title: index === 0 ? 'if' : index === parsedBlocks.length - 1 ? 'else' : 'else if',
@@ -187,14 +183,14 @@ export function ConditionInput({
setConditionalBlocks(blocksWithCorrectTitles)
hasInitializedRef.current = true
if (!isReady) setIsReady(true)
shouldPersistRef.current = false
} else if (!hasInitializedRef.current) {
// Only set default blocks if we haven't initialized yet
setConditionalBlocks(createDefaultBlocks())
hasInitializedRef.current = true
setIsReady(true)
shouldPersistRef.current = false
}
} finally {
// Reset the syncing flag after a short delay
setTimeout(() => {
isSyncingFromStoreRef.current = false
}, 0)
@@ -203,18 +199,21 @@ export function ConditionInput({
// Update store whenever conditional blocks change
useEffect(() => {
// Skip if we're currently syncing from store to prevent loops
// or if we're not ready yet (still initializing) or in preview mode
if (isSyncingFromStoreRef.current || !isReady || conditionalBlocks.length === 0 || isPreview)
if (
isSyncingFromStoreRef.current ||
!isReady ||
conditionalBlocks.length === 0 ||
isPreview ||
!shouldPersistRef.current
)
return
const newValue = JSON.stringify(conditionalBlocks)
// Only update if the value has actually changed
if (newValue !== prevStoreValueRef.current) {
prevStoreValueRef.current = newValue
setStoreValue(newValue)
updateNodeInternals(`${blockId}-${subBlockId}`)
updateNodeInternals(blockId)
}
}, [
conditionalBlocks,
@@ -224,6 +223,15 @@ export function ConditionInput({
updateNodeInternals,
isReady,
isPreview,
}, [
conditionalBlocks,
blockId,
subBlockId,
setStoreValue,
updateNodeInternals,
isReady,
isPreview,
])
])
// Cleanup when component unmounts
@@ -341,6 +349,7 @@ export function ConditionInput({
)
const dropPosition = textarea?.selectionStart ?? 0
shouldPersistRef.current = true
setConditionalBlocks((blocks) =>
blocks.map((block) => {
if (block.id === blockId) {
@@ -373,6 +382,7 @@ export function ConditionInput({
// Handle tag selection - updated for individual blocks
const handleTagSelect = (blockId: string, newValue: string) => {
if (isPreview || disabled) return
shouldPersistRef.current = true
setConditionalBlocks((blocks) =>
blocks.map((block) =>
block.id === blockId
@@ -390,6 +400,7 @@ export function ConditionInput({
// Handle environment variable selection - updated for individual blocks
const handleEnvVarSelect = (blockId: string, newValue: string) => {
if (isPreview || disabled) return
shouldPersistRef.current = true
setConditionalBlocks((blocks) =>
blocks.map((block) =>
block.id === blockId
@@ -407,6 +418,7 @@ export function ConditionInput({
const handleTagSelectImmediate = (blockId: string, newValue: string) => {
if (isPreview || disabled) return
shouldPersistRef.current = true
setConditionalBlocks((blocks) =>
blocks.map((block) =>
block.id === blockId
@@ -436,6 +448,7 @@ export function ConditionInput({
const handleEnvVarSelectImmediate = (blockId: string, newValue: string) => {
if (isPreview || disabled) return
shouldPersistRef.current = true
setConditionalBlocks((blocks) =>
blocks.map((block) =>
block.id === blockId
@@ -475,13 +488,13 @@ export function ConditionInput({
if (isPreview || disabled) return
const blockIndex = conditionalBlocks.findIndex((block) => block.id === afterId)
if (conditionalBlocks[blockIndex]?.title === 'else') return
// Generate a stable ID using the blockId and a timestamp
const newBlockId = generateStableId(blockId, `else-if-${Date.now()}`)
const newBlock: ConditionalBlock = {
id: newBlockId,
title: '', // Will be set by updateBlockTitles
title: '',
value: '',
showTags: false,
showEnvVars: false,
@@ -492,9 +505,9 @@ export function ConditionInput({
const newBlocks = [...conditionalBlocks]
newBlocks.splice(blockIndex + 1, 0, newBlock)
shouldPersistRef.current = true
setConditionalBlocks(updateBlockTitles(newBlocks))
// Focus the new block's editor after a short delay
setTimeout(() => {
const textarea: any = containerRef.current?.querySelector(
`[data-block-id="${newBlock.id}"] textarea`
@@ -516,13 +529,20 @@ export function ConditionInput({
})
if (conditionalBlocks.length === 1) return
shouldPersistRef.current = true
setConditionalBlocks((blocks) => updateBlockTitles(blocks.filter((block) => block.id !== id)))
setTimeout(() => updateNodeInternals(blockId), 0)
}
const moveBlock = (id: string, direction: 'up' | 'down') => {
if (isPreview || disabled) return
const blockIndex = conditionalBlocks.findIndex((block) => block.id === id)
if (blockIndex === -1) return
if (conditionalBlocks[blockIndex]?.title === 'else') return
if (
(direction === 'up' && blockIndex === 0) ||
(direction === 'down' && blockIndex === conditionalBlocks.length - 1)
@@ -531,11 +551,17 @@ export function ConditionInput({
const newBlocks = [...conditionalBlocks]
const targetIndex = direction === 'up' ? blockIndex - 1 : blockIndex + 1
if (direction === 'down' && newBlocks[targetIndex]?.title === 'else') return
;[newBlocks[blockIndex], newBlocks[targetIndex]] = [
newBlocks[targetIndex],
newBlocks[blockIndex],
]
shouldPersistRef.current = true
setConditionalBlocks(updateBlockTitles(newBlocks))
setTimeout(() => updateNodeInternals(blockId), 0)
}
// Add useEffect to handle keyboard events for both dropdowns
@@ -626,7 +652,7 @@ export function ConditionInput({
variant='ghost'
size='sm'
onClick={() => addBlock(block.id)}
disabled={isPreview || disabled}
disabled={isPreview || disabled || block.title === 'else'}
className='h-8 w-8'
>
<Plus className='h-4 w-4' />
@@ -643,7 +669,7 @@ export function ConditionInput({
variant='ghost'
size='sm'
onClick={() => moveBlock(block.id, 'up')}
disabled={isPreview || index === 0 || disabled}
disabled={isPreview || index === 0 || disabled || block.title === 'else'}
className='h-8 w-8'
>
<ChevronUp className='h-4 w-4' />
@@ -659,7 +685,13 @@ export function ConditionInput({
variant='ghost'
size='sm'
onClick={() => moveBlock(block.id, 'down')}
disabled={isPreview || index === conditionalBlocks.length - 1 || disabled}
disabled={
isPreview ||
disabled ||
index === conditionalBlocks.length - 1 ||
conditionalBlocks[index + 1]?.title === 'else' ||
block.title === 'else'
}
className='h-8 w-8'
>
<ChevronDown className='h-4 w-4' />
@@ -723,6 +755,7 @@ export function ConditionInput({
const tagTrigger = checkTagTrigger(newCode, pos)
const envVarTrigger = checkEnvVarTrigger(newCode, pos)
shouldPersistRef.current = true
setConditionalBlocks((blocks) =>
blocks.map((b) => {
if (b.id === block.id) {

View File

@@ -202,6 +202,15 @@ export function useCollaborativeWorkflow() {
if (payload.autoConnectEdge) {
workflowStore.addEdge(payload.autoConnectEdge)
}
// Apply subblock values from duplicate payload so collaborators see content immediately
if (payload.subBlocks && typeof payload.subBlocks === 'object') {
Object.entries(payload.subBlocks).forEach(([subblockId, subblock]) => {
const value = (subblock as any)?.value
if (value !== undefined) {
subBlockStore.setValue(payload.id, subblockId, value)
}
})
}
break
}
} else if (target === 'edge') {

View File

@@ -574,6 +574,7 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
}
useWorkflowStore.setState(workflowState)
useSubBlockStore.getState().initializeFromWorkflow(id, (workflowState as any).blocks || {})
set({ activeWorkflowId: id, error: null })