From a697eb8530552b277b9c6b2770c460fff35a260f Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 7 May 2022 00:05:51 -0700 Subject: [PATCH] Basic frontend settings retrieval --- .../manage/settings/settings.component.ts | 179 ++++++++++-------- src-ui/src/app/services/settings.service.ts | 52 ++++- src/documents/serialisers.py | 2 +- 3 files changed, 146 insertions(+), 87 deletions(-) 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 45785c709..0978a94ab 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -17,7 +17,7 @@ import { } from 'src/app/services/settings.service' import { ToastService } from 'src/app/services/toast.service' import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms' -import { Observable, Subscription, BehaviorSubject } from 'rxjs' +import { Observable, Subscription, BehaviorSubject, first } from 'rxjs' @Component({ selector: 'app-settings', @@ -72,85 +72,93 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { 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 - ), - } + this.settings + .retrieveSettings() + .pipe(first()) + .subscribe(() => { + 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 + ), + } - 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.storeSub = this.store.asObservable().subscribe((state) => { + this.settingsForm.patchValue(state, { emitEvent: false }) }) - ) - } - this.store = new BehaviorSubject(storeData) + // Initialize dirtyCheck + this.isDirty$ = dirtyCheck( + this.settingsForm, + this.store.asObservable() + ) - this.storeSub = this.store.asObservable().subscribe((state) => { - this.settingsForm.patchValue(state, { emitEvent: false }) - }) + // Record dirty in case we need to 'undo' appearance settings if not saved on close + this.isDirty$.subscribe((dirty) => { + this.isDirty = dirty + }) - // 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) => { - this.isDirty = dirty - }) - - // "Live" visual changes prior to save - this.settingsForm.valueChanges.subscribe(() => { - this.settings.updateAppearanceSettings( - this.settingsForm.get('darkModeUseSystem').value, - this.settingsForm.get('darkModeEnabled').value, - this.settingsForm.get('themeColor').value - ) - }) + // "Live" visual changes prior to save + this.settingsForm.valueChanges.subscribe(() => { + this.settings.updateAppearanceSettings( + this.settingsForm.get('darkModeUseSystem').value, + this.settingsForm.get('darkModeEnabled').value, + this.settingsForm.get('themeColor').value + ) + }) + }) }) } @@ -227,10 +235,23 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { this.settingsForm.value.notificationsConsumerSuppressOnDashboard ) this.settings.setLanguage(this.settingsForm.value.displayLanguage) - this.store.next(this.settingsForm.value) - this.documentListViewService.updatePageSize() - this.settings.updateAppearanceSettings() - this.toastService.showInfo($localize`Settings saved successfully.`) + this.settings + .storeSettings() + .pipe(first()) + .subscribe({ + next: () => { + this.store.next(this.settingsForm.value) + this.documentListViewService.updatePageSize() + this.settings.updateAppearanceSettings() + this.toastService.showInfo($localize`Settings saved successfully.`) + }, + error: (error) => { + this.toastService.showError( + $localize`An error occurred while saving settings.` + ) + console.log(error) + }, + }) } get displayLanguageOptions(): LanguageOption[] { diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index 802c55611..f4708880a 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -1,4 +1,5 @@ import { DOCUMENT } from '@angular/common' +import { HttpClient } from '@angular/common/http' import { Inject, Injectable, @@ -9,11 +10,14 @@ import { } from '@angular/core' import { Meta } from '@angular/platform-browser' import { CookieService } from 'ngx-cookie-service' +import { first, Observable } from 'rxjs' import { BRIGHTNESS, estimateBrightnessForColor, hexToHsl, } from 'src/app/utils/color' +import { environment } from 'src/environments/environment' +import { Results } from '../data/results' export interface PaperlessSettings { key: string @@ -132,17 +136,34 @@ const SETTINGS: PaperlessSettings[] = [ }) export class SettingsService { private renderer: Renderer2 + protected baseUrl: string = environment.apiBaseUrl + 'frontend_settings/' + + private settings: Object = {} + private settings$: Observable> constructor( - private rendererFactory: RendererFactory2, + rendererFactory: RendererFactory2, @Inject(DOCUMENT) private document, private cookieService: CookieService, private meta: Meta, - @Inject(LOCALE_ID) private localeId: string + @Inject(LOCALE_ID) private localeId: string, + protected http: HttpClient ) { this.renderer = rendererFactory.createRenderer(null, null) - this.updateAppearanceSettings() + this.retrieveSettings() + .pipe(first()) + .subscribe((response) => { + Object.assign(this.settings, response['settings']) + + this.updateAppearanceSettings() + }) + } + + public retrieveSettings(): Observable> { + if (!this.settings$) + this.settings$ = this.http.get>(this.baseUrl) + return this.settings$ } public updateAppearanceSettings( @@ -390,7 +411,16 @@ export class SettingsService { return null } - let value = localStorage.getItem(key) + let value = null + // parse key:key:key into nested object + const keys = key.replace('general-settings:', '').split(':') + let settingObj = this.settings + keys.forEach((keyPart, index) => { + keyPart = keyPart.replace(/-/g, '_') + if (!settingObj.hasOwnProperty(keyPart)) return + if (index == keys.length - 1) value = settingObj[keyPart] + else settingObj = settingObj[keyPart] + }) if (value != null) { switch (setting.type) { @@ -409,10 +439,18 @@ export class SettingsService { } set(key: string, value: any) { - localStorage.setItem(key, value.toString()) + // parse key:key:key into nested object + let settingObj = this.settings + const keys = key.replace('general-settings:', '').split(':') + keys.forEach((keyPart, index) => { + keyPart = keyPart.replace(/-/g, '_') + if (!settingObj.hasOwnProperty(keyPart)) settingObj[keyPart] = {} + if (index == keys.length - 1) settingObj[keyPart] = value + else settingObj = settingObj[keyPart] + }) } - unset(key: string) { - localStorage.removeItem(key) + storeSettings(): Observable { + return this.http.post(this.baseUrl, { settings: this.settings }) } } diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 0339db88e..75b6b8cdd 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -515,5 +515,5 @@ class FrontendSettingsViewSerializer(serializers.ModelSerializer): return instance def create(self, validated_data): - frontend_settings = FrontendSettings.objects.create(**validated_data) + frontend_settings = FrontendSettings.objects.update_or_create(**validated_data) return frontend_settings