Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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[] => [
Expand Down Expand Up @@ -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',
Expand All @@ -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)
Expand All @@ -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,
Expand All @@ -224,6 +223,15 @@ export function ConditionInput({
updateNodeInternals,
isReady,
isPreview,
}, [
conditionalBlocks,
blockId,
subBlockId,
setStoreValue,
updateNodeInternals,
isReady,
isPreview,
])
])

// Cleanup when component unmounts
Expand Down Expand Up @@ -341,6 +349,7 @@ export function ConditionInput({
)
const dropPosition = textarea?.selectionStart ?? 0

shouldPersistRef.current = true
setConditionalBlocks((blocks) =>
blocks.map((block) => {
if (block.id === blockId) {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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`
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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' />
Expand All @@ -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' />
Expand All @@ -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' />
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions apps/sim/hooks/use-collaborative-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
1 change: 1 addition & 0 deletions apps/sim/stores/workflows/registry/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })

Expand Down
Loading