@@ -59,6 +59,9 @@ export class Remote {
59
59
private readonly pathResolver :PathResolver ;
60
60
private readonly cliManager :CliManager ;
61
61
62
+ private loginDetectedResolver :( ( ) => void ) | undefined ;
63
+ private loginDetectedPromise :Promise < void > = Promise . resolve ( ) ;
64
+
62
65
public constructor (
63
66
serviceContainer :ServiceContainer ,
64
67
private readonly commands :Commands ,
@@ -70,6 +73,27 @@ export class Remote {
70
73
this . cliManager = serviceContainer . getCliManager ( ) ;
71
74
}
72
75
76
+ /**
77
+ * Creates a new promise that will be resolved when login is detected in another window.
78
+ * This should be called when starting a setup operation that might need login.
79
+ */
80
+ private createLoginDetectionPromise ( ) :void {
81
+ this . loginDetectedPromise = new Promise < void > ( ( resolve ) => {
82
+ this . loginDetectedResolver = resolve ;
83
+ } ) ;
84
+ }
85
+
86
+ /**
87
+ * Resolves the current login detection promise if one exists.
88
+ * This should be called from the extension when login is detected.
89
+ */
90
+ public resolveLoginDetected ( ) :void {
91
+ if ( this . loginDetectedResolver ) {
92
+ this . loginDetectedResolver ( ) ;
93
+ this . loginDetectedResolver = undefined ;
94
+ }
95
+ }
96
+
73
97
private async confirmStart ( workspaceName :string ) :Promise < boolean > {
74
98
const action = await this . vscodeProposed . window . showInformationMessage (
75
99
`Unable to connect to the workspace${ workspaceName } because it is not running. Start the workspace?` ,
@@ -233,34 +257,54 @@ export class Remote {
233
257
// Migrate "session_token" file to "session", if needed.
234
258
await this . migrateSessionToken ( parts . label ) ;
235
259
260
+ // Try to detect any login event that might happen after we read the current configs
261
+ this . createLoginDetectionPromise ( ) ;
236
262
// Get the URL and token belonging to this host.
237
263
const { url :baseUrlRaw , token} = await this . cliManager . readConfig (
238
264
parts . label ,
239
265
) ;
240
266
241
- // It could be that the cli config was deleted. If so, ask for the url.
242
- if (
243
- ! baseUrlRaw ||
244
- ( ! token && needToken ( vscode . workspace . getConfiguration ( ) ) )
245
- ) {
246
- const result = await this . vscodeProposed . window . showInformationMessage (
247
- "You are not logged in..." ,
267
+ const showLoginDialog = async ( message :string ) => {
268
+ const dialogPromise = this . vscodeProposed . window . showInformationMessage (
269
+ message ,
248
270
{
249
271
useCustom :true ,
250
272
modal :true ,
251
- detail :`You must log in to access${ workspaceName } .` ,
273
+ detail :`You must log in to access${ workspaceName } . If you've already logged in, you may close this dialog. ` ,
252
274
} ,
253
275
"Log In" ,
254
276
) ;
255
- if ( ! result ) {
256
- // User declined to log in.
257
- await this . closeRemote ( ) ;
277
+
278
+ // Race between dialog and login detection
279
+ const result = await Promise . race ( [
280
+ this . loginDetectedPromise . then ( ( ) => ( { type :"login" as const } ) ) ,
281
+ dialogPromise . then ( ( userChoice ) => ( {
282
+ type :"dialog" as const ,
283
+ userChoice,
284
+ } ) ) ,
285
+ ] ) ;
286
+
287
+ if ( result . type === "login" ) {
288
+ return this . setup ( remoteAuthority , firstConnect ) ;
258
289
} else {
259
- // Log in then try again.
260
- await this . commands . login ( { url :baseUrlRaw , label :parts . label } ) ;
261
- await this . setup ( remoteAuthority , firstConnect ) ;
290
+ if ( ! result . userChoice ) {
291
+ // User declined to log in.
292
+ await this . closeRemote ( ) ;
293
+ return ;
294
+ } else {
295
+ // Log in then try again.
296
+ await this . commands . login ( { url :baseUrlRaw , label :parts . label } ) ;
297
+ return this . setup ( remoteAuthority , firstConnect ) ;
298
+ }
262
299
}
263
- return ;
300
+ } ;
301
+
302
+ // It could be that the cli config was deleted. If so, ask for the url.
303
+ if (
304
+ ! baseUrlRaw ||
305
+ ( ! token && needToken ( vscode . workspace . getConfiguration ( ) ) )
306
+ ) {
307
+ return showLoginDialog ( "You are not logged in..." ) ;
264
308
}
265
309
266
310
this . logger . info ( "Using deployment URL" , baseUrlRaw ) ;
@@ -326,6 +370,8 @@ export class Remote {
326
370
// Next is to find the workspace from the URI scheme provided.
327
371
let workspace :Workspace ;
328
372
try {
373
+ // We could've logged out in the meantime
374
+ this . createLoginDetectionPromise ( ) ;
329
375
this . logger . info ( `Looking for workspace${ workspaceName } ...` ) ;
330
376
workspace = await workspaceClient . getWorkspaceByOwnerAndName (
331
377
parts . username ,
@@ -359,23 +405,7 @@ export class Remote {
359
405
return ;
360
406
}
361
407
case 401 :{
362
- const result =
363
- await this . vscodeProposed . window . showInformationMessage (
364
- "Your session expired..." ,
365
- {
366
- useCustom :true ,
367
- modal :true ,
368
- detail :`You must log in to access${ workspaceName } .` ,
369
- } ,
370
- "Log In" ,
371
- ) ;
372
- if ( ! result ) {
373
- await this . closeRemote ( ) ;
374
- } else {
375
- await this . commands . login ( { url :baseUrlRaw , label :parts . label } ) ;
376
- await this . setup ( remoteAuthority , firstConnect ) ;
377
- }
378
- return ;
408
+ return showLoginDialog ( "Your session expired..." ) ;
379
409
}
380
410
default :
381
411
throw error ;