Merge pull request #1396 from GNS3/bugfix/1379

Bugfix/1379
This commit is contained in:
Jeremy Grossmann 2022-09-07 23:58:41 +02:00 committed by GitHub
commit 88f44554de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 235 additions and 23 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 { 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 { ProjectMapLockConfirmationDialogComponent } from './components/project-map/project-map-menu/project-map-lock-confirmation-dialog/project-map-lock-confirmation-dialog.component';
@NgModule({
declarations: [
@ -561,6 +562,7 @@ import { ConfirmationDeleteAllProjectsComponent } from './components/projects/co
ExportPortableProjectComponent,
NodesMenuConfirmationDialogComponent,
ConfirmationDeleteAllProjectsComponent,
ProjectMapLockConfirmationDialogComponent,
],
imports: [
BrowserModule,

View File

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

View File

@ -7,6 +7,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatToolbarModule } from '@angular/material/toolbar';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ProjectService } from '../../../services/project.service';
import { ElectronService } from 'ngx-electron';
import { ANGULAR_MAP_DECLARATIONS } from '../../../cartography/angular-map.imports';
import { D3MapComponent } from '../../../cartography/components/d3-map/d3-map.component';
@ -15,8 +16,14 @@ import { MapSettingsService } from '../../../services/mapsettings.service';
import { SymbolService } from '../../../services/symbol.service';
import { ToolsService } from '../../../services/tools.service';
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 { 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', () => {
let component: ProjectMapMenuComponent;
@ -24,6 +31,11 @@ describe('ProjectMapMenuComponent', () => {
let drawingService = new MockedDrawingService();
let mapSettingService = new MapSettingsService();
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() => {
await TestBed.configureTestingModule({
@ -38,9 +50,14 @@ describe('ProjectMapMenuComponent', () => {
],
providers: [
{ 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: SymbolService, useValue: mockedSymbolService },
{ provide: NodeService, useValue: mockedNodeService },
{ provide: NodesDataSource, useValue: mockedNodesDataSource },
{ provide: ElectronService },
],
declarations: [ProjectMapMenuComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS],
@ -77,9 +94,9 @@ describe('ProjectMapMenuComponent', () => {
spyOn(mapSettingService, 'changeMapLockValue');
component.changeLockValue();
expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(true);
expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(true);;
component.changeLockValue();
expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(false);
expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(false);;
});
});

View File

@ -1,17 +1,24 @@
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { NodeService } from '../../../services/node.service';
import { select } from 'd3-selection';
import { Subscription } from 'rxjs';
import * as svg from 'save-svg-as-png';
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{ Controller } from '../../../models/controller';
import { DrawingService } from '../../../services/drawing.service';
import { MapSettingsService } from '../../../services/mapsettings.service';
import { ProjectService } from '../../../services/project.service';
import { SymbolService } from '../../../services/symbol.service';
import { ThemeService } from '../../../services/theme.service';
import { ToolsService } from '../../../services/tools.service';
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({
selector: 'app-project-map-menu',
templateUrl: './project-map-menu.component.html',
@ -20,9 +27,12 @@ import { Screenshot, ScreenshotDialogComponent } from '../screenshot-dialog/scre
})
export class ProjectMapMenuComponent implements OnInit, OnDestroy {
@Input() project: Project;
@Input() controller:Controller ;
@Input() controller: Controller;
private nodes: Node[] = [];
private drawing: Drawing[] = [];
public selectedDrawing: string;
public lock: string = 'lock_open';
public drawTools = {
isRectangleChosen: false,
isEllipseChosen: false,
@ -31,20 +41,30 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
};
public isLocked: boolean = false;
public isLightThemeEnabled: boolean = false;
private projectSubscription: Subscription;
constructor(
private toolsService: ToolsService,
private mapSettingsService: MapSettingsService,
private drawingService: DrawingService,
private symbolService: SymbolService,
private dialog: MatDialog,
private themeService: ThemeService
private themeService: ThemeService,
private projectServices: ProjectService,
private nodeService : NodeService,
private nodesDataSource: NodesDataSource,
private drawingsDataSource: DrawingsDataSource,
) {}
ngOnInit() {
this.themeService.getActualTheme() === 'light'
? (this.isLightThemeEnabled = true)
: (this.isLightThemeEnabled = false);
this.projectSubscription = this.projectServices.projectLockIconSubject.subscribe((isRedraw: boolean) => {
if (isRedraw) {
this.getAllNodesAndDrawingStatus();
}
});
this.getAllNodesAndDrawingStatus();
}
getCssClassForIcon(type: string) {
@ -104,11 +124,15 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
}
public addDrawing(selectedObject: string) {
if ((selectedObject === 'rectangle' && this.drawTools.isRectangleChosen) || (selectedObject === 'ellipse' && this.drawTools.isEllipseChosen) ||
(selectedObject === 'line' && this.drawTools.isLineChosen) || (selectedObject === 'text' && this.drawTools.isTextChosen)) {
document.documentElement.style.cursor = "default";
if (
(selectedObject === 'rectangle' && this.drawTools.isRectangleChosen) ||
(selectedObject === 'ellipse' && this.drawTools.isEllipseChosen) ||
(selectedObject === 'line' && this.drawTools.isLineChosen) ||
(selectedObject === 'text' && this.drawTools.isTextChosen)
) {
document.documentElement.style.cursor = 'default';
} else {
document.documentElement.style.cursor = "crosshair";
document.documentElement.style.cursor = 'crosshair';
}
switch (selectedObject) {
@ -147,8 +171,7 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
}
public resetDrawToolChoice() {
document.documentElement.style.cursor = "default";
document.documentElement.style.cursor = 'default';
this.drawTools.isRectangleChosen = false;
this.drawTools.isEllipseChosen = false;
this.drawTools.isLineChosen = false;
@ -156,10 +179,70 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
this.selectedDrawing = '';
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() {
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();
}
} 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) {
@ -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>`;
}
ngOnDestroy() {}
ngOnDestroy() {
// this.projectSubscription.unsubscribe();
}
}

View File

@ -83,4 +83,13 @@ export class DrawingService {
delete(controller:Controller , drawing: Drawing) {
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 projectLockIconSubject = new Subject<boolean>();
constructor(
private httpController: HttpController,
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)
}
}