diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 4e00fb6..b76ddd2 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -11,6 +11,7 @@ import {RegisterComponent} from "./user/register/register.component"; import {MapLocationEditComponent} from "./map-location/map-location-edit/map-location-edit.component"; import {AuthGuardService} from "./auth/auth-guard.service"; import {CanEditService} from "./auth/can-edit.service"; +import {CanDeactivateGuard} from "./shared/guards/can-deactivate-guard.service"; @@ -29,21 +30,25 @@ const routes: Routes = [ path: 'point/new/:routeId', canActivate: [AuthGuardService], component: MapLocationEditComponent, + canDeactivate: [CanDeactivateGuard] }, { path: 'point/:pointId/edit', canActivate: [AuthGuardService], component: MapLocationEditComponent, + canDeactivate: [CanDeactivateGuard] }, { path: 'routes/new', canActivate: [AuthGuardService], - component: RouteEditComponent + component: RouteEditComponent, + canDeactivate: [CanDeactivateGuard] }, { path: 'yourRoutes/new', canActivate: [AuthGuardService], - component: RouteEditComponent + component: RouteEditComponent, + canDeactivate: [CanDeactivateGuard] }, { path: 'users/:id', @@ -100,6 +105,7 @@ const routes: Routes = [ { path: 'yourRoutes/:id/edit', canActivate: [AuthGuardService,CanEditService], + canDeactivate: [CanDeactivateGuard], component: RouteEditComponent }, { diff --git a/src/app/map-location/map-location-edit/map-location-edit.component.ts b/src/app/map-location/map-location-edit/map-location-edit.component.ts index bb0fd51..413ef64 100644 --- a/src/app/map-location/map-location-edit/map-location-edit.component.ts +++ b/src/app/map-location/map-location-edit/map-location-edit.component.ts @@ -11,6 +11,9 @@ import {DomSanitizer, SafeUrl} from "@angular/platform-browser"; import {Audio} from "../../audio/audio.model"; import {AudioService} from "../../audio/audio.service"; import {maxPageSize} from "../../shared/http.config"; +import {CanComponentDeactivate} from "../../shared/guards/can-deactivate-guard.service"; +import {CanDeactivateFormGuardService} from "../../shared/guards/can-deactivate-form-guard.service"; +import {ConfirmationDialogService} from "../../shared/confirmation-dialog/confirmation-dialog.service"; @Component({ @@ -25,7 +28,7 @@ import {maxPageSize} from "../../shared/http.config"; templateUrl: './map-location-edit.component.html', styleUrl: './map-location-edit.component.css' }) -export class MapLocationEditComponent implements OnInit { +export class MapLocationEditComponent implements OnInit, CanComponentDeactivate { @ViewChild('audioFileInput') audioFileInput: ElementRef; routeId: string; mapLocationId: string; @@ -43,6 +46,7 @@ export class MapLocationEditComponent implements OnInit { selectedAudioFile: File | null = null; temporaryAudioData: { audio: Audio, url: SafeUrl,file: File }[] = []; returnUrl: string | null = null; + private submittingChangesInProcess: boolean = false; constructor(private activatedRoute: ActivatedRoute, private router: Router, @@ -50,7 +54,9 @@ export class MapLocationEditComponent implements OnInit { private mapLocationService: MapLocationService, private routeMapLocationService: RouteMapLocationService, private audioService: AudioService, - private sanitizer: DomSanitizer) { + private sanitizer: DomSanitizer, + private canDeactivateFormGuardService: CanDeactivateFormGuardService, + private confirmationDialogService: ConfirmationDialogService,) { } ngOnInit(): void { @@ -93,8 +99,13 @@ export class MapLocationEditComponent implements OnInit { }); }) + } - + canDeactivate(): Promise { + if(this.submittingChangesInProcess) { + return Promise.resolve(true); + } + return this.canDeactivateFormGuardService.canDeactivateForm(this.mapLocationForm.dirty && !this.mapLocationForm.pristine); } @@ -157,6 +168,7 @@ export class MapLocationEditComponent implements OnInit { } onSubmit() { + this.submittingChangesInProcess = true; let newMapLocation = { name: this.mapLocationForm.value.name, description: this.mapLocationForm.value.description, diff --git a/src/app/map-location/map-location-list/map-location-list.component.ts b/src/app/map-location/map-location-list/map-location-list.component.ts index fcfc33e..45653c4 100644 --- a/src/app/map-location/map-location-list/map-location-list.component.ts +++ b/src/app/map-location/map-location-list/map-location-list.component.ts @@ -17,6 +17,7 @@ import {ModalService} from "../../shared/modal/modal.service"; import { MapLocationAddToYourRouteModalComponent } from "../map-location-add-to-your-route-modal/map-location-add-to-your-route-modal.component"; +import {ConfirmationDialogService} from "../../shared/confirmation-dialog/confirmation-dialog.service"; @Component({ selector: 'app-map-location-list', @@ -55,7 +56,8 @@ export class MapLocationListComponent implements OnInit, OnChanges { private router: Router, private modalService: ModalService, private activatedRoute: ActivatedRoute, - private routeService: RouteService) { + private routeService: RouteService, + private confirmationDialogService: ConfirmationDialogService) { } @@ -106,11 +108,21 @@ export class MapLocationListComponent implements OnInit, OnChanges { } onMapLocationDelete(mapLocationId: string) { - if (confirm("You are about to delete map location. Do you want to continue?")) { - this.mapLocationService.deleteMapLocationFromRoute(mapLocationId, this.route.id).subscribe(() => { - this.fetchMapLocations(); + + this.confirmationDialogService + .confirm( + 'Confirm Deletion', + `You are about to delete your map location. Do you want to proceed?`, + 'Yes', + 'Cancel' + ) + .subscribe((confirmed: boolean) => { + if (confirmed) { + this.mapLocationService.deleteMapLocationFromRoute(mapLocationId, this.route.id).subscribe(() => { + this.fetchMapLocations(); + }); + } }); - } } onAddToYourRoute(mapLocation : MapLocation) { diff --git a/src/app/route/route-edit/route-edit.component.ts b/src/app/route/route-edit/route-edit.component.ts index 7cb34da..0d1fe9d 100644 --- a/src/app/route/route-edit/route-edit.component.ts +++ b/src/app/route/route-edit/route-edit.component.ts @@ -9,6 +9,12 @@ import { Location, NgForOf, NgIf } from "@angular/common"; import { maxPageSize } from "../../shared/http.config"; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import {MapLocationListComponent} from "../../map-location/map-location-list/map-location-list.component"; +import {CanComponentDeactivate} from "../../shared/guards/can-deactivate-guard.service"; +import {CanDeactivateFormGuardService} from "../../shared/guards/can-deactivate-form-guard.service"; +import {catchError, of} from "rxjs"; +import {MapLocationConflictError} from "../../shared/errors/route-map-location-conflict.error"; +import {SnackbarType} from "../../shared/snackbar/snackbar-type"; +import {ConfirmationDialogService} from "../../shared/confirmation-dialog/confirmation-dialog.service"; @Component({ selector: 'app-route-edit', @@ -22,7 +28,7 @@ import {MapLocationListComponent} from "../../map-location/map-location-list/map templateUrl: './route-edit.component.html', styleUrl: './route-edit.component.css' }) -export class RouteEditComponent implements OnInit { +export class RouteEditComponent implements OnInit, CanComponentDeactivate { id: string; routeForm: FormGroup; userLogin: string; @@ -32,13 +38,15 @@ export class RouteEditComponent implements OnInit { selectedFile: File = null; currentImageUrl: SafeUrl = null; imagePreview: string | ArrayBuffer | null = null; + private submittingChangesInProcess: boolean = false; constructor(private routeService: RouteService, private mapLocationService: MapLocationService, private activatedRoute: ActivatedRoute, private router: Router, - private location: Location, - private sanitizer: DomSanitizer) { + private sanitizer: DomSanitizer, + private canDeactivateFormGuardService: CanDeactivateFormGuardService, + private confirmationDialogService: ConfirmationDialogService,) { } ngOnInit() { @@ -56,20 +64,31 @@ export class RouteEditComponent implements OnInit { } } + canDeactivate(): Promise { + if(this.submittingChangesInProcess) { + return Promise.resolve(true); + } + return this.canDeactivateFormGuardService.canDeactivateForm(this.routeForm.dirty && !this.routeForm.pristine); + } + + onFileSelected(event: Event) { const input = event.target as HTMLInputElement; if (input.files && input.files.length > 0) { this.selectedFile = input.files[0]; - } - const reader = new FileReader(); - reader.onload = () => { - this.imagePreview = reader.result; - }; - reader.readAsDataURL(this.selectedFile); + this.routeForm.markAsDirty(); + + const reader = new FileReader(); + reader.onload = () => { + this.imagePreview = reader.result; + }; + reader.readAsDataURL(this.selectedFile); + } } onSubmit() { + this.submittingChangesInProcess = true; if (this.editMode) { this.routeService.patchRouteById(this.id, this.routeForm.value) .subscribe(() => { @@ -92,7 +111,7 @@ export class RouteEditComponent implements OnInit { this.goBack(); }); } else { - this.goBack(); + this.router.navigate(['yourRoutes', 'list']); } }); } @@ -107,12 +126,23 @@ export class RouteEditComponent implements OnInit { } onDelete() { - if (confirm("You are about to delete " + this.route.name + " route. Do you want to continue?")) { - this.routeService.deleteRouteById(this.id) - .subscribe(() => { - this.router.navigate(['../../', 'list'], { relativeTo: this.activatedRoute }); - }); - } + this.submittingChangesInProcess = true; + + this.confirmationDialogService + .confirm( + 'Confirm Deletion', + `You are about to delete your route. Do you want to proceed?`, + 'Yes', + 'Cancel' + ) + .subscribe((confirmed: boolean) => { + if (confirmed) { + this.routeService.deleteRouteById(this.id) + .subscribe(() => { + this.router.navigate(['../../', 'list'], { relativeTo: this.activatedRoute }); + }); + } + }); } diff --git a/src/app/shared/guards/can-deactivate-form-guard.service.ts b/src/app/shared/guards/can-deactivate-form-guard.service.ts new file mode 100644 index 0000000..28ac70e --- /dev/null +++ b/src/app/shared/guards/can-deactivate-form-guard.service.ts @@ -0,0 +1,29 @@ +import {Injectable} from "@angular/core"; +import {ConfirmationDialogService} from "../confirmation-dialog/confirmation-dialog.service"; + +@Injectable({ + providedIn: 'root', +}) +export class CanDeactivateFormGuardService { + + constructor(private confirmationDialogService: ConfirmationDialogService) {} + + canDeactivateForm(isDirty: boolean):Promise { + if (isDirty) { + return new Promise((resolve) => { + this.confirmationDialogService + .confirm( + 'Confirm Exiting', + `You are about to exit without saving the changes. Do you want to proceed?`, + 'Yes', + 'Cancel' + ) + .subscribe((confirmed: boolean) => { + resolve(confirmed); + }); + }); + } else { + return Promise.resolve(true); + } + } +} diff --git a/src/app/shared/guards/can-deactivate-guard.service.ts b/src/app/shared/guards/can-deactivate-guard.service.ts new file mode 100644 index 0000000..3f8b2e6 --- /dev/null +++ b/src/app/shared/guards/can-deactivate-guard.service.ts @@ -0,0 +1,16 @@ +import {Observable} from "rxjs"; +import {Injectable} from "@angular/core"; +import {CanDeactivate} from "@angular/router"; + +export interface CanComponentDeactivate { + canDeactivate: () => Observable | Promise | boolean; +} + +@Injectable({ + providedIn: 'root' +}) +export class CanDeactivateGuard implements CanDeactivate { + canDeactivate (component: CanComponentDeactivate) : Observable | Promise | boolean { + return component.canDeactivate(); + } +}