Skip to content

Commit b177b29

Browse files
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>
1 parent 9c3b433 commit b177b29

File tree

3 files changed

+62
-19
lines changed

3 files changed

+62
-19
lines changed

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

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export function ConditionInput({
7272
const hasInitializedRef = useRef(false)
7373
// Track previous blockId to detect workflow changes
7474
const previousBlockIdRef = useRef<string>(blockId)
75+
const shouldPersistRef = useRef<boolean>(false)
7576

7677
// Create default blocks with stable IDs
7778
const createDefaultBlocks = (): ConditionalBlock[] => [
@@ -150,35 +151,30 @@ export function ConditionInput({
150151
// If effective value is null, and we've already initialized, keep current state
151152
if (effectiveValueStr === null) {
152153
if (hasInitializedRef.current) {
153-
// We already have blocks, just mark as ready if not already
154154
if (!isReady) setIsReady(true)
155155
isSyncingFromStoreRef.current = false
156156
return
157157
}
158158

159-
// If we haven't initialized yet, set default blocks
160159
setConditionalBlocks(createDefaultBlocks())
161160
hasInitializedRef.current = true
162161
setIsReady(true)
162+
shouldPersistRef.current = false
163163
isSyncingFromStoreRef.current = false
164164
return
165165
}
166166

167-
// Skip if the effective value hasn't changed and we're already initialized
168167
if (effectiveValueStr === prevStoreValueRef.current && hasInitializedRef.current) {
169168
if (!isReady) setIsReady(true)
170169
isSyncingFromStoreRef.current = false
171170
return
172171
}
173172

174-
// Update the previous store value ref
175173
prevStoreValueRef.current = effectiveValueStr
176174

177-
// Parse the effective value
178175
const parsedBlocks = safeParseJSON(effectiveValueStr)
179176

180177
if (parsedBlocks) {
181-
// Use the parsed blocks, but ensure titles are correct based on position
182178
const blocksWithCorrectTitles = parsedBlocks.map((block, index) => ({
183179
...block,
184180
title: index === 0 ? 'if' : index === parsedBlocks.length - 1 ? 'else' : 'else if',
@@ -187,14 +183,14 @@ export function ConditionInput({
187183
setConditionalBlocks(blocksWithCorrectTitles)
188184
hasInitializedRef.current = true
189185
if (!isReady) setIsReady(true)
186+
shouldPersistRef.current = false
190187
} else if (!hasInitializedRef.current) {
191-
// Only set default blocks if we haven't initialized yet
192188
setConditionalBlocks(createDefaultBlocks())
193189
hasInitializedRef.current = true
194190
setIsReady(true)
191+
shouldPersistRef.current = false
195192
}
196193
} finally {
197-
// Reset the syncing flag after a short delay
198194
setTimeout(() => {
199195
isSyncingFromStoreRef.current = false
200196
}, 0)
@@ -203,18 +199,21 @@ export function ConditionInput({
203199

204200
// Update store whenever conditional blocks change
205201
useEffect(() => {
206-
// Skip if we're currently syncing from store to prevent loops
207-
// or if we're not ready yet (still initializing) or in preview mode
208-
if (isSyncingFromStoreRef.current || !isReady || conditionalBlocks.length === 0 || isPreview)
202+
if (
203+
isSyncingFromStoreRef.current ||
204+
!isReady ||
205+
conditionalBlocks.length === 0 ||
206+
isPreview ||
207+
!shouldPersistRef.current
208+
)
209209
return
210210

211211
const newValue = JSON.stringify(conditionalBlocks)
212212

213-
// Only update if the value has actually changed
214213
if (newValue !== prevStoreValueRef.current) {
215214
prevStoreValueRef.current = newValue
216215
setStoreValue(newValue)
217-
updateNodeInternals(`${blockId}-${subBlockId}`)
216+
updateNodeInternals(blockId)
218217
}
219218
}, [
220219
conditionalBlocks,
@@ -224,6 +223,15 @@ export function ConditionInput({
224223
updateNodeInternals,
225224
isReady,
226225
isPreview,
226+
}, [
227+
conditionalBlocks,
228+
blockId,
229+
subBlockId,
230+
setStoreValue,
231+
updateNodeInternals,
232+
isReady,
233+
isPreview,
234+
])
227235
])
228236

229237
// Cleanup when component unmounts
@@ -341,6 +349,7 @@ export function ConditionInput({
341349
)
342350
const dropPosition = textarea?.selectionStart ?? 0
343351

352+
shouldPersistRef.current = true
344353
setConditionalBlocks((blocks) =>
345354
blocks.map((block) => {
346355
if (block.id === blockId) {
@@ -373,6 +382,7 @@ export function ConditionInput({
373382
// Handle tag selection - updated for individual blocks
374383
const handleTagSelect = (blockId: string, newValue: string) => {
375384
if (isPreview || disabled) return
385+
shouldPersistRef.current = true
376386
setConditionalBlocks((blocks) =>
377387
blocks.map((block) =>
378388
block.id === blockId
@@ -390,6 +400,7 @@ export function ConditionInput({
390400
// Handle environment variable selection - updated for individual blocks
391401
const handleEnvVarSelect = (blockId: string, newValue: string) => {
392402
if (isPreview || disabled) return
403+
shouldPersistRef.current = true
393404
setConditionalBlocks((blocks) =>
394405
blocks.map((block) =>
395406
block.id === blockId
@@ -407,6 +418,7 @@ export function ConditionInput({
407418
const handleTagSelectImmediate = (blockId: string, newValue: string) => {
408419
if (isPreview || disabled) return
409420

421+
shouldPersistRef.current = true
410422
setConditionalBlocks((blocks) =>
411423
blocks.map((block) =>
412424
block.id === blockId
@@ -436,6 +448,7 @@ export function ConditionInput({
436448
const handleEnvVarSelectImmediate = (blockId: string, newValue: string) => {
437449
if (isPreview || disabled) return
438450

451+
shouldPersistRef.current = true
439452
setConditionalBlocks((blocks) =>
440453
blocks.map((block) =>
441454
block.id === blockId
@@ -475,13 +488,13 @@ export function ConditionInput({
475488
if (isPreview || disabled) return
476489

477490
const blockIndex = conditionalBlocks.findIndex((block) => block.id === afterId)
491+
if (conditionalBlocks[blockIndex]?.title === 'else') return
478492

479-
// Generate a stable ID using the blockId and a timestamp
480493
const newBlockId = generateStableId(blockId, `else-if-${Date.now()}`)
481494

482495
const newBlock: ConditionalBlock = {
483496
id: newBlockId,
484-
title: '', // Will be set by updateBlockTitles
497+
title: '',
485498
value: '',
486499
showTags: false,
487500
showEnvVars: false,
@@ -492,9 +505,9 @@ export function ConditionInput({
492505

493506
const newBlocks = [...conditionalBlocks]
494507
newBlocks.splice(blockIndex + 1, 0, newBlock)
508+
shouldPersistRef.current = true
495509
setConditionalBlocks(updateBlockTitles(newBlocks))
496510

497-
// Focus the new block's editor after a short delay
498511
setTimeout(() => {
499512
const textarea: any = containerRef.current?.querySelector(
500513
`[data-block-id="${newBlock.id}"] textarea`
@@ -516,13 +529,20 @@ export function ConditionInput({
516529
})
517530

518531
if (conditionalBlocks.length === 1) return
532+
shouldPersistRef.current = true
519533
setConditionalBlocks((blocks) => updateBlockTitles(blocks.filter((block) => block.id !== id)))
534+
535+
setTimeout(() => updateNodeInternals(blockId), 0)
520536
}
521537

522538
const moveBlock = (id: string, direction: 'up' | 'down') => {
523539
if (isPreview || disabled) return
524540

525541
const blockIndex = conditionalBlocks.findIndex((block) => block.id === id)
542+
if (blockIndex === -1) return
543+
544+
if (conditionalBlocks[blockIndex]?.title === 'else') return
545+
526546
if (
527547
(direction === 'up' && blockIndex === 0) ||
528548
(direction === 'down' && blockIndex === conditionalBlocks.length - 1)
@@ -531,11 +551,17 @@ export function ConditionInput({
531551

532552
const newBlocks = [...conditionalBlocks]
533553
const targetIndex = direction === 'up' ? blockIndex - 1 : blockIndex + 1
554+
555+
if (direction === 'down' && newBlocks[targetIndex]?.title === 'else') return
556+
534557
;[newBlocks[blockIndex], newBlocks[targetIndex]] = [
535558
newBlocks[targetIndex],
536559
newBlocks[blockIndex],
537560
]
561+
shouldPersistRef.current = true
538562
setConditionalBlocks(updateBlockTitles(newBlocks))
563+
564+
setTimeout(() => updateNodeInternals(blockId), 0)
539565
}
540566

541567
// Add useEffect to handle keyboard events for both dropdowns
@@ -626,7 +652,7 @@ export function ConditionInput({
626652
variant='ghost'
627653
size='sm'
628654
onClick={() => addBlock(block.id)}
629-
disabled={isPreview || disabled}
655+
disabled={isPreview || disabled || block.title === 'else'}
630656
className='h-8 w-8'
631657
>
632658
<Plus className='h-4 w-4' />
@@ -643,7 +669,7 @@ export function ConditionInput({
643669
variant='ghost'
644670
size='sm'
645671
onClick={() => moveBlock(block.id, 'up')}
646-
disabled={isPreview || index === 0 || disabled}
672+
disabled={isPreview || index === 0 || disabled || block.title === 'else'}
647673
className='h-8 w-8'
648674
>
649675
<ChevronUp className='h-4 w-4' />
@@ -659,7 +685,13 @@ export function ConditionInput({
659685
variant='ghost'
660686
size='sm'
661687
onClick={() => moveBlock(block.id, 'down')}
662-
disabled={isPreview || index === conditionalBlocks.length - 1 || disabled}
688+
disabled={
689+
isPreview ||
690+
disabled ||
691+
index === conditionalBlocks.length - 1 ||
692+
conditionalBlocks[index + 1]?.title === 'else' ||
693+
block.title === 'else'
694+
}
663695
className='h-8 w-8'
664696
>
665697
<ChevronDown className='h-4 w-4' />
@@ -723,6 +755,7 @@ export function ConditionInput({
723755
const tagTrigger = checkTagTrigger(newCode, pos)
724756
const envVarTrigger = checkEnvVarTrigger(newCode, pos)
725757

758+
shouldPersistRef.current = true
726759
setConditionalBlocks((blocks) =>
727760
blocks.map((b) => {
728761
if (b.id === block.id) {

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,15 @@ export function useCollaborativeWorkflow() {
202202
if (payload.autoConnectEdge) {
203203
workflowStore.addEdge(payload.autoConnectEdge)
204204
}
205+
// Apply subblock values from duplicate payload so collaborators see content immediately
206+
if (payload.subBlocks && typeof payload.subBlocks === 'object') {
207+
Object.entries(payload.subBlocks).forEach(([subblockId, subblock]) => {
208+
const value = (subblock as any)?.value
209+
if (value !== undefined) {
210+
subBlockStore.setValue(payload.id, subblockId, value)
211+
}
212+
})
213+
}
205214
break
206215
}
207216
} else if (target === 'edge') {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
574574
}
575575

576576
useWorkflowStore.setState(workflowState)
577+
useSubBlockStore.getState().initializeFromWorkflow(id, (workflowState as any).blocks || {})
577578

578579
set({ activeWorkflowId: id, error: null })
579580

0 commit comments

Comments
 (0)