mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-19 16:13:06 +00:00
298 lines
8.7 KiB
TypeScript
298 lines
8.7 KiB
TypeScript
/*
|
|
* Copyright (C) 2018 StApps
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
import {Injectable} from '@angular/core';
|
|
import {SCSetting, SCSettingMultipleChoice, SCSettingSingleChoice, SCSettingValue} from '@openstapps/core';
|
|
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';
|
|
|
|
/**
|
|
* Category structure of settings cache
|
|
*/
|
|
export interface CategoryWithSettings {
|
|
category: string;
|
|
settings: { [key: string]: SCSetting };
|
|
}
|
|
|
|
/**
|
|
* Structure of SettingsCache
|
|
*/
|
|
export interface SettingsCache {
|
|
[key: string]: CategoryWithSettings;
|
|
}
|
|
|
|
/**
|
|
* Provider for app settings
|
|
*/
|
|
@Injectable()
|
|
export class SettingsProvider {
|
|
private categoriesOrder: string[];
|
|
private initialized = false;
|
|
private settingsCache: SettingsCache;
|
|
|
|
/**
|
|
* Return true if all given values are valid to possible values in given settingInput
|
|
* @param settingInput
|
|
* @param values
|
|
*/
|
|
public static checkMultipleChoiceValue(settingInput: SCSettingMultipleChoice, values: SCSettingValue[]): boolean {
|
|
for (const value of values) {
|
|
if (!settingInput.values.includes(value)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns true if given value is valid to possible values in given settingInput
|
|
* @param settingInput
|
|
* @param value
|
|
*/
|
|
public static checkSingleChoiceValue(settingInput: SCSettingSingleChoice, value: SCSettingValue): boolean {
|
|
return settingInput.values !== undefined
|
|
&& settingInput.values.includes(value);
|
|
}
|
|
|
|
/**
|
|
* Validates value for given settings inputType. Returns true if value is valid.
|
|
* @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) {
|
|
case 'number':
|
|
if (typeof value === 'number') {
|
|
isValueValid = true;
|
|
}
|
|
break;
|
|
case 'multipleChoice':
|
|
if (!value.isArray) {
|
|
isValueValid = false;
|
|
} else {
|
|
isValueValid = SettingsProvider.checkMultipleChoiceValue(setting.input, value);
|
|
}
|
|
break;
|
|
case 'password':
|
|
case 'text':
|
|
if (typeof value === 'string') {
|
|
isValueValid = true;
|
|
}
|
|
break;
|
|
case 'singleChoice':
|
|
isValueValid = SettingsProvider.checkSingleChoiceValue(setting.input, value);
|
|
break;
|
|
default:
|
|
}
|
|
return isValueValid;
|
|
}
|
|
|
|
constructor(public storage: StorageProvider) {
|
|
this.categoriesOrder = [];
|
|
this.settingsCache = {};
|
|
}
|
|
|
|
/**
|
|
* Initializes settings cache from storage
|
|
*/
|
|
private async initSettings(): Promise<void> {
|
|
try {
|
|
this.settingsCache = await this.storage.get<SettingsCache>(STORAGE_KEY_SETTING_VALUES);
|
|
for (const category of Object.keys(this.settingsCache)) {
|
|
if (!this.categoriesOrder.includes(category)) {
|
|
this.categoriesOrder.push(category);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
this.settingsCache = {};
|
|
}
|
|
await this.saveSettings();
|
|
this.initialized = true;
|
|
}
|
|
|
|
/**
|
|
* Add category if not exists
|
|
* @param category the category to provide
|
|
*/
|
|
private async provideCategory(category: string): Promise<void> {
|
|
if (!this.categoryExists(category)) {
|
|
if (!this.categoriesOrder.includes(category)) {
|
|
this.categoriesOrder.push(category);
|
|
}
|
|
this.settingsCache[category] = {
|
|
category: category,
|
|
settings: {},
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if category exists
|
|
* @param category
|
|
*/
|
|
public categoryExists(category: string): boolean {
|
|
return this.settingsCache[category] !== undefined;
|
|
}
|
|
|
|
/**
|
|
* Removes all provided settings
|
|
*/
|
|
public async clear(): Promise<void> {
|
|
await this.init();
|
|
this.settingsCache = {};
|
|
await this.saveSettings();
|
|
}
|
|
|
|
/**
|
|
* returns an array with the order of categories
|
|
*/
|
|
public getCategoriesOrder(): string[] {
|
|
return this.categoriesOrder;
|
|
}
|
|
|
|
/**
|
|
* Returns copy of a setting if exist
|
|
* @param category the category of requested setting
|
|
* @param name the name of requested setting
|
|
*
|
|
* @throws Exception if setting is not provided
|
|
*/
|
|
public async getSetting(category: string, name: string): Promise<any> {
|
|
await this.init();
|
|
if (this.settingExists(category, name)) {
|
|
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name]));
|
|
} else {
|
|
throw new Error('Setting "' + name + '" not provided');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns copy of cached settings
|
|
*/
|
|
public async getSettingsCache(): Promise<SettingsCache> {
|
|
await this.init();
|
|
return JSON.parse(JSON.stringify(this.settingsCache));
|
|
}
|
|
|
|
/**
|
|
* Returns copy of a setting if exist
|
|
* @param category the category of requested setting
|
|
* @param name the name of requested setting
|
|
*
|
|
* @throws Exception if setting is not provided
|
|
*/
|
|
public async getSettingValue(category: string, name: string): Promise<any> {
|
|
await this.init();
|
|
if (this.settingExists(category, name)) {
|
|
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].input.value));
|
|
} else {
|
|
throw new Error('Setting "' + name + '" not provided');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* initializes settingsProvider
|
|
*/
|
|
public async init(): Promise<void> {
|
|
if (!this.initialized) {
|
|
await this.initSettings();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds given setting and its category if not exist
|
|
* @param setting the setting to add
|
|
*/
|
|
public async provideSetting(setting: SCSetting): Promise<void> {
|
|
await this.init();
|
|
if (!this.categoryExists(setting.categories[0])) {
|
|
await this.provideCategory(setting.categories[0]);
|
|
}
|
|
if (!this.settingExists(setting.categories[0], setting.name)) {
|
|
// set value to default
|
|
if (setting.input.value === undefined) {
|
|
setting.input.value = setting.input.defaultValue;
|
|
}
|
|
this.settingsCache[setting.categories[0]].settings[setting.name] = setting;
|
|
await this.saveSettings();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets values of all settings to defaultValue
|
|
*/
|
|
public async resetDefault(): Promise<void> {
|
|
await this.init();
|
|
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;
|
|
settingInput.value = settingInput.defaultValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves cached settings in app storage
|
|
*/
|
|
public async saveSettings(): Promise<void> {
|
|
await this.storage.put(STORAGE_KEY_SETTING_VALUES, this.settingsCache);
|
|
}
|
|
|
|
/**
|
|
* Sets the order the given categories showup in the settings page
|
|
* @param categoryNames the order of the categories
|
|
*/
|
|
public setCategoriesOrder(categoryNames: string[]) {
|
|
this.categoriesOrder = categoryNames;
|
|
}
|
|
|
|
/**
|
|
* Sets a valid value of a setting and persists changes in storage
|
|
* @param category
|
|
* @param name
|
|
* @param value
|
|
*
|
|
* @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> {
|
|
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;
|
|
await this.saveSettings();
|
|
} else {
|
|
throw new Error('Value "' + value + '" of type ' +
|
|
typeof value + ' is not valid for ' + setting.input.inputType);
|
|
}
|
|
} else {
|
|
throw new Error('setting ' + name + ' is not provided');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if setting in category exists
|
|
* @param category
|
|
* @param setting
|
|
*/
|
|
public settingExists(category: string, setting: string): boolean {
|
|
return this.categoryExists(category) && this.settingsCache[category].settings[setting] !== undefined;
|
|
}
|
|
}
|