11import React , { useCallback , useMemo , useState , useEffect } from 'react' ;
22import ReactFlow , { useNodesState , useEdgesState , addEdge , Controls , Background , ControlButton } from 'reactflow' ;
33import 'reactflow/dist/style.css' ;
4+ import { cloneDeep , isEqual } from 'lodash' ;
45
56// css
67import './index.css' ;
@@ -27,12 +28,97 @@ const StartNode = () => (
2728 < FlowNode title = 'Start' handleLeft = { false } handleRight = { true } handleRightData = { { type : 'source' } } > </ FlowNode >
2829) ;
2930
31+ const init = ( flowData ) => {
32+ // Initialization
33+ if ( flowData && flowData . nodes && flowData . edges ) {
34+ return {
35+ nodes : flowData . nodes ,
36+ edges : flowData . edges ,
37+ } ;
38+ } else if ( flowData && flowData . nodes && ! flowData . edges ) {
39+ // AI prompt generated
40+ const nodes = [
41+ { id : '0' , type : 'startNode' , position : { x : 150 , y : 150 } , deletable : false } ,
42+ { id : '1' , type : 'authNode' , position : { x : 400 , y : 150 } , data : { } , deletable : false } ,
43+ ] ;
44+ const edges = [
45+ {
46+ id : `reactflow__edge-0-1` ,
47+ source : `0` ,
48+ sourceHandle : null ,
49+ target : `1` ,
50+ targetHandle : null ,
51+ type : 'buttonedge' ,
52+ } ,
53+ ] ;
54+ for ( let i = 2 ; i <= flowData . nodes . length ; i ++ ) {
55+ nodes . push ( {
56+ id : `${ i } ` ,
57+ type : flowData . nodes [ i - 1 ] . type ,
58+ position : { x : 150 + i * 500 , y : 50 } ,
59+ data : flowData . nodes [ i - 1 ] ,
60+ } ) ;
61+ edges . push ( {
62+ id : `reactflow__edge-${ i - 1 } -${ i } ` ,
63+ source : `${ i - 1 } ` ,
64+ sourceHandle : null ,
65+ target : `${ i } ` ,
66+ targetHandle : null ,
67+ type : 'buttonedge' ,
68+ } ) ;
69+ }
70+ return {
71+ nodes : flowData . nodes ,
72+ edges : flowData . edges ,
73+ } ;
74+ } else {
75+ return {
76+ nodes : [
77+ { id : '0' , type : 'startNode' , position : { x : 150 , y : 150 } , deletable : false } ,
78+ { id : '1' , type : 'authNode' , position : { x : 400 , y : 150 } , data : { } , deletable : false } ,
79+ ] ,
80+ edges : [
81+ {
82+ id : `reactflow__edge-0-1` ,
83+ source : `0` ,
84+ sourceHandle : null ,
85+ target : `1` ,
86+ targetHandle : null ,
87+ type : 'buttonedge' ,
88+ } ,
89+ ] ,
90+ } ;
91+ }
92+ } ;
93+
3094const Flow = ( { tabId, collectionId, flowData } ) => {
95+ useEffect ( ( ) => {
96+ // Action to perform on tab change
97+ console . log ( `Tab changed to: ${ tabId } ` ) ;
98+ console . log ( flowData ) ;
99+ // perform actions based on the new tabId
100+ const result = init ( cloneDeep ( flowData ) ) ;
101+ setNodes ( result . nodes ) ;
102+ setEdges ( result . edges ) ;
103+ } , [ tabId ] ) ;
104+
31105 const setCanvasDirty = ( ) => {
106+ console . debug ( 'set canvas dirty' ) ;
32107 const tab = useTabStore . getState ( ) . tabs . find ( ( t ) => t . id === tabId ) ;
33108 if ( tab ) {
34109 tab . isDirty = true ;
35- tab . flowData = getFlowData ( ) ;
110+ tab . flowData = {
111+ nodes : nodes . map ( ( node ) => {
112+ const _node = JSON . parse ( JSON . stringify ( node ) ) ;
113+ return { ..._node } ;
114+ } ) ,
115+ edges : edges . map ( ( edge ) => {
116+ return {
117+ ...edge ,
118+ animated : false ,
119+ } ;
120+ } ) ,
121+ } ;
36122 }
37123 } ;
38124
@@ -41,21 +127,6 @@ const Flow = ({ tabId, collectionId, flowData }) => {
41127
42128 const [ reactFlowInstance , setReactFlowInstance ] = useState ( null ) ;
43129
44- const getFlowData = ( ) => {
45- if ( reactFlowInstance ) {
46- // save might get triggered when the flow is running, don't store animated edges
47- const updatedEdges = reactFlowInstance . getEdges ( ) . map ( ( edge ) => {
48- return {
49- ...edge ,
50- animated : false ,
51- } ;
52- } ) ;
53- const rfInstanceObject = reactFlowInstance . toObject ( ) ;
54- rfInstanceObject . edges = updatedEdges ;
55- return rfInstanceObject ;
56- }
57- } ;
58-
59130 const nodeTypes = useMemo (
60131 ( ) => ( {
61132 startNode : StartNode ,
@@ -75,9 +146,21 @@ const Flow = ({ tabId, collectionId, flowData }) => {
75146 [ ] ,
76147 ) ;
77148
78- const [ nodes , setNodes , onNodesChange ] = useNodesState ( ) ;
149+ const [ nodes , setNodes , onNodesChange ] = useNodesState ( [ ] ) ;
79150 const [ edges , setEdges , onEdgesChange ] = useEdgesState ( [ ] ) ;
80151
152+ useEffect ( ( ) => {
153+ // skip inital render
154+ if ( isEqual ( nodes , [ ] ) && isEqual ( edges , [ ] ) ) {
155+ return ;
156+ }
157+ if ( flowData && isEqual ( JSON . parse ( JSON . stringify ( nodes ) ) , flowData . nodes ) && isEqual ( edges , flowData . edges ) ) {
158+ console . debug ( 'canvas is unchanged' ) ;
159+ return ;
160+ }
161+ setCanvasDirty ( ) ;
162+ } , [ nodes , edges ] ) ;
163+
81164 const onConnect = ( params ) => {
82165 const newEdge = {
83166 ...params ,
@@ -133,72 +216,10 @@ const Flow = ({ tabId, collectionId, flowData }) => {
133216 console . debug ( 'Dropped node: ' , newNode ) ;
134217
135218 setNodes ( ( nds ) => nds . concat ( newNode ) ) ;
136- setCanvasDirty ( ) ;
137219 } ,
138220 [ reactFlowInstance ] ,
139221 ) ;
140222
141- // Initialization
142- useEffect ( ( ) => {
143- if ( flowData && flowData . nodes && flowData . edges ) {
144- setNodes ( flowData . nodes ) ;
145- setEdges ( flowData . edges ) ;
146- } else if ( flowData && flowData . nodes ) {
147- // AI prompt generated
148- const nodes = [
149- { id : '0' , type : 'startNode' , position : { x : 150 , y : 150 } , deletable : false } ,
150- { id : '1' , type : 'authNode' , position : { x : 400 , y : 150 } , data : { } , deletable : false } ,
151- ] ;
152- const edges = [
153- {
154- id : `reactflow__edge-0-1` ,
155- source : `0` ,
156- sourceHandle : null ,
157- target : `1` ,
158- targetHandle : null ,
159- type : 'buttonedge' ,
160- } ,
161- ] ;
162-
163- for ( let i = 2 ; i <= flowData . nodes . length ; i ++ ) {
164- nodes . push ( {
165- id : `${ i } ` ,
166- type : flowData . nodes [ i - 1 ] . type ,
167- position : { x : 150 + i * 500 , y : 50 } ,
168- data : flowData . nodes [ i - 1 ] ,
169- } ) ;
170-
171- edges . push ( {
172- id : `reactflow__edge-${ i - 1 } -${ i } ` ,
173- source : `${ i - 1 } ` ,
174- sourceHandle : null ,
175- target : `${ i } ` ,
176- targetHandle : null ,
177- type : 'buttonedge' ,
178- } ) ;
179- }
180- setNodes ( nodes ) ;
181- setEdges ( edges ) ;
182- setCanvasDirty ( ) ;
183- } else {
184- setNodes ( [
185- { id : '0' , type : 'startNode' , position : { x : 150 , y : 150 } , deletable : false } ,
186- { id : '1' , type : 'authNode' , position : { x : 400 , y : 150 } , data : { } , deletable : false } ,
187- ] ) ;
188- setEdges ( [
189- {
190- id : `reactflow__edge-0-1` ,
191- source : `0` ,
192- sourceHandle : null ,
193- target : `1` ,
194- targetHandle : null ,
195- type : 'buttonedge' ,
196- } ,
197- ] ) ;
198- setCanvasDirty ( ) ;
199- }
200- } , [ ] ) ;
201-
202223 const isValidConnection = ( connection ) => {
203224 let canConnect = true ;
204225 // Only 1 outgoing edge from each (source, sourceHandle) is allowed
0 commit comments