diff --git a/src-ui/src/app/components/manage/settings/settings.component.ts b/src-ui/src/app/components/manage/settings/settings.component.ts index 8e26e8b22..0cf336a01 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -16,7 +16,15 @@ 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' @@ -57,7 +65,9 @@ export class SettingsComponent store: BehaviorSubject storeSub: Subscription isDirty$: Observable - isDirty: Boolean = false + isDirty: boolean = false + unsubscribeNotifier: Subject = new Subject() + savePending: boolean = false get computedDateLocale(): string { return ( @@ -75,7 +85,11 @@ export class SettingsComponent @Inject(LOCALE_ID) public currentLocale: string, private viewportScroller: ViewportScroller, private activatedRoute: ActivatedRoute - ) {} + ) { + this.settings.settingsSaved.subscribe(() => { + if (!this.savePending) this.initialize() + }) + } ngAfterViewInit(): void { if (this.activatedRoute.snapshot.fragment) { @@ -88,90 +102,98 @@ export class SettingsComponent ngOnInit() { this.savedViewService.listAll().subscribe((r) => { this.savedViews = r.results - let storeData = { - bulkEditConfirmationDialogs: this.settings.get( - SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS - ), - bulkEditApplyOnClose: this.settings.get( - SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE - ), - documentListItemPerPage: this.settings.get( - SETTINGS_KEYS.DOCUMENT_LIST_SIZE - ), - darkModeUseSystem: this.settings.get( - SETTINGS_KEYS.DARK_MODE_USE_SYSTEM - ), - darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), - darkModeInvertThumbs: this.settings.get( - SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED - ), - themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR), - useNativePdfViewer: this.settings.get( - SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER - ), - savedViews: {}, - displayLanguage: this.settings.getLanguage(), - dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE), - dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT), - notificationsConsumerNewDocument: this.settings.get( - SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT - ), - notificationsConsumerSuccess: this.settings.get( - SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS - ), - notificationsConsumerFailed: this.settings.get( - SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED - ), - 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 - ), + this.initialize() + }) + } + + initialize() { + this.unsubscribeNotifier.next(true) + + let storeData = { + bulkEditConfirmationDialogs: this.settings.get( + SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS + ), + bulkEditApplyOnClose: this.settings.get( + SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE + ), + documentListItemPerPage: this.settings.get( + SETTINGS_KEYS.DOCUMENT_LIST_SIZE + ), + darkModeUseSystem: this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM), + darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), + darkModeInvertThumbs: this.settings.get( + SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED + ), + themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR), + useNativePdfViewer: this.settings.get( + SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER + ), + savedViews: {}, + displayLanguage: this.settings.getLanguage(), + dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE), + dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT), + notificationsConsumerNewDocument: this.settings.get( + SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT + ), + notificationsConsumerSuccess: this.settings.get( + SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS + ), + notificationsConsumerFailed: this.settings.get( + SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED + ), + 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) { - 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() { @@ -190,6 +212,7 @@ export class SettingsComponent } private saveLocalSettings() { + this.savePending = true const reloadRequired = this.settingsForm.value.displayLanguage != this.store?.getValue()['displayLanguage'] || // displayLanguage is dirty @@ -265,6 +288,7 @@ export class SettingsComponent this.settings .storeSettings() .pipe(first()) + .pipe(tap(() => (this.savePending = false))) .subscribe({ next: () => { this.store.next(this.settingsForm.value) diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index b58a1f845..10cc93ce0 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -1,6 +1,7 @@ import { DOCUMENT } from '@angular/common' import { HttpClient } from '@angular/common/http' import { + EventEmitter, Inject, Injectable, LOCALE_ID, @@ -46,6 +47,8 @@ export class SettingsService { public displayName: string + public settingsSaved: EventEmitter = new EventEmitter() + constructor( rendererFactory: RendererFactory2, @Inject(DOCUMENT) private document, @@ -370,7 +373,13 @@ export class SettingsService { } storeSettings(): Observable { - 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() {