1- import React , { useCallback , useState } from 'react' ;
1+ import React , { useCallback , useEffect , useRef , useState } from 'react' ;
22import {
33 Button ,
44 css ,
@@ -13,6 +13,7 @@ import {
1313 RadioGroup ,
1414 spacing ,
1515 SpinLoader ,
16+ useToast ,
1617} from '@mongodb-js/compass-components' ;
1718import {
1819 closeExportModal ,
@@ -24,6 +25,7 @@ import type { DataModelingState } from '../store/reducer';
2425import type { StaticModel } from '../services/data-model-storage' ;
2526import { exportToJson , exportToPng } from '../services/export-diagram' ;
2627import { useDiagram } from '@mongodb-js/diagramming' ;
28+ import { isCancelError } from '@mongodb-js/compass-utils' ;
2729
2830const nbsp = '\u00a0' ;
2931
@@ -64,25 +66,68 @@ const ExportDiagramModal = ({
6466 const [ exportFormat , setExportFormat ] = useState < 'png' | 'json' | null > ( null ) ;
6567 const diagram = useDiagram ( ) ;
6668 const [ isExporting , setIsExporting ] = useState ( false ) ;
69+ const abortControllerRef = useRef < AbortController | null > ( null ) ;
70+ const toast = useToast ( ) ;
71+ useEffect ( ( ) => {
72+ const cleanup = ( ) => {
73+ if ( abortControllerRef . current ) {
74+ abortControllerRef . current . abort ( ) ;
75+ abortControllerRef . current = null ;
76+ }
77+ } ;
78+ const abortController = new AbortController ( ) ;
79+ if ( isModalOpen ) {
80+ abortControllerRef . current = abortController ;
81+ } else {
82+ cleanup ( ) ;
83+ }
84+ return cleanup ;
85+ } , [ isModalOpen ] ) ;
86+
87+ const onClose = useCallback ( ( ) => {
88+ setExportFormat ( null ) ;
89+ setIsExporting ( false ) ;
90+ abortControllerRef . current ?. abort ( ) ;
91+ abortControllerRef . current = null ;
92+ onCloseClick ( ) ;
93+ } , [ onCloseClick ] ) ;
6794
6895 const onExport = useCallback ( async ( ) => {
69- if ( ! exportFormat || ! model ) {
70- return ;
96+ try {
97+ if ( ! exportFormat || ! model ) {
98+ return ;
99+ }
100+ setIsExporting ( true ) ;
101+ if ( exportFormat === 'json' ) {
102+ exportToJson ( diagramLabel , model ) ;
103+ } else if ( exportFormat === 'png' ) {
104+ await exportToPng (
105+ diagramLabel ,
106+ diagram ,
107+ abortControllerRef . current ?. signal
108+ ) ;
109+ }
110+ } catch ( error ) {
111+ if ( isCancelError ( error ) ) {
112+ return ;
113+ }
114+ toast . pushToast ( {
115+ id : 'export-diagram-error' ,
116+ variant : 'warning' ,
117+ title : 'Export failed' ,
118+ description : `An error occurred while exporting the diagram: ${
119+ ( error as Error ) . message
120+ } `,
121+ } ) ;
122+ } finally {
123+ onClose ( ) ;
71124 }
72- setIsExporting ( true ) ;
73- if ( exportFormat === 'json' ) {
74- exportToJson ( diagramLabel , model ) ;
75- } else if ( exportFormat === 'png' ) {
76- await exportToPng ( diagramLabel , diagram ) ;
77- }
78- onCloseClick ( ) ;
79- setIsExporting ( false ) ;
80- } , [ exportFormat , onCloseClick , model , diagram , diagramLabel ] ) ;
125+ } , [ exportFormat , onClose , model , diagram , diagramLabel , toast ] ) ;
81126
82127 return (
83128 < Modal
84129 open = { isModalOpen }
85- setOpen = { onCloseClick }
130+ setOpen = { onClose }
86131 data-testid = "export-diagram-modal"
87132 >
88133 < ModalHeader
@@ -140,11 +185,7 @@ const ExportDiagramModal = ({
140185 >
141186 Export
142187 </ Button >
143- < Button
144- variant = "default"
145- onClick = { onCloseClick }
146- data-testid = "cancel-button"
147- >
188+ < Button variant = "default" onClick = { onClose } data-testid = "cancel-button" >
148189 Cancel
149190 </ Button >
150191 </ ModalFooter >
0 commit comments