diff --git a/static/skywire-manager-src/src/app/app.component.html b/static/skywire-manager-src/src/app/app.component.html
index a461d0eea..7c8353e9a 100644
--- a/static/skywire-manager-src/src/app/app.component.html
+++ b/static/skywire-manager-src/src/app/app.component.html
@@ -7,6 +7,6 @@
diff --git a/static/skywire-manager-src/src/app/app.component.ts b/static/skywire-manager-src/src/app/app.component.ts
index 1decc85d1..59c344c98 100644
--- a/static/skywire-manager-src/src/app/app.component.ts
+++ b/static/skywire-manager-src/src/app/app.component.ts
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
-import { of } from 'rxjs';
+import { of, Subscription } from 'rxjs';
import { delay, flatMap } from 'rxjs/operators';
import { StorageService } from './services/storage.service';
@@ -19,12 +19,20 @@ import { processServiceError } from './utils/errors';
styleUrls: ['./app.component.scss']
})
export class AppComponent {
+ static currentInstance: AppComponent;
+
// If the app is showing the VPN client.
inVpnClient = false;
+ // If the app is in the login page. Needed to know if content should be shown even if
+ // hypervisorPkObtained is false.
+ inLoginPage = false;
// If the pk of the hypervisor has been obtained.
hypervisorPkObtained = false;
pkErrorShown = false;
+ pkErrorsFound = 0;
+
+ obtainPkSubscription: Subscription;
constructor(
// Imported to call its constructor right after opening the app.
@@ -35,6 +43,8 @@ export class AppComponent {
private languageService: LanguageService,
private apiService: ApiService,
) {
+ AppComponent.currentInstance = this;
+
// Close the snackbar when opening a modal window.
dialog.afterOpened.subscribe(() => snackbarService.closeCurrent());
@@ -53,9 +63,19 @@ export class AppComponent {
dialog.afterAllClosed.subscribe(() => snackbarService.closeCurrentIfTemporaryError());
// Check if the app is showing the VPN client.
- router.events.subscribe(() => {
+ router.events.subscribe((e: any) => {
this.inVpnClient = router.url.includes('/vpn/') || router.url.includes('vpnlogin');
+ // Check if the user enters or leaves the login page.
+ if (e.url) {
+ const previousInLoginPageValue = this.inLoginPage;
+ this.inLoginPage = e.url.includes('login');
+
+ if (previousInLoginPageValue && !this.inLoginPage && !this.hypervisorPkObtained) {
+ this.checkHypervisorPk(0);
+ }
+ }
+
// Show the correct document title.
if (router.url.length > 2) {
if (this.inVpnClient) {
@@ -72,11 +92,26 @@ export class AppComponent {
this.checkHypervisorPk(0);
}
+ /**
+ * This should be called a frame before leaving the login page, to avoid race conditions in which the
+ * automatic event code in the constructor changes the value of inLoginPage to false but Angular still
+ * loads the content of the new page just before taking that value into account.
+ */
+ processLoginDone() {
+ this.inLoginPage = false;
+ if (!this.hypervisorPkObtained) {
+ this.checkHypervisorPk(0);
+ }
+ }
+
/**
* Gets the pk of the hypervisor. After that, it initializes services and allows the app to start working.
*/
private checkHypervisorPk(delayMs: number) {
- of(1).pipe(delay(delayMs), flatMap(() => this.apiService.get('about'))).subscribe(result => {
+ if (this.obtainPkSubscription) {
+ this.obtainPkSubscription.unsubscribe();
+ }
+ this.obtainPkSubscription = of(1).pipe(delay(delayMs), flatMap(() => this.apiService.get('about'))).subscribe(result => {
if (result.public_key) {
this.finishStartup(result.public_key);
this.hypervisorPkObtained = true;
@@ -88,12 +123,17 @@ export class AppComponent {
this.checkHypervisorPk(1000);
}
}, err => {
- if (!this.pkErrorShown) {
+ this.pkErrorsFound += 1;
+
+ if (this.pkErrorsFound > 4 && !this.pkErrorShown) {
const e = processServiceError(err);
this.snackbarService.showError('start.loading-error', null, true, e);
this.pkErrorShown = true;
}
- this.checkHypervisorPk(1000);
+
+ if (!this.inLoginPage) {
+ this.checkHypervisorPk(1000);
+ }
});
}
diff --git a/static/skywire-manager-src/src/app/components/pages/login/login.component.ts b/static/skywire-manager-src/src/app/components/pages/login/login.component.ts
index 5d604931b..4fc45381d 100644
--- a/static/skywire-manager-src/src/app/components/pages/login/login.component.ts
+++ b/static/skywire-manager-src/src/app/components/pages/login/login.component.ts
@@ -10,6 +10,7 @@ import { SnackbarService } from '../../../services/snackbar.service';
import { InitialSetupComponent } from './initial-setup/initial-setup.component';
import { OperationError } from '../../../utils/operation-error';
import { processServiceError } from '../../../utils/errors';
+import { AppComponent } from 'src/app/app.component';
/**
* Login page.
@@ -46,8 +47,12 @@ export class LoginComponent implements OnInit, OnDestroy {
// Check if the user is already logged.
this.verificationSubscription = this.authService.checkLogin().subscribe(response => {
if (response !== AuthStates.NotLogged) {
- const destination = !this.isForVpn ? ['nodes'] : ['vpn', this.vpnKey, 'status'];
- this.router.navigate(destination, { replaceUrl: true });
+ // Inform about the redirect a frame before it is done, to avoid problems.
+ AppComponent.currentInstance.processLoginDone();
+ setTimeout(() => {
+ const destination = !this.isForVpn ? ['nodes'] : ['vpn', this.vpnKey, 'status'];
+ this.router.navigate(destination, { replaceUrl: true });
+ });
}
});
});
@@ -83,8 +88,12 @@ export class LoginComponent implements OnInit, OnDestroy {
}
private onLoginSuccess() {
- const destination = !this.isForVpn ? ['nodes'] : ['vpn', this.vpnKey, 'status'];
- this.router.navigate(destination, { replaceUrl: true });
+ // Inform about the redirect a frame before it is done, to avoid problems.
+ AppComponent.currentInstance.processLoginDone();
+ setTimeout(() => {
+ const destination = !this.isForVpn ? ['nodes'] : ['vpn', this.vpnKey, 'status'];
+ this.router.navigate(destination, { replaceUrl: true });
+ });
}
private onLoginError(err: OperationError) {