Resolve issue Lock or unlock all items button does not reflect the nodes status on the web-ui workspace GNS3 3.0.0 alpha 1

This commit is contained in:
Rajnikant Lodhi 2022-09-06 17:03:00 +05:30
parent fcfcb7566d
commit 0c0d27fd6e
11 changed files with 237 additions and 25 deletions

View File

@ -323,6 +323,7 @@ import { UploadingProcessbarComponent } from './common/uploading-processbar/uplo
import { ExportPortableProjectComponent } from './components/export-portable-project/export-portable-project.component'; import { ExportPortableProjectComponent } from './components/export-portable-project/export-portable-project.component';
import { NodesMenuConfirmationDialogComponent } from './components/project-map/nodes-menu/nodes-menu-confirmation-dialog/nodes-menu-confirmation-dialog.component'; import { NodesMenuConfirmationDialogComponent } from './components/project-map/nodes-menu/nodes-menu-confirmation-dialog/nodes-menu-confirmation-dialog.component';
import { ConfirmationDeleteAllProjectsComponent } from './components/projects/confirmation-delete-all-projects/confirmation-delete-all-projects.component'; import { ConfirmationDeleteAllProjectsComponent } from './components/projects/confirmation-delete-all-projects/confirmation-delete-all-projects.component';
import { ProjectMapLockConfirmationDialogComponent } from './components/project-map/project-map-menu/project-map-lock-confirmation-dialog/project-map-lock-confirmation-dialog.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -561,6 +562,7 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
ExportPortableProjectComponent, ExportPortableProjectComponent,
NodesMenuConfirmationDialogComponent, NodesMenuConfirmationDialogComponent,
ConfirmationDeleteAllProjectsComponent, ConfirmationDeleteAllProjectsComponent,
ProjectMapLockConfirmationDialogComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -3,16 +3,17 @@ import { DrawingsDataSource } from '../../../../../cartography/datasources/drawi
import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource'; import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource';
import { Drawing } from '../../../../../cartography/models/drawing'; import { Drawing } from '../../../../../cartography/models/drawing';
import { Node } from '../../../../../cartography/models/node'; import { Node } from '../../../../../cartography/models/node';
import{ Controller } from '../../../../../models/controller'; import { Controller } from '../../../../../models/controller';
import { DrawingService } from '../../../../../services/drawing.service'; import { DrawingService } from '../../../../../services/drawing.service';
import { NodeService } from '../../../../../services/node.service'; import { NodeService } from '../../../../../services/node.service';
import { ProjectService } from '../../../../../services/project.service';
@Component({ @Component({
selector: 'app-lock-action', selector: 'app-lock-action',
templateUrl: './lock-action.component.html', templateUrl: './lock-action.component.html',
}) })
export class LockActionComponent implements OnChanges { export class LockActionComponent implements OnChanges {
@Input() controller:Controller ; @Input() controller: Controller;
@Input() nodes: Node[]; @Input() nodes: Node[];
@Input() drawings: Drawing[]; @Input() drawings: Drawing[];
command: string; command: string;
@ -21,7 +22,8 @@ export class LockActionComponent implements OnChanges {
private nodesDataSource: NodesDataSource, private nodesDataSource: NodesDataSource,
private drawingsDataSource: DrawingsDataSource, private drawingsDataSource: DrawingsDataSource,
private nodeService: NodeService, private nodeService: NodeService,
private drawingService: DrawingService private drawingService: DrawingService,
private projectService: ProjectService
) {} ) {}
ngOnChanges() { ngOnChanges() {
@ -34,19 +36,20 @@ export class LockActionComponent implements OnChanges {
} }
} }
lock() { async lock() {
this.nodes.forEach((node) => { await this.nodes.forEach((node) => {
node.locked = !node.locked; node.locked = !node.locked;
this.nodeService.updateNode(this.controller, node).subscribe((node) => { this.nodeService.updateNode(this.controller, node).subscribe((node) => {
this.nodesDataSource.update(node); this.nodesDataSource.update(node);
}); });
}); });
this.drawings.forEach((drawing) => { await this.drawings.forEach((drawing) => {
drawing.locked = !drawing.locked; drawing.locked = !drawing.locked;
this.drawingService.update(this.controller, drawing).subscribe((drawing) => { this.drawingService.update(this.controller, drawing).subscribe((drawing) => {
this.drawingsDataSource.update(drawing); this.drawingsDataSource.update(drawing);
}); });
}); });
this.projectService.projectUpdateLockIcon()
} }
} }

View File

@ -0,0 +1,14 @@
<div class="row">
<div class="col-md-12">
<h5 class="heading-txt">Confirm {{ confirmActionData.actionType}} All</h5>
</div>
</div>
<mat-divider></mat-divider>
<mat-dialog-content>
<p class="text-padding">Are you sure you want to {{confirmActionData.actionType}} all devices?</p>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button mat-dialog-close>No</button>
<button mat-button (click)="confirmAction()" cdkFocusInitial>Yes</button>
</mat-dialog-actions>

View File

@ -0,0 +1,44 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
import { ProjectMapLockConfirmationDialogComponent } from './project-map-lock-confirmation-dialog.component';
describe('ProjectMapLockConfirmationDialogComponent', () => {
let component: ProjectMapLockConfirmationDialogComponent;
let fixture: ComponentFixture<ProjectMapLockConfirmationDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports:[
MatIconModule,
MatToolbarModule,
MatMenuModule,
MatCheckboxModule,
MatDialogModule,
MatSnackBarModule,
],
providers: [
{ provide: MAT_DIALOG_DATA, useValue: {} },
{ provide: MatDialogRef, useValue: {} },
],
declarations: [ ProjectMapLockConfirmationDialogComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProjectMapLockConfirmationDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,28 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-project-map-lock-confirmation-dialog',
templateUrl: './project-map-lock-confirmation-dialog.component.html',
styleUrls: ['./project-map-lock-confirmation-dialog.component.scss']
})
export class ProjectMapLockConfirmationDialogComponent implements OnInit {
confirmActionData = {
actionType: 'Unlock',
isAction:false
};
constructor(
@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<ProjectMapLockConfirmationDialogComponent>
) {}
ngOnInit(): void {
this.confirmActionData.actionType = this.data.actionType;
}
confirmAction() {
this.confirmActionData.isAction = this.data.actionType == 'Lock'? true : false;
this.dialogRef.close(this.confirmActionData);
}
}

View File

@ -86,7 +86,7 @@
class="menu-button" class="menu-button"
(click)="changeLockValue()" (click)="changeLockValue()"
> >
<mat-icon [ngClass]="{ unmarkedLight: !isLocked && isLightThemeEnabled, marked: isLocked }">lock</mat-icon> <mat-icon [ngClass]="{ unmarkedLight: !isLocked && isLightThemeEnabled, marked: isLocked }">{{lock}}</mat-icon>
</button> </button>
<button <button
matTooltip="Take a screenshot" matTooltip="Take a screenshot"

View File

@ -7,6 +7,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ProjectService } from '../../../services/project.service';
import { ElectronService } from 'ngx-electron'; import { ElectronService } from 'ngx-electron';
import { ANGULAR_MAP_DECLARATIONS } from '../../../cartography/angular-map.imports'; import { ANGULAR_MAP_DECLARATIONS } from '../../../cartography/angular-map.imports';
import { D3MapComponent } from '../../../cartography/components/d3-map/d3-map.component'; import { D3MapComponent } from '../../../cartography/components/d3-map/d3-map.component';
@ -15,15 +16,26 @@ import { MapSettingsService } from '../../../services/mapsettings.service';
import { SymbolService } from '../../../services/symbol.service'; import { SymbolService } from '../../../services/symbol.service';
import { ToolsService } from '../../../services/tools.service'; import { ToolsService } from '../../../services/tools.service';
import { MockedSymbolService } from '../../preferences/common/symbols/symbols.component.spec'; import { MockedSymbolService } from '../../preferences/common/symbols/symbols.component.spec';
import { MockedDrawingService } from '../project-map.component.spec'; import { MockedDrawingService,MockedDrawingsDataSource } from '../project-map.component.spec';
import { ProjectMapMenuComponent } from './project-map-menu.component'; import { ProjectMapMenuComponent } from './project-map-menu.component';
import { MockedProjectService } from '../../../services/project.service.spec';
import { MockedNodesDataSource, MockedNodeService } from '../project-map.component.spec';
import { NodeService } from '../../../services/node.service';
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
describe('ProjectMapMenuComponent', () => { fdescribe('ProjectMapMenuComponent', () => {
let component: ProjectMapMenuComponent; let component: ProjectMapMenuComponent;
let fixture: ComponentFixture<ProjectMapMenuComponent>; let fixture: ComponentFixture<ProjectMapMenuComponent>;
let drawingService = new MockedDrawingService(); let drawingService = new MockedDrawingService();
let mapSettingService = new MapSettingsService(); let mapSettingService = new MapSettingsService();
let mockedSymbolService = new MockedSymbolService(); let mockedSymbolService = new MockedSymbolService();
let mockedProjectService: MockedProjectService = new MockedProjectService();
let mockedNodeService: MockedNodeService = new MockedNodeService();
let mockedNodesDataSource: MockedNodesDataSource = new MockedNodesDataSource();
let mockedDrawingsDataSource = new MockedDrawingsDataSource();
let mockedDrawingsEventSource = new DrawingsEventSource();
beforeEach(async() => { beforeEach(async() => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
@ -38,9 +50,14 @@ describe('ProjectMapMenuComponent', () => {
], ],
providers: [ providers: [
{ provide: DrawingService, useValue: drawingService }, { provide: DrawingService, useValue: drawingService },
{ provide: ToolsService }, { provide: DrawingsDataSource, useValue: mockedDrawingsDataSource },
{ provide: DrawingsEventSource, useValue: mockedDrawingsEventSource },
{ provide: ProjectService, useValue: mockedProjectService },
{ provide: ToolsService, useClass: ToolsService },
{ provide: MapSettingsService, useValue: mapSettingService }, { provide: MapSettingsService, useValue: mapSettingService },
{ provide: SymbolService, useValue: mockedSymbolService }, { provide: SymbolService, useValue: mockedSymbolService },
{ provide: NodeService, useValue: mockedNodeService },
{ provide: NodesDataSource, useValue: mockedNodesDataSource },
{ provide: ElectronService }, { provide: ElectronService },
], ],
declarations: [ProjectMapMenuComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS], declarations: [ProjectMapMenuComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS],
@ -77,9 +94,9 @@ describe('ProjectMapMenuComponent', () => {
spyOn(mapSettingService, 'changeMapLockValue'); spyOn(mapSettingService, 'changeMapLockValue');
component.changeLockValue(); component.changeLockValue();
expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(true); expect(mapSettingService.changeMapLockValue).toHaveBeenCalled();
component.changeLockValue(); component.changeLockValue();
expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(false); expect(mapSettingService.changeMapLockValue).toHaveBeenCalled();
}); });
}); });

View File

@ -1,17 +1,24 @@
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { NodeService } from '../../../services/node.service';
import { select } from 'd3-selection'; import { select } from 'd3-selection';
import { Subscription } from 'rxjs';
import * as svg from 'save-svg-as-png'; import * as svg from 'save-svg-as-png';
import downloadSvg from 'svg-crowbar'; import downloadSvg from 'svg-crowbar';
import { Drawing } from '../../../cartography/models/drawing';
import { Node } from '../../../cartography/models/node';
import { Controller } from '../../../models/controller';
import { Project } from '../../../models/project'; import { Project } from '../../../models/project';
import{ Controller } from '../../../models/controller';
import { DrawingService } from '../../../services/drawing.service'; import { DrawingService } from '../../../services/drawing.service';
import { MapSettingsService } from '../../../services/mapsettings.service'; import { MapSettingsService } from '../../../services/mapsettings.service';
import { ProjectService } from '../../../services/project.service';
import { SymbolService } from '../../../services/symbol.service'; import { SymbolService } from '../../../services/symbol.service';
import { ThemeService } from '../../../services/theme.service'; import { ThemeService } from '../../../services/theme.service';
import { ToolsService } from '../../../services/tools.service'; import { ToolsService } from '../../../services/tools.service';
import { Screenshot, ScreenshotDialogComponent } from '../screenshot-dialog/screenshot-dialog.component'; import { Screenshot, ScreenshotDialogComponent } from '../screenshot-dialog/screenshot-dialog.component';
import { ProjectMapLockConfirmationDialogComponent } from './project-map-lock-confirmation-dialog/project-map-lock-confirmation-dialog.component';
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
@Component({ @Component({
selector: 'app-project-map-menu', selector: 'app-project-map-menu',
templateUrl: './project-map-menu.component.html', templateUrl: './project-map-menu.component.html',
@ -20,9 +27,12 @@ import { Screenshot, ScreenshotDialogComponent } from '../screenshot-dialog/scre
}) })
export class ProjectMapMenuComponent implements OnInit, OnDestroy { export class ProjectMapMenuComponent implements OnInit, OnDestroy {
@Input() project: Project; @Input() project: Project;
@Input() controller:Controller ; @Input() controller: Controller;
private nodes: Node[] = [];
private drawing: Drawing[] = [];
public selectedDrawing: string; public selectedDrawing: string;
public lock: string = 'lock_open';
public drawTools = { public drawTools = {
isRectangleChosen: false, isRectangleChosen: false,
isEllipseChosen: false, isEllipseChosen: false,
@ -31,20 +41,30 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
}; };
public isLocked: boolean = false; public isLocked: boolean = false;
public isLightThemeEnabled: boolean = false; public isLightThemeEnabled: boolean = false;
private projectSubscription: Subscription;
constructor( constructor(
private toolsService: ToolsService, private toolsService: ToolsService,
private mapSettingsService: MapSettingsService, private mapSettingsService: MapSettingsService,
private drawingService: DrawingService, private drawingService: DrawingService,
private symbolService: SymbolService, private symbolService: SymbolService,
private dialog: MatDialog, private dialog: MatDialog,
private themeService: ThemeService private themeService: ThemeService,
private projectServices: ProjectService,
private nodeService : NodeService,
private nodesDataSource: NodesDataSource,
private drawingsDataSource: DrawingsDataSource,
) {} ) {}
ngOnInit() { ngOnInit() {
this.themeService.getActualTheme() === 'light' this.themeService.getActualTheme() === 'light'
? (this.isLightThemeEnabled = true) ? (this.isLightThemeEnabled = true)
: (this.isLightThemeEnabled = false); : (this.isLightThemeEnabled = false);
this.projectSubscription = this.projectServices.projectLockIconSubject.subscribe((isRedraw: boolean) => {
if (isRedraw) {
this.getAllNodesAndDrawingStatus();
}
});
this.getAllNodesAndDrawingStatus();
} }
getCssClassForIcon(type: string) { getCssClassForIcon(type: string) {
@ -104,11 +124,15 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
} }
public addDrawing(selectedObject: string) { public addDrawing(selectedObject: string) {
if ((selectedObject === 'rectangle' && this.drawTools.isRectangleChosen) || (selectedObject === 'ellipse' && this.drawTools.isEllipseChosen) || if (
(selectedObject === 'line' && this.drawTools.isLineChosen) || (selectedObject === 'text' && this.drawTools.isTextChosen)) { (selectedObject === 'rectangle' && this.drawTools.isRectangleChosen) ||
document.documentElement.style.cursor = "default"; (selectedObject === 'ellipse' && this.drawTools.isEllipseChosen) ||
(selectedObject === 'line' && this.drawTools.isLineChosen) ||
(selectedObject === 'text' && this.drawTools.isTextChosen)
) {
document.documentElement.style.cursor = 'default';
} else { } else {
document.documentElement.style.cursor = "crosshair"; document.documentElement.style.cursor = 'crosshair';
} }
switch (selectedObject) { switch (selectedObject) {
@ -147,8 +171,7 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
} }
public resetDrawToolChoice() { public resetDrawToolChoice() {
document.documentElement.style.cursor = "default"; document.documentElement.style.cursor = 'default';
this.drawTools.isRectangleChosen = false; this.drawTools.isRectangleChosen = false;
this.drawTools.isEllipseChosen = false; this.drawTools.isEllipseChosen = false;
this.drawTools.isLineChosen = false; this.drawTools.isLineChosen = false;
@ -156,10 +179,70 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
this.selectedDrawing = ''; this.selectedDrawing = '';
this.toolsService.textAddingToolActivation(this.drawTools.isTextChosen); this.toolsService.textAddingToolActivation(this.drawTools.isTextChosen);
} }
getAllNodesAndDrawingStatus() {
this.projectServices.getProjectStatus(this.controller,this.project.project_id).subscribe((status)=>{
if (status) {
this.isLocked = true;
this.lock = 'lock';
} else {
this.isLocked = false;
this.lock = 'lock_open';
}
})
this.projectServices.nodes(this.controller, this.project.project_id).subscribe((response) => {
this.nodes = response;
this.nodes.forEach((node) => {
this.nodeService.updateNode(this.controller, node).subscribe((node) => {
this.nodesDataSource.update(node);
});
});
});
this.projectServices.drawings(this.controller, this.project.project_id).subscribe((response) => {
this.drawing = response;
this.drawing.forEach((drawing) => {
this.drawingService.update(this.controller, drawing).subscribe((drawing) => {
this.drawingsDataSource.update(drawing);
});
});
});
}
public changeLockValue() { public changeLockValue() {
this.isLocked = !this.isLocked; this.isLocked = !this.isLocked;
this.mapSettingsService.changeMapLockValue(this.isLocked); const dialogRef = this.dialog.open(ProjectMapLockConfirmationDialogComponent, {
width: '500px',
maxHeight: '200px',
autoFocus: false,
disableClose: true,
data: { isAction: this.isLocked, actionType: this.isLocked == true ? 'Lock' : 'Unlock' },
});
dialogRef.afterClosed().subscribe((confirmAction_result) => {
if (confirmAction_result && confirmAction_result != '') {
if (confirmAction_result.actionType == 'Lock' && confirmAction_result.isAction) {
this.lockAllNode();
} else {
this.unlockAllNode();
}
this.mapSettingsService.changeMapLockValue(this.isLocked);
} else {
}
});
}
lockAllNode() {
this.lock = 'lock';
this.drawingService.lockAllNodes(this.controller, this.project).subscribe((res) => {
this.getAllNodesAndDrawingStatus();
});
}
unlockAllNode() {
this.lock = 'lock_open';
this.drawingService.unLockAllNodes(this.controller, this.project).subscribe((res) => {
this.getAllNodesAndDrawingStatus();
});
} }
public uploadImageFile(event) { public uploadImageFile(event) {
@ -190,5 +273,7 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\" xlink:href=\"${image}\"/>\n</svg>`; width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\" xlink:href=\"${image}\"/>\n</svg>`;
} }
ngOnDestroy() {} ngOnDestroy() {
// this.projectSubscription.unsubscribe();
}
} }

View File

@ -83,4 +83,13 @@ export class DrawingService {
delete(controller:Controller , drawing: Drawing) { delete(controller:Controller , drawing: Drawing) {
return this.httpController.delete<Drawing>(controller, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`); return this.httpController.delete<Drawing>(controller, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`);
} }
lockAllNodes(controller:Controller,project:Project){
return this.httpController.post(controller, `/projects/${project.project_id}/lock`,{});
}
unLockAllNodes(controller:Controller,project:Project){
return this.httpController.post(controller, `/projects/${project.project_id}/unlock`,{});
}
} }

View File

@ -33,6 +33,7 @@ export class ProjectService {
]; ];
public projectListSubject = new Subject<boolean>(); public projectListSubject = new Subject<boolean>();
public projectLockIconSubject = new Subject<boolean>();
constructor( constructor(
private httpController: HttpController, private httpController: HttpController,
private settingsService: SettingsService, private settingsService: SettingsService,
@ -145,4 +146,13 @@ export class ProjectService {
} }
} }
getProjectStatus(controller :Controller, project_id: string): Observable<any> {
return this.get(controller,`${project_id}/locked`)
}
projectUpdateLockIcon(){
this.projectLockIconSubject.next(true)
}
} }