mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-04-10 20:09:53 +00:00
Progress error status, Fixes: #146
This commit is contained in:
parent
ca45804b5e
commit
db3e700204
@ -1,8 +1,20 @@
|
||||
<div class="overlay" *ngIf="visible">
|
||||
<div class="loading-spinner">
|
||||
<div class="overlay" *ngIf="visible || error">
|
||||
<div class="loading-spinner" *ngIf="visible && !error">
|
||||
<mat-spinner color="primary">
|
||||
</mat-spinner>
|
||||
</div>
|
||||
<div class="error-state" *ngIf="error">
|
||||
<div class="error-icon"><mat-icon>error_outline</mat-icon></div>
|
||||
<div>Error occurred: {{ error.message }}</div>
|
||||
<div>
|
||||
<button mat-button (click)="refresh()" matTooltip="Refresh page" >
|
||||
<mat-icon>refresh</mat-icon>
|
||||
</button>
|
||||
<button mat-button routerLink="/" matTooltip="Go to home" >
|
||||
<mat-icon>home</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -10,9 +10,19 @@
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
.loading-spinner, .error-state {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.error-state div {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-icon mat-icon {
|
||||
font-size: 64px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
@ -1,25 +1,45 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProgressComponent } from './progress.component';
|
||||
import { MatProgressSpinnerModule } from "@angular/material";
|
||||
import { MatIconModule, MatProgressSpinnerModule } from "@angular/material";
|
||||
import { ProgressService } from "./progress.service";
|
||||
import { RouterTestingModule } from "@angular/router/testing";
|
||||
import { Router } from "@angular/router";
|
||||
import { BehaviorSubject } from "rxjs/BehaviorSubject";
|
||||
|
||||
|
||||
class MockedRouter {
|
||||
events: BehaviorSubject<boolean>;
|
||||
|
||||
constructor() {
|
||||
this.events = new BehaviorSubject(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
describe('ProgressComponent', () => {
|
||||
let component: ProgressComponent;
|
||||
let fixture: ComponentFixture<ProgressComponent>;
|
||||
let progressService: ProgressService;
|
||||
let router: MockedRouter;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatIconModule
|
||||
],
|
||||
providers: [
|
||||
ProgressService,
|
||||
{ provide: Router, useClass: MockedRouter}
|
||||
],
|
||||
providers: [ ProgressService ],
|
||||
declarations: [ ProgressComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
progressService = TestBed.get(ProgressService);
|
||||
router = TestBed.get(Router);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
@ -45,4 +65,21 @@ describe('ProgressComponent', () => {
|
||||
expect(component.visible).toEqual(false);
|
||||
});
|
||||
|
||||
it( 'should set error state when error defined', () => {
|
||||
const error = new Error("test");
|
||||
progressService.setError(error);
|
||||
expect(component.error).toEqual(error);
|
||||
});
|
||||
|
||||
it( 'should clear error when changes route', () => {
|
||||
const error = new Error("test");
|
||||
component.error = error;
|
||||
|
||||
spyOn(progressService, 'clear');
|
||||
|
||||
router.events.next(true);
|
||||
|
||||
expect(progressService.clear).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,22 +1,49 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ProgressService } from "./progress.service";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subscription } from "rxjs/Subscription";
|
||||
|
||||
@Component({
|
||||
selector: 'app-progress',
|
||||
templateUrl: './progress.component.html',
|
||||
styleUrls: ['./progress.component.scss']
|
||||
})
|
||||
export class ProgressComponent implements OnInit {
|
||||
export class ProgressComponent implements OnInit, OnDestroy {
|
||||
visible = false;
|
||||
error: Error;
|
||||
routerSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private progressService: ProgressService
|
||||
private progressService: ProgressService,
|
||||
private router: Router,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.progressService.state.subscribe((state) => {
|
||||
this.visible = state.visible;
|
||||
|
||||
// only set error state once; ignore next "correct" states
|
||||
if (state.error && !this.error) {
|
||||
this.error = state.error;
|
||||
}
|
||||
|
||||
if (state.clear) {
|
||||
this.error = null;
|
||||
}
|
||||
});
|
||||
|
||||
// when page changes clear error state
|
||||
this.routerSubscription = this.router.events.subscribe(() => {
|
||||
this.progressService.clear();
|
||||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
// unfortunately we need to use global var
|
||||
location.reload();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.routerSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,43 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { ProgressService } from './progress.service';
|
||||
import { ProgressService, State } from './progress.service';
|
||||
|
||||
|
||||
describe('ProgressService', () => {
|
||||
let progressService: ProgressService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [ProgressService]
|
||||
});
|
||||
|
||||
progressService = TestBed.get(ProgressService);
|
||||
|
||||
spyOn(progressService.state, 'next');
|
||||
});
|
||||
|
||||
it('should be created', inject([ProgressService], (service: ProgressService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
it('should be created', () => {
|
||||
expect(progressService).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should propagate event when activated', () => {
|
||||
progressService.activate();
|
||||
expect(progressService.state.next).toHaveBeenCalledWith(new State(true));
|
||||
});
|
||||
|
||||
it('should propagate event when deactivated', () => {
|
||||
progressService.deactivate();
|
||||
expect(progressService.state.next).toHaveBeenCalledWith(new State(false));
|
||||
});
|
||||
|
||||
it('should propagate event on error', () => {
|
||||
const error = new Error();
|
||||
progressService.setError(error);
|
||||
expect(progressService.state.next).toHaveBeenCalledWith(new State(false, error));
|
||||
});
|
||||
|
||||
it('should clear an error', () => {
|
||||
progressService.clear();
|
||||
expect(progressService.state.next).toHaveBeenCalledWith(new State(false, null, true));
|
||||
});
|
||||
});
|
||||
|
@ -5,9 +5,13 @@ import { BehaviorSubject } from "rxjs/BehaviorSubject";
|
||||
|
||||
export class State {
|
||||
public visible: boolean;
|
||||
public error: Error;
|
||||
public clear: boolean;
|
||||
|
||||
constructor(visible: boolean) {
|
||||
constructor(visible: boolean, error?: Error, clear: boolean = false) {
|
||||
this.visible = visible;
|
||||
this.error = error;
|
||||
this.clear = clear;
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +21,14 @@ export class ProgressService {
|
||||
|
||||
constructor() {}
|
||||
|
||||
public setError(error: Error) {
|
||||
this.state.next(new State(false, error));
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.state.next(new State(false, null, true));
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.state.next(new State(true));
|
||||
}
|
||||
|
@ -62,9 +62,6 @@
|
||||
<app-node-select-interface (onChooseInterface)="onChooseInterface($event)"></app-node-select-interface>
|
||||
</div>
|
||||
|
||||
<div class="loading-spinner" *ngIf="isLoading">
|
||||
<mat-spinner color="primary">
|
||||
</mat-spinner>
|
||||
</div>
|
||||
<app-progress></app-progress>
|
||||
|
||||
<app-project-map-shortcuts *ngIf="project" [project]="project" [server]="server" [selectionManager]="selectionManager"></app-project-map-shortcuts>
|
||||
|
@ -41,6 +41,7 @@ import { InRectangleHelper } from "../../cartography/components/map/helpers/in-r
|
||||
import { DrawingsDataSource } from "../../cartography/datasources/drawings-datasource";
|
||||
import { Subscription } from "rxjs/Subscription";
|
||||
import { SettingsService } from "../../services/settings.service";
|
||||
import { ProgressService } from "../../common/progress/progress.service";
|
||||
|
||||
|
||||
@Component({
|
||||
@ -65,8 +66,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected selectionManager: SelectionManager;
|
||||
|
||||
public isLoading = true;
|
||||
|
||||
@ViewChild(MapComponent) mapChild: MapComponent;
|
||||
|
||||
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
||||
@ -84,6 +83,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
private linkService: LinkService,
|
||||
private dialog: MatDialog,
|
||||
private progressDialogService: ProgressDialogService,
|
||||
private progressService: ProgressService,
|
||||
private toaster: ToasterService,
|
||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||
private settingsService: SettingsService,
|
||||
@ -98,6 +98,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.progressService.activate();
|
||||
|
||||
const routeSub = this.route.paramMap.subscribe((paramMap: ParamMap) => {
|
||||
const server_id = parseInt(paramMap.get('server_id'), 10);
|
||||
@ -123,6 +124,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
.subscribe((project: Project) => {
|
||||
this.onProjectLoad(project);
|
||||
}, (error) => {
|
||||
this.progressService.setError(error);
|
||||
}, () => {
|
||||
this.progressService.deactivate();
|
||||
});
|
||||
});
|
||||
|
||||
@ -183,7 +188,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.setUpMapCallbacks(project);
|
||||
this.setUpWS(project);
|
||||
this.isLoading = false;
|
||||
|
||||
this.progressService.deactivate();
|
||||
});
|
||||
this.subscriptions.push(subscription);
|
||||
}
|
||||
@ -335,7 +341,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
this.nodesDataSource.clear();
|
||||
this.linksDataSource.clear();
|
||||
|
||||
this.ws.unsubscribe();
|
||||
if (this.ws) {
|
||||
this.ws.unsubscribe();
|
||||
}
|
||||
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,8 @@ export class ProjectsComponent implements OnInit {
|
||||
.list(this.server)
|
||||
.subscribe((projects: Project[]) => {
|
||||
this.projectDatabase.addProjects(projects);
|
||||
}, (error) => {
|
||||
this.progressService.setError(error);
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user