mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-20 08:33:11 +00:00
fix: update core and apply stricter tslint rules
This commit is contained in:
@@ -17,34 +17,56 @@ import {AlertController} from '@ionic/angular';
|
||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {
|
||||
SCSetting,
|
||||
SCSettingValue,
|
||||
SCSettingValues,
|
||||
SCThingTranslator,
|
||||
SCTranslations,
|
||||
} from '@openstapps/core';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {SettingsProvider} from '../settings.provider';
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
@Component({
|
||||
selector: 'stapps-settings-item',
|
||||
templateUrl: 'settings-item.html',
|
||||
})
|
||||
export class SettingsItemComponent {
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
isVisible = true;
|
||||
// limit to languages that are available in StApps Core
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* limit to languages that are available in StApps Core
|
||||
*/
|
||||
language: keyof SCTranslations<any>;
|
||||
logger = new Logger();
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
@Input() setting: SCSetting;
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
translator: SCThingTranslator;
|
||||
|
||||
constructor(private alertCtrl: AlertController,
|
||||
private translateService: TranslateService,
|
||||
private settingsProvider: SettingsProvider) {
|
||||
/**
|
||||
*
|
||||
* @param alertCtrl TODO
|
||||
* @param translateService TODO
|
||||
* @param settingsProvider TODO
|
||||
*/
|
||||
constructor(private readonly alertCtrl: AlertController,
|
||||
private readonly translateService: TranslateService,
|
||||
private readonly settingsProvider: SettingsProvider) {
|
||||
this.language = translateService.currentLang as keyof SCTranslations<any>;
|
||||
this.translator = new SCThingTranslator(this.language, 'de');
|
||||
this.translator = new SCThingTranslator(this.language);
|
||||
|
||||
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.isVisible = false;
|
||||
this.language = event.lang as keyof SCTranslations<any>;
|
||||
this.translator = new SCThingTranslator(this.language, 'de');
|
||||
this.translator = new SCThingTranslator(this.language);
|
||||
// TODO: Issue #53 check workaround for selected 'select option' not updating translation
|
||||
setTimeout(() => this.isVisible = true);
|
||||
});
|
||||
@@ -58,7 +80,7 @@ export class SettingsItemComponent {
|
||||
const permissionGranted = await this.settingsProvider.checkGeoLocationPermission();
|
||||
if (!permissionGranted) {
|
||||
// revert setting value
|
||||
this.setting.input.value = false;
|
||||
this.setting.value = false;
|
||||
await this.presentGeoLocationAlert();
|
||||
}
|
||||
}
|
||||
@@ -67,8 +89,10 @@ export class SettingsItemComponent {
|
||||
* Shows alert with error message on denied user permission or disabled location services
|
||||
*/
|
||||
private async presentGeoLocationAlert() {
|
||||
const title = await this.translateService.get('settings.geoLocation.permission_denied_title').toPromise();
|
||||
const message = await this.translateService.get('settings.geoLocation.permission_denied_message').toPromise();
|
||||
const title = await this.translateService.get('settings.geoLocation.permission_denied_title')
|
||||
.toPromise();
|
||||
const message = await this.translateService.get('settings.geoLocation.permission_denied_message')
|
||||
.toPromise();
|
||||
await this.presentAlert(title, message);
|
||||
}
|
||||
|
||||
@@ -91,29 +115,34 @@ export class SettingsItemComponent {
|
||||
* Handles value changes of the setting
|
||||
*/
|
||||
async settingChanged(): Promise<void> {
|
||||
if (typeof this.setting.input.value !== 'undefined'
|
||||
&& SettingsProvider.validateValue(this.setting, this.setting.input.value)) {
|
||||
if (typeof this.setting.value !== 'undefined'
|
||||
&& SettingsProvider.validateValue(this.setting, this.setting.value)) {
|
||||
// handle general settings, with special actions
|
||||
switch (this.setting.name) {
|
||||
case 'language':
|
||||
this.translateService.use(this.setting.input.value.toString());
|
||||
this.translateService.use(this.setting.value.toString());
|
||||
break;
|
||||
case 'geoLocation':
|
||||
if (this.setting.input.value) {
|
||||
if (this.setting.value) {
|
||||
await this.checkGeoLocationPermission();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
await this.settingsProvider
|
||||
.setSettingValue(this.setting.categories[0], this.setting.name, this.setting.input.value);
|
||||
.setSettingValue(this.setting.categories[0], this.setting.name, this.setting.value);
|
||||
} else {
|
||||
// reset setting
|
||||
this.setting.input.value =
|
||||
await this.settingsProvider.getValue(this.setting.categories[0], this.setting.name);
|
||||
this.setting.value =
|
||||
await this.settingsProvider
|
||||
.getValue(this.setting.categories[0], this.setting.name) as (SCSettingValue | SCSettingValues);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
// tslint:disable-next-line:prefer-function-over-method
|
||||
typeOf(val: any) {
|
||||
return typeof (val);
|
||||
}
|
||||
|
||||
@@ -5,27 +5,27 @@
|
||||
<ion-card-content>
|
||||
<ion-note>{{ translator.translate(setting).description() }}</ion-note>
|
||||
|
||||
<div [ngSwitch]="setting.input.inputType" *ngIf="isVisible" >
|
||||
<div [ngSwitch]="setting.inputType" *ngIf="isVisible" >
|
||||
<ion-item *ngSwitchCase="'number'">
|
||||
<ion-label></ion-label>
|
||||
<ion-input type='number' [(ngModel)]="setting.input.value" value={{setting.input.value}} (ionChange)="settingChanged()"></ion-input>
|
||||
<ion-input type='number' [(ngModel)]="setting.value" value={{setting.value}} (ionChange)="settingChanged()"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngSwitchCase="'text'">
|
||||
<ion-label></ion-label>
|
||||
<ion-input type="text" [(ngModel)]="setting.input.value" value={{setting.input.value}} (ionChange)="settingChanged()"></ion-input>
|
||||
<ion-input type="text" [(ngModel)]="setting.value" value={{setting.value}} (ionChange)="settingChanged()"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngSwitchCase="'password'">
|
||||
<ion-label></ion-label>
|
||||
<ion-input type="password" [(ngModel)]="setting.input.value" value={{setting.input.value}} (ionChange)="settingChanged()"></ion-input>
|
||||
<ion-input type="password" [(ngModel)]="setting.value" value={{setting.value}} (ionChange)="settingChanged()"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngSwitchCase="'singleChoice'">
|
||||
<ion-label></ion-label>
|
||||
<ion-toggle *ngIf="typeOf(setting.input.defaultValue) === 'boolean'" [(ngModel)]="setting.input.value" (ionChange)="settingChanged()"></ion-toggle>
|
||||
<ion-select *ngIf="typeOf(setting.input.defaultValue) !== 'boolean'" interface="popover" [(ngModel)]="setting.input.value" (ionChange)="settingChanged()">
|
||||
<ion-select-option *ngFor="let val of setting.input.values" [value]="val">
|
||||
<ion-toggle *ngIf="typeOf(setting.defaultValue) === 'boolean'" [(ngModel)]="setting.value" (ionChange)="settingChanged()"></ion-toggle>
|
||||
<ion-select *ngIf="typeOf(setting.defaultValue) !== 'boolean'" interface="popover" [(ngModel)]="setting.value" (ionChange)="settingChanged()">
|
||||
<ion-select-option *ngFor="let val of setting.values" [value]="val">
|
||||
<div *ngIf="typeOf(val) !== 'number'">{{ val }}</div>
|
||||
<div *ngIf="typeOf(val) === 'number'">{{ val }}</div>
|
||||
</ion-select-option>
|
||||
@@ -34,8 +34,8 @@
|
||||
|
||||
<ion-item *ngSwitchCase="'multipleChoice'">
|
||||
<ion-label></ion-label>
|
||||
<ion-select [(ngModel)]="setting.input.value" multiple="true" (ionChange)="settingChanged()">
|
||||
<ion-select-option *ngFor="let val of setting.input.values" [value]="val">
|
||||
<ion-select [(ngModel)]="setting.value" multiple="true" (ionChange)="settingChanged()">
|
||||
<ion-select-option *ngFor="let val of setting.values" [value]="val">
|
||||
<div *ngIf="typeOf(val) !== 'number'">{{ val }}</div>
|
||||
<div *ngIf="typeOf(val) === 'number'">{{ val }}</div>
|
||||
</ion-select-option>
|
||||
|
||||
@@ -18,29 +18,58 @@ import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {SCSettingMeta, SCThingTranslator, SCTranslations} from '@openstapps/core';
|
||||
import {SettingsCache, SettingsProvider} from '../settings.provider';
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
@Component({
|
||||
selector: 'stapps-settings-page',
|
||||
templateUrl: 'settings-page.html',
|
||||
})
|
||||
export class SettingsPageComponent {
|
||||
/**
|
||||
* Order of the categories
|
||||
*/
|
||||
categoriesOrder: string[];
|
||||
// limit to languages that are available in StApps Core
|
||||
/**
|
||||
* Possible languages to be used for translation
|
||||
*
|
||||
* limit to languages that are available in StApps Core
|
||||
*/
|
||||
language: keyof SCTranslations<any>;
|
||||
/**
|
||||
* Meta information about settings
|
||||
*/
|
||||
meta = SCSettingMeta;
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
objectKeys = Object.keys;
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
settingsCache: SettingsCache;
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
translator: SCThingTranslator;
|
||||
|
||||
constructor(private alertController: AlertController,
|
||||
private settingsProvider: SettingsProvider,
|
||||
private toastController: ToastController,
|
||||
private translateService: TranslateService) {
|
||||
/**
|
||||
*
|
||||
* @param alertController TODO
|
||||
* @param settingsProvider TODO
|
||||
* @param toastController TODO
|
||||
* @param translateService TODO
|
||||
*/
|
||||
constructor(private readonly alertController: AlertController,
|
||||
private readonly settingsProvider: SettingsProvider,
|
||||
private readonly toastController: ToastController,
|
||||
private readonly translateService: TranslateService) {
|
||||
this.language = translateService.currentLang as keyof SCTranslations<any>;
|
||||
this.translator = new SCThingTranslator(this.language, 'de');
|
||||
this.translator = new SCThingTranslator(this.language);
|
||||
|
||||
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.language = event.lang as keyof SCTranslations<any>;
|
||||
this.translator = new SCThingTranslator(this.language, 'de');
|
||||
this.translator = new SCThingTranslator(this.language);
|
||||
});
|
||||
this.settingsCache = {};
|
||||
this.categoriesOrder = settingsProvider.getCategoriesOrder();
|
||||
@@ -66,6 +95,9 @@ export class SettingsPageComponent {
|
||||
this.settingsCache = await this.settingsProvider.getCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component initialize method
|
||||
*/
|
||||
async ngOnInit() {
|
||||
await this.loadSettings();
|
||||
}
|
||||
@@ -74,10 +106,14 @@ export class SettingsPageComponent {
|
||||
* Presents an alert to the user to reset settings to default values
|
||||
*/
|
||||
async presentResetAlert() {
|
||||
const cancelText = await this.translateService.get('settings.resetAlert.buttonCancel').toPromise();
|
||||
const yesText = await this.translateService.get('settings.resetAlert.buttonYes').toPromise();
|
||||
const title = await this.translateService.get('settings.resetAlert.title').toPromise();
|
||||
const message = await this.translateService.get('settings.resetAlert.message').toPromise();
|
||||
const cancelText = await this.translateService.get('settings.resetAlert.buttonCancel')
|
||||
.toPromise();
|
||||
const yesText = await this.translateService.get('settings.resetAlert.buttonYes')
|
||||
.toPromise();
|
||||
const title = await this.translateService.get('settings.resetAlert.title')
|
||||
.toPromise();
|
||||
const message = await this.translateService.get('settings.resetAlert.message')
|
||||
.toPromise();
|
||||
|
||||
const alert = await this.alertController.create({
|
||||
buttons: [
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<ion-list *ngFor="let categoryKey of categoriesOrder ">
|
||||
<div *ngIf="objectKeys(settingsCache).includes(categoryKey)">
|
||||
<ion-item-divider>
|
||||
<h5>{{translator.translate(settingsCache[categoryKey].settings[objectKeys(settingsCache[categoryKey].settings)[0]]).categories()[0]}}
|
||||
<h5>{{ settingsCache[categoryKey].settings[objectKeys(settingsCache[categoryKey].settings)[0]].translations[language].categories[0]}}
|
||||
</h5>
|
||||
</ion-item-divider>
|
||||
<stapps-settings-item *ngFor="let settingKeys of objectKeys(settingsCache[categoryKey].settings)" [setting]="settingsCache[categoryKey].settings[settingKeys]"></stapps-settings-item>
|
||||
|
||||
@@ -29,6 +29,9 @@ const settingsRoutes: Routes = [
|
||||
{path: 'settings', component: SettingsPageComponent},
|
||||
];
|
||||
|
||||
/**
|
||||
* Settings Module
|
||||
*/
|
||||
@NgModule({
|
||||
declarations: [
|
||||
SettingsPageComponent,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {SCSetting, SCThingOriginType, SCThingType} from '@openstapps/core';
|
||||
import {SCSetting, SCThingOriginType, SCThingType, SCSettingInputType} from '@openstapps/core';
|
||||
import {ConfigProvider} from '../config/config.provider';
|
||||
import {StorageProvider} from '../storage/storage.provider';
|
||||
import {SettingsProvider, SettingValuesContainer, STORAGE_KEY_SETTING_VALUES} from './settings.provider';
|
||||
@@ -53,14 +53,14 @@ describe('SettingsProvider', () => {
|
||||
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
|
||||
const setting: SCSetting = await settingsProvider
|
||||
.getSetting(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name);
|
||||
await expect(setting.input.value).toBeDefined();
|
||||
await expect(setting.value).toBeDefined();
|
||||
});
|
||||
|
||||
it('should provide and get settings value', async () => {
|
||||
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
|
||||
const value = await settingsProvider
|
||||
.getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name);
|
||||
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue);
|
||||
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue);
|
||||
});
|
||||
|
||||
it('should get persisted setting value', async () => {
|
||||
@@ -79,7 +79,7 @@ describe('SettingsProvider', () => {
|
||||
storageProviderSpy.get.and.returnValue(Promise.resolve([]));
|
||||
const value = await settingsProvider
|
||||
.getValue(CONFIG_SETTINGS_MOCK[3].categories[0], CONFIG_SETTINGS_MOCK[3].name);
|
||||
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[3].input.defaultValue);
|
||||
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[3].defaultValue);
|
||||
});
|
||||
|
||||
it('should keep persisted setting values from settings that are not contained in loaded config', async () => {
|
||||
@@ -108,10 +108,10 @@ describe('SettingsProvider', () => {
|
||||
const name = CONFIG_SETTINGS_MOCK[0].name;
|
||||
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
|
||||
const settings = await settingsProvider.getCache();
|
||||
settings[category].settings[name].input.value = 'testValue';
|
||||
settings[category].settings[name].value = 'testValue';
|
||||
// cached setting value should still be defaultValue
|
||||
await expect((await settingsProvider.getValue(category, name)))
|
||||
.toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue);
|
||||
.toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue);
|
||||
});
|
||||
|
||||
it('should call storage put on setSettingValue', async () => {
|
||||
@@ -134,7 +134,7 @@ describe('SettingsProvider', () => {
|
||||
await settingsProvider.resetDefault();
|
||||
const value = await settingsProvider
|
||||
.getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name);
|
||||
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue);
|
||||
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue);
|
||||
});
|
||||
|
||||
it('should validate wrong values for inputType text', async () => {
|
||||
@@ -172,7 +172,7 @@ describe('SettingsProvider', () => {
|
||||
await testValue(CONFIG_SETTINGS_MOCK[6], '');
|
||||
await testValue(CONFIG_SETTINGS_MOCK[6], 123456);
|
||||
await testValue(CONFIG_SETTINGS_MOCK[6], false);
|
||||
await testValue(CONFIG_SETTINGS_MOCK[6], [1, 2, 3, 4]);
|
||||
await testValue(CONFIG_SETTINGS_MOCK[6], [1, 9]);
|
||||
});
|
||||
|
||||
async function testValue(setting: SCSetting, value: any) {
|
||||
@@ -192,10 +192,8 @@ describe('SettingsProvider', () => {
|
||||
const CONFIG_SETTINGS_MOCK: SCSetting[] = [
|
||||
{
|
||||
categories: ['credentials'],
|
||||
input: {
|
||||
defaultValue: '',
|
||||
inputType: 'text',
|
||||
},
|
||||
defaultValue: '',
|
||||
inputType: SCSettingInputType.Text,
|
||||
name: 'username',
|
||||
order: 0,
|
||||
origin: {
|
||||
@@ -219,10 +217,8 @@ describe('SettingsProvider', () => {
|
||||
{
|
||||
categories: ['credentials'],
|
||||
description: '',
|
||||
input: {
|
||||
defaultValue: '',
|
||||
inputType: 'password',
|
||||
},
|
||||
defaultValue: '',
|
||||
inputType: SCSettingInputType.Password,
|
||||
name: 'password',
|
||||
order: 1,
|
||||
origin: {
|
||||
@@ -246,10 +242,8 @@ describe('SettingsProvider', () => {
|
||||
{
|
||||
categories: ['profile'],
|
||||
description: '',
|
||||
input: {
|
||||
defaultValue: 0,
|
||||
inputType: 'number',
|
||||
},
|
||||
defaultValue: 0,
|
||||
inputType: SCSettingInputType.Number,
|
||||
name: 'age',
|
||||
order: 0,
|
||||
origin: {
|
||||
@@ -273,11 +267,8 @@ describe('SettingsProvider', () => {
|
||||
{
|
||||
categories: ['profile'],
|
||||
description: '',
|
||||
input: {
|
||||
defaultValue: 'student',
|
||||
inputType: 'singleChoice',
|
||||
values: ['student', 'employee', 'guest'],
|
||||
},
|
||||
defaultValue: 'student',
|
||||
inputType: SCSettingInputType.SingleChoice,
|
||||
name: 'group',
|
||||
order: 1,
|
||||
origin: {
|
||||
@@ -301,15 +292,13 @@ describe('SettingsProvider', () => {
|
||||
},
|
||||
type: SCThingType.Setting,
|
||||
uid: '',
|
||||
values: ['student', 'employee', 'guest'],
|
||||
},
|
||||
{
|
||||
categories: ['profile'],
|
||||
description: '',
|
||||
input: {
|
||||
defaultValue: 'en',
|
||||
inputType: 'singleChoice',
|
||||
values: ['en', 'de'],
|
||||
},
|
||||
defaultValue: 'en',
|
||||
inputType: SCSettingInputType.SingleChoice,
|
||||
name: 'language',
|
||||
order: 0,
|
||||
origin: {
|
||||
@@ -331,15 +320,13 @@ describe('SettingsProvider', () => {
|
||||
},
|
||||
type: SCThingType.Setting,
|
||||
uid: '',
|
||||
values: ['en', 'de'],
|
||||
},
|
||||
{
|
||||
categories: ['privacy'],
|
||||
description: '',
|
||||
input: {
|
||||
defaultValue: false,
|
||||
inputType: 'singleChoice',
|
||||
values: [true, false],
|
||||
},
|
||||
defaultValue: false,
|
||||
inputType: SCSettingInputType.SingleChoice,
|
||||
name: 'geoLocation',
|
||||
order: 0,
|
||||
origin: {
|
||||
@@ -363,15 +350,13 @@ describe('SettingsProvider', () => {
|
||||
},
|
||||
type: SCThingType.Setting,
|
||||
uid: '',
|
||||
values: [true, false],
|
||||
},
|
||||
{
|
||||
categories: ['others'],
|
||||
description: '',
|
||||
input: {
|
||||
defaultValue: [],
|
||||
inputType: 'multipleChoice',
|
||||
values: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
},
|
||||
defaultValue: [],
|
||||
inputType: SCSettingInputType.MultipleChoice,
|
||||
name: 'numbers',
|
||||
order: 0,
|
||||
origin: {
|
||||
@@ -393,6 +378,7 @@ describe('SettingsProvider', () => {
|
||||
},
|
||||
type: SCThingType.Setting,
|
||||
uid: '',
|
||||
values: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
@@ -16,25 +16,29 @@ import {Injectable} from '@angular/core';
|
||||
import {Geolocation} from '@ionic-native/geolocation/ngx';
|
||||
import {
|
||||
SCSetting,
|
||||
SCSettingMultipleChoice,
|
||||
SCSettingSingleChoice,
|
||||
SCSettingValue,
|
||||
SCSettingValues,
|
||||
} from '@openstapps/core';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import * as deepMerge from 'deepmerge';
|
||||
import {ConfigProvider} from '../config/config.provider';
|
||||
import {StorageProvider} from '../storage/storage.provider';
|
||||
|
||||
export const STORAGE_KEY_SETTINGS = 'settings';
|
||||
export const STORAGE_KEY_SETTINGS_SEPARATOR = '.';
|
||||
export const STORAGE_KEY_SETTING_VALUES = STORAGE_KEY_SETTINGS + STORAGE_KEY_SETTINGS_SEPARATOR + 'values';
|
||||
export const STORAGE_KEY_SETTING_VALUES = `${STORAGE_KEY_SETTINGS}${STORAGE_KEY_SETTINGS_SEPARATOR}values`;
|
||||
|
||||
/**
|
||||
* Category structure of settings cache
|
||||
*/
|
||||
export interface CategoryWithSettings {
|
||||
/**
|
||||
* Category name
|
||||
*/
|
||||
category: string;
|
||||
settings: { [key: string]: SCSetting };
|
||||
/**
|
||||
* Settings that belong in this category
|
||||
*/
|
||||
settings: { [key: string]: SCSetting; };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,36 +64,61 @@ export interface SettingValueContainer {
|
||||
|
||||
/**
|
||||
* Provider for app settings
|
||||
*
|
||||
*/
|
||||
@Injectable()
|
||||
export class SettingsProvider {
|
||||
/**
|
||||
* Order of the setting categories
|
||||
*/
|
||||
categoriesOrder: string[];
|
||||
/**
|
||||
* Is provider initialized
|
||||
*/
|
||||
initialized = false;
|
||||
logger = new Logger();
|
||||
/**
|
||||
* Cache for the imported settings
|
||||
*/
|
||||
settingsCache: SettingsCache;
|
||||
|
||||
/**
|
||||
* Return true if all given values are valid to possible values in given settingInput
|
||||
* @param settingInput
|
||||
* @param values
|
||||
* @param possibleValues Possible values
|
||||
* @param enteredValues Entered value
|
||||
*/
|
||||
public static checkMultipleChoiceValue(settingInput: SCSettingMultipleChoice, values: SCSettingValue[]): boolean {
|
||||
for (const value of values) {
|
||||
if (!settingInput.values.includes(value)) {
|
||||
public static checkMultipleChoiceValue(
|
||||
possibleValues: SCSettingValues | undefined,
|
||||
enteredValues: SCSettingValues,
|
||||
): boolean {
|
||||
if ( typeof possibleValues === 'undefined' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const value of enteredValues) {
|
||||
if (!possibleValues.includes(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given value is valid to possible values in given settingInput
|
||||
* @param settingInput
|
||||
* @param value
|
||||
* @param possibleValues Possible values
|
||||
* @param enteredValue Entered value
|
||||
*/
|
||||
public static checkSingleChoiceValue(settingInput: SCSettingSingleChoice, value: SCSettingValue): boolean {
|
||||
return settingInput.values !== undefined
|
||||
&& settingInput.values.includes(value);
|
||||
public static checkSingleChoiceValue(
|
||||
possibleValues: SCSettingValues | undefined,
|
||||
enteredValue: SCSettingValue,
|
||||
): boolean {
|
||||
if ( typeof possibleValues === 'undefined' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return possibleValues !== undefined
|
||||
&& (Array.isArray(possibleValues)
|
||||
&& possibleValues.includes(enteredValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,19 +126,19 @@ export class SettingsProvider {
|
||||
* @param setting setting to check value against
|
||||
* @param value value to validate
|
||||
*/
|
||||
public static validateValue(setting: SCSetting, value: any): boolean {
|
||||
let isValueValid: boolean = false;
|
||||
switch (setting.input.inputType) {
|
||||
public static validateValue(setting: SCSetting, value: SCSettingValue | SCSettingValues): boolean {
|
||||
let isValueValid = false;
|
||||
switch (setting.inputType) {
|
||||
case 'number':
|
||||
if (typeof value === 'number') {
|
||||
isValueValid = true;
|
||||
}
|
||||
break;
|
||||
case 'multipleChoice':
|
||||
if (!value.isArray) {
|
||||
case 'multiple choice':
|
||||
if (!Array.isArray(value)) {
|
||||
isValueValid = false;
|
||||
} else {
|
||||
isValueValid = SettingsProvider.checkMultipleChoiceValue(setting.input, value);
|
||||
isValueValid = SettingsProvider.checkMultipleChoiceValue(setting.values, value);
|
||||
}
|
||||
break;
|
||||
case 'password':
|
||||
@@ -118,32 +147,43 @@ export class SettingsProvider {
|
||||
isValueValid = true;
|
||||
}
|
||||
break;
|
||||
case 'singleChoice':
|
||||
isValueValid = SettingsProvider.checkSingleChoiceValue(setting.input, value);
|
||||
case 'single choice':
|
||||
if (Array.isArray(value)) {
|
||||
isValueValid = false;
|
||||
} else {
|
||||
isValueValid = SettingsProvider.checkSingleChoiceValue(setting.values, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return isValueValid;
|
||||
}
|
||||
|
||||
constructor(private storage: StorageProvider,
|
||||
private configProvider: ConfigProvider,
|
||||
private geoLocation: Geolocation) {
|
||||
/**
|
||||
*
|
||||
* @param storage TODO
|
||||
* @param configProvider TODO
|
||||
* @param geoLocation TODO
|
||||
*/
|
||||
constructor(private readonly storage: StorageProvider,
|
||||
private readonly configProvider: ConfigProvider,
|
||||
private readonly geoLocation: Geolocation) {
|
||||
this.categoriesOrder = [];
|
||||
this.settingsCache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an Setting to the Cache if not exist and set undefined value to defaultValue
|
||||
* @param setting
|
||||
* @param setting Setting with categories, defautlValue, name, input type and valid values
|
||||
*/
|
||||
private async addSetting(setting: SCSetting): Promise<void> {
|
||||
if (!this.categoryExists(setting.categories[0])) {
|
||||
await this.provideCategory(setting.categories[0]);
|
||||
}
|
||||
if (!this.settingExists(setting.categories[0], setting.name)) {
|
||||
if (setting.input.value === undefined) {
|
||||
setting.input.value = setting.input.defaultValue;
|
||||
if (setting.value === undefined) {
|
||||
setting.value = setting.defaultValue;
|
||||
}
|
||||
this.settingsCache[setting.categories[0]].settings[setting.name] = setting;
|
||||
}
|
||||
@@ -162,9 +202,10 @@ export class SettingsProvider {
|
||||
settingValuesContainer[categoryKey] = {};
|
||||
}
|
||||
settingValuesContainer[categoryKey][settingKey] =
|
||||
this.settingsCache[categoryKey].settings[settingKey].input.value;
|
||||
this.settingsCache[categoryKey].settings[settingKey].value;
|
||||
}
|
||||
}
|
||||
|
||||
return settingValuesContainer;
|
||||
}
|
||||
|
||||
@@ -196,11 +237,11 @@ export class SettingsProvider {
|
||||
// if saved setting value exists set it, otherwise set to default value
|
||||
if (typeof valuesContainer[categoryKey] !== 'undefined'
|
||||
&& typeof valuesContainer[categoryKey][settingKey] !== 'undefined') {
|
||||
this.settingsCache[categoryKey].settings[settingKey].input.value =
|
||||
this.settingsCache[categoryKey].settings[settingKey].value =
|
||||
valuesContainer[categoryKey][settingKey];
|
||||
} else {
|
||||
this.settingsCache[categoryKey].settings[settingKey].input.value =
|
||||
this.settingsCache[categoryKey].settings[settingKey].input.defaultValue;
|
||||
this.settingsCache[categoryKey].settings[settingKey].value =
|
||||
this.settingsCache[categoryKey].settings[settingKey].defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,7 +269,7 @@ export class SettingsProvider {
|
||||
|
||||
/**
|
||||
* Returns true if category exists
|
||||
* @param category
|
||||
* @param category Category key name
|
||||
*/
|
||||
public categoryExists(category: string): boolean {
|
||||
return this.settingsCache[category] !== undefined;
|
||||
@@ -256,6 +297,7 @@ export class SettingsProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -264,6 +306,7 @@ export class SettingsProvider {
|
||||
*/
|
||||
public async getCache(): Promise<SettingsCache> {
|
||||
await this.init();
|
||||
|
||||
return JSON.parse(JSON.stringify(this.settingsCache));
|
||||
}
|
||||
|
||||
@@ -281,14 +324,13 @@ export class SettingsProvider {
|
||||
*
|
||||
* @throws Exception if setting is not provided
|
||||
*/
|
||||
public async getSetting(category: string, name: string): Promise<any> {
|
||||
public async getSetting(category: string, name: string): Promise<SCSetting> {
|
||||
await this.init();
|
||||
if (this.settingExists(category, name)) {
|
||||
// return a copy of the settings
|
||||
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name]));
|
||||
} else {
|
||||
throw new Error(`Setting "${name}" not provided`);
|
||||
}
|
||||
throw new Error(`Setting "${name}" not provided`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,10 +344,9 @@ export class SettingsProvider {
|
||||
await this.init();
|
||||
if (this.settingExists(category, name)) {
|
||||
// return a copy of the settings value
|
||||
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].input.value));
|
||||
} else {
|
||||
throw new Error(`Setting "${name}" not provided`);
|
||||
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].value));
|
||||
}
|
||||
throw new Error(`Setting "${name}" not provided`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,7 +381,7 @@ export class SettingsProvider {
|
||||
async resetDefault(): Promise<void> {
|
||||
for (const catKey of Object.keys(this.settingsCache)) {
|
||||
for (const settingKey of Object.keys(this.settingsCache[catKey].settings)) {
|
||||
const settingInput = this.settingsCache[catKey].settings[settingKey].input;
|
||||
const settingInput = this.settingsCache[catKey].settings[settingKey];
|
||||
settingInput.value = settingInput.defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -373,24 +414,24 @@ export class SettingsProvider {
|
||||
|
||||
/**
|
||||
* Sets a valid value of a setting and persists changes in storage
|
||||
* @param category
|
||||
* @param name
|
||||
* @param value
|
||||
* @param category Category key name
|
||||
* @param name Setting key name
|
||||
* @param value Value to be set
|
||||
*
|
||||
* @throws Exception if setting is not provided or value not valid to the settings inputType
|
||||
*/
|
||||
public async setSettingValue(category: string, name: string,
|
||||
value: any): Promise<void> {
|
||||
value: SCSettingValue | SCSettingValues): Promise<void> {
|
||||
await this.init();
|
||||
if (this.settingExists(category, name)) {
|
||||
const setting: SCSetting = this.settingsCache[category].settings[name];
|
||||
const isValueValid = SettingsProvider.validateValue(setting, value);
|
||||
if (isValueValid) {
|
||||
this.settingsCache[category].settings[name].input.value = value;
|
||||
this.settingsCache[category].settings[name].value = value;
|
||||
await this.saveSettingValues();
|
||||
} else {
|
||||
throw new Error(`Value "${value}" of type
|
||||
${typeof value} is not valid for ${setting.input.inputType}`);
|
||||
${typeof value} is not valid for ${setting.inputType}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`setting ${name} is not provided`);
|
||||
@@ -399,8 +440,8 @@ export class SettingsProvider {
|
||||
|
||||
/**
|
||||
* Returns true if setting in category exists
|
||||
* @param category
|
||||
* @param setting
|
||||
* @param category Category key name
|
||||
* @param setting Setting key name
|
||||
*/
|
||||
public settingExists(category: string, setting: string): boolean {
|
||||
return this.categoryExists(category) && this.settingsCache[category].settings[setting] !== undefined;
|
||||
|
||||
Reference in New Issue
Block a user