@@ -123,6 +123,7 @@ export function useCollaborativeWorkflow() {
123123 onVariableUpdate,
124124 onWorkflowDeleted,
125125 onWorkflowReverted,
126+ onWorkflowUpdated,
126127 onOperationConfirmed,
127128 onOperationFailed,
128129 } = useSocket ( )
@@ -537,81 +538,99 @@ export function useCollaborativeWorkflow() {
537538 }
538539 }
539540
541+ const reloadWorkflowFromApi = async ( workflowId : string , reason : string ) : Promise < boolean > => {
542+ const response = await fetch ( `/api/workflows/${ workflowId } ` )
543+ if ( ! response . ok ) {
544+ logger . error ( `Failed to fetch workflow data after ${ reason } : ${ response . statusText } ` )
545+ return false
546+ }
547+
548+ const responseData = await response . json ( )
549+ const workflowData = responseData . data
550+
551+ if ( ! workflowData ?. state ) {
552+ logger . error ( `No state found in workflow data after ${ reason } ` , { workflowData } )
553+ return false
554+ }
555+
556+ isApplyingRemoteChange . current = true
557+ try {
558+ useWorkflowStore . getState ( ) . replaceWorkflowState ( {
559+ blocks : workflowData . state . blocks || { } ,
560+ edges : workflowData . state . edges || [ ] ,
561+ loops : workflowData . state . loops || { } ,
562+ parallels : workflowData . state . parallels || { } ,
563+ lastSaved : workflowData . state . lastSaved || Date . now ( ) ,
564+ } )
565+
566+ const subblockValues : Record < string , Record < string , any > > = { }
567+ Object . entries ( workflowData . state . blocks || { } ) . forEach ( ( [ blockId , block ] ) => {
568+ const blockState = block as any
569+ subblockValues [ blockId ] = { }
570+ Object . entries ( blockState . subBlocks || { } ) . forEach ( ( [ subblockId , subblock ] ) => {
571+ subblockValues [ blockId ] [ subblockId ] = ( subblock as any ) . value
572+ } )
573+ } )
574+
575+ useSubBlockStore . setState ( ( state : any ) => ( {
576+ workflowValues : {
577+ ...state . workflowValues ,
578+ [ workflowId ] : subblockValues ,
579+ } ,
580+ } ) )
581+
582+ const graph = {
583+ blocksById : workflowData . state . blocks || { } ,
584+ edgesById : Object . fromEntries (
585+ ( workflowData . state . edges || [ ] ) . map ( ( e : any ) => [ e . id , e ] )
586+ ) ,
587+ }
588+
589+ const undoRedoStore = useUndoRedoStore . getState ( )
590+ const stackKeys = Object . keys ( undoRedoStore . stacks )
591+ stackKeys . forEach ( ( key ) => {
592+ const [ wfId , userId ] = key . split ( ':' )
593+ if ( wfId === workflowId ) {
594+ undoRedoStore . pruneInvalidEntries ( wfId , userId , graph )
595+ }
596+ } )
597+
598+ logger . info ( `Successfully reloaded workflow state after ${ reason } ` , { workflowId } )
599+ return true
600+ } finally {
601+ isApplyingRemoteChange . current = false
602+ }
603+ }
604+
540605 const handleWorkflowReverted = async ( data : any ) => {
541606 const { workflowId } = data
542607 logger . info ( `Workflow ${ workflowId } has been reverted to deployed state` )
543608
544- // If the reverted workflow is the currently active one, reload the workflow state
545- if ( activeWorkflowId === workflowId ) {
546- logger . info ( `Currently active workflow ${ workflowId } was reverted, reloading state` )
547-
548- try {
549- // Fetch the updated workflow state from the server (which loads from normalized tables)
550- const response = await fetch ( `/api/workflows/${ workflowId } ` )
551- if ( response . ok ) {
552- const responseData = await response . json ( )
553- const workflowData = responseData . data
554-
555- if ( workflowData ?. state ) {
556- // Update the workflow store with the reverted state
557- isApplyingRemoteChange . current = true
558- try {
559- // Update the main workflow state using the API response
560- useWorkflowStore . getState ( ) . replaceWorkflowState ( {
561- blocks : workflowData . state . blocks || { } ,
562- edges : workflowData . state . edges || [ ] ,
563- loops : workflowData . state . loops || { } ,
564- parallels : workflowData . state . parallels || { } ,
565- lastSaved : workflowData . state . lastSaved || Date . now ( ) ,
566- } )
609+ if ( activeWorkflowId !== workflowId ) return
567610
568- // Update subblock store with reverted values
569- const subblockValues : Record < string , Record < string , any > > = { }
570- Object . entries ( workflowData . state . blocks || { } ) . forEach ( ( [ blockId , block ] ) => {
571- const blockState = block as any
572- subblockValues [ blockId ] = { }
573- Object . entries ( blockState . subBlocks || { } ) . forEach ( ( [ subblockId , subblock ] ) => {
574- subblockValues [ blockId ] [ subblockId ] = ( subblock as any ) . value
575- } )
576- } )
611+ try {
612+ await reloadWorkflowFromApi ( workflowId , 'revert' )
613+ } catch ( error ) {
614+ logger . error ( 'Error reloading workflow state after revert:' , error )
615+ }
616+ }
577617
578- // Update subblock store for this workflow
579- useSubBlockStore . setState ( ( state : any ) => ( {
580- workflowValues : {
581- ...state . workflowValues ,
582- [ workflowId ] : subblockValues ,
583- } ,
584- } ) )
618+ const handleWorkflowUpdated = async ( data : any ) => {
619+ const { workflowId } = data
620+ logger . info ( `Workflow ${ workflowId } has been updated externally` )
585621
586- logger . info ( `Successfully loaded reverted workflow state for ${ workflowId } ` )
622+ if ( activeWorkflowId !== workflowId ) return
587623
588- const graph = {
589- blocksById : workflowData . state . blocks || { } ,
590- edgesById : Object . fromEntries (
591- ( workflowData . state . edges || [ ] ) . map ( ( e : any ) => [ e . id , e ] )
592- ) ,
593- }
624+ const { hasActiveDiff } = useWorkflowDiffStore . getState ( )
625+ if ( hasActiveDiff ) {
626+ logger . info ( 'Skipping workflow-updated: active diff in progress' , { workflowId } )
627+ return
628+ }
594629
595- const undoRedoStore = useUndoRedoStore . getState ( )
596- const stackKeys = Object . keys ( undoRedoStore . stacks )
597- stackKeys . forEach ( ( key ) => {
598- const [ wfId , userId ] = key . split ( ':' )
599- if ( wfId === workflowId ) {
600- undoRedoStore . pruneInvalidEntries ( wfId , userId , graph )
601- }
602- } )
603- } finally {
604- isApplyingRemoteChange . current = false
605- }
606- } else {
607- logger . error ( 'No state found in workflow data after revert' , { workflowData } )
608- }
609- } else {
610- logger . error ( `Failed to fetch workflow data after revert: ${ response . statusText } ` )
611- }
612- } catch ( error ) {
613- logger . error ( 'Error reloading workflow state after revert:' , error )
614- }
630+ try {
631+ await reloadWorkflowFromApi ( workflowId , 'external update' )
632+ } catch ( error ) {
633+ logger . error ( 'Error reloading workflow state after external update:' , error )
615634 }
616635 }
617636
@@ -633,6 +652,7 @@ export function useCollaborativeWorkflow() {
633652 onVariableUpdate ( handleVariableUpdate )
634653 onWorkflowDeleted ( handleWorkflowDeleted )
635654 onWorkflowReverted ( handleWorkflowReverted )
655+ onWorkflowUpdated ( handleWorkflowUpdated )
636656 onOperationConfirmed ( handleOperationConfirmed )
637657 onOperationFailed ( handleOperationFailed )
638658 } , [
@@ -641,6 +661,7 @@ export function useCollaborativeWorkflow() {
641661 onVariableUpdate ,
642662 onWorkflowDeleted ,
643663 onWorkflowReverted ,
664+ onWorkflowUpdated ,
644665 onOperationConfirmed ,
645666 onOperationFailed ,
646667 activeWorkflowId ,
0 commit comments