1
1
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext' ;
2
2
import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant' ;
3
3
import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose' ;
4
+ import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue' ;
5
+ import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2' ;
4
6
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2' ;
5
7
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2' ;
6
8
import { WorkflowDiagramCustomMarkers } from '@/workflow/workflow-diagram/components/WorkflowDiagramCustomMarkers' ;
7
9
import { useRightDrawerState } from '@/workflow/workflow-diagram/hooks/useRightDrawerState' ;
8
10
import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState' ;
11
+ import { workflowDiagramWaitingNodesDimensionsComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramWaitingNodesDimensionsComponentState' ;
9
12
import {
13
+ WorkflowDiagram ,
10
14
WorkflowDiagramEdge ,
11
15
WorkflowDiagramEdgeType ,
12
16
WorkflowDiagramNode ,
13
17
WorkflowDiagramNodeType ,
14
18
} from '@/workflow/workflow-diagram/types/WorkflowDiagram' ;
15
19
import { getOrganizedDiagram } from '@/workflow/workflow-diagram/utils/getOrganizedDiagram' ;
20
+ import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState' ;
16
21
import { useTheme } from '@emotion/react' ;
17
22
import styled from '@emotion/styled' ;
18
23
import {
@@ -28,18 +33,11 @@ import {
28
33
useReactFlow ,
29
34
} from '@xyflow/react' ;
30
35
import '@xyflow/react/dist/style.css' ;
31
- import React , {
32
- useCallback ,
33
- useContext ,
34
- useEffect ,
35
- useMemo ,
36
- useRef ,
37
- useState ,
38
- } from 'react' ;
36
+ import React , { useContext , useEffect , useMemo , useRef , useState } from 'react' ;
37
+ import { useRecoilCallback } from 'recoil' ;
39
38
import { isDefined } from 'twenty-shared/utils' ;
40
39
import { Tag , TagColor } from 'twenty-ui/components' ;
41
40
import { THEME_COMMON } from 'twenty-ui/theme' ;
42
- import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState' ;
43
41
44
42
const StyledResetReactflowStyles = styled . div `
45
43
height: 100%;
@@ -134,15 +132,25 @@ export const WorkflowDiagramCanvasBase = ({
134
132
const workflowDiagram = useRecoilComponentValueV2 (
135
133
workflowDiagramComponentState ,
136
134
) ;
137
-
135
+ const workflowDiagramState = useRecoilComponentCallbackStateV2 (
136
+ workflowDiagramComponentState ,
137
+ ) ;
138
+ const setWorkflowDiagram = useSetRecoilComponentStateV2 (
139
+ workflowDiagramComponentState ,
140
+ ) ;
138
141
const setWorkflowInsertStepIds = useSetRecoilComponentStateV2 (
139
142
workflowInsertStepIdsComponentState ,
140
143
) ;
144
+ const workflowDiagramWaitingNodesDimensionsState =
145
+ useRecoilComponentCallbackStateV2 (
146
+ workflowDiagramWaitingNodesDimensionsComponentState ,
147
+ ) ;
148
+ const setWorkflowDiagramWaitingNodesDimensions = useSetRecoilComponentStateV2 (
149
+ workflowDiagramWaitingNodesDimensionsComponentState ,
150
+ ) ;
141
151
142
- const [
143
- workflowDiagramFlowInitializationStatus ,
144
- setWorkflowDiagramFlowInitializationStatus ,
145
- ] = useState < 'not-initialized' | 'initialized' > ( 'not-initialized' ) ;
152
+ const [ workflowDiagramFlowInitialized , setWorkflowDiagramFlowInitialized ] =
153
+ useState < boolean > ( false ) ;
146
154
147
155
const { nodes, edges } = useMemo (
148
156
( ) =>
@@ -155,10 +163,6 @@ export const WorkflowDiagramCanvasBase = ({
155
163
const { rightDrawerState } = useRightDrawerState ( ) ;
156
164
const { isInRightDrawer } = useContext ( ActionMenuContext ) ;
157
165
158
- const setWorkflowDiagram = useSetRecoilComponentStateV2 (
159
- workflowDiagramComponentState ,
160
- ) ;
161
-
162
166
const handleEdgesChange = (
163
167
edgeChanges : Array < EdgeChange < WorkflowDiagramEdge > > ,
164
168
) => {
@@ -188,97 +192,172 @@ export const WorkflowDiagramCanvasBase = ({
188
192
189
193
const containerRef = useRef < HTMLDivElement > ( null ) ;
190
194
191
- const setFlowViewport = useCallback (
192
- ( {
193
- rightDrawerState,
194
- noAnimation,
195
- workflowDiagramFlowInitializationStatus,
196
- isInRightDrawer,
197
- } : {
198
- rightDrawerState : CommandMenuAnimationVariant ;
199
- noAnimation ?: boolean ;
200
- workflowDiagramFlowInitializationStatus :
201
- | 'not-initialized'
202
- | 'initialized' ;
203
- isInRightDrawer : boolean ;
204
- } ) => {
205
- if (
206
- ! isDefined ( containerRef . current ) ||
207
- workflowDiagramFlowInitializationStatus !== 'initialized'
208
- ) {
209
- return ;
210
- }
195
+ const setFlowViewport = useRecoilCallback (
196
+ ( ) =>
197
+ ( {
198
+ rightDrawerState,
199
+ noAnimation,
200
+ workflowDiagramFlowInitialized,
201
+ isInRightDrawer,
202
+ workflowDiagram,
203
+ } : {
204
+ rightDrawerState : CommandMenuAnimationVariant ;
205
+ noAnimation ?: boolean ;
206
+ workflowDiagramFlowInitialized : boolean ;
207
+ isInRightDrawer : boolean ;
208
+ workflowDiagram : WorkflowDiagram | undefined ;
209
+ } ) => {
210
+ if (
211
+ ! isDefined ( containerRef . current ) ||
212
+ ! workflowDiagramFlowInitialized
213
+ ) {
214
+ return ;
215
+ }
211
216
212
- const currentViewport = reactflow . getViewport ( ) ;
213
- const flowBounds = reactflow . getNodesBounds ( reactflow . getNodes ( ) ) ;
217
+ const currentViewport = reactflow . getViewport ( ) ;
218
+ const nodes = workflowDiagram ?. nodes ?? [ ] ;
214
219
215
- let visibleRightDrawerWidth = 0 ;
216
- if ( rightDrawerState === 'normal' && ! isInRightDrawer ) {
217
- const rightDrawerWidth = Number (
218
- THEME_COMMON . rightDrawerWidth . replace ( 'px' , '' ) ,
220
+ const canComputeNodesBounds = nodes . every ( ( node ) =>
221
+ isDefined ( node . measured ) ,
219
222
) ;
220
223
221
- visibleRightDrawerWidth = rightDrawerWidth ;
222
- }
224
+ if ( ! canComputeNodesBounds ) {
225
+ setWorkflowDiagramWaitingNodesDimensions ( true ) ;
226
+ return ;
227
+ }
228
+
229
+ setWorkflowDiagramWaitingNodesDimensions ( false ) ;
230
+
231
+ let visibleRightDrawerWidth = 0 ;
232
+ if ( rightDrawerState === 'normal' && ! isInRightDrawer ) {
233
+ const rightDrawerWidth = Number (
234
+ THEME_COMMON . rightDrawerWidth . replace ( 'px' , '' ) ,
235
+ ) ;
236
+
237
+ visibleRightDrawerWidth = rightDrawerWidth ;
238
+ }
223
239
224
- const viewportX =
225
- ( containerRef . current . offsetWidth + visibleRightDrawerWidth ) / 2 -
226
- flowBounds . width / 2 ;
227
-
228
- reactflow . setViewport (
229
- {
230
- ...currentViewport ,
231
- x : viewportX - visibleRightDrawerWidth ,
232
- zoom : defaultFitViewOptions . maxZoom ,
233
- } ,
234
- { duration : noAnimation ? 0 : 300 } ,
235
- ) ;
236
- } ,
237
- [ reactflow ] ,
240
+ const flowBounds = reactflow . getNodesBounds ( nodes ) ;
241
+ const viewportX =
242
+ ( containerRef . current . offsetWidth + visibleRightDrawerWidth ) / 2 -
243
+ flowBounds . width / 2 ;
244
+
245
+ reactflow . setViewport (
246
+ {
247
+ ...currentViewport ,
248
+ x : viewportX - visibleRightDrawerWidth ,
249
+ zoom : defaultFitViewOptions . maxZoom ,
250
+ } ,
251
+ { duration : noAnimation ? 0 : 300 } ,
252
+ ) ;
253
+ } ,
254
+ [ reactflow , setWorkflowDiagramWaitingNodesDimensions ] ,
255
+ ) ;
256
+
257
+ const handleSetFlowViewportOnChange = useRecoilCallback (
258
+ ( { snapshot } ) =>
259
+ ( {
260
+ rightDrawerState,
261
+ workflowDiagramFlowInitialized,
262
+ isInRightDrawer,
263
+ } : {
264
+ rightDrawerState : CommandMenuAnimationVariant ;
265
+ workflowDiagramFlowInitialized : boolean ;
266
+ isInRightDrawer : boolean ;
267
+ } ) => {
268
+ setFlowViewport ( {
269
+ rightDrawerState,
270
+ isInRightDrawer,
271
+ workflowDiagramFlowInitialized,
272
+ workflowDiagram : getSnapshotValue ( snapshot , workflowDiagramState ) ,
273
+ } ) ;
274
+ } ,
275
+ [ setFlowViewport , workflowDiagramState ] ,
238
276
) ;
239
277
240
278
useEffect ( ( ) => {
241
- setFlowViewport ( {
279
+ handleSetFlowViewportOnChange ( {
242
280
rightDrawerState,
281
+ workflowDiagramFlowInitialized,
243
282
isInRightDrawer,
244
- workflowDiagramFlowInitializationStatus,
245
283
} ) ;
246
284
} , [
285
+ handleSetFlowViewportOnChange ,
247
286
isInRightDrawer ,
248
287
rightDrawerState ,
249
- setFlowViewport ,
250
- workflowDiagramFlowInitializationStatus ,
288
+ workflowDiagramFlowInitialized ,
251
289
] ) ;
252
290
253
- const handleNodesChanges = ( changes : NodeChange < WorkflowDiagramNode > [ ] ) => {
254
- setWorkflowDiagram ( ( diagram ) => {
255
- if ( ! isDefined ( diagram ) ) {
256
- return diagram ;
257
- }
291
+ const handleNodesChanges = useRecoilCallback (
292
+ ( { snapshot, set } ) =>
293
+ ( changes : NodeChange < WorkflowDiagramNode > [ ] ) => {
294
+ const workflowDiagram = getSnapshotValue (
295
+ snapshot ,
296
+ workflowDiagramState ,
297
+ ) ;
298
+ let updatedWorkflowDiagram = workflowDiagram ;
299
+ if ( isDefined ( workflowDiagram ) ) {
300
+ updatedWorkflowDiagram = {
301
+ ...workflowDiagram ,
302
+ nodes : applyNodeChanges ( changes , workflowDiagram . nodes ) ,
303
+ } ;
304
+ }
258
305
259
- return {
260
- ...diagram ,
261
- nodes : applyNodeChanges ( changes , diagram . nodes ) ,
262
- } ;
263
- } ) ;
264
- } ;
306
+ set ( workflowDiagramState , updatedWorkflowDiagram ) ;
265
307
266
- const handleInit = ( ) => {
267
- if ( ! isDefined ( containerRef . current ) ) {
268
- return ;
269
- }
308
+ const workflowDiagramWaitingNodesDimensions = getSnapshotValue (
309
+ snapshot ,
310
+ workflowDiagramWaitingNodesDimensionsState ,
311
+ ) ;
312
+ if ( ! workflowDiagramWaitingNodesDimensions ) {
313
+ return ;
314
+ }
270
315
271
- setFlowViewport ( {
272
- rightDrawerState,
273
- noAnimation : true ,
316
+ setFlowViewport ( {
317
+ rightDrawerState,
318
+ noAnimation : true ,
319
+ isInRightDrawer,
320
+ workflowDiagramFlowInitialized,
321
+ workflowDiagram : updatedWorkflowDiagram ,
322
+ } ) ;
323
+ } ,
324
+ [
274
325
isInRightDrawer ,
275
- workflowDiagramFlowInitializationStatus : 'initialized' ,
276
- } ) ;
326
+ rightDrawerState ,
327
+ setFlowViewport ,
328
+ workflowDiagramFlowInitialized ,
329
+ workflowDiagramState ,
330
+ workflowDiagramWaitingNodesDimensionsState ,
331
+ ] ,
332
+ ) ;
333
+
334
+ const handleInit = useRecoilCallback (
335
+ ( { snapshot } ) =>
336
+ ( ) => {
337
+ if ( ! isDefined ( containerRef . current ) ) {
338
+ return ;
339
+ }
277
340
278
- setWorkflowDiagramFlowInitializationStatus ( 'initialized' ) ;
341
+ setFlowViewport ( {
342
+ rightDrawerState,
343
+ noAnimation : true ,
344
+ isInRightDrawer,
345
+ workflowDiagramFlowInitialized : true ,
346
+ workflowDiagram : getSnapshotValue ( snapshot , workflowDiagramState ) ,
347
+ } ) ;
279
348
280
- onInit ?.( ) ;
281
- } ;
349
+ setWorkflowDiagramFlowInitialized ( true ) ;
350
+
351
+ onInit ?.( ) ;
352
+ } ,
353
+ [
354
+ isInRightDrawer ,
355
+ onInit ,
356
+ rightDrawerState ,
357
+ setFlowViewport ,
358
+ workflowDiagramState ,
359
+ ] ,
360
+ ) ;
282
361
283
362
return (
284
363
< StyledResetReactflowStyles ref = { containerRef } >
0 commit comments