mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Implement warning on close unsaved view
This commit is contained in:
		| @@ -15,6 +15,7 @@ import { DirtyFormGuard } from './guards/dirty-form.guard' | ||||
| import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component' | ||||
| import { TasksComponent } from './components/manage/tasks/tasks.component' | ||||
| import { DirtyDocGuard } from './guards/dirty-doc.guard' | ||||
| import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard' | ||||
|  | ||||
| const routes: Routes = [ | ||||
|   { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, | ||||
| @@ -24,8 +25,16 @@ const routes: Routes = [ | ||||
|     canDeactivate: [DirtyDocGuard], | ||||
|     children: [ | ||||
|       { path: 'dashboard', component: DashboardComponent }, | ||||
|       { path: 'documents', component: DocumentListComponent }, | ||||
|       { path: 'view/:id', component: DocumentListComponent }, | ||||
|       { | ||||
|         path: 'documents', | ||||
|         component: DocumentListComponent, | ||||
|         canDeactivate: [DirtySavedViewGuard], | ||||
|       }, | ||||
|       { | ||||
|         path: 'view/:id', | ||||
|         component: DocumentListComponent, | ||||
|         canDeactivate: [DirtySavedViewGuard], | ||||
|       }, | ||||
|       { path: 'documents/:id', component: DocumentDetailComponent }, | ||||
|       { path: 'asn/:id', component: DocumentAsnComponent }, | ||||
|       { path: 'tags', component: TagListComponent }, | ||||
|   | ||||
| @@ -69,6 +69,7 @@ import { ColorComponent } from './components/common/input/color/color.component' | ||||
| import { DocumentAsnComponent } from './components/document-asn/document-asn.component' | ||||
| import { DocumentCommentsComponent } from './components/document-comments/document-comments.component' | ||||
| import { DirtyDocGuard } from './guards/dirty-doc.guard' | ||||
| import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard' | ||||
| import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component' | ||||
| import { StoragePathEditDialogComponent } from './components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component' | ||||
| import { SettingsService } from './services/settings.service' | ||||
| @@ -215,6 +216,7 @@ function initializeApp(settings: SettingsService) { | ||||
|     { provide: NgbDateAdapter, useClass: ISODateAdapter }, | ||||
|     { provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter }, | ||||
|     DirtyDocGuard, | ||||
|     DirtySavedViewGuard, | ||||
|   ], | ||||
|   bootstrap: [AppComponent], | ||||
| }) | ||||
|   | ||||
| @@ -16,4 +16,7 @@ | ||||
|         <ngb-progressbar *ngIf="!confirmButtonEnabled" style="height: 1px;" type="dark" [max]="secondsTotal" [value]="seconds"></ngb-progressbar> | ||||
|         <span class="visually-hidden">{{ seconds | number: '1.0-0' }} seconds</span> | ||||
|       </button> | ||||
|       <button *ngIf="alternativeBtnCaption" type="button" class="btn" [class]="alternativeBtnClass" (click)="alternative()" [disabled]="!alternativeButtonEnabled || !buttonsEnabled"> | ||||
|         {{alternativeBtnCaption}} | ||||
|       </button> | ||||
|     </div> | ||||
|   | ||||
| @@ -13,6 +13,9 @@ export class ConfirmDialogComponent { | ||||
|   @Output() | ||||
|   public confirmClicked = new EventEmitter() | ||||
|  | ||||
|   @Output() | ||||
|   public alternativeClicked = new EventEmitter() | ||||
|  | ||||
|   @Input() | ||||
|   title = $localize`Confirmation` | ||||
|  | ||||
| @@ -28,14 +31,22 @@ export class ConfirmDialogComponent { | ||||
|   @Input() | ||||
|   btnCaption = $localize`Confirm` | ||||
|  | ||||
|   @Input() | ||||
|   alternativeBtnClass = 'btn-secondary' | ||||
|  | ||||
|   @Input() | ||||
|   alternativeBtnCaption | ||||
|  | ||||
|   @Input() | ||||
|   buttonsEnabled = true | ||||
|  | ||||
|   confirmButtonEnabled = true | ||||
|   alternativeButtonEnabled = true | ||||
|   seconds = 0 | ||||
|   secondsTotal = 0 | ||||
|  | ||||
|   confirmSubject: Subject<boolean> | ||||
|   alternativeSubject: Subject<boolean> | ||||
|  | ||||
|   delayConfirm(seconds: number) { | ||||
|     const refreshInterval = 0.15 // s | ||||
| @@ -68,4 +79,10 @@ export class ConfirmDialogComponent { | ||||
|     this.confirmSubject?.next(true) | ||||
|     this.confirmSubject?.complete() | ||||
|   } | ||||
|  | ||||
|   alternative() { | ||||
|     this.alternativeClicked.emit() | ||||
|     this.alternativeSubject?.next(true) | ||||
|     this.alternativeSubject?.complete() | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -60,7 +60,12 @@ | ||||
|   </div> | ||||
|  | ||||
|   <div class="btn-group ms-2 flex-fill" ngbDropdown role="group"> | ||||
|     <button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" tourAnchor="tour.documents-views" ngbDropdownToggle i18n>Views</button> | ||||
|     <button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" tourAnchor="tour.documents-views" ngbDropdownToggle> | ||||
|       <ng-container i18n>Views</ng-container> | ||||
|       <div *ngIf="savedViewIsModified" class="position-absolute top-0 start-100 p-2 translate-middle badge bg-secondary border border-light rounded-circle"> | ||||
|         <span class="visually-hidden">selected</span> | ||||
|       </div> | ||||
|     </button> | ||||
|     <div class="dropdown-menu shadow dropdown-menu-right" ngbDropdownMenu> | ||||
|       <ng-container *ngIf="!list.activeSavedViewId"> | ||||
|         <button ngbDropdownItem *ngFor="let view of savedViewService.allViews" (click)="loadViewConfig(view.id)">{{view.name}}</button> | ||||
|   | ||||
							
								
								
									
										51
									
								
								src-ui/src/app/guards/dirty-saved-view.guard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src-ui/src/app/guards/dirty-saved-view.guard.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| import { CanDeactivate } from '@angular/router' | ||||
| import { Injectable } from '@angular/core' | ||||
| import { first, Observable, Subject } from 'rxjs' | ||||
| import { DocumentListComponent } from '../components/document-list/document-list.component' | ||||
| import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { ConfirmDialogComponent } from '../components/common/confirm-dialog/confirm-dialog.component' | ||||
|  | ||||
| @Injectable() | ||||
| export class DirtySavedViewGuard | ||||
|   implements CanDeactivate<DocumentListComponent> | ||||
| { | ||||
|   constructor(private modalService: NgbModal) {} | ||||
|  | ||||
|   canDeactivate( | ||||
|     component: DocumentListComponent | ||||
|   ): boolean | Observable<boolean> { | ||||
|     return component.savedViewIsModified ? this.warn(component) : true | ||||
|   } | ||||
|  | ||||
|   warn(component: DocumentListComponent) { | ||||
|     let modal = this.modalService.open(ConfirmDialogComponent, { | ||||
|       backdrop: 'static', | ||||
|     }) | ||||
|     modal.componentInstance.title = $localize`Unsaved Changes` | ||||
|     modal.componentInstance.messageBold = | ||||
|       $localize`You have unsaved changes to the saved view` + | ||||
|       ' "' + | ||||
|       component.getTitle() | ||||
|     ;('".') | ||||
|     modal.componentInstance.message = $localize`Are you sure you want to close this saved view?` | ||||
|     modal.componentInstance.btnClass = 'btn-secondary' | ||||
|     modal.componentInstance.btnCaption = $localize`Close` | ||||
|     modal.componentInstance.alternativeBtnClass = 'btn-primary' | ||||
|     modal.componentInstance.alternativeBtnCaption = $localize`Save and close` | ||||
|     modal.componentInstance.alternativeClicked.pipe(first()).subscribe(() => { | ||||
|       modal.componentInstance.buttonsEnabled = false | ||||
|       component.saveViewConfig() | ||||
|       modal.close() | ||||
|     }) | ||||
|     modal.componentInstance.confirmClicked.pipe(first()).subscribe(() => { | ||||
|       modal.componentInstance.buttonsEnabled = false | ||||
|       modal.close() | ||||
|     }) | ||||
|  | ||||
|     const subject = new Subject<boolean>() | ||||
|     modal.componentInstance.confirmSubject = subject | ||||
|     modal.componentInstance.alternativeSubject = subject | ||||
|  | ||||
|     return subject | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon