@jsxc/jsxc
Version:
Real-time XMPP chat application with video calls, file transfer and encrypted communication
180 lines (137 loc) • 5.21 kB
text/typescript
import Log from './util/Log';
import * as defaultOptions from './OptionsDefault';
import IStorage from './Storage.interface';
import Utils from '@util/Utils';
import Storage from './Storage';
import { IJID } from './JID.interface';
import Client from './Client';
const KEY = 'options';
interface IOptionData {
[key: string]: any;
}
export default class Options {
private static defaults: IOptionData = defaultOptions;
public static overwriteDefaults(options: IOptionData) {
let optionKeys = Object.keys(options);
let defaultKeys = Object.keys(Options.defaults);
let unknownOptionKeys = optionKeys.filter(e => defaultKeys.indexOf(e) < 0);
if (optionKeys.indexOf('storage') > -1) {
// We have to make sure the storage is already set before we call e.g. Log.warn.
Options.defaults.storage = options.storage;
}
if (unknownOptionKeys.length > 0) {
Log.warn("I don't know the following options and therefore I will ignore them.", unknownOptionKeys);
for (let unknownKey of unknownOptionKeys) {
delete options[unknownKey];
}
}
Object.assign(Options.defaults, options);
}
public static addDefaults(options: IOptionData) {
let optionKeys = Object.keys(options);
let defaultKeys = Object.keys(Options.defaults);
let knownOptionKeys = optionKeys.filter(e => defaultKeys.indexOf(e) > -1);
if (knownOptionKeys.length > 0) {
Log.debug('I already know the following options: ', knownOptionKeys);
for (let knownKey of knownOptionKeys) {
delete options[knownKey];
}
}
Object.assign(Options.defaults, options);
}
public static async load(username: string, password: string, jid?: IJID) {
if (typeof Options.defaults.loadOptions !== 'function') {
return;
}
let optionData;
try {
optionData = await Options.defaults.loadOptions(username, password);
} catch (err) {
Log.warn('Error while loading options', err);
return;
}
for (let id in optionData) {
if (
id !== 'client' &&
id !== 'current' &&
jid &&
id !== jid.bare &&
!Client.getAccountManager().getAccount(id)
) {
Log.info(`Skip option block with id "${id}"`);
continue;
}
if (id === 'current') {
if (!jid) {
Log.info(`Skip option block with id "current", because no jid is provided`);
continue;
}
id = jid.bare;
}
let storage = new Storage(id);
let options = new Options(storage);
for (let key in optionData[id]) {
Log.debug(`Set option "${key}"`, optionData[id][key]);
options.set(key, optionData[id][key], true);
}
storage.destroy();
}
}
public static getDefault(key: string) {
return Options.defaults[key];
}
constructor(private storage: IStorage) {}
public getId(): string {
return this.storage.getName() || 'client';
}
public get(keyChain: string): any {
function get(keys: string[], primary: any = {}, secondary: any = {}) {
let key = keys.shift();
if (keys.length) {
return get(keys, primary[key], secondary[key]);
} else if (typeof primary[key] !== 'undefined') {
return Utils.isObject(primary[key]) && Utils.isObject(secondary[key])
? { ...secondary[key], ...primary[key] }
: primary[key];
} else if (typeof secondary[key] !== 'undefined') {
return secondary[key];
}
Log.debug(`I don't know any "${keyChain}" option.`);
return undefined;
}
return get(keyChain.split('.'), this.storage.getItem(KEY), Options.defaults);
}
public set(keyChain: string, value: any, preventOnChange: boolean = false) {
let subKeys = keyChain.split('.');
let options = this.storage.getItem(KEY) || {};
function set(keys: string[], data: any = {}) {
let key = keys.shift();
if (keys.length) {
data[key] = set(keys, data[key]);
} else {
if (Utils.isObject(data[key]) && Utils.isObject(value)) {
Utils.mergeDeep(data[key], value);
} else {
data[key] = value;
}
}
return data;
}
this.storage.setItem(KEY, set(subKeys, options));
if (typeof Options.defaults.onOptionChange === 'function' && !preventOnChange) {
Options.defaults.onOptionChange(this.getId(), keyChain, value, () => this.export());
}
}
public registerHook(key: string, func: (newValue: any, oldValue?: any) => void) {
this.storage.registerHook(KEY, (newData, oldData) => {
let n = newData[key];
let o = oldData && typeof oldData[key] !== 'undefined' ? oldData[key] : Options.defaults[key];
if (n !== o) {
func(n, o);
}
});
}
public export(): IOptionData {
return this.storage.getItem(KEY) || {};
}
}