]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Fix browser unsaved changes with custom guard 1369/head
authorMichael Shamoon <4887959+shamoon@users.noreply.github.com>
Sun, 7 Aug 2022 02:30:39 +0000 (19:30 -0700)
committerMichael Shamoon <4887959+shamoon@users.noreply.github.com>
Sun, 7 Aug 2022 03:23:05 +0000 (20:23 -0700)
src-ui/messages.xlf
src-ui/src/app/app-routing.module.ts
src-ui/src/app/app.module.ts
src-ui/src/app/components/app-frame/app-frame.component.ts
src-ui/src/app/components/document-detail/document-detail.component.ts
src-ui/src/app/guards/dirty-doc.guard.ts [new file with mode: 0644]
src-ui/src/app/services/open-documents.service.ts

index 08e08608deb7bac2ee8d0e62eb91a5082d022b8d..c79413a4b72ee121a7b6876072edf39bb4cd4ecf 100644 (file)
         <source>Decrement hours</source>
         <context-group purpose="location">
           <context context-type="sourcefile">node_modules/src/timepicker/timepicker.ts</context>
-          <context context-type="linenumber">240,243</context>
+          <context context-type="linenumber">239,240</context>
         </context-group>
       </trans-unit>
       <trans-unit id="ngb.timepicker.increment-minutes" datatype="html">
         <source>Increment minutes</source>
         <context-group purpose="location">
           <context context-type="sourcefile">node_modules/src/timepicker/timepicker.ts</context>
-          <context context-type="linenumber">268</context>
+          <context context-type="linenumber">264,268</context>
         </context-group>
       </trans-unit>
       <trans-unit id="ngb.timepicker.decrement-minutes" datatype="html">
         <source>Decrement minutes</source>
         <context-group purpose="location">
           <context context-type="sourcefile">node_modules/src/timepicker/timepicker.ts</context>
-          <context context-type="linenumber">288,289</context>
+          <context context-type="linenumber">287,289</context>
         </context-group>
       </trans-unit>
       <trans-unit id="ngb.timepicker.SS" datatype="html">
         <source>Confirm delete</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">439</context>
+          <context context-type="linenumber">442</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
         <source>Do you really want to delete document &quot;<x id="PH" equiv-text="this.document.title"/>&quot;?</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">440</context>
+          <context context-type="linenumber">443</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6691075929777935948" datatype="html">
         <source>The files for this document will be deleted permanently. This operation cannot be undone.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">441</context>
+          <context context-type="linenumber">444</context>
         </context-group>
       </trans-unit>
       <trans-unit id="719892092227206532" datatype="html">
         <source>Delete document</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">443</context>
+          <context context-type="linenumber">446</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1844801255494293730" datatype="html">
         <source>Error deleting document: <x id="PH" equiv-text="JSON.stringify(error)"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">459</context>
+          <context context-type="linenumber">462</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7362691899087997122" datatype="html">
         <source>Redo OCR confirm</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">479</context>
+          <context context-type="linenumber">482</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
         <source>This operation will permanently redo OCR for this document.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">480</context>
+          <context context-type="linenumber">483</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5641451190833696892" datatype="html">
         <source>This operation cannot be undone.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">481</context>
+          <context context-type="linenumber">484</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
         <source>Proceed</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">483</context>
+          <context context-type="linenumber">486</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
         <source>Redo OCR operation will begin in the background.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">491</context>
+          <context context-type="linenumber">494</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8008978164775353960" datatype="html">
               )"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
-          <context context-type="linenumber">502,504</context>
+          <context context-type="linenumber">505,507</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6857598786757174736" datatype="html">
           <context context-type="linenumber">39</context>
         </context-group>
       </trans-unit>
+      <trans-unit id="5948496158474272829" datatype="html">
+        <source>Warning: You have unsaved changes to your document(s).</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/guards/dirty-doc.guard.ts</context>
+          <context context-type="linenumber">18</context>
+        </context-group>
+      </trans-unit>
       <trans-unit id="159901853873315050" datatype="html">
         <source>Unsaved Changes</source>
         <context-group purpose="location">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">111</context>
+          <context context-type="linenumber">116</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">138</context>
+          <context context-type="linenumber">143</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2573823578527613511" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">139</context>
+          <context context-type="linenumber">144</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3305084982600522070" datatype="html">
         <source>You have unsaved changes to the document</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">113</context>
+          <context context-type="linenumber">118</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2089045849587358256" datatype="html">
         <source>Are you sure you want to close this document?</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">117</context>
+          <context context-type="linenumber">122</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2885986061416655600" datatype="html">
         <source>Close document</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">119</context>
+          <context context-type="linenumber">124</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6755718693176327396" datatype="html">
         <source>Are you sure you want to close all documents?</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">140</context>
+          <context context-type="linenumber">145</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4215561719980781894" datatype="html">
         <source>Close documents</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
-          <context context-type="linenumber">142</context>
+          <context context-type="linenumber">147</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3553216189604488439" datatype="html">
index 65db5f97e5258ea1d4a24c6609e5067be8b81a10..c62357c5d0f7cd17494b1b6b2ac2dd700d1b7624 100644 (file)
@@ -14,12 +14,14 @@ import { DocumentAsnComponent } from './components/document-asn/document-asn.com
 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'
 
 const routes: Routes = [
   { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
   {
     path: '',
     component: AppFrameComponent,
+    canDeactivate: [DirtyDocGuard],
     children: [
       { path: 'dashboard', component: DashboardComponent },
       { path: 'documents', component: DocumentListComponent },
index f4c49d95db65a3bd3091c7d8a675a9d2bdcd6bf1..edbd261f6f517c2ddab0ad4666d96a89d25fef04 100644 (file)
@@ -67,6 +67,7 @@ import { ApiVersionInterceptor } from './interceptors/api-version.interceptor'
 import { ColorSliderModule } from 'ngx-color/slider'
 import { ColorComponent } from './components/common/input/color/color.component'
 import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
+import { DirtyDocGuard } from './guards/dirty-doc.guard'
 
 import localeBe from '@angular/common/locales/be'
 import localeCs from '@angular/common/locales/cs'
@@ -209,6 +210,7 @@ function initializeApp(settings: SettingsService) {
     DocumentTitlePipe,
     { provide: NgbDateAdapter, useClass: ISODateAdapter },
     { provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter },
+    DirtyDocGuard,
   ],
   bootstrap: [AppComponent],
 })
index 441a753309bb192148d1192a8634d9e265af2398..6e758d262a379b1a8cff59774305d3f7e1d2295c 100644 (file)
@@ -1,6 +1,6 @@
-import { Component } from '@angular/core'
+import { Component, HostListener } from '@angular/core'
 import { FormControl } from '@angular/forms'
-import { ActivatedRoute, Router, Params } from '@angular/router'
+import { ActivatedRoute, Router } from '@angular/router'
 import { from, Observable } from 'rxjs'
 import {
   debounceTime,
@@ -23,13 +23,14 @@ import {
 } from 'src/app/services/rest/remote-version.service'
 import { SettingsService } from 'src/app/services/settings.service'
 import { TasksService } from 'src/app/services/tasks.service'
+import { ComponentCanDeactivate } from 'src/app/guards/dirty-doc.guard'
 
 @Component({
   selector: 'app-app-frame',
   templateUrl: './app-frame.component.html',
   styleUrls: ['./app-frame.component.scss'],
 })
-export class AppFrameComponent {
+export class AppFrameComponent implements ComponentCanDeactivate {
   constructor(
     public router: Router,
     private activatedRoute: ActivatedRoute,
@@ -64,6 +65,11 @@ export class AppFrameComponent {
     return this.openDocumentsService.getOpenDocuments()
   }
 
+  @HostListener('window:beforeunload')
+  canDeactivate(): Observable<boolean> | boolean {
+    return !this.openDocumentsService.hasDirty()
+  }
+
   searchAutoComplete = (text$: Observable<string>) =>
     text$.pipe(
       debounceTime(200),
index f86ee1ea8a142372648276b1795fab9c50fa8a93..203a56f0447ec9e3cc1871b1b1fc0984e3e0cdca 100644 (file)
@@ -206,7 +206,7 @@ export class DocumentDetailComponent
                   this.store.getValue().title !==
                   this.documentForm.get('title').value
                 ) {
-                  this.openDocumentService.setDirty(doc.id, true)
+                  this.openDocumentService.setDirty(doc, true)
                 }
               },
             })
@@ -228,12 +228,15 @@ export class DocumentDetailComponent
             this.store.asObservable()
           )
 
-          return this.isDirty$.pipe(map((dirty) => ({ doc, dirty })))
+          return this.isDirty$.pipe(
+            takeUntil(this.unsubscribeNotifier),
+            map((dirty) => ({ doc, dirty }))
+          )
         })
       )
       .subscribe({
         next: ({ doc, dirty }) => {
-          this.openDocumentService.setDirty(doc.id, dirty)
+          this.openDocumentService.setDirty(doc, dirty)
         },
         error: (error) => {
           this.router.navigate(['404'])
@@ -349,7 +352,7 @@ export class DocumentDetailComponent
           Object.assign(this.document, doc)
           this.title = doc.title
           this.documentForm.patchValue(doc)
-          this.openDocumentService.setDirty(doc.id, false)
+          this.openDocumentService.setDirty(doc, false)
         },
         error: () => {
           this.router.navigate(['404'])
diff --git a/src-ui/src/app/guards/dirty-doc.guard.ts b/src-ui/src/app/guards/dirty-doc.guard.ts
new file mode 100644 (file)
index 0000000..10362db
--- /dev/null
@@ -0,0 +1,20 @@
+import { CanDeactivate } from '@angular/router'
+import { Injectable } from '@angular/core'
+import { Observable } from 'rxjs'
+
+export interface ComponentCanDeactivate {
+  canDeactivate: () => boolean | Observable<boolean>
+}
+
+@Injectable()
+export class DirtyDocGuard implements CanDeactivate<ComponentCanDeactivate> {
+  canDeactivate(
+    component: ComponentCanDeactivate
+  ): boolean | Observable<boolean> {
+    return component.canDeactivate()
+      ? true
+      : confirm(
+          $localize`Warning: You have unsaved changes to your document(s).`
+        )
+  }
+}
index d7746d261be00b309a058ba3bdccb77cfd2938b9..8533166c330a532aa1e979d3cbb827564aa049c9 100644 (file)
@@ -92,9 +92,14 @@ export class OpenDocumentsService {
     }
   }
 
-  setDirty(documentId: number, dirty: boolean) {
-    if (dirty) this.dirtyDocuments.add(documentId)
-    else this.dirtyDocuments.delete(documentId)
+  setDirty(doc: PaperlessDocument, dirty: boolean) {
+    if (!this.openDocuments.find((d) => d.id == doc.id)) return
+    if (dirty) this.dirtyDocuments.add(doc.id)
+    else this.dirtyDocuments.delete(doc.id)
+  }
+
+  hasDirty(): boolean {
+    return this.dirtyDocuments.size > 0
   }
 
   closeDocument(doc: PaperlessDocument): Observable<boolean> {