mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-20 03:06:10 -05:00 
			
		
		
		
	Handle settings live refresh, unsubscribe subscriptions
This commit is contained in:
		| @@ -16,7 +16,15 @@ import { | |||||||
| } from 'src/app/services/settings.service' | } from 'src/app/services/settings.service' | ||||||
| import { Toast, ToastService } from 'src/app/services/toast.service' | import { Toast, ToastService } from 'src/app/services/toast.service' | ||||||
| import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms' | 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 { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||||
| import { ActivatedRoute } from '@angular/router' | import { ActivatedRoute } from '@angular/router' | ||||||
| import { ViewportScroller } from '@angular/common' | import { ViewportScroller } from '@angular/common' | ||||||
| @@ -57,7 +65,9 @@ export class SettingsComponent | |||||||
|   store: BehaviorSubject<any> |   store: BehaviorSubject<any> | ||||||
|   storeSub: Subscription |   storeSub: Subscription | ||||||
|   isDirty$: Observable<boolean> |   isDirty$: Observable<boolean> | ||||||
|   isDirty: Boolean = false |   isDirty: boolean = false | ||||||
|  |   unsubscribeNotifier: Subject<any> = new Subject() | ||||||
|  |   savePending: boolean = false | ||||||
|  |  | ||||||
|   get computedDateLocale(): string { |   get computedDateLocale(): string { | ||||||
|     return ( |     return ( | ||||||
| @@ -75,7 +85,11 @@ export class SettingsComponent | |||||||
|     @Inject(LOCALE_ID) public currentLocale: string, |     @Inject(LOCALE_ID) public currentLocale: string, | ||||||
|     private viewportScroller: ViewportScroller, |     private viewportScroller: ViewportScroller, | ||||||
|     private activatedRoute: ActivatedRoute |     private activatedRoute: ActivatedRoute | ||||||
|   ) {} |   ) { | ||||||
|  |     this.settings.settingsSaved.subscribe(() => { | ||||||
|  |       if (!this.savePending) this.initialize() | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   ngAfterViewInit(): void { |   ngAfterViewInit(): void { | ||||||
|     if (this.activatedRoute.snapshot.fragment) { |     if (this.activatedRoute.snapshot.fragment) { | ||||||
| @@ -88,90 +102,98 @@ export class SettingsComponent | |||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this.savedViewService.listAll().subscribe((r) => { |     this.savedViewService.listAll().subscribe((r) => { | ||||||
|       this.savedViews = r.results |       this.savedViews = r.results | ||||||
|       let storeData = { |       this.initialize() | ||||||
|         bulkEditConfirmationDialogs: this.settings.get( |     }) | ||||||
|           SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS |   } | ||||||
|         ), |  | ||||||
|         bulkEditApplyOnClose: this.settings.get( |   initialize() { | ||||||
|           SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE |     this.unsubscribeNotifier.next(true) | ||||||
|         ), |  | ||||||
|         documentListItemPerPage: this.settings.get( |     let storeData = { | ||||||
|           SETTINGS_KEYS.DOCUMENT_LIST_SIZE |       bulkEditConfirmationDialogs: this.settings.get( | ||||||
|         ), |         SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS | ||||||
|         darkModeUseSystem: this.settings.get( |       ), | ||||||
|           SETTINGS_KEYS.DARK_MODE_USE_SYSTEM |       bulkEditApplyOnClose: this.settings.get( | ||||||
|         ), |         SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE | ||||||
|         darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), |       ), | ||||||
|         darkModeInvertThumbs: this.settings.get( |       documentListItemPerPage: this.settings.get( | ||||||
|           SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED |         SETTINGS_KEYS.DOCUMENT_LIST_SIZE | ||||||
|         ), |       ), | ||||||
|         themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR), |       darkModeUseSystem: this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM), | ||||||
|         useNativePdfViewer: this.settings.get( |       darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), | ||||||
|           SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER |       darkModeInvertThumbs: this.settings.get( | ||||||
|         ), |         SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED | ||||||
|         savedViews: {}, |       ), | ||||||
|         displayLanguage: this.settings.getLanguage(), |       themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR), | ||||||
|         dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE), |       useNativePdfViewer: this.settings.get( | ||||||
|         dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT), |         SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER | ||||||
|         notificationsConsumerNewDocument: this.settings.get( |       ), | ||||||
|           SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT |       savedViews: {}, | ||||||
|         ), |       displayLanguage: this.settings.getLanguage(), | ||||||
|         notificationsConsumerSuccess: this.settings.get( |       dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE), | ||||||
|           SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS |       dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT), | ||||||
|         ), |       notificationsConsumerNewDocument: this.settings.get( | ||||||
|         notificationsConsumerFailed: this.settings.get( |         SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT | ||||||
|           SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED |       ), | ||||||
|         ), |       notificationsConsumerSuccess: this.settings.get( | ||||||
|         notificationsConsumerSuppressOnDashboard: this.settings.get( |         SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS | ||||||
|           SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD |       ), | ||||||
|         ), |       notificationsConsumerFailed: this.settings.get( | ||||||
|         commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED), |         SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED | ||||||
|         updateCheckingEnabled: this.settings.get( |       ), | ||||||
|           SETTINGS_KEYS.UPDATE_CHECKING_ENABLED |       notificationsConsumerSuppressOnDashboard: this.settings.get( | ||||||
|         ), |         SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD | ||||||
|  |       ), | ||||||
|  |       commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED), | ||||||
|  |       updateCheckingEnabled: this.settings.get( | ||||||
|  |         SETTINGS_KEYS.UPDATE_CHECKING_ENABLED | ||||||
|  |       ), | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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), | ||||||
|  |         }) | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |  | ||||||
|       for (let view of this.savedViews) { |     this.store = new BehaviorSubject(storeData) | ||||||
|         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.storeSub = this.store.asObservable().subscribe((state) => { | ||||||
|  |       this.settingsForm.patchValue(state, { emitEvent: false }) | ||||||
|  |     }) | ||||||
|  |  | ||||||
|       this.storeSub = this.store.asObservable().subscribe((state) => { |     // Initialize dirtyCheck | ||||||
|         this.settingsForm.patchValue(state, { emitEvent: false }) |     this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable()) | ||||||
|       }) |  | ||||||
|  |  | ||||||
|       // Initialize dirtyCheck |     // Record dirty in case we need to 'undo' appearance settings if not saved on close | ||||||
|       this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable()) |     this.isDirty$ | ||||||
|  |       .pipe(takeUntil(this.unsubscribeNotifier)) | ||||||
|       // Record dirty in case we need to 'undo' appearance settings if not saved on close |       .subscribe((dirty) => { | ||||||
|       this.isDirty$.subscribe((dirty) => { |  | ||||||
|         this.isDirty = dirty |         this.isDirty = dirty | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|       // "Live" visual changes prior to save |     // "Live" visual changes prior to save | ||||||
|       this.settingsForm.valueChanges.subscribe(() => { |     this.settingsForm.valueChanges | ||||||
|  |       .pipe(takeUntil(this.unsubscribeNotifier)) | ||||||
|  |       .subscribe(() => { | ||||||
|         this.settings.updateAppearanceSettings( |         this.settings.updateAppearanceSettings( | ||||||
|           this.settingsForm.get('darkModeUseSystem').value, |           this.settingsForm.get('darkModeUseSystem').value, | ||||||
|           this.settingsForm.get('darkModeEnabled').value, |           this.settingsForm.get('darkModeEnabled').value, | ||||||
|           this.settingsForm.get('themeColor').value |           this.settingsForm.get('themeColor').value | ||||||
|         ) |         ) | ||||||
|       }) |       }) | ||||||
|     }) |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ngOnDestroy() { |   ngOnDestroy() { | ||||||
| @@ -190,6 +212,7 @@ export class SettingsComponent | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private saveLocalSettings() { |   private saveLocalSettings() { | ||||||
|  |     this.savePending = true | ||||||
|     const reloadRequired = |     const reloadRequired = | ||||||
|       this.settingsForm.value.displayLanguage != |       this.settingsForm.value.displayLanguage != | ||||||
|         this.store?.getValue()['displayLanguage'] || // displayLanguage is dirty |         this.store?.getValue()['displayLanguage'] || // displayLanguage is dirty | ||||||
| @@ -265,6 +288,7 @@ export class SettingsComponent | |||||||
|     this.settings |     this.settings | ||||||
|       .storeSettings() |       .storeSettings() | ||||||
|       .pipe(first()) |       .pipe(first()) | ||||||
|  |       .pipe(tap(() => (this.savePending = false))) | ||||||
|       .subscribe({ |       .subscribe({ | ||||||
|         next: () => { |         next: () => { | ||||||
|           this.store.next(this.settingsForm.value) |           this.store.next(this.settingsForm.value) | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import { DOCUMENT } from '@angular/common' | import { DOCUMENT } from '@angular/common' | ||||||
| import { HttpClient } from '@angular/common/http' | import { HttpClient } from '@angular/common/http' | ||||||
| import { | import { | ||||||
|  |   EventEmitter, | ||||||
|   Inject, |   Inject, | ||||||
|   Injectable, |   Injectable, | ||||||
|   LOCALE_ID, |   LOCALE_ID, | ||||||
| @@ -46,6 +47,8 @@ export class SettingsService { | |||||||
|  |  | ||||||
|   public displayName: string |   public displayName: string | ||||||
|  |  | ||||||
|  |   public settingsSaved: EventEmitter<any> = new EventEmitter() | ||||||
|  |  | ||||||
|   constructor( |   constructor( | ||||||
|     rendererFactory: RendererFactory2, |     rendererFactory: RendererFactory2, | ||||||
|     @Inject(DOCUMENT) private document, |     @Inject(DOCUMENT) private document, | ||||||
| @@ -370,7 +373,13 @@ export class SettingsService { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   storeSettings(): Observable<any> { |   storeSettings(): Observable<any> { | ||||||
|     return this.http.post(this.baseUrl, { settings: this.settings }) |     return this.http.post(this.baseUrl, { settings: this.settings }).pipe( | ||||||
|  |       tap((results) => { | ||||||
|  |         if (results.success) { | ||||||
|  |           this.settingsSaved.emit() | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   maybeMigrateSettings() { |   maybeMigrateSettings() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon