1
- import { MenuModelRegistry } from '@theia/core/lib/common/menu ' ;
1
+ import { DialogError } from '@theia/core/lib/browser/dialogs ' ;
2
2
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding' ;
3
+ import { LabelProvider } from '@theia/core/lib/browser/label-provider' ;
3
4
import { CompositeTreeNode } from '@theia/core/lib/browser/tree' ;
4
- import { DisposableCollection } from '@theia/core/lib/common/disposable' ;
5
+ import { Widget } from '@theia/core/lib/browser/widgets/widget' ;
6
+ import { CancellationTokenSource } from '@theia/core/lib/common/cancellation' ;
7
+ import {
8
+ Disposable ,
9
+ DisposableCollection ,
10
+ } from '@theia/core/lib/common/disposable' ;
11
+ import { MenuModelRegistry } from '@theia/core/lib/common/menu' ;
12
+ import {
13
+ Progress ,
14
+ ProgressUpdate ,
15
+ } from '@theia/core/lib/common/message-service-protocol' ;
5
16
import { nls } from '@theia/core/lib/common/nls' ;
6
17
import { inject , injectable } from '@theia/core/shared/inversify' ;
18
+ import { WorkspaceInputDialogProps } from '@theia/workspace/lib/browser/workspace-input-dialog' ;
19
+ import { v4 } from 'uuid' ;
7
20
import { MainMenuManager } from '../../common/main-menu-manager' ;
8
21
import type { AuthenticationSession } from '../../node/auth/types' ;
9
22
import { AuthenticationClientService } from '../auth/authentication-client-service' ;
@@ -90,7 +103,7 @@ export class NewCloudSketch extends Contribution {
90
103
91
104
private async createNewSketch (
92
105
initialValue ?: string | undefined
93
- ) : Promise < URI | undefined > {
106
+ ) : Promise < unknown > {
94
107
const widget = await this . widgetContribution . widget ;
95
108
const treeModel = this . treeModelFrom ( widget ) ;
96
109
if ( ! treeModel ) {
@@ -102,34 +115,50 @@ export class NewCloudSketch extends Contribution {
102
115
if ( ! rootNode ) {
103
116
return undefined ;
104
117
}
118
+ return this . openWizard ( rootNode , treeModel , initialValue ) ;
119
+ }
105
120
106
- const newSketchName = await this . newSketchName ( rootNode , initialValue ) ;
107
- if ( ! newSketchName ) {
108
- return undefined ;
109
- }
110
- let result : Create . Sketch | undefined | 'conflict' ;
111
- try {
112
- result = await this . createApi . createSketch ( newSketchName ) ;
113
- } catch ( err ) {
114
- if ( isConflict ( err ) ) {
115
- result = 'conflict' ;
116
- } else {
117
- throw err ;
121
+ private withProgress (
122
+ value : string ,
123
+ treeModel : CloudSketchbookTreeModel
124
+ ) : ( progress : Progress ) => Promise < unknown > {
125
+ return async ( progress : Progress ) => {
126
+ let result : Create . Sketch | undefined | 'conflict' ;
127
+ try {
128
+ progress . report ( {
129
+ message : nls . localize (
130
+ 'arduino/cloudSketch/creating' ,
131
+ "Creating remote sketch '{0}'..." ,
132
+ value
133
+ ) ,
134
+ } ) ;
135
+ result = await this . createApi . createSketch ( value ) ;
136
+ } catch ( err ) {
137
+ if ( isConflict ( err ) ) {
138
+ result = 'conflict' ;
139
+ } else {
140
+ throw err ;
141
+ }
142
+ } finally {
143
+ if ( result ) {
144
+ progress . report ( {
145
+ message : nls . localize (
146
+ 'arduino/cloudSketch/synchronizing' ,
147
+ "Synchronizing sketchbook, pulling '{0}'..." ,
148
+ value
149
+ ) ,
150
+ } ) ;
151
+ await treeModel . refresh ( ) ;
152
+ }
153
+ }
154
+ if ( result === 'conflict' ) {
155
+ return this . createNewSketch ( value ) ;
118
156
}
119
- } finally {
120
157
if ( result ) {
121
- await treeModel . refresh ( ) ;
158
+ return this . open ( treeModel , result ) ;
122
159
}
123
- }
124
-
125
- if ( result === 'conflict' ) {
126
- return this . createNewSketch ( newSketchName ) ;
127
- }
128
-
129
- if ( result ) {
130
- return this . open ( treeModel , result ) ;
131
- }
132
- return undefined ;
160
+ return undefined ;
161
+ } ;
133
162
}
134
163
135
164
private async open (
@@ -183,14 +212,15 @@ export class NewCloudSketch extends Contribution {
183
212
return undefined ;
184
213
}
185
214
186
- private async newSketchName (
215
+ private async openWizard (
187
216
rootNode : CompositeTreeNode ,
217
+ treeModel : CloudSketchbookTreeModel ,
188
218
initialValue ?: string | undefined
189
- ) : Promise < string | undefined > {
219
+ ) : Promise < unknown > {
190
220
const existingNames = rootNode . children
191
221
. filter ( CloudSketchbookTree . CloudSketchDirNode . is )
192
222
. map ( ( { fileStat } ) => fileStat . name ) ;
193
- return new WorkspaceInputDialog (
223
+ return new NewCloudSketchDialog (
194
224
{
195
225
title : nls . localize (
196
226
'arduino/newCloudSketch/newSketchTitle' ,
@@ -216,7 +246,8 @@ export class NewCloudSketch extends Contribution {
216
246
) ;
217
247
} ,
218
248
} ,
219
- this . labelProvider
249
+ this . labelProvider ,
250
+ ( value ) => this . withProgress ( value , treeModel )
220
251
) . open ( ) ;
221
252
}
222
253
}
@@ -245,3 +276,97 @@ function isErrorWithStatusOf(
245
276
}
246
277
return false ;
247
278
}
279
+
280
+ @injectable ( )
281
+ class NewCloudSketchDialog extends WorkspaceInputDialog {
282
+ constructor (
283
+ @inject ( WorkspaceInputDialogProps )
284
+ protected override readonly props : WorkspaceInputDialogProps ,
285
+ @inject ( LabelProvider )
286
+ protected override readonly labelProvider : LabelProvider ,
287
+ private readonly withProgress : (
288
+ value : string
289
+ ) => ( progress : Progress ) => Promise < unknown >
290
+ ) {
291
+ super ( props , labelProvider ) ;
292
+ }
293
+ protected override async accept ( ) : Promise < void > {
294
+ if ( ! this . resolve ) {
295
+ return ;
296
+ }
297
+ this . acceptCancellationSource . cancel ( ) ;
298
+ this . acceptCancellationSource = new CancellationTokenSource ( ) ;
299
+ const token = this . acceptCancellationSource . token ;
300
+ const value = this . value ;
301
+ const error = await this . isValid ( value , 'open' ) ;
302
+ if ( token . isCancellationRequested ) {
303
+ return ;
304
+ }
305
+ if ( ! DialogError . getResult ( error ) ) {
306
+ this . setErrorMessage ( error ) ;
307
+ } else {
308
+ const spinner = document . createElement ( 'div' ) ;
309
+ spinner . classList . add ( 'spinner' ) ;
310
+ const disposables = new DisposableCollection ( ) ;
311
+ try {
312
+ this . toggleButtons ( true ) ;
313
+ disposables . push ( Disposable . create ( ( ) => this . toggleButtons ( false ) ) ) ;
314
+
315
+ const closeParent = this . closeCrossNode . parentNode ;
316
+ closeParent ?. removeChild ( this . closeCrossNode ) ;
317
+ disposables . push (
318
+ Disposable . create ( ( ) => {
319
+ closeParent ?. appendChild ( this . closeCrossNode ) ;
320
+ } )
321
+ ) ;
322
+
323
+ this . errorMessageNode . classList . add ( 'progress' ) ;
324
+ disposables . push (
325
+ Disposable . create ( ( ) =>
326
+ this . errorMessageNode . classList . remove ( 'progress' )
327
+ )
328
+ ) ;
329
+
330
+ const errorParent = this . errorMessageNode . parentNode ;
331
+ errorParent ?. insertBefore ( spinner , this . errorMessageNode ) ;
332
+ disposables . push (
333
+ Disposable . create ( ( ) => errorParent ?. removeChild ( spinner ) )
334
+ ) ;
335
+
336
+ const cancellationSource = new CancellationTokenSource ( ) ;
337
+ const progress : Progress = {
338
+ id : v4 ( ) ,
339
+ cancel : ( ) => cancellationSource . cancel ( ) ,
340
+ report : ( update : ProgressUpdate ) => {
341
+ this . setProgressMessage ( update ) ;
342
+ } ,
343
+ result : Promise . resolve ( value ) ,
344
+ } ;
345
+ await this . withProgress ( value ) ( progress ) ;
346
+ } finally {
347
+ disposables . dispose ( ) ;
348
+ }
349
+ this . resolve ( value ) ;
350
+ Widget . detach ( this ) ;
351
+ }
352
+ }
353
+
354
+ private toggleButtons ( disabled : boolean ) : void {
355
+ if ( this . acceptButton ) {
356
+ this . acceptButton . disabled = disabled ;
357
+ }
358
+ if ( this . closeButton ) {
359
+ this . closeButton . disabled = disabled ;
360
+ }
361
+ }
362
+
363
+ private setProgressMessage ( update : ProgressUpdate ) : void {
364
+ if ( update . work && update . work . done === update . work . total ) {
365
+ this . errorMessageNode . innerText = '' ;
366
+ } else {
367
+ if ( update . message ) {
368
+ this . errorMessageNode . innerText = update . message ;
369
+ }
370
+ }
371
+ }
372
+ }
0 commit comments