11import React , { useCallback , useLayoutEffect , useState } from 'react' ;
2+ import type { ElectronShowFileDialogProvider } from '@mongodb-js/compass-components' ;
23import {
34 HorizontalRule ,
45 css ,
56 resetGlobalCSS ,
67 spacing ,
8+ FileInputBackendProvider ,
9+ createElectronFileInputBackend ,
10+ type ElectronFileDialogOptions ,
711} from '@mongodb-js/compass-components' ;
12+ import type { ConnectionOptions } from 'mongodb-data-service' ;
813
914import OverviewHeader from './overview-header' ;
1015import ConnectionStatus from './connection-status' ;
1116import ConnectHelper from './connect-helper' ;
1217import AtlasCta from './atlas-cta' ;
1318import ResourcesPanel from './resources-panel/panel' ;
1419import { ConnectionForm } from './connection-form' ;
15- import useConnectionForm from './use-connection-form' ;
20+ import useConnectionForm , {
21+ FILE_CHOOSER_MODE ,
22+ type FileChooserOptions ,
23+ } from './use-connection-form' ;
24+ import type { MessageFromExtensionToWebview } from './extension-app-message-constants' ;
25+ import { MESSAGE_TYPES } from './extension-app-message-constants' ;
1626
1727const pageStyles = css ( {
1828 width : '90%' ,
@@ -39,6 +49,7 @@ const OverviewPage: React.FC = () => {
3949 handleCancelConnectClicked,
4050 handleSaveConnectionClicked,
4151 handleConnectClicked,
52+ handleOpenFileChooser,
4253 } = useConnectionForm ( ) ;
4354 const handleResourcesPanelClose = useCallback (
4455 ( ) => setShowResourcesPanel ( false ) ,
@@ -55,30 +66,91 @@ const OverviewPage: React.FC = () => {
5566 resetGlobalCSS ( ) ;
5667 } , [ ] ) ;
5768
69+ function handleOpenFileChooserResult < T > (
70+ options : FileChooserOptions
71+ ) : Promise < T > {
72+ const requestId = handleOpenFileChooser ( options ) ;
73+ return new Promise ( ( resolve ) => {
74+ const messageHandler = (
75+ event : MessageEvent < MessageFromExtensionToWebview >
76+ ) : void => {
77+ const message = event . data ;
78+ if (
79+ message . command === MESSAGE_TYPES . OPEN_FILE_CHOOSER_RESULT &&
80+ message . requestId === requestId
81+ ) {
82+ window . removeEventListener ( 'message' , messageHandler ) ;
83+ resolve ( message . fileChooserResult as T ) ;
84+ }
85+ } ;
86+ window . addEventListener ( 'message' , messageHandler ) ;
87+ } ) ;
88+ }
89+
90+ // Electron 32.0 removed support for the `path` property of the Web File object in favor of the webUtils.getPathForFile method.
91+ // https://github.com/electron/electron/blob/83d704009687956fb4b69cb13ab03664d7950118/docs/breaking-changes.md%23removed-filepath
92+ // We can not import `dialog` and `webUtils` from 'electron' in the sandboxed webview.
93+ // To work around this, we use a custom dialog provider that uses webview APIs
94+ // to send a message to the extension process to open the electron file dialog
95+ // and listen for the response to get the file path and send them to the electron file input backend.
96+ const dialogProvider : ElectronShowFileDialogProvider < void > = {
97+ getCurrentWindow ( ) : void { } ,
98+ dialog : {
99+ async showSaveDialog (
100+ window : void ,
101+ electronFileDialogOptions : Partial < ElectronFileDialogOptions >
102+ ) : Promise < { canceled : boolean ; filePath ?: string } > {
103+ return handleOpenFileChooserResult ( {
104+ electronFileDialogOptions,
105+ mode : FILE_CHOOSER_MODE . SAVE ,
106+ } ) ;
107+ } ,
108+ async showOpenDialog (
109+ window : void ,
110+ electronFileDialogOptions : Partial < ElectronFileDialogOptions >
111+ ) : Promise < { canceled : boolean ; filePaths : string [ ] } > {
112+ return handleOpenFileChooserResult ( {
113+ electronFileDialogOptions,
114+ mode : FILE_CHOOSER_MODE . OPEN ,
115+ } ) ;
116+ } ,
117+ } ,
118+ } ;
119+
58120 return (
59121 < div data-testid = "overview-page" className = { pageStyles } >
60122 { showResourcesPanel && (
61123 < ResourcesPanel onClose = { handleResourcesPanelClose } />
62124 ) }
63125 { isConnectionFormOpen && (
64- < ConnectionForm
65- isConnecting = { isConnecting }
66- initialConnectionInfo = { initialConnectionInfo }
67- onSaveAndConnectClicked = { ( { id, connectionOptions } ) => {
68- void handleSaveConnectionClicked ( {
69- id,
70- connectionOptions,
71- } ) ;
72- handleConnectClicked ( {
126+ < FileInputBackendProvider
127+ createFileInputBackend = { createElectronFileInputBackend (
128+ dialogProvider ,
129+ null
130+ ) }
131+ >
132+ < ConnectionForm
133+ isConnecting = { isConnecting }
134+ initialConnectionInfo = { initialConnectionInfo }
135+ onSaveAndConnectClicked = { ( {
73136 id,
74137 connectionOptions,
75- } ) ;
76- } }
77- onCancelConnectClicked = { handleCancelConnectClicked }
78- onClose = { closeConnectionForm }
79- open = { isConnectionFormOpen }
80- connectionErrorMessage = { connectionErrorMessage }
81- />
138+ } : {
139+ id : string ;
140+ connectionOptions : ConnectionOptions ;
141+ } ) : void => {
142+ void handleSaveConnectionClicked ( ) ;
143+ handleConnectClicked ( {
144+ id,
145+ connectionOptions,
146+ } ) ;
147+ } }
148+ onCancelConnectClicked = { handleCancelConnectClicked }
149+ onClose = { closeConnectionForm }
150+ open = { isConnectionFormOpen }
151+ connectionErrorMessage = { connectionErrorMessage }
152+ />
153+ </ FileInputBackendProvider >
82154 ) }
83155 < OverviewHeader onResourcesClick = { handleResourcesClick } />
84156 < HorizontalRule />
0 commit comments