Async setting and category events
- Async setting and category events - Added deepfreeze utility - Fix dropdown staying open after selecting an option
This commit is contained in:
parent
91275f4332
commit
00d909f16c
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
import Globals from './globals';
|
||||
import { FileUtils, ClientLogger as Logger } from 'common';
|
||||
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
||||
import path from 'path';
|
||||
import { Events } from 'modules';
|
||||
import { SettingsSet, ErrorEvent } from 'structs';
|
||||
|
@ -176,33 +176,41 @@ export default class {
|
|||
const readConfig = await this.readConfig(contentPath);
|
||||
const mainPath = path.join(contentPath, readConfig.main);
|
||||
|
||||
const defaultConfig = new SettingsSet({
|
||||
settings: readConfig.defaultConfig,
|
||||
schemes: readConfig.configSchemes
|
||||
});
|
||||
|
||||
const userConfig = {
|
||||
enabled: false,
|
||||
config: new SettingsSet({
|
||||
settings: readConfig.defaultConfig,
|
||||
schemes: readConfig.configSchemes
|
||||
})
|
||||
config: undefined,
|
||||
data: {}
|
||||
};
|
||||
|
||||
for (let category of userConfig.config.settings) {
|
||||
for (let setting of category.settings) {
|
||||
setting.setContentPath(contentPath);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const readUserConfig = await this.readUserConfig(contentPath);
|
||||
userConfig.enabled = readUserConfig.enabled || false;
|
||||
userConfig.config.merge({ settings: readUserConfig.config });
|
||||
userConfig.config.setSaved();
|
||||
// await userConfig.config.merge({ settings: readUserConfig.config });
|
||||
// userConfig.config.setSaved();
|
||||
// userConfig.config = userConfig.config.clone({ settings: readUserConfig.config });
|
||||
userConfig.config = readUserConfig.config;
|
||||
userConfig.data = readUserConfig.data || {};
|
||||
} catch (err) { /*We don't care if this fails it either means that user config doesn't exist or there's something wrong with it so we revert to default config*/
|
||||
console.info(`Failed reading config for ${this.contentType} ${readConfig.info.name} in ${dirName}`);
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
userConfig.config = defaultConfig.clone({ settings: userConfig.config });
|
||||
userConfig.config.setSaved();
|
||||
|
||||
for (let setting of userConfig.config.findSettings(() => true)) {
|
||||
setting.setContentPath(contentPath);
|
||||
}
|
||||
|
||||
Utils.deepfreeze(defaultConfig);
|
||||
|
||||
const configs = {
|
||||
defaultConfig: readConfig.defaultConfig,
|
||||
defaultConfig,
|
||||
schemes: userConfig.schemes,
|
||||
userConfig
|
||||
};
|
||||
|
|
|
@ -83,7 +83,8 @@ export default class PluginApi {
|
|||
tryParseJson: () => Utils.tryParseJson.apply(Utils, arguments),
|
||||
toCamelCase: () => Utils.toCamelCase.apply(Utils, arguments),
|
||||
compare: () => Utils.compare.apply(Utils, arguments),
|
||||
deepclone: () => Utils.deepclone.apply(Utils, arguments)
|
||||
deepclone: () => Utils.deepclone.apply(Utils, arguments),
|
||||
deepfreeze: () => Utils.deepfreeze.apply(Utils, arguments)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,30 +9,31 @@
|
|||
*/
|
||||
|
||||
import Setting from './setting';
|
||||
import EventEmitter from 'events';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { ClientLogger as Logger, AsyncEventEmitter } from 'common';
|
||||
import { SettingUpdatedEvent, SettingsUpdatedEvent } from 'structs';
|
||||
|
||||
export default class SettingsCategory {
|
||||
|
||||
constructor(args) {
|
||||
this.emitter = new EventEmitter();
|
||||
constructor(args, ...merge) {
|
||||
this.emitter = new AsyncEventEmitter();
|
||||
this.args = args.args || args;
|
||||
|
||||
this.args.settings = this.settings.map(setting => new Setting(setting));
|
||||
|
||||
for (let newCategory of merge) {
|
||||
this._merge(newCategory);
|
||||
}
|
||||
|
||||
for (let setting of this.settings) {
|
||||
setting.on('setting-updated', ({ value, old_value }) => {
|
||||
this.emit('setting-updated', new SettingUpdatedEvent({
|
||||
category: this, category_id: this.id,
|
||||
setting, setting_id: setting.id,
|
||||
value, old_value
|
||||
}));
|
||||
});
|
||||
setting.on('setting-updated', ({ value, old_value }) => this.emit('setting-updated', new SettingUpdatedEvent({
|
||||
category: this, category_id: this.id,
|
||||
setting, setting_id: setting.id,
|
||||
value, old_value
|
||||
})));
|
||||
setting.on('settings-updated', ({ updatedSettings }) => this.emit('settings-updated', new SettingsUpdatedEvent({
|
||||
updatedSettings: updatedSettings.map(updatedSetting => Object.assign({
|
||||
updatedSettings: updatedSettings.map(updatedSetting => new SettingUpdatedEvent(Object.assign({
|
||||
category: this, category_id: this.id
|
||||
}, updatedSetting))
|
||||
}, updatedSetting)))
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +79,11 @@ export default class SettingsCategory {
|
|||
return this.findSetting(setting => setting.id === id);
|
||||
}
|
||||
|
||||
merge(newCategory, emit_multi = true) {
|
||||
/**
|
||||
* Merges a category into this category without emitting events (and therefore synchronously).
|
||||
* Only exists for use by SettingsSet.
|
||||
*/
|
||||
_merge(newCategory) {
|
||||
let updatedSettings = [];
|
||||
|
||||
for (let newSetting of newCategory.settings) {
|
||||
|
@ -88,7 +93,29 @@ export default class SettingsCategory {
|
|||
continue;
|
||||
}
|
||||
|
||||
const updatedSetting = setting.merge(newSetting, false);
|
||||
const updatedSetting = setting._merge(newSetting);
|
||||
if (!updatedSetting) continue;
|
||||
updatedSettings = updatedSettings.concat(updatedSetting.map(({ setting, value, old_value }) => ({
|
||||
category: this, category_id: this.id,
|
||||
setting, setting_id: setting.id,
|
||||
value, old_value
|
||||
})));
|
||||
}
|
||||
|
||||
return updatedSettings;
|
||||
}
|
||||
|
||||
async merge(newCategory, emit_multi = true) {
|
||||
let updatedSettings = [];
|
||||
|
||||
for (let newSetting of newCategory.settings) {
|
||||
const setting = this.settings.find(setting => setting.id === newSetting.id);
|
||||
if (!setting) {
|
||||
Logger.warn('SettingsCategory', `Trying to merge setting ${this.id}/${newSetting.id}, which does not exist.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const updatedSetting = await setting._merge(newSetting, false);
|
||||
if (!updatedSetting) continue;
|
||||
updatedSettings = updatedSettings.concat(updatedSetting.map(({ setting, value, old_value }) => ({
|
||||
category: this, category_id: this.id,
|
||||
|
@ -98,9 +125,10 @@ export default class SettingsCategory {
|
|||
}
|
||||
|
||||
if (emit_multi)
|
||||
this.emit('settings-updated', new SettingsUpdatedEvent({
|
||||
await this.emit('settings-updated', new SettingsUpdatedEvent({
|
||||
updatedSettings
|
||||
}));
|
||||
|
||||
return updatedSettings;
|
||||
}
|
||||
|
||||
|
@ -117,7 +145,7 @@ export default class SettingsCategory {
|
|||
};
|
||||
}
|
||||
|
||||
clone() {
|
||||
clone(...merge) {
|
||||
return new SettingsCategory({
|
||||
id: this.id,
|
||||
category: this.id,
|
||||
|
@ -125,7 +153,7 @@ export default class SettingsCategory {
|
|||
category_name: this.category_name,
|
||||
type: this.type,
|
||||
settings: this.settings.map(setting => setting.clone())
|
||||
});
|
||||
}, ...merge);
|
||||
}
|
||||
|
||||
on(...args) { return this.emitter.on(...args); }
|
||||
|
|
|
@ -16,14 +16,18 @@ import { Modals } from 'ui';
|
|||
|
||||
export default class SettingsSet {
|
||||
|
||||
constructor(args) {
|
||||
constructor(args, ...merge) {
|
||||
this.emitter = new AsyncEventEmitter();
|
||||
this.args = args.args || args;
|
||||
|
||||
this.args.settings = this.settings.map(category => new SettingsCategory(category));
|
||||
this.args.categories = this.categories.map(category => new SettingsCategory(category));
|
||||
this.args.schemes = this.schemes.map(scheme => new SettingsScheme(scheme));
|
||||
|
||||
for (let category of this.settings) {
|
||||
for (let newSet of merge) {
|
||||
this._merge(newSet);
|
||||
}
|
||||
|
||||
for (let category of this.categories) {
|
||||
category.on('setting-updated', ({ setting, value, old_value }) => this.emit('setting-updated', new SettingUpdatedEvent({
|
||||
set: this, set_id: this.id,
|
||||
category, category_id: category.id,
|
||||
|
@ -31,9 +35,9 @@ export default class SettingsSet {
|
|||
value, old_value
|
||||
})));
|
||||
category.on('settings-updated', ({ updatedSettings }) => this.emit('settings-updated', new SettingsUpdatedEvent({
|
||||
updatedSettings: updatedSettings.map(updatedSetting => Object.assign({
|
||||
updatedSettings: updatedSettings.map(updatedSetting => new SettingUpdatedEvent(Object.assign({
|
||||
set: this, set_id: this.id
|
||||
}, updatedSetting))
|
||||
}, updatedSetting)))
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +59,7 @@ export default class SettingsSet {
|
|||
}
|
||||
|
||||
get categories() {
|
||||
return this.args.settings || [];
|
||||
return this.args.categories || this.args.settings || [];
|
||||
}
|
||||
|
||||
get settings() {
|
||||
|
@ -91,10 +95,7 @@ export default class SettingsSet {
|
|||
}
|
||||
|
||||
findSettings(f) {
|
||||
for (let category of this.categories) {
|
||||
const setting = category.findSettings(f);
|
||||
if (setting) return setting;
|
||||
}
|
||||
return this.findSettingsInCategory(() => true, f);
|
||||
}
|
||||
|
||||
findSettingInCategory(cf, f) {
|
||||
|
@ -105,10 +106,11 @@ export default class SettingsSet {
|
|||
}
|
||||
|
||||
findSettingsInCategory(cf, f) {
|
||||
let settings = [];
|
||||
for (let category of this.categories.filter(cf)) {
|
||||
const setting = category.findSettings(f);
|
||||
if (setting) return setting;
|
||||
settings = settings.concat(category.findSettings(f));
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
getSetting(id, sid) {
|
||||
|
@ -125,9 +127,14 @@ export default class SettingsSet {
|
|||
Modals.settings(this, headertext ? headertext : this.headertext);
|
||||
}
|
||||
|
||||
async merge(newSet, emit_multi = true) {
|
||||
/**
|
||||
* Merges a set into this set without emitting events (and therefore synchronously).
|
||||
* Only exists for use by the constructor.
|
||||
*/
|
||||
_merge(newSet, emit_multi = true) {
|
||||
let updatedSettings = [];
|
||||
const categories = newSet instanceof Array ? newSet : newSet.settings;
|
||||
// const categories = newSet instanceof Array ? newSet : newSet.settings;
|
||||
const categories = newSet && newSet.args ? newSet.args.settings : newSet ? newSet.settings : newSet;
|
||||
if (!categories) return [];
|
||||
|
||||
for (let newCategory of categories) {
|
||||
|
@ -137,7 +144,33 @@ export default class SettingsSet {
|
|||
continue;
|
||||
}
|
||||
|
||||
const updatedSetting = category.merge(newCategory, false);
|
||||
const updatedSetting = category._merge(newCategory, false);
|
||||
if (!updatedSetting) continue;
|
||||
updatedSettings = updatedSettings.concat(updatedSetting.map(({ category, setting, value, old_value }) => ({
|
||||
set: this, set_id: this.id,
|
||||
category, category_id: category.id,
|
||||
setting, setting_id: setting.id,
|
||||
value, old_value
|
||||
})));
|
||||
}
|
||||
|
||||
return updatedSettings;
|
||||
}
|
||||
|
||||
async merge(newSet, emit_multi = true) {
|
||||
let updatedSettings = [];
|
||||
// const categories = newSet instanceof Array ? newSet : newSet.settings;
|
||||
const categories = newSet && newSet.args ? newSet.args.settings : newSet ? newSet.settings : newSet;
|
||||
if (!categories) return [];
|
||||
|
||||
for (let newCategory of categories) {
|
||||
const category = this.find(category => category.category === newCategory.category);
|
||||
if (!category) {
|
||||
Logger.warn('SettingsCategory', `Trying to merge category ${newCategory.id}, which does not exist.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const updatedSetting = await category.merge(newCategory, false);
|
||||
if (!updatedSetting) continue;
|
||||
updatedSettings = updatedSettings.concat(updatedSetting.map(({ category, setting, value, old_value }) => ({
|
||||
set: this, set_id: this.id,
|
||||
|
@ -168,14 +201,14 @@ export default class SettingsSet {
|
|||
return stripped;
|
||||
}
|
||||
|
||||
clone() {
|
||||
clone(...merge) {
|
||||
return new SettingsSet({
|
||||
id: this.id,
|
||||
text: this.text,
|
||||
headertext: this.headertext,
|
||||
settings: this.categories.map(category => category.clone()),
|
||||
schemes: this.schemes.map(scheme => scheme.clone())
|
||||
});
|
||||
}, ...merge);
|
||||
}
|
||||
|
||||
on(...args) { return this.emitter.on(...args); }
|
||||
|
|
|
@ -17,8 +17,8 @@ import SettingsScheme from '../settingsscheme';
|
|||
|
||||
export default class ArraySetting extends Setting {
|
||||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
constructor(args, ...merge) {
|
||||
super(args, ...merge);
|
||||
|
||||
this.args.settings = this.settings.map(category => new SettingsCategory(category));
|
||||
this.args.schemes = this.schemes.map(scheme => new SettingsScheme(scheme));
|
||||
|
@ -85,9 +85,9 @@ export default class ArraySetting extends Setting {
|
|||
const set = new SettingsSet({
|
||||
settings: Utils.deepclone(this.settings),
|
||||
schemes: this.schemes
|
||||
});
|
||||
}, item ? item.args || item : undefined);
|
||||
|
||||
if (item) set.merge(item.args || item);
|
||||
// if (item) set.merge(item.args || item);
|
||||
set.setSaved();
|
||||
set.on('settings-updated', () => this.updateValue());
|
||||
return set;
|
||||
|
@ -124,8 +124,8 @@ export default class ArraySetting extends Setting {
|
|||
return maps.length ? maps.join(', ') + ',' : '()';
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new ArraySetting(Utils.deepclone(this.args));
|
||||
}
|
||||
// clone() {
|
||||
// return new ArraySetting(Utils.deepclone(this.args));
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -9,13 +9,12 @@
|
|||
*/
|
||||
|
||||
import { ThemeManager } from 'modules';
|
||||
import { Utils } from 'common';
|
||||
import { Utils, AsyncEventEmitter } from 'common';
|
||||
import { SettingUpdatedEvent, SettingsUpdatedEvent } from 'structs';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
export default class Setting {
|
||||
|
||||
constructor(args) {
|
||||
constructor(args, ...merge) {
|
||||
this.args = args.args || args;
|
||||
|
||||
if (!this.args.hasOwnProperty('value'))
|
||||
|
@ -23,7 +22,11 @@ export default class Setting {
|
|||
if (!this.args.hasOwnProperty('saved_value'))
|
||||
this.args.saved_value = this.args.value;
|
||||
|
||||
this.emitter = new EventEmitter();
|
||||
for (let newSetting of merge) {
|
||||
this._merge(newSetting);
|
||||
}
|
||||
|
||||
this.emitter = new AsyncEventEmitter();
|
||||
this.changed = !Utils.compare(this.args.value, this.args.saved_value);
|
||||
}
|
||||
|
||||
|
@ -67,8 +70,44 @@ export default class Setting {
|
|||
return this.args.fullwidth || false;
|
||||
}
|
||||
|
||||
merge(newSetting, emit_multi = true) {
|
||||
return this.setValue(newSetting.args ? newSetting.args.value : newSetting.value, emit_multi);
|
||||
/**
|
||||
* Merges a setting into this setting without emitting events (and therefore synchronously).
|
||||
* Only exists for use by SettingsCategory.
|
||||
*/
|
||||
_merge(newSetting) {
|
||||
const value = newSetting.args ? newSetting.args.value : newSetting.value;
|
||||
const old_value = this.args.value;
|
||||
if (Utils.compare(value, old_value)) return [];
|
||||
this.args.value = value;
|
||||
this.changed = !Utils.compare(this.args.value, this.args.saved_value);
|
||||
|
||||
return [{
|
||||
setting: this, setting_id: this.id,
|
||||
value, old_value
|
||||
}];
|
||||
}
|
||||
|
||||
async merge(newSetting, emit_multi = true) {
|
||||
const value = newSetting.args ? newSetting.args.value : newSetting.value;
|
||||
const old_value = this.args.value;
|
||||
if (Utils.compare(value, old_value)) return [];
|
||||
this.args.value = value;
|
||||
this.changed = !Utils.compare(this.args.value, this.args.saved_value);
|
||||
|
||||
const updatedSetting = {
|
||||
setting: this, setting_id: this.id,
|
||||
value, old_value
|
||||
};
|
||||
|
||||
if (emit)
|
||||
await this.emit('setting-updated', new SettingUpdatedEvent(updatedSetting));
|
||||
|
||||
if (emit_multi)
|
||||
await this.emit('settings-updated', new SettingsUpdatedEvent({
|
||||
updatedSettings: [updatedSetting]
|
||||
}));
|
||||
|
||||
return [updatedSetting];
|
||||
}
|
||||
|
||||
setValue(value, emit_multi = true, emit = true) {
|
||||
|
@ -109,8 +148,8 @@ export default class Setting {
|
|||
};
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new this.constructor(Utils.deepclone(this.args));
|
||||
clone(...merge) {
|
||||
return new this.constructor(Utils.deepclone(this.args), ...merge);
|
||||
}
|
||||
|
||||
toSCSS() {
|
||||
|
|
|
@ -15,8 +15,8 @@ import path from 'path';
|
|||
|
||||
export default class CustomSetting extends Setting {
|
||||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
constructor(args, ...merge) {
|
||||
super(args, ...merge);
|
||||
|
||||
if (this.args.class_file && this.path)
|
||||
this.setClass(this.args.class_file, this.args.class);
|
||||
|
|
|
@ -13,8 +13,8 @@ import MultipleChoiceOption from '../multiplechoiceoption';
|
|||
|
||||
export default class DropdownSetting extends Setting {
|
||||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
constructor(args, ...merge) {
|
||||
super(args, ...merge);
|
||||
|
||||
this.args.options = this.options.map(option => new MultipleChoiceOption(option));
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ import MultipleChoiceOption from '../multiplechoiceoption';
|
|||
|
||||
export default class RadioSetting extends Setting {
|
||||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
constructor(args, ...merge) {
|
||||
super(args, ...merge);
|
||||
|
||||
this.args.options = this.options.map(option => new MultipleChoiceOption(option));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="bd-dropdown-options bd-flex bd-flex-col" ref="options" v-if="active">
|
||||
<div class="bd-dropdown-option" v-for="option in options" :class="{'bd-dropdown-option-selected': selected === option.value}" @click="change(option.value)">{{ option.text }}</div>
|
||||
<div class="bd-dropdown-option" v-for="option in options" :class="{'bd-dropdown-option-selected': selected === option.value}" @click="change(option.value); active = false">{{ option.text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -83,6 +83,20 @@ export class Utils {
|
|||
|
||||
return value;
|
||||
}
|
||||
|
||||
static deepfreeze(object) {
|
||||
if (typeof object === 'object' && object !== null) {
|
||||
const properties = Object.getOwnPropertyNames(object);
|
||||
|
||||
for (let property of properties) {
|
||||
this.deepfreeze(object[property]);
|
||||
}
|
||||
|
||||
Object.freeze(object);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileUtils {
|
||||
|
|
Loading…
Reference in New Issue