diff --git a/frontend/src/app/routes/settings/general/settings-general.component.html b/frontend/src/app/routes/settings/general/settings-general.component.html index 52b633a..07f8adb 100644 --- a/frontend/src/app/routes/settings/general/settings-general.component.html +++ b/frontend/src/app/routes/settings/general/settings-general.component.html @@ -1 +1,6 @@

Settings

+ +

+ {{setting.key}} + {{setting.value}} +

diff --git a/frontend/src/app/routes/settings/general/settings-general.component.ts b/frontend/src/app/routes/settings/general/settings-general.component.ts index 3d69789..8dde759 100644 --- a/frontend/src/app/routes/settings/general/settings-general.component.ts +++ b/frontend/src/app/routes/settings/general/settings-general.component.ts @@ -1,10 +1,11 @@ import { Component, OnInit } from '@angular/core'; +import { UsrPrefService } from 'src/app/services/api/usrpref.service'; @Component({ templateUrl: './settings-general.component.html', }) export class SettingsGeneralComponent implements OnInit { - constructor() {} + constructor(public userPref: UsrPrefService) {} ngOnInit(): void {} } diff --git a/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts b/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts index beb237e..d0241af 100644 --- a/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts +++ b/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { DecodedSysPref } from 'picsur-shared/dist/dto/preferences.dto'; import { Observable } from 'rxjs'; -import { SysprefService as SysPrefService } from 'src/app/services/api/syspref.service'; +import { SysPrefService } from 'src/app/services/api/syspref.service'; @Component({ templateUrl: './settings-syspref.component.html', diff --git a/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts b/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts index 489f11c..2b07282 100644 --- a/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts +++ b/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts @@ -6,7 +6,7 @@ import { HasFailed } from 'picsur-shared/dist/types'; import { Subject, throttleTime } from 'rxjs'; import { SysPreferenceFriendlyNames } from 'src/app/i18n/syspref.i18n'; import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto'; -import { SysprefService } from 'src/app/services/api/syspref.service'; +import { SysPrefService } from 'src/app/services/api/syspref.service'; import { UtilService } from 'src/app/util/util.service'; @Component({ @@ -20,7 +20,7 @@ export class SettingsSysprefOptionComponent implements OnInit { private updateSubject = new Subject(); constructor( - private sysprefService: SysprefService, + private sysprefService: SysPrefService, private utilService: UtilService ) {} diff --git a/frontend/src/app/services/api/syspref.service.ts b/frontend/src/app/services/api/syspref.service.ts index f9cc301..e83b209 100644 --- a/frontend/src/app/services/api/syspref.service.ts +++ b/frontend/src/app/services/api/syspref.service.ts @@ -1,13 +1,16 @@ import { Injectable } from '@angular/core'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator'; import { - GetSysPreferenceResponse, - MultipleSysPreferencesResponse, - UpdateSysPreferenceRequest, - UpdateSysPreferenceResponse -} from 'picsur-shared/dist/dto/api/syspref.dto'; + GetPreferenceResponse, + MultiplePreferencesResponse, + UpdatePreferenceRequest, + UpdatePreferenceResponse +} from 'picsur-shared/dist/dto/api/pref.dto'; import { Permission } from 'picsur-shared/dist/dto/permissions.dto'; -import { DecodedSysPref, PrefValueType } from 'picsur-shared/dist/dto/preferences.dto'; +import { + DecodedPref, + PrefValueType +} from 'picsur-shared/dist/dto/preferences.dto'; import { AsyncFailable, Fail, HasFailed, Map } from 'picsur-shared/dist/types'; import { BehaviorSubject } from 'rxjs'; import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto'; @@ -19,14 +22,12 @@ import { PermissionService } from './permission.service'; @Injectable({ providedIn: 'root', }) -export class SysprefService { - private readonly logger = new Logger('SysprefService'); +export class SysPrefService { + private readonly logger = new Logger('SysPrefService'); private hasPermission = false; - private sysprefObservable = new BehaviorSubject( - [] - ); + private sysprefObservable = new BehaviorSubject([]); public get snapshot() { return this.sysprefObservable.getValue(); @@ -56,12 +57,12 @@ export class SysprefService { } } - public async getPreferences(): AsyncFailable { + public async getPreferences(): AsyncFailable { if (!this.hasPermission) return Fail('You do not have permission to edit system preferences'); const response = await this.api.get( - MultipleSysPreferencesResponse, + MultiplePreferencesResponse, '/api/pref/sys' ); @@ -73,12 +74,12 @@ export class SysprefService { public async getPreference( key: string - ): AsyncFailable { + ): AsyncFailable { if (!this.hasPermission) return Fail('You do not have permission to edit system preferences'); const response = await this.api.get( - GetSysPreferenceResponse, + GetPreferenceResponse, `/api/pref/sys/${key}` ); @@ -89,13 +90,13 @@ export class SysprefService { public async setPreference( key: string, value: PrefValueType - ): AsyncFailable { + ): AsyncFailable { if (!this.hasPermission) return Fail('You do not have permission to edit system preferences'); const response = await this.api.post( - UpdateSysPreferenceRequest, - UpdateSysPreferenceResponse, + UpdatePreferenceRequest, + UpdatePreferenceResponse, `/api/pref/sys/${key}`, { value } ); @@ -104,7 +105,7 @@ export class SysprefService { return response; } - private updatePrefArray(pref: DecodedSysPref) { + private updatePrefArray(pref: DecodedPref) { const prefArray = this.snapshot; // Replace the old pref with the new one const index = prefArray.findIndex((i) => pref.key === i.key); diff --git a/frontend/src/app/services/api/usrpref.service.ts b/frontend/src/app/services/api/usrpref.service.ts new file mode 100644 index 0000000..c5c36a3 --- /dev/null +++ b/frontend/src/app/services/api/usrpref.service.ts @@ -0,0 +1,134 @@ +import { Injectable } from '@angular/core'; +import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator'; +import { + GetPreferenceResponse, + MultiplePreferencesResponse, + UpdatePreferenceRequest, + UpdatePreferenceResponse +} from 'picsur-shared/dist/dto/api/pref.dto'; +import { Permission } from 'picsur-shared/dist/dto/permissions.dto'; +import { + DecodedPref, + PrefValueType +} from 'picsur-shared/dist/dto/preferences.dto'; +import { AsyncFailable, Fail, HasFailed, Map } from 'picsur-shared/dist/types'; +import { BehaviorSubject } from 'rxjs'; +import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto'; +import { UtilService } from 'src/app/util/util.service'; +import { Logger } from '../logger/logger.service'; +import { ApiService } from './api.service'; +import { PermissionService } from './permission.service'; + +@Injectable({ + providedIn: 'root', +}) +export class UsrPrefService { + private readonly logger = new Logger('UsrPrefService'); + + private hasPermission = false; + + private usrprefObservable = new BehaviorSubject([]); + + public get snapshot() { + return this.usrprefObservable.getValue(); + } + + public get live() { + return this.usrprefObservable.asObservable(); + } + + constructor( + private api: ApiService, + private permissionsService: PermissionService, + private utilService: UtilService + ) { + this.subscribePermissions(); + this.init().catch(this.logger.error); + } + + private async init() { + const result = await this.getPreferences(); + if (HasFailed(result)) { + this.utilService.showSnackBar( + "Couldn't load user preferences", + SnackBarType.Error + ); + this.flush(); + } + } + + public async getPreferences(): AsyncFailable { + if (!this.hasPermission) + return Fail('You do not have permission to edit user preferences'); + + const response = await this.api.get( + MultiplePreferencesResponse, + '/api/pref/usr' + ); + + return Map(response, (pref) => { + this.usrprefObservable.next(pref.preferences); + return pref.preferences; + }); + } + + public async getPreference( + key: string + ): AsyncFailable { + if (!this.hasPermission) + return Fail('You do not have permission to edit user preferences'); + + const response = await this.api.get( + GetPreferenceResponse, + `/api/pref/usr/${key}` + ); + + if (!HasFailed(response)) this.updatePrefArray(response); + return response; + } + + public async setPreference( + key: string, + value: PrefValueType + ): AsyncFailable { + if (!this.hasPermission) + return Fail('You do not have permission to edit user preferences'); + + const response = await this.api.post( + UpdatePreferenceRequest, + UpdatePreferenceResponse, + `/api/pref/usr/${key}`, + { value } + ); + + if (!HasFailed(response)) this.updatePrefArray(response); + return response; + } + + private updatePrefArray(pref: DecodedPref) { + const prefArray = this.snapshot; + // Replace the old pref with the new one + const index = prefArray.findIndex((i) => pref.key === i.key); + if (index === -1) { + const newArray = [...prefArray, pref]; + this.usrprefObservable.next(newArray); + } else { + const newArray = [...prefArray]; + newArray[index] = pref; + this.usrprefObservable.next(newArray); + } + } + + private flush() { + this.usrprefObservable.next([]); + } + + // We want to flush on logout, because the syspreferences can contain sensitive information + @AutoUnsubscribe() + private subscribePermissions() { + return this.permissionsService.live.subscribe((permissions) => { + this.hasPermission = permissions.includes(Permission.Settings); + if (!this.hasPermission) this.flush(); + }); + } +}