Merge branch 'master' into Node-information-dialog-improvements

This commit is contained in:
piotrpekala7 2020-02-27 08:29:12 +01:00
commit ca3045d236
37 changed files with 183 additions and 93 deletions

View File

@ -149,7 +149,7 @@ const routes: Routes = [
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes)], imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled'})],
exports: [RouterModule] exports: [RouterModule]
}) })
export class AppRoutingModule {} export class AppRoutingModule {}

View File

@ -1,5 +1,5 @@
import * as Raven from 'raven-js'; import * as Raven from 'raven-js';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule, Title } from '@angular/platform-browser';
import { NgModule, ErrorHandler } from '@angular/core'; import { NgModule, ErrorHandler } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CdkTableModule } from '@angular/cdk/table'; import { CdkTableModule } from '@angular/cdk/table';
@ -538,7 +538,8 @@ if (environment.production) {
NotificationService, NotificationService,
Gns3vmService, Gns3vmService,
ThemeService, ThemeService,
GoogleAnalyticsService GoogleAnalyticsService,
Title
], ],
entryComponents: [ entryComponents: [
AddServerDialogComponent, AddServerDialogComponent,
@ -580,7 +581,8 @@ if (environment.production) {
NavigationDialogComponent, NavigationDialogComponent,
ScreenshotDialogComponent, ScreenshotDialogComponent,
ConfirmationBottomSheetComponent, ConfirmationBottomSheetComponent,
ConfigDialogComponent ConfigDialogComponent,
AdbutlerComponent
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -9,7 +9,8 @@ export class ProgressDialogService {
public open() { public open() {
const ref = this.dialog.open(ProgressDialogComponent, { const ref = this.dialog.open(ProgressDialogComponent, {
width: '250px', width: '250px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
return ref; return ref;
} }

View File

@ -11,13 +11,12 @@ import { ToasterService } from '../../services/toaster.service';
}) })
export class AdbutlerComponent implements OnInit { export class AdbutlerComponent implements OnInit {
@ViewChild('ad', {static: false}) ad: ElementRef; @ViewChild('ad', {static: false}) ad: ElementRef;
@Input() theme: string; theme: string;
@Output() onLoad = new EventEmitter(); onLoad = new EventEmitter();
htmlCode: string; htmlCode: string;
constructor( constructor(
private httpClient: HttpClient, private httpClient: HttpClient
private toasterService: ToasterService
) {} ) {}
ngOnInit() { ngOnInit() {

View File

@ -2,7 +2,7 @@
<mat-progress-bar mode="determinate" [value]="progress"></mat-progress-bar> <mat-progress-bar mode="determinate" [value]="progress"></mat-progress-bar>
<div style="display: flex; height: 102px;"> <div style="display: flex; height: 102px;">
<div class="content" [ngClass]="{lightTheme: isLightThemeEnabled}"> <div class="content" [ngClass]="{lightTheme: isLightThemeEnabled}">
<app-adbutler (onLoad)="onLoadingAdbutler($event)" theme="dark"></app-adbutler> <template #dynamicComponentContainer></template>
<mat-icon (click)="closeNotification()" class="close-button">close</mat-icon> <mat-icon (click)="closeNotification()" class="close-button">close</mat-icon>
</div> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy, ComponentFactoryResolver, ViewContainerRef, ViewChild } from '@angular/core';
import { timer, Observable, Subscription } from 'rxjs'; import { timer, Observable, Subscription } from 'rxjs';
import { ThemeService } from '../../services/theme.service'; import { ThemeService } from '../../services/theme.service';
import { AdbutlerComponent } from '../adbutler/adbutler.component';
@Component({ @Component({
selector: 'app-notification-box', selector: 'app-notification-box',
@ -8,6 +9,8 @@ import { ThemeService } from '../../services/theme.service';
styleUrls: ['./notification-box.component.scss'] styleUrls: ['./notification-box.component.scss']
}) })
export class NotificationBoxComponent implements OnInit, OnDestroy { export class NotificationBoxComponent implements OnInit, OnDestroy {
@ViewChild('dynamicComponentContainer', {read: ViewContainerRef, static: false}) dynamicComponentContainer;
timer: Observable<number>; timer: Observable<number>;
viewTimer: Observable<number>; viewTimer: Observable<number>;
timerSubscription: Subscription; timerSubscription: Subscription;
@ -26,7 +29,9 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
isLightThemeEnabled: boolean = false; isLightThemeEnabled: boolean = false;
constructor( constructor(
private themeService: ThemeService private themeService: ThemeService,
private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef
){} ){}
ngOnInit() { ngOnInit() {
@ -34,6 +39,20 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false; this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
} }
ngAfterViewInit() {
this.createDynamicAdComponent();
}
createDynamicAdComponent() : void {
const factory = this.componentFactoryResolver.resolveComponentFactory(AdbutlerComponent);
const componentRef = this.dynamicComponentContainer.createComponent(factory);
componentRef.instance.theme = this.themeService.getActualTheme() === 'light';
componentRef.instance.onLoad.subscribe(event => {
this.onLoadingAdbutler(event);
})
componentRef.changeDetectorRef.detectChanges();
}
startTimer() { startTimer() {
this.timer = timer(this.delayTime, 1000); this.timer = timer(this.delayTime, 1000);

View File

@ -27,7 +27,8 @@ export class DeleteTemplateComponent {
data: { data: {
templateName: templateName templateName: templateName
}, },
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe((answer: boolean) => { dialogRef.afterClosed().subscribe((answer: boolean) => {

View File

@ -20,7 +20,8 @@ export class ChangeSymbolActionComponent implements OnInit {
const dialogRef = this.dialog.open(ChangeSymbolDialogComponent, { const dialogRef = this.dialog.open(ChangeSymbolDialogComponent, {
width: '1000px', width: '1000px',
height: '500px', height: '500px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;

View File

@ -27,7 +27,8 @@ export class ConfigActionComponent {
@Input() node: Node; @Input() node: Node;
private conf = { private conf = {
autoFocus: false, autoFocus: false,
width: '800px' width: '800px',
disableClose: true
}; };
dialogRef; dialogRef;

View File

@ -20,7 +20,8 @@ export class EditConfigActionComponent {
const dialogRef = this.dialog.open(ConfigEditorDialogComponent, { const dialogRef = this.dialog.open(ConfigEditorDialogComponent, {
width: '600px', width: '600px',
height: '500px', height: '500px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;

View File

@ -25,7 +25,8 @@ export class EditStyleActionComponent implements OnChanges {
editStyle() { editStyle() {
const dialogRef = this.dialog.open(StyleEditorDialogComponent, { const dialogRef = this.dialog.open(StyleEditorDialogComponent, {
width: '800px', width: '800px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;

View File

@ -29,7 +29,8 @@ export class EditTextActionComponent implements OnInit {
editText() { editText() {
const dialogRef = this.dialog.open(TextEditorDialogComponent, { const dialogRef = this.dialog.open(TextEditorDialogComponent, {
width: '300px', width: '300px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;

View File

@ -26,7 +26,8 @@ export class ExportConfigActionComponent {
} else { } else {
const dialogRef = this.dialog.open(ConfigDialogComponent, { const dialogRef = this.dialog.open(ConfigDialogComponent, {
width: '500px', width: '500px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
dialogRef.afterClosed().subscribe((configType: string) => { dialogRef.afterClosed().subscribe((configType: string) => {

View File

@ -27,7 +27,8 @@ export class ImportConfigActionComponent {
if (this.node.node_type !== 'vpcs') { if (this.node.node_type !== 'vpcs') {
const dialogRef = this.dialog.open(ConfigDialogComponent, { const dialogRef = this.dialog.open(ConfigDialogComponent, {
width: '500px', width: '500px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
dialogRef.afterClosed().subscribe((configType: string) => { dialogRef.afterClosed().subscribe((configType: string) => {

View File

@ -20,7 +20,8 @@ export class PacketFiltersActionComponent {
const dialogRef = this.dialog.open(PacketFiltersDialogComponent, { const dialogRef = this.dialog.open(PacketFiltersDialogComponent, {
width: '900px', width: '900px',
height: '400px', height: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;

View File

@ -18,7 +18,8 @@ export class ShowNodeActionComponent {
const dialogRef = this.dialog.open(InfoDialogComponent, { const dialogRef = this.dialog.open(InfoDialogComponent, {
width: '600px', width: '600px',
maxHeight: '600px', maxHeight: '600px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.node = this.node; instance.node = this.node;

View File

@ -19,7 +19,8 @@ export class StartCaptureActionComponent {
startCapture() { startCapture() {
const dialogRef = this.dialog.open(StartCaptureDialogComponent, { const dialogRef = this.dialog.open(StartCaptureDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;

View File

@ -35,7 +35,8 @@ export class ConfiguratorDialogQemuComponent implements OnInit {
private conf = { private conf = {
autoFocus: false, autoFocus: false,
width: '800px' width: '800px',
disableClose: true
}; };
dialogRefQemuImageCreator; dialogRefQemuImageCreator;

View File

@ -1,4 +1,4 @@
import { Component, Input } from "@angular/core"; import { Component, Input, ChangeDetectionStrategy } from "@angular/core";
import { Project } from '../../../models/project'; import { Project } from '../../../models/project';
import { Server } from '../../../models/server'; import { Server } from '../../../models/server';
import { NodeService } from '../../../services/node.service'; import { NodeService } from '../../../services/node.service';
@ -11,7 +11,8 @@ import { ServerService } from '../../../services/server.service';
@Component({ @Component({
selector: 'app-nodes-menu', selector: 'app-nodes-menu',
templateUrl: './nodes-menu.component.html', templateUrl: './nodes-menu.component.html',
styleUrls: ['./nodes-menu.component.scss'] styleUrls: ['./nodes-menu.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class NodesMenuComponent { export class NodesMenuComponent {
@Input('project') project: Project; @Input('project') project: Project;

View File

@ -80,7 +80,8 @@ export class PacketFiltersDialogComponent implements OnInit{
onHelpClick() { onHelpClick() {
const dialogRef = this.dialog.open(HelpDialogComponent, { const dialogRef = this.dialog.open(HelpDialogComponent, {
width: '500px', width: '500px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.title = 'Help for filters'; instance.title = 'Help for filters';

View File

@ -3,7 +3,7 @@
mat-icon-button mat-icon-button
class="menu-button" class="menu-button"
(click)="addDrawing('text')"> (click)="addDrawing('text')">
<mat-icon [ngClass]="{unmarkedLight: !drawTools.isTextChosen && isLightThemeEnabled, marked: drawTools.isTextChosen}">create</mat-icon> <mat-icon [ngClass]="getCssClassForIcon('text')">create</mat-icon>
</button> </button>
<input <input
type="file" type="file"
@ -23,14 +23,14 @@
mat-icon-button mat-icon-button
class="menu-button" class="menu-button"
(click)="addDrawing('rectangle')"> (click)="addDrawing('rectangle')">
<mat-icon [ngClass]="{unmarkedLight: !drawTools.isRectangleChosen && isLightThemeEnabled, marked: drawTools.isRectangleChosen}">crop_3_2</mat-icon> <mat-icon [ngClass]="getCssClassForIcon('rectangle')">crop_3_2</mat-icon>
</button> </button>
<button <button
matTooltip="Draw an ellipse" matTooltip="Draw an ellipse"
mat-icon-button mat-icon-button
class="menu-button" class="menu-button"
(click)="addDrawing('ellipse')"> (click)="addDrawing('ellipse')">
<mat-icon [ngClass]="{unmarkedLight: !drawTools.isEllipseChosen && isLightThemeEnabled, marked: drawTools.isEllipseChosen}">panorama_fish_eye</mat-icon> <mat-icon [ngClass]="getCssClassForIcon('ellipse')">panorama_fish_eye</mat-icon>
</button> </button>
<button *ngIf="!isLightThemeEnabled" <button *ngIf="!isLightThemeEnabled"
matTooltip="Draw a line" matTooltip="Draw a line"

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core'; import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy } from '@angular/core';
import { Project } from '../../../models/project'; import { Project } from '../../../models/project';
import { Server } from '../../../models/server'; import { Server } from '../../../models/server';
import { ToolsService } from '../../../services/tools.service'; import { ToolsService } from '../../../services/tools.service';
@ -14,11 +14,11 @@ import { ScreenshotDialogComponent, Screenshot } from '../screenshot-dialog/scre
import { saveAsPng, saveAsJpeg } from 'save-html-as-image'; import { saveAsPng, saveAsJpeg } from 'save-html-as-image';
import { ThemeService } from '../../../services/theme.service'; import { ThemeService } from '../../../services/theme.service';
@Component({ @Component({
selector: 'app-project-map-menu', selector: 'app-project-map-menu',
templateUrl: './project-map-menu.component.html', templateUrl: './project-map-menu.component.html',
styleUrls: ['./project-map-menu.component.scss'] styleUrls: ['./project-map-menu.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ProjectMapMenuComponent implements OnInit, OnDestroy { export class ProjectMapMenuComponent implements OnInit, OnDestroy {
@Input() project: Project; @Input() project: Project;
@ -47,10 +47,29 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false; this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
} }
getCssClassForIcon(type: string) {
if (type === 'text') {
return {
'unmarkedLight': !this.drawTools.isTextChosen && this.isLightThemeEnabled,
'marked': this.drawTools.isTextChosen
};
} else if (type === 'rectangle') {
return {
'unmarkedLight': !this.drawTools.isRectangleChosen && this.isLightThemeEnabled,
'marked': this.drawTools.isRectangleChosen
};
}
return {
'unmarkedLight': !this.drawTools.isEllipseChosen && this.isLightThemeEnabled,
'marked': this.drawTools.isEllipseChosen
};
}
public takeScreenshot() { public takeScreenshot() {
const dialogRef = this.dialog.open(ScreenshotDialogComponent, { const dialogRef = this.dialog.open(ScreenshotDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe((result: Screenshot) => { dialogRef.afterClosed().subscribe((result: Screenshot) => {
if (result) this.saveImage(result); if (result) this.saveImage(result);

View File

@ -66,6 +66,7 @@ import { ConfirmationBottomSheetComponent } from '../projects/confirmation-botto
import { NodeAddedEvent } from '../template/template-list-dialog/template-list-dialog.component'; import { NodeAddedEvent } from '../template/template-list-dialog/template-list-dialog.component';
import { NotificationService } from '../../services/notification.service'; import { NotificationService } from '../../services/notification.service';
import { ThemeService } from '../../services/theme.service'; import { ThemeService } from '../../services/theme.service';
import { Title } from '@angular/platform-browser';
@Component({ @Component({
@ -109,7 +110,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
@ViewChild(D3MapComponent, {static: false}) mapChild: D3MapComponent; @ViewChild(D3MapComponent, {static: false}) mapChild: D3MapComponent;
@ViewChild(ProjectMapMenuComponent, {static: false}) projectMapMenuComponent: ProjectMapMenuComponent; @ViewChild(ProjectMapMenuComponent, {static: false}) projectMapMenuComponent: ProjectMapMenuComponent;
private subscriptions: Subscription[] = []; private projectMapSubscription: Subscription = new Subscription();
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
@ -153,7 +154,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
private serialLinkWidget: SerialLinkWidget, private serialLinkWidget: SerialLinkWidget,
private bottomSheet: MatBottomSheet, private bottomSheet: MatBottomSheet,
private notificationService: NotificationService, private notificationService: NotificationService,
private themeService: ThemeService private themeService: ThemeService,
private title: Title
) {} ) {}
ngOnInit() { ngOnInit() {
@ -178,6 +180,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
}), }),
mergeMap((project: Project) => { mergeMap((project: Project) => {
this.project = project; this.project = project;
this.title.setTitle(this.project.name);
if (this.mapSettingsService.interfaceLabels.has(project.project_id)) { if (this.mapSettingsService.interfaceLabels.has(project.project_id)) {
this.isInterfaceLabelVisible = this.mapSettingsService.interfaceLabels.get(project.project_id); this.isInterfaceLabelVisible = this.mapSettingsService.interfaceLabels.get(project.project_id);
@ -210,22 +213,22 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
); );
}); });
this.subscriptions.push(routeSub); this.projectMapSubscription.add(routeSub);
this.subscriptions.push( this.projectMapSubscription.add(
this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => { this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => {
if (this.scrollEnabled) this.centerCanvas(); if (this.scrollEnabled) this.centerCanvas();
}) })
); );
this.subscriptions.push( this.projectMapSubscription.add(
this.drawingsDataSource.changes.subscribe((drawings: Drawing[]) => { this.drawingsDataSource.changes.subscribe((drawings: Drawing[]) => {
this.drawings = drawings; this.drawings = drawings;
this.mapChangeDetectorRef.detectChanges(); this.mapChangeDetectorRef.detectChanges();
}) })
); );
this.subscriptions.push( this.projectMapSubscription.add(
this.nodesDataSource.changes.subscribe((nodes: Node[]) => { this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
if (!this.server) return; if (!this.server) return;
nodes.forEach((node: Node) => { nodes.forEach((node: Node) => {
@ -237,21 +240,21 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
}) })
); );
this.subscriptions.push( this.projectMapSubscription.add(
this.linksDataSource.changes.subscribe((links: Link[]) => { this.linksDataSource.changes.subscribe((links: Link[]) => {
this.links = links; this.links = links;
this.mapChangeDetectorRef.detectChanges(); this.mapChangeDetectorRef.detectChanges();
}) })
); );
this.subscriptions.push(this.projectWebServiceHandler.errorNotificationEmitter.subscribe((message) => { this.projectMapSubscription.add(this.projectWebServiceHandler.errorNotificationEmitter.subscribe((message) => {
this.showMessage({ this.showMessage({
type: 'error', type: 'error',
message: message message: message
}); });
})); }));
this.subscriptions.push(this.projectWebServiceHandler.warningNotificationEmitter.subscribe((message) => { this.projectMapSubscription.add(this.projectWebServiceHandler.warningNotificationEmitter.subscribe((message) => {
this.showMessage({ this.showMessage({
type: 'warning', type: 'warning',
message: message message: message
@ -323,7 +326,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.progressService.deactivate(); this.progressService.deactivate();
}); });
this.subscriptions.push(subscription); this.projectMapSubscription.add(subscription);
} }
setUpProjectWS(project: Project) { setUpProjectWS(project: Project) {
@ -408,14 +411,14 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.contextMenu.openMenuForListOfElements(drawings, nodes, labels, links, event.pageY, event.pageX); this.contextMenu.openMenuForListOfElements(drawings, nodes, labels, links, event.pageY, event.pageX);
}); });
this.subscriptions.push(onLinkContextMenu); this.projectMapSubscription.add(onLinkContextMenu);
this.subscriptions.push(onEthernetLinkContextMenu); this.projectMapSubscription.add(onEthernetLinkContextMenu);
this.subscriptions.push(onSerialLinkContextMenu); this.projectMapSubscription.add(onSerialLinkContextMenu);
this.subscriptions.push(onNodeContextMenu); this.projectMapSubscription.add(onNodeContextMenu);
this.subscriptions.push(onDrawingContextMenu); this.projectMapSubscription.add(onDrawingContextMenu);
this.subscriptions.push(onContextMenu); this.projectMapSubscription.add(onContextMenu);
this.subscriptions.push(onLabelContextMenu); this.projectMapSubscription.add(onLabelContextMenu);
this.subscriptions.push(onInterfaceLabelContextMenu); this.projectMapSubscription.add(onInterfaceLabelContextMenu);
this.mapChangeDetectorRef.detectChanges(); this.mapChangeDetectorRef.detectChanges();
} }
@ -424,10 +427,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
return; return;
} }
this.nodeService.createFromTemplate(this.server, this.project, nodeAddedEvent.template, nodeAddedEvent.x, nodeAddedEvent.y, nodeAddedEvent.server).subscribe((node: Node) => { this.nodeService.createFromTemplate(this.server, this.project, nodeAddedEvent.template, nodeAddedEvent.x, nodeAddedEvent.y, nodeAddedEvent.server).subscribe((node: Node) => {
if (nodeAddedEvent.name !== nodeAddedEvent.template.name) { // if (nodeAddedEvent.name !== nodeAddedEvent.template.name) {
node.name = nodeAddedEvent.name; // node.name = nodeAddedEvent.name;
this.nodeService.updateNode(this.server, node).subscribe(()=>{}); // this.nodeService.updateNode(this.server, node).subscribe(()=>{});
} // }
this.projectService.nodes(this.server, this.project.project_id).subscribe((nodes: Node[]) => { this.projectService.nodes(this.server, this.project.project_id).subscribe((nodes: Node[]) => {
nodes.filter((node) => node.label.style === null).forEach((node) => { nodes.filter((node) => node.label.style === null).forEach((node) => {
@ -702,7 +705,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
addNewProject() { addNewProject() {
const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, { const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;
@ -711,7 +715,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
saveProject() { saveProject() {
const dialogRef = this.dialog.open(SaveProjectDialogComponent, { const dialogRef = this.dialog.open(SaveProjectDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;
@ -721,7 +726,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
editProject() { editProject() {
const dialogRef = this.dialog.open(EditProjectDialogComponent, { const dialogRef = this.dialog.open(EditProjectDialogComponent, {
width: '600px', width: '600px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;
@ -732,7 +738,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
let uuid: string = ''; let uuid: string = '';
const dialogRef = this.dialog.open(ImportProjectDialogComponent, { const dialogRef = this.dialog.open(ImportProjectDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;
@ -819,6 +826,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
} }
public ngOnDestroy() { public ngOnDestroy() {
this.title.setTitle('GNS3 Web UI');
this.drawingsDataSource.clear(); this.drawingsDataSource.clear();
this.nodesDataSource.clear(); this.nodesDataSource.clear();
this.linksDataSource.clear(); this.linksDataSource.clear();
@ -829,7 +837,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
if (this.ws) { if (this.ws) {
if (this.ws.OPEN) this.ws.close(); if (this.ws.OPEN) this.ws.close();
} }
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); this.projectMapSubscription.unsubscribe();
} }
} }

View File

@ -87,7 +87,8 @@ export class AddBlankProjectDialogComponent implements OnInit {
data: { data: {
existingProject: existingProject existingProject: existingProject
}, },
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe((answer: boolean) => { dialogRef.afterClosed().subscribe((answer: boolean) => {

View File

@ -110,7 +110,8 @@ export class ImportProjectDialogComponent implements OnInit {
data: { data: {
existingProject: existingProject existingProject: existingProject
}, },
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe((answer: boolean) => { dialogRef.afterClosed().subscribe((answer: boolean) => {

View File

@ -78,7 +78,8 @@ export class ProjectsComponent implements OnInit {
const dialogRef = this.dialog.open(ConfigureGns3VMDialogComponent, { const dialogRef = this.dialog.open(ConfigureGns3VMDialogComponent, {
width: '350px', width: '350px',
height: '120px', height: '120px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe((answer: boolean) => { dialogRef.afterClosed().subscribe((answer: boolean) => {
@ -149,7 +150,8 @@ export class ProjectsComponent implements OnInit {
duplicate(project: Project) { duplicate(project: Project) {
const dialogRef = this.dialog.open(ChooseNameDialogComponent, { const dialogRef = this.dialog.open(ChooseNameDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;
@ -162,7 +164,8 @@ export class ProjectsComponent implements OnInit {
addBlankProject() { addBlankProject() {
const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, { const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;
@ -172,7 +175,8 @@ export class ProjectsComponent implements OnInit {
let uuid: string = ''; let uuid: string = '';
const dialogRef = this.dialog.open(ImportProjectDialogComponent, { const dialogRef = this.dialog.open(ImportProjectDialogComponent, {
width: '400px', width: '400px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
let instance = dialogRef.componentInstance; let instance = dialogRef.componentInstance;
instance.server = this.server; instance.server = this.server;

View File

@ -94,7 +94,8 @@ export class ServersComponent implements OnInit, OnDestroy {
createModal() { createModal() {
const dialogRef = this.dialog.open(AddServerDialogComponent, { const dialogRef = this.dialog.open(AddServerDialogComponent, {
width: '350px', width: '350px',
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe(server => { dialogRef.afterClosed().subscribe(server => {

View File

@ -35,7 +35,8 @@ export class SnapshotMenuItemComponent implements OnInit {
server: this.server, server: this.server,
project: this.project project: this.project
}, },
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe(snapshot => { dialogRef.afterClosed().subscribe(snapshot => {

View File

@ -13,7 +13,7 @@
[unitsFontSize]="20" [unitsFontSize]="20"
[titleColor]="'#C0C0C0'" [titleColor]="'#C0C0C0'"
[titleFontSize]="30" [titleFontSize]="30"
[subtitle]="'CPU usage percent'" [subtitle]="'CPU usage'"
[subtitleColor]="'#C0C0C0'" [subtitleColor]="'#C0C0C0'"
[subtitleFontSize]="15" [subtitleFontSize]="15"
></circle-progress> ></circle-progress>
@ -31,7 +31,7 @@
[unitsFontSize]="20" [unitsFontSize]="20"
[titleColor]="'#C0C0C0'" [titleColor]="'#C0C0C0'"
[titleFontSize]="30" [titleFontSize]="30"
[subtitle]="'Disk usage percent'" [subtitle]="'Disk usage'"
[subtitleColor]="'#C0C0C0'" [subtitleColor]="'#C0C0C0'"
[subtitleFontSize]="15" [subtitleFontSize]="15"
></circle-progress> ></circle-progress>
@ -49,7 +49,7 @@
[unitsFontSize]="20" [unitsFontSize]="20"
[titleColor]="'#C0C0C0'" [titleColor]="'#C0C0C0'"
[titleFontSize]="30" [titleFontSize]="30"
[subtitle]="'Memory usage percent'" [subtitle]="'Memory usage'"
[subtitleColor]="'#C0C0C0'" [subtitleColor]="'#C0C0C0'"
[subtitleFontSize]="15" [subtitleFontSize]="15"
></circle-progress> ></circle-progress>
@ -67,7 +67,7 @@
[unitsFontSize]="20" [unitsFontSize]="20"
[titleColor]="'#C0C0C0'" [titleColor]="'#C0C0C0'"
[titleFontSize]="30" [titleFontSize]="30"
[subtitle]="'SWAP usage percent'" [subtitle]="'SWAP usage'"
[subtitleColor]="'#C0C0C0'" [subtitleColor]="'#C0C0C0'"
[subtitleFontSize]="15" [subtitleFontSize]="15"
></circle-progress> ></circle-progress>
@ -87,7 +87,7 @@
[unitsFontSize]="20" [unitsFontSize]="20"
[titleColor]="'#C0C0C0'" [titleColor]="'#C0C0C0'"
[titleFontSize]="30" [titleFontSize]="30"
[subtitle]="['Load average percent', '(last 1 minute)']" [subtitle]="['Load average', '(last 1 minute)']"
[subtitleColor]="'#C0C0C0'" [subtitleColor]="'#C0C0C0'"
[subtitleFontSize]="15" [subtitleFontSize]="15"
></circle-progress> ></circle-progress>
@ -105,7 +105,7 @@
[unitsFontSize]="20" [unitsFontSize]="20"
[titleColor]="'#C0C0C0'" [titleColor]="'#C0C0C0'"
[titleFontSize]="30" [titleFontSize]="30"
[subtitle]="['Load average percent', '(last 5 minutes)']" [subtitle]="['Load average', '(last 5 minutes)']"
[subtitleColor]="'#C0C0C0'" [subtitleColor]="'#C0C0C0'"
[subtitleFontSize]="15" [subtitleFontSize]="15"
></circle-progress> ></circle-progress>
@ -123,7 +123,7 @@
[unitsFontSize]="20" [unitsFontSize]="20"
[titleColor]="'#C0C0C0'" [titleColor]="'#C0C0C0'"
[titleFontSize]="30" [titleFontSize]="30"
[subtitle]="['Load average percent', '(last 15 minutes)']" [subtitle]="['Load average', '(last 15 minutes)']"
[subtitleColor]="'#C0C0C0'" [subtitleColor]="'#C0C0C0'"
[subtitleFontSize]="15" [subtitleFontSize]="15"
></circle-progress> ></circle-progress>

View File

@ -2,5 +2,5 @@
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-bottom: 20px; margin-bottom: 40px;
} }

View File

@ -39,9 +39,9 @@
<h6>Configuration</h6> <h6>Configuration</h6>
</div> </div>
<form [formGroup]="configurationForm"> <form [formGroup]="configurationForm">
<mat-form-field class="form-field"> <!-- <mat-form-field class="form-field">
<input type="text" matInput formControlName="name" placeholder="Enter name (default is taken from template)" /> <input type="text" matInput formControlName="name" placeholder="Enter name (default is taken from template)" />
</mat-form-field> </mat-form-field> -->
<mat-form-field class="form-field"> <mat-form-field class="form-field">
<input type="number" matInput formControlName="numberOfNodes" placeholder="Enter number of nodes (default value is 1)" /> <input type="number" matInput formControlName="numberOfNodes" placeholder="Enter number of nodes (default value is 1)" />
</mat-form-field> </mat-form-field>

View File

@ -41,7 +41,7 @@ export class TemplateListDialogComponent implements OnInit {
this.server = data['server']; this.server = data['server'];
this.project = data['project']; this.project = data['project'];
this.configurationForm = this.formBuilder.group({ this.configurationForm = this.formBuilder.group({
name: new FormControl('new node', Validators.required), // name: new FormControl('new node', Validators.required),
numberOfNodes: new FormControl(1, [Validators.required, nonNegativeValidator.get]) numberOfNodes: new FormControl(1, [Validators.required, nonNegativeValidator.get])
}); });
this.positionForm = this.formBuilder.group({ this.positionForm = this.formBuilder.group({
@ -70,13 +70,13 @@ export class TemplateListDialogComponent implements OnInit {
chooseTemplate(event) { chooseTemplate(event) {
this.selectedTemplate = event.value; this.selectedTemplate = event.value;
this.configurationForm.controls['name'].setValue(this.selectedTemplate.default_name_format); // this.configurationForm.controls['name'].setValue(this.selectedTemplate.default_name_format);
} }
onAddClick(): void { onAddClick(): void {
if (!this.selectedTemplate || this.filteredTemplates.length === 0) { if (!this.selectedTemplate || this.filteredTemplates.length === 0) {
this.toasterService.error('Please firstly choose template.'); this.toasterService.error('Please firstly choose template.');
} else if (!this.positionForm.valid || !this.configurationForm.valid) { } else if (!this.positionForm.valid || !this.configurationForm.valid || !this.selectedTemplate.compute_id) {
this.toasterService.error('Please fill all required fields.'); this.toasterService.error('Please fill all required fields.');
} else { } else {
let x: number = this.positionForm.get('left').value; let x: number = this.positionForm.get('left').value;
@ -87,7 +87,7 @@ export class TemplateListDialogComponent implements OnInit {
let event: NodeAddedEvent = { let event: NodeAddedEvent = {
template: this.selectedTemplate, template: this.selectedTemplate,
server: this.selectedTemplate.compute_id, server: this.selectedTemplate.compute_id,
name: this.configurationForm.get('name').value, // name: this.configurationForm.get('name').value,
numberOfNodes: this.configurationForm.get('numberOfNodes').value, numberOfNodes: this.configurationForm.get('numberOfNodes').value,
x: x, x: x,
y: y y: y
@ -101,7 +101,7 @@ export class TemplateListDialogComponent implements OnInit {
export interface NodeAddedEvent { export interface NodeAddedEvent {
template: Template, template: Template,
server: string, server: string,
name: string, name?: string,
numberOfNodes: number; numberOfNodes: number;
x: number; x: number;
y: number; y: number;

View File

@ -27,7 +27,8 @@ export class TemplateComponent implements OnInit {
server: this.server, server: this.server,
project: this.project project: this.project
}, },
autoFocus: false autoFocus: false,
disableClose: true
}); });
dialogRef.afterClosed().subscribe((nodeAddedEvent: NodeAddedEvent) => { dialogRef.afterClosed().subscribe((nodeAddedEvent: NodeAddedEvent) => {

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, Input, AfterViewInit, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, OnDestroy, Input, AfterViewInit, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
import { Project } from '../../models/project'; import { Project } from '../../models/project';
import { Server } from '../../models/server'; import { Server } from '../../models/server';
import { NodesDataSource } from '../../cartography/datasources/nodes-datasource'; import { NodesDataSource } from '../../cartography/datasources/nodes-datasource';
@ -16,7 +16,8 @@ import { ThemeService } from '../../services/theme.service';
@Component({ @Component({
selector: 'app-topology-summary', selector: 'app-topology-summary',
templateUrl: './topology-summary.component.html', templateUrl: './topology-summary.component.html',
styleUrls: ['./topology-summary.component.scss'] styleUrls: ['./topology-summary.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
}) })
export class TopologySummaryComponent implements OnInit, OnDestroy { export class TopologySummaryComponent implements OnInit, OnDestroy {
@Input() server: Server; @Input() server: Server;

View File

@ -46,6 +46,13 @@ export class NodeService {
} }
createFromTemplate(server: Server, project: Project, template: Template, x: number, y: number, compute_id: string): Observable<Node> { createFromTemplate(server: Server, project: Project, template: Template, x: number, y: number, compute_id: string): Observable<Node> {
if (!compute_id) {
return this.httpServer.post(server, `/projects/${project.project_id}/templates/${template.template_id}`, {
x: Math.round(x),
y: Math.round(y),
compute_id: 'local'
});
}
return this.httpServer.post(server, `/projects/${project.project_id}/templates/${template.template_id}`, { return this.httpServer.post(server, `/projects/${project.project_id}/templates/${template.template_id}`, {
x: Math.round(x), x: Math.round(x),
y: Math.round(y), y: Math.round(y),

View File

@ -7,7 +7,7 @@ export class QemuConfigurationService {
} }
getDiskInterfaces() { getDiskInterfaces() {
return ['ide', 'sata', 'scsi', 'sd', 'mtd', 'floppy', 'pflash', 'virtio', 'none']; return ['ide', 'sata', 'scsi', 'sd', 'mtd', 'floppy', 'pflash', 'virtio', 'nvme', 'none'];
} }
getNetworkTypes() { getNetworkTypes() {
@ -33,7 +33,12 @@ export class QemuConfigurationService {
// ["virtio-net-pci", "Paravirtualized Network I/O"], // ["virtio-net-pci", "Paravirtualized Network I/O"],
// ["vmxnet3", "VMWare Paravirtualized Ethernet v3"]]; // ["vmxnet3", "VMWare Paravirtualized Ethernet v3"]];
let networkTypes = ["e1000", "Intel Gigabit Ethernet", let networkTypes = ["e1000",
"e1000-82544gc",
"e1000-82545em",
"e1000e",
"rocker",
"Intel Gigabit Ethernet",
"i82550", "i82550",
"i82551", "i82551",
"i82557a", "i82557a",

View File

@ -1,14 +1,20 @@
import 'hammerjs'; import 'hammerjs';
import { enableProdMode } from '@angular/core'; import { enableProdMode, ApplicationRef } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module'; import { AppModule } from './app/app.module';
import { environment } from './environments/environment'; import { environment } from './environments/environment';
import { enableDebugTools } from '@angular/platform-browser';
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic() platformBrowserDynamic().bootstrapModule(AppModule)
.bootstrapModule(AppModule) .then(moduleRef => {
.catch(err => console.error(err)); const applicationRef = moduleRef.injector.get(ApplicationRef);
const componentRef = applicationRef.components[0];
// allows to run `ng.profiler.timeChangeDetection();`
enableDebugTools(componentRef);
})
.catch(err => console.log(err));