mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge branch 'dev' into feature/slim-sidebar
This commit is contained in:
		
							
								
								
									
										743
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										743
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -13,48 +13,48 @@ | ||||
|   }, | ||||
|   "private": true, | ||||
|   "dependencies": { | ||||
|     "@angular/common": "~14.2.0", | ||||
|     "@angular/compiler": "~14.2.0", | ||||
|     "@angular/core": "~14.2.0", | ||||
|     "@angular/forms": "~14.2.0", | ||||
|     "@angular/localize": "~14.2.0", | ||||
|     "@angular/platform-browser": "~14.2.0", | ||||
|     "@angular/platform-browser-dynamic": "~14.2.0", | ||||
|     "@angular/router": "~14.2.0", | ||||
|     "@angular/common": "~14.2.4", | ||||
|     "@angular/compiler": "~14.2.4", | ||||
|     "@angular/core": "~14.2.4", | ||||
|     "@angular/forms": "~14.2.4", | ||||
|     "@angular/localize": "~14.2.4", | ||||
|     "@angular/platform-browser": "~14.2.4", | ||||
|     "@angular/platform-browser-dynamic": "~14.2.4", | ||||
|     "@angular/router": "~14.2.4", | ||||
|     "@ng-bootstrap/ng-bootstrap": "^13.0.0", | ||||
|     "@ng-select/ng-select": "^9.0.2", | ||||
|     "@ngneat/dirty-check-forms": "^3.0.2", | ||||
|     "@popperjs/core": "^2.11.6", | ||||
|     "bootstrap": "^5.2.0", | ||||
|     "bootstrap": "^5.2.1", | ||||
|     "file-saver": "^2.0.5", | ||||
|     "ng2-pdf-viewer": "^9.1.0", | ||||
|     "ngx-color": "^8.0.2", | ||||
|     "ng2-pdf-viewer": "^9.1.2", | ||||
|     "ngx-color": "^8.0.3", | ||||
|     "ngx-cookie-service": "^14.0.1", | ||||
|     "ngx-file-drop": "^14.0.1", | ||||
|     "rxjs": "~7.5.6", | ||||
|     "rxjs": "~7.5.7", | ||||
|     "tslib": "^2.3.1", | ||||
|     "uuid": "^8.3.1", | ||||
|     "uuid": "^9.0.0", | ||||
|     "zone.js": "~0.11.8" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@angular-builders/jest": "14.0.1", | ||||
|     "@angular-devkit/build-angular": "~14.2.1", | ||||
|     "@angular/cli": "~14.2.1", | ||||
|     "@angular/compiler-cli": "~14.2.0", | ||||
|     "@angular-devkit/build-angular": "~14.2.4", | ||||
|     "@angular/cli": "~14.2.4", | ||||
|     "@angular/compiler-cli": "~14.2.4", | ||||
|     "@types/jest": "28.1.6", | ||||
|     "@types/node": "^18.7.14", | ||||
|     "@types/node": "^18.7.23", | ||||
|     "codelyzer": "^6.0.2", | ||||
|     "concurrently": "7.3.0", | ||||
|     "concurrently": "7.4.0", | ||||
|     "jest": "28.1.3", | ||||
|     "jest-environment-jsdom": "^29.0.1", | ||||
|     "jest-environment-jsdom": "^29.1.2", | ||||
|     "jest-preset-angular": "^12.2.2", | ||||
|     "ts-node": "~10.9.1", | ||||
|     "tslint": "~6.1.3", | ||||
|     "typescript": "~4.7.4", | ||||
|     "typescript": "~4.8.4", | ||||
|     "wait-on": "~6.0.1" | ||||
|   }, | ||||
|   "optionalDependencies": { | ||||
|     "@cypress/schematic": "^2.1.1", | ||||
|     "cypress": "~10.7.0" | ||||
|     "cypress": "~10.9.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -207,14 +207,25 @@ | ||||
|           <li class="nav-item mt-2" [class.visually-hidden]="slimSidebarEnabled"> | ||||
|             <div class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap"> | ||||
|               <div class="me-3">{{ versionString }}</div> | ||||
|               <div *ngIf="appRemoteVersion" class="version-check"> | ||||
|               <div *ngIf="!settingsService.updateCheckingIsSet || appRemoteVersion" class="version-check"> | ||||
|                 <ng-template #updateAvailablePopContent> | ||||
|                   <span class="small">Paperless-ngx {{ appRemoteVersion.version }} <ng-container i18n>is available.</ng-container><br/><ng-container i18n>Click to view.</ng-container></span> | ||||
|                 </ng-template> | ||||
|                 <ng-template #updateCheckingNotEnabledPopContent> | ||||
|                   <span class="small"><ng-container i18n>Checking for updates is disabled.</ng-container><br/><ng-container i18n>Click for more information.</ng-container></span> | ||||
|                   <p class="small mb-2"> | ||||
|                     <ng-container i18n>Paperless-ngx can automatically check for updates</ng-container> | ||||
|                   </p> | ||||
|                   <div class="btn-group btn-group-xs flex-fill w-100"> | ||||
|                     <button class="btn btn-outline-primary" (click)="setUpdateChecking(true)">Enable</button> | ||||
|                     <button class="btn btn-outline-secondary" (click)="setUpdateChecking(false)">Disable</button> | ||||
|                   </div> | ||||
|                   <p class="small mb-0 mt-2"> | ||||
|                     <a class="small text-decoration-none fst-italic" routerLink="/settings" fragment="update-checking" i18n> | ||||
|                       How does this work? | ||||
|                     </a> | ||||
|                   </p> | ||||
|                 </ng-template> | ||||
|                 <ng-container *ngIf="appRemoteVersion.feature_is_set; else updateCheckNotSet"> | ||||
|                 <ng-container *ngIf="settingsService.updateCheckingIsSet; else updateCheckNotSet"> | ||||
|                   <a *ngIf="appRemoteVersion.update_available" class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/releases" | ||||
|                   [ngbPopover]="updateAvailablePopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body"> | ||||
|                     <svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16"> | ||||
| @@ -224,8 +235,8 @@ | ||||
|                   </a> | ||||
|                 </ng-container> | ||||
|                 <ng-template #updateCheckNotSet> | ||||
|                   <a class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://paperless-ngx.readthedocs.io/en/latest/configuration.html#update-checking" | ||||
|                   [ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body"> | ||||
|                   <a class="small text-decoration-none" routerLink="/settings" fragment="update-checking" | ||||
|                   [ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter" container="body"> | ||||
|                     <svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16"> | ||||
|                       <use xlink:href="assets/bootstrap-icons.svg#info-circle" /> | ||||
|                     </svg> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Component, HostListener } from '@angular/core' | ||||
| import { Component, HostListener, OnInit } from '@angular/core' | ||||
| import { FormControl } from '@angular/forms' | ||||
| import { ActivatedRoute, Router } from '@angular/router' | ||||
| import { from, Observable } from 'rxjs' | ||||
| @@ -32,7 +32,7 @@ import { ToastService } from 'src/app/services/toast.service' | ||||
|   templateUrl: './app-frame.component.html', | ||||
|   styleUrls: ['./app-frame.component.scss'], | ||||
| }) | ||||
| export class AppFrameComponent implements ComponentCanDeactivate { | ||||
| export class AppFrameComponent implements OnInit, ComponentCanDeactivate { | ||||
|   constructor( | ||||
|     public router: Router, | ||||
|     private activatedRoute: ActivatedRoute, | ||||
| @@ -43,14 +43,14 @@ export class AppFrameComponent implements ComponentCanDeactivate { | ||||
|     private list: DocumentListViewService, | ||||
|     public settingsService: SettingsService, | ||||
|     public tasksService: TasksService, | ||||
|     private toastService: ToastService | ||||
|   ) { | ||||
|     this.remoteVersionService | ||||
|       .checkForUpdates() | ||||
|       .subscribe((appRemoteVersion: AppRemoteVersion) => { | ||||
|         this.appRemoteVersion = appRemoteVersion | ||||
|       }) | ||||
|     tasksService.reload() | ||||
|     private readonly toastService: ToastService | ||||
|   ) {} | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|     if (this.settingsService.get(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED)) { | ||||
|       this.checkForUpdates() | ||||
|     } | ||||
|     this.tasksService.reload() | ||||
|   } | ||||
|  | ||||
|   versionString = `${environment.appTitle} ${environment.version}` | ||||
| @@ -182,4 +182,30 @@ export class AppFrameComponent implements ComponentCanDeactivate { | ||||
|         } | ||||
|       }) | ||||
|   } | ||||
|  | ||||
|   private checkForUpdates() { | ||||
|     this.remoteVersionService | ||||
|       .checkForUpdates() | ||||
|       .subscribe((appRemoteVersion: AppRemoteVersion) => { | ||||
|         this.appRemoteVersion = appRemoteVersion | ||||
|       }) | ||||
|   } | ||||
|  | ||||
|   setUpdateChecking(enable: boolean) { | ||||
|     this.settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, enable) | ||||
|     this.settingsService | ||||
|       .storeSettings() | ||||
|       .pipe(first()) | ||||
|       .subscribe({ | ||||
|         error: (error) => { | ||||
|           this.toastService.showError( | ||||
|             $localize`An error occurred while saving update checking settings.` | ||||
|           ) | ||||
|           console.log(error) | ||||
|         }, | ||||
|       }) | ||||
|     if (enable) { | ||||
|       this.checkForUpdates() | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import { Component } from '@angular/core' | ||||
| import { FormControl, FormGroup } from '@angular/forms' | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' | ||||
| import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' | ||||
| import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent' | ||||
| import { CorrespondentService } from 'src/app/services/rest/correspondent.service' | ||||
| import { ToastService } from 'src/app/services/toast.service' | ||||
| @@ -31,7 +32,7 @@ export class CorrespondentEditDialogComponent extends EditDialogComponent<Paperl | ||||
|   getForm(): FormGroup { | ||||
|     return new FormGroup({ | ||||
|       name: new FormControl(''), | ||||
|       matching_algorithm: new FormControl(1), | ||||
|       matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), | ||||
|       match: new FormControl(''), | ||||
|       is_insensitive: new FormControl(true), | ||||
|     }) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import { Component } from '@angular/core' | ||||
| import { FormControl, FormGroup } from '@angular/forms' | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' | ||||
| import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' | ||||
| import { PaperlessDocumentType } from 'src/app/data/paperless-document-type' | ||||
| import { DocumentTypeService } from 'src/app/services/rest/document-type.service' | ||||
| import { ToastService } from 'src/app/services/toast.service' | ||||
| @@ -31,7 +32,7 @@ export class DocumentTypeEditDialogComponent extends EditDialogComponent<Paperle | ||||
|   getForm(): FormGroup { | ||||
|     return new FormGroup({ | ||||
|       name: new FormControl(''), | ||||
|       matching_algorithm: new FormControl(1), | ||||
|       matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), | ||||
|       match: new FormControl(''), | ||||
|       is_insensitive: new FormControl(true), | ||||
|     }) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import { Component } from '@angular/core' | ||||
| import { FormControl, FormGroup } from '@angular/forms' | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' | ||||
| import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' | ||||
| import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path' | ||||
| import { StoragePathService } from 'src/app/services/rest/storage-path.service' | ||||
| import { ToastService } from 'src/app/services/toast.service' | ||||
| @@ -42,7 +43,7 @@ export class StoragePathEditDialogComponent extends EditDialogComponent<Paperles | ||||
|     return new FormGroup({ | ||||
|       name: new FormControl(''), | ||||
|       path: new FormControl(''), | ||||
|       matching_algorithm: new FormControl(1), | ||||
|       matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), | ||||
|       match: new FormControl(''), | ||||
|       is_insensitive: new FormControl(true), | ||||
|     }) | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { PaperlessTag } from 'src/app/data/paperless-tag' | ||||
| import { TagService } from 'src/app/services/rest/tag.service' | ||||
| import { ToastService } from 'src/app/services/toast.service' | ||||
| import { randomColor } from 'src/app/utils/color' | ||||
| import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-tag-edit-dialog', | ||||
| @@ -34,7 +35,7 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> { | ||||
|       name: new FormControl(''), | ||||
|       color: new FormControl(randomColor()), | ||||
|       is_inbox_tag: new FormControl(false), | ||||
|       matching_algorithm: new FormControl(1), | ||||
|       matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), | ||||
|       match: new FormControl(''), | ||||
|       is_insensitive: new FormControl(true), | ||||
|     }) | ||||
|   | ||||
| @@ -17,25 +17,6 @@ | ||||
|   } | ||||
| } | ||||
|  | ||||
| .btn-group-xs { | ||||
|   > .btn { | ||||
|     padding: 0.2rem 0.25rem; | ||||
|     font-size: 0.675rem; | ||||
|     line-height: 1.2; | ||||
|     border-radius: 0.15rem; | ||||
|   } | ||||
|  | ||||
|   > .btn:not(:first-child) { | ||||
|     border-top-left-radius: 0; | ||||
|     border-bottom-left-radius: 0; | ||||
|   } | ||||
|  | ||||
|   > .btn:not(:last-child) { | ||||
|     border-top-right-radius: 0; | ||||
|     border-bottom-right-radius: 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .btn-group > label.disabled { | ||||
|   filter: brightness(0.5); | ||||
|  | ||||
|   | ||||
| @@ -64,9 +64,9 @@ | ||||
|      </div> | ||||
|    </div> | ||||
|    <div class="w-100 d-xxl-none"></div> | ||||
|    <div class="col col-xl-auto ps-0"> | ||||
|    <div class="col col-xl-auto ps-xxl-0"> | ||||
|      <button class="btn btn-link btn-sm px-0" [disabled]="!rulesModified" (click)="resetSelected()"> | ||||
|        <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x me-1" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|        <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x me-1 ms-n1" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|          <path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/> | ||||
|        </svg><ng-container i18n>Reset filters</ng-container> | ||||
|      </button> | ||||
|   | ||||
| @@ -127,6 +127,21 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <h4 class="mt-4" id="update-checking" i18n>Update checking</h4> | ||||
|  | ||||
|         <div class="row mb-3"> | ||||
|           <div class="offset-md-3 col"> | ||||
|             <p i18n> | ||||
|               Update checking works by pinging the the public <a href="https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest" target="_blank" rel="noopener noreferrer">Github API</a> for the latest release to determine whether a new version is available.<br/> | ||||
|               Actual updating of the app must still be performed manually. | ||||
|             </p> | ||||
|             <p i18n> | ||||
|               <em>No tracking data is collected by the app in any way.</em> | ||||
|             </p> | ||||
|             <app-input-check i18n-title title="Enable update checking" formControlName="updateCheckingEnabled" i18n-hint hint="Note that for users of thirdy-party containers e.g. linuxserver.io this notification may be 'ahead' of the current third-party release."></app-input-check> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <h4 class="mt-4" i18n>Bulk editing</h4> | ||||
|  | ||||
|         <div class="row mb-3"> | ||||
| @@ -205,5 +220,5 @@ | ||||
|  | ||||
|   <div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div> | ||||
|  | ||||
|   <button type="submit" class="btn btn-primary" [disabled]="!(isDirty$ | async)" i18n>Save</button> | ||||
|   <button type="submit" class="btn btn-primary mb-2" [disabled]="!(isDirty$ | async)" i18n>Save</button> | ||||
| </form> | ||||
|   | ||||
| @@ -1,4 +1,11 @@ | ||||
| import { Component, Inject, LOCALE_ID, OnInit, OnDestroy } from '@angular/core' | ||||
| import { | ||||
|   Component, | ||||
|   Inject, | ||||
|   LOCALE_ID, | ||||
|   OnInit, | ||||
|   OnDestroy, | ||||
|   AfterViewInit, | ||||
| } from '@angular/core' | ||||
| import { FormControl, FormGroup } from '@angular/forms' | ||||
| import { PaperlessSavedView } from 'src/app/data/paperless-saved-view' | ||||
| import { DocumentListViewService } from 'src/app/services/document-list-view.service' | ||||
| @@ -9,8 +16,18 @@ import { | ||||
| } from 'src/app/services/settings.service' | ||||
| import { Toast, ToastService } from 'src/app/services/toast.service' | ||||
| import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms' | ||||
| import { Observable, Subscription, BehaviorSubject, first } from 'rxjs' | ||||
| import { | ||||
|   Observable, | ||||
|   Subscription, | ||||
|   BehaviorSubject, | ||||
|   first, | ||||
|   tap, | ||||
|   takeUntil, | ||||
|   Subject, | ||||
| } from 'rxjs' | ||||
| import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||
| import { ActivatedRoute } from '@angular/router' | ||||
| import { ViewportScroller } from '@angular/common' | ||||
| import { ForwardRefHandling } from '@angular/compiler' | ||||
|  | ||||
| @Component({ | ||||
| @@ -18,7 +35,9 @@ import { ForwardRefHandling } from '@angular/compiler' | ||||
|   templateUrl: './settings.component.html', | ||||
|   styleUrls: ['./settings.component.scss'], | ||||
| }) | ||||
| export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
| export class SettingsComponent | ||||
|   implements OnInit, AfterViewInit, OnDestroy, DirtyComponent | ||||
| { | ||||
|   savedViewGroup = new FormGroup({}) | ||||
|  | ||||
|   settingsForm = new FormGroup({ | ||||
| @@ -40,6 +59,7 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
|     notificationsConsumerFailed: new FormControl(null), | ||||
|     notificationsConsumerSuppressOnDashboard: new FormControl(null), | ||||
|     commentsEnabled: new FormControl(null), | ||||
|     updateCheckingEnabled: new FormControl(null), | ||||
|   }) | ||||
|  | ||||
|   savedViews: PaperlessSavedView[] | ||||
| @@ -47,7 +67,9 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
|   store: BehaviorSubject<any> | ||||
|   storeSub: Subscription | ||||
|   isDirty$: Observable<boolean> | ||||
|   isDirty: Boolean = false | ||||
|   isDirty: boolean = false | ||||
|   unsubscribeNotifier: Subject<any> = new Subject() | ||||
|   savePending: boolean = false | ||||
|  | ||||
|   get computedDateLocale(): string { | ||||
|     return ( | ||||
| @@ -57,29 +79,28 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   get displayLanguageIsDirty(): boolean { | ||||
|     return ( | ||||
|       this.settingsForm.get('displayLanguage').value != | ||||
|       this.store?.getValue()['displayLanguage'] | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   constructor( | ||||
|     public savedViewService: SavedViewService, | ||||
|     private documentListViewService: DocumentListViewService, | ||||
|     private toastService: ToastService, | ||||
|     private settings: SettingsService, | ||||
|     @Inject(LOCALE_ID) public currentLocale: string | ||||
|     @Inject(LOCALE_ID) public currentLocale: string, | ||||
|     private viewportScroller: ViewportScroller, | ||||
|     private activatedRoute: ActivatedRoute | ||||
|   ) { | ||||
|     this.settings.changed.subscribe({ | ||||
|       next: () => { | ||||
|         this.settingsForm.patchValue(this.getCurrentSettings(), { | ||||
|           emitEvent: false, | ||||
|         }) | ||||
|       }, | ||||
|     this.settings.settingsSaved.subscribe(() => { | ||||
|       if (!this.savePending) this.initialize() | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   ngAfterViewInit(): void { | ||||
|     if (this.activatedRoute.snapshot.fragment) { | ||||
|       this.viewportScroller.scrollToAnchor( | ||||
|         this.activatedRoute.snapshot.fragment | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private getCurrentSettings() { | ||||
|     return { | ||||
|       bulkEditConfirmationDialogs: this.settings.get( | ||||
| @@ -91,7 +112,6 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
|       documentListItemPerPage: this.settings.get( | ||||
|         SETTINGS_KEYS.DOCUMENT_LIST_SIZE | ||||
|       ), | ||||
|       slimSidebarEnabled: this.settings.get(SETTINGS_KEYS.SLIM_SIDEBAR), | ||||
|       darkModeUseSystem: this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM), | ||||
|       darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), | ||||
|       darkModeInvertThumbs: this.settings.get( | ||||
| @@ -118,55 +138,68 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
|         SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD | ||||
|       ), | ||||
|       commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED), | ||||
|       updateCheckingEnabled: this.settings.get( | ||||
|         SETTINGS_KEYS.UPDATE_CHECKING_ENABLED | ||||
|       ), | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|     this.savedViewService.listAll().subscribe((r) => { | ||||
|       this.savedViews = r.results | ||||
|       let storeData = this.getCurrentSettings() | ||||
|       this.initialize() | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|       for (let view of this.savedViews) { | ||||
|         storeData.savedViews[view.id.toString()] = { | ||||
|           id: view.id, | ||||
|           name: view.name, | ||||
|           show_on_dashboard: view.show_on_dashboard, | ||||
|           show_in_sidebar: view.show_in_sidebar, | ||||
|         } | ||||
|         this.savedViewGroup.addControl( | ||||
|           view.id.toString(), | ||||
|           new FormGroup({ | ||||
|             id: new FormControl(null), | ||||
|             name: new FormControl(null), | ||||
|             show_on_dashboard: new FormControl(null), | ||||
|             show_in_sidebar: new FormControl(null), | ||||
|           }) | ||||
|         ) | ||||
|   initialize() { | ||||
|     this.unsubscribeNotifier.next(true) | ||||
|  | ||||
|     let storeData = this.getCurrentSettings() | ||||
|  | ||||
|     for (let view of this.savedViews) { | ||||
|       storeData.savedViews[view.id.toString()] = { | ||||
|         id: view.id, | ||||
|         name: view.name, | ||||
|         show_on_dashboard: view.show_on_dashboard, | ||||
|         show_in_sidebar: view.show_in_sidebar, | ||||
|       } | ||||
|       this.savedViewGroup.addControl( | ||||
|         view.id.toString(), | ||||
|         new FormGroup({ | ||||
|           id: new FormControl(null), | ||||
|           name: new FormControl(null), | ||||
|           show_on_dashboard: new FormControl(null), | ||||
|           show_in_sidebar: new FormControl(null), | ||||
|         }) | ||||
|       ) | ||||
|     } | ||||
|  | ||||
|       this.store = new BehaviorSubject(storeData) | ||||
|     this.store = new BehaviorSubject(storeData) | ||||
|  | ||||
|       this.storeSub = this.store.asObservable().subscribe((state) => { | ||||
|         this.settingsForm.patchValue(state, { emitEvent: false }) | ||||
|       }) | ||||
|     this.storeSub = this.store.asObservable().subscribe((state) => { | ||||
|       this.settingsForm.patchValue(state, { emitEvent: false }) | ||||
|     }) | ||||
|  | ||||
|       // Initialize dirtyCheck | ||||
|       this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable()) | ||||
|     // Initialize dirtyCheck | ||||
|     this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable()) | ||||
|  | ||||
|       // Record dirty in case we need to 'undo' appearance settings if not saved on close | ||||
|       this.isDirty$.subscribe((dirty) => { | ||||
|     // Record dirty in case we need to 'undo' appearance settings if not saved on close | ||||
|     this.isDirty$ | ||||
|       .pipe(takeUntil(this.unsubscribeNotifier)) | ||||
|       .subscribe((dirty) => { | ||||
|         this.isDirty = dirty | ||||
|       }) | ||||
|  | ||||
|       // "Live" visual changes prior to save | ||||
|       this.settingsForm.valueChanges.subscribe(() => { | ||||
|     // "Live" visual changes prior to save | ||||
|     this.settingsForm.valueChanges | ||||
|       .pipe(takeUntil(this.unsubscribeNotifier)) | ||||
|       .subscribe(() => { | ||||
|         this.settings.updateAppearanceSettings( | ||||
|           this.settingsForm.get('darkModeUseSystem').value, | ||||
|           this.settingsForm.get('darkModeEnabled').value, | ||||
|           this.settingsForm.get('themeColor').value | ||||
|         ) | ||||
|       }) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
| @@ -185,7 +218,14 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
|   } | ||||
|  | ||||
|   private saveLocalSettings() { | ||||
|     const reloadRequired = this.displayLanguageIsDirty // just this one, for now | ||||
|     this.savePending = true | ||||
|     const reloadRequired = | ||||
|       this.settingsForm.value.displayLanguage != | ||||
|         this.store?.getValue()['displayLanguage'] || // displayLanguage is dirty | ||||
|       (this.settingsForm.value.updateCheckingEnabled != | ||||
|         this.store?.getValue()['updateCheckingEnabled'] && | ||||
|         this.settingsForm.value.updateCheckingEnabled) // update checking was turned on | ||||
|  | ||||
|     this.settings.set( | ||||
|       SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, | ||||
|       this.settingsForm.value.bulkEditApplyOnClose | ||||
| @@ -250,10 +290,15 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { | ||||
|       SETTINGS_KEYS.COMMENTS_ENABLED, | ||||
|       this.settingsForm.value.commentsEnabled | ||||
|     ) | ||||
|     this.settings.set( | ||||
|       SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, | ||||
|       this.settingsForm.value.updateCheckingEnabled | ||||
|     ) | ||||
|     this.settings.setLanguage(this.settingsForm.value.displayLanguage) | ||||
|     this.settings | ||||
|       .storeSettings() | ||||
|       .pipe(first()) | ||||
|       .pipe(tap(() => (this.savePending = false))) | ||||
|       .subscribe({ | ||||
|         next: () => { | ||||
|           this.store.next(this.settingsForm.value) | ||||
|   | ||||
| @@ -6,6 +6,7 @@ export const MATCH_LITERAL = 3 | ||||
| export const MATCH_REGEX = 4 | ||||
| export const MATCH_FUZZY = 5 | ||||
| export const MATCH_AUTO = 6 | ||||
| export const DEFAULT_MATCHING_ALGORITHM = MATCH_AUTO | ||||
|  | ||||
| export const MATCHING_ALGORITHMS = [ | ||||
|   { | ||||
|   | ||||
| @@ -29,8 +29,6 @@ export interface PaperlessDocument extends ObjectWithId { | ||||
|  | ||||
|   content?: string | ||||
|  | ||||
|   file_type?: string | ||||
|  | ||||
|   tags$?: Observable<PaperlessTag[]> | ||||
|  | ||||
|   tags?: number[] | ||||
| @@ -47,7 +45,7 @@ export interface PaperlessDocument extends ObjectWithId { | ||||
|  | ||||
|   added?: Date | ||||
|  | ||||
|   file_name?: string | ||||
|   original_file_name?: string | ||||
|  | ||||
|   download_url?: string | ||||
|  | ||||
|   | ||||
| @@ -38,6 +38,9 @@ export const SETTINGS_KEYS = { | ||||
|     'general-settings:notifications:consumer-suppress-on-dashboard', | ||||
|   COMMENTS_ENABLED: 'general-settings:comments-enabled', | ||||
|   SLIM_SIDEBAR: 'general-settings:slim-sidebar', | ||||
|   UPDATE_CHECKING_ENABLED: 'general-settings:update-checking:enabled', | ||||
|   UPDATE_CHECKING_BACKEND_SETTING: | ||||
|     'general-settings:update-checking:backend-setting', | ||||
| } | ||||
|  | ||||
| export const SETTINGS: PaperlessUiSetting[] = [ | ||||
| @@ -126,4 +129,14 @@ export const SETTINGS: PaperlessUiSetting[] = [ | ||||
|     type: 'boolean', | ||||
|     default: true, | ||||
|   }, | ||||
|   { | ||||
|     key: SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, | ||||
|     type: 'boolean', | ||||
|     default: false, | ||||
|   }, | ||||
|   { | ||||
|     key: SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING, | ||||
|     type: 'string', | ||||
|     default: '', | ||||
|   }, | ||||
| ] | ||||
|   | ||||
| @@ -6,7 +6,6 @@ import { environment } from 'src/environments/environment' | ||||
| export interface AppRemoteVersion { | ||||
|   version: string | ||||
|   update_available: boolean | ||||
|   feature_is_set: boolean | ||||
| } | ||||
|  | ||||
| @Injectable({ | ||||
|   | ||||
| @@ -47,7 +47,7 @@ export class SettingsService { | ||||
|  | ||||
|   public displayName: string | ||||
|  | ||||
|   public changed = new EventEmitter() | ||||
|   public settingsSaved: EventEmitter<any> = new EventEmitter() | ||||
|  | ||||
|   constructor( | ||||
|     rendererFactory: RendererFactory2, | ||||
| @@ -316,13 +316,7 @@ export class SettingsService { | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   get(key: string): any { | ||||
|     let setting = SETTINGS.find((s) => s.key == key) | ||||
|  | ||||
|     if (!setting) { | ||||
|       return null | ||||
|     } | ||||
|  | ||||
|   private getSettingRawValue(key: string): any { | ||||
|     let value = null | ||||
|     // parse key:key:key into nested object | ||||
|     const keys = key.replace('general-settings:', '').split(':') | ||||
| @@ -333,6 +327,17 @@ export class SettingsService { | ||||
|       if (index == keys.length - 1) value = settingObj[keyPart] | ||||
|       else settingObj = settingObj[keyPart] | ||||
|     }) | ||||
|     return value | ||||
|   } | ||||
|  | ||||
|   get(key: string): any { | ||||
|     let setting = SETTINGS.find((s) => s.key == key) | ||||
|  | ||||
|     if (!setting) { | ||||
|       return null | ||||
|     } | ||||
|  | ||||
|     let value = this.getSettingRawValue(key) | ||||
|  | ||||
|     if (value != null) { | ||||
|       switch (setting.type) { | ||||
| @@ -362,10 +367,19 @@ export class SettingsService { | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   private settingIsSet(key: string): boolean { | ||||
|     let value = this.getSettingRawValue(key) | ||||
|     return value != null | ||||
|   } | ||||
|  | ||||
|   storeSettings(): Observable<any> { | ||||
|     return this.http | ||||
|       .post(this.baseUrl, { settings: this.settings }) | ||||
|       .pipe(tap((result) => this.changed.emit(!!result.success))) | ||||
|     return this.http.post(this.baseUrl, { settings: this.settings }).pipe( | ||||
|       tap((results) => { | ||||
|         if (results.success) { | ||||
|           this.settingsSaved.emit() | ||||
|         } | ||||
|       }) | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   maybeMigrateSettings() { | ||||
| @@ -405,5 +419,31 @@ export class SettingsService { | ||||
|           }, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     if ( | ||||
|       !this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) && | ||||
|       this.get(SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING) != 'default' | ||||
|     ) { | ||||
|       this.set( | ||||
|         SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, | ||||
|         this.get(SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING).toString() === | ||||
|           'true' | ||||
|       ) | ||||
|  | ||||
|       this.storeSettings() | ||||
|         .pipe(first()) | ||||
|         .subscribe({ | ||||
|           error: (e) => { | ||||
|             this.toastService.showError( | ||||
|               'Error migrating update checking setting' | ||||
|             ) | ||||
|             console.log(e) | ||||
|           }, | ||||
|         }) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   get updateCheckingIsSet(): boolean { | ||||
|     return this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ export const environment = { | ||||
|   apiBaseUrl: document.baseURI + 'api/', | ||||
|   apiVersion: '2', | ||||
|   appTitle: 'Paperless-ngx', | ||||
|   version: '1.9.0-dev', | ||||
|   version: '1.9.2-dev', | ||||
|   webSocketHost: window.location.host, | ||||
|   webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:', | ||||
|   webSocketBaseUrl: base_url.pathname + 'ws/', | ||||
|   | ||||
| @@ -540,6 +540,25 @@ a.badge { | ||||
|     border-color: var(--bs-primary); | ||||
| } | ||||
|  | ||||
| .btn-group-xs { | ||||
|   > .btn { | ||||
|     padding: 0.2rem 0.25rem; | ||||
|     font-size: 0.675rem; | ||||
|     line-height: 1.2; | ||||
|     border-radius: 0.15rem; | ||||
|   } | ||||
|  | ||||
|   > .btn:not(:first-child) { | ||||
|     border-top-left-radius: 0; | ||||
|     border-bottom-left-radius: 0; | ||||
|   } | ||||
|  | ||||
|   > .btn:not(:last-child) { | ||||
|     border-top-right-radius: 0; | ||||
|     border-bottom-right-radius: 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| code { | ||||
|   color: var(--pngx-body-color-accent) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon