mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
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:
committed by
GitHub
parent
9c3b43325b
commit
b177b291cf
@@ -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) {
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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 })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user