@@ -59,6 +59,9 @@ export class Remote {
5959 private readonly pathResolver : PathResolver ;
6060 private readonly cliManager : CliManager ;
6161
62+ private loginDetectedResolver : ( ( ) => void ) | undefined ;
63+ private loginDetectedPromise : Promise < void > = Promise . resolve ( ) ;
64+
6265 public constructor (
6366 serviceContainer : ServiceContainer ,
6467 private readonly commands : Commands ,
@@ -70,6 +73,27 @@ export class Remote {
7073 this . cliManager = serviceContainer . getCliManager ( ) ;
7174 }
7275
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+
7397 private async confirmStart ( workspaceName : string ) : Promise < boolean > {
7498 const action = await this . vscodeProposed . window . showInformationMessage (
7599 `Unable to connect to the workspace ${ workspaceName } because it is not running. Start the workspace?` ,
@@ -233,34 +257,54 @@ export class Remote {
233257 // Migrate "session_token" file to "session", if needed.
234258 await this . migrateSessionToken ( parts . label ) ;
235259
260+ // Try to detect any login event that might happen after we read the current configs
261+ this . createLoginDetectionPromise ( ) ;
236262 // Get the URL and token belonging to this host.
237263 const { url : baseUrlRaw , token } = await this . cliManager . readConfig (
238264 parts . label ,
239265 ) ;
240266
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 ,
248270 {
249271 useCustom : true ,
250272 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. ` ,
252274 } ,
253275 "Log In" ,
254276 ) ;
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 ) ;
258289 } 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+ }
262299 }
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..." ) ;
264308 }
265309
266310 this . logger . info ( "Using deployment URL" , baseUrlRaw ) ;
@@ -331,6 +375,8 @@ export class Remote {
331375 // Next is to find the workspace from the URI scheme provided.
332376 let workspace : Workspace ;
333377 try {
378+ // We could've logged out in the meantime
379+ this . createLoginDetectionPromise ( ) ;
334380 this . logger . info ( `Looking for workspace ${ workspaceName } ...` ) ;
335381 workspace = await workspaceClient . getWorkspaceByOwnerAndName (
336382 parts . username ,
@@ -364,23 +410,7 @@ export class Remote {
364410 return ;
365411 }
366412 case 401 : {
367- const result =
368- await this . vscodeProposed . window . showInformationMessage (
369- "Your session expired..." ,
370- {
371- useCustom : true ,
372- modal : true ,
373- detail : `You must log in to access ${ workspaceName } .` ,
374- } ,
375- "Log In" ,
376- ) ;
377- if ( ! result ) {
378- await this . closeRemote ( ) ;
379- } else {
380- await this . commands . login ( { url : baseUrlRaw , label : parts . label } ) ;
381- await this . setup ( remoteAuthority , firstConnect ) ;
382- }
383- return ;
413+ return showLoginDialog ( "Your session expired..." ) ;
384414 }
385415 default :
386416 throw error ;
0 commit comments