Use settings structs

This commit is contained in:
Samuel Elliott 2018-03-01 19:00:24 +00:00
parent 3574d6a5ba
commit 3437c36b87
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
13 changed files with 193 additions and 385 deletions

View File

@ -12,7 +12,7 @@ import Globals from './globals';
import { FileUtils, ClientLogger as Logger } from 'common'; import { FileUtils, ClientLogger as Logger } from 'common';
import path from 'path'; import path from 'path';
import { Events } from 'modules'; import { Events } from 'modules';
import { ErrorEvent } from 'structs'; import { SettingsSet, ErrorEvent } from 'structs';
import { Modals } from 'ui'; import { Modals } from 'ui';
/** /**
@ -54,6 +54,10 @@ export default class {
const directories = await FileUtils.listDirectory(this.contentPath); const directories = await FileUtils.listDirectory(this.contentPath);
for (let dir of directories) { for (let dir of directories) {
try {
await FileUtils.directoryExists(path.join(this.contentPath, dir));
} catch (err) { continue; }
try { try {
await this.preloadContent(dir); await this.preloadContent(dir);
} catch (err) { } catch (err) {
@ -98,6 +102,10 @@ export default class {
// If content is already loaded this should resolve. // If content is already loaded this should resolve.
if (this.getContentByDirName(dir)) continue; if (this.getContentByDirName(dir)) continue;
try {
await FileUtils.directoryExists(path.join(this.contentPath, dir));
} catch (err) { continue; }
try { try {
// Load if not // Load if not
await this.preloadContent(dir); await this.preloadContent(dir);
@ -168,37 +176,34 @@ export default class {
const readConfig = await this.readConfig(contentPath); const readConfig = await this.readConfig(contentPath);
const mainPath = path.join(contentPath, readConfig.main); const mainPath = path.join(contentPath, readConfig.main);
readConfig.defaultConfig = readConfig.defaultConfig || [];
const userConfig = { const userConfig = {
enabled: false, enabled: false,
config: readConfig.defaultConfig config: new SettingsSet({
settings: readConfig.defaultConfig,
schemes: readConfig.configSchemes
})
}; };
for (let category of userConfig.config.settings) {
for (let setting of category.settings) {
setting.setContentPath(contentPath);
}
}
try { try {
const readUserConfig = await this.readUserConfig(contentPath); const readUserConfig = await this.readUserConfig(contentPath);
userConfig.enabled = readUserConfig.enabled || false; userConfig.enabled = readUserConfig.enabled || false;
for (let category of userConfig.config) { userConfig.config.merge({ settings: readUserConfig.config });
const newCategory = readUserConfig.config.find(c => c.category === category.category); userConfig.config.setSaved();
for (let setting of category.settings) {
setting.path = contentPath;
if (!newCategory) continue;
const newSetting = newCategory.settings.find(s => s.id === setting.id);
if (!newSetting) continue;
setting.value = newSetting.value;
}
}
userConfig.css = readUserConfig.css || null; userConfig.css = readUserConfig.css || null;
} 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*/ } 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);
} }
const configs = { const configs = {
defaultConfig: readConfig.defaultConfig, defaultConfig: readConfig.defaultConfig,
schemes: readConfig.configSchemes, schemes: userConfig.schemes,
userConfig userConfig
}; };

View File

@ -42,6 +42,10 @@ export default class Plugin {
this.hasSettings = this.config && this.config.length > 0; this.hasSettings = this.config && this.config.length > 0;
this.start = this.start.bind(this); this.start = this.start.bind(this);
this.stop = this.stop.bind(this); this.stop = this.stop.bind(this);
this.settings.on('setting-updated', event => this.events.emit('setting-updated', event));
this.settings.on('settings-updated', event => this.events.emit('settings-updated', event));
this.settings.on('settings-updated', event => this.saveConfiguration());
} }
get type() { return 'plugin' } get type() { return 'plugin' }
@ -61,7 +65,8 @@ export default class Plugin {
get pluginPath() { return this.paths.contentPath } get pluginPath() { return this.paths.contentPath }
get dirName() { return this.paths.dirName } get dirName() { return this.paths.dirName }
get enabled() { return this.userConfig.enabled } get enabled() { return this.userConfig.enabled }
get config() { return this.userConfig.config || [] } get settings() { return this.userConfig.config }
get config() { return this.settings.settings }
get pluginConfig() { return this.config } get pluginConfig() { return this.config }
get exports() { return this._exports ? this._exports : (this._exports = this.getExports()) } get exports() { return this._exports ? this._exports : (this._exports = this.getExports()) }
get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new PluginEvents(this)) } get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new PluginEvents(this)) }
@ -81,45 +86,22 @@ export default class Plugin {
} }
async saveSettings(newSettings) { async saveSettings(newSettings) {
const updatedSettings = []; const updatedSettings = this.settings.merge(newSettings);
for (let newCategory of newSettings) { await this.saveConfiguration();
const category = this.config.find(c => c.category === newCategory.category); return updatedSettings;
for (let newSetting of newCategory.settings) {
const setting = category.settings.find(s => s.id === newSetting.id);
if (Utils.compare(setting.value, newSetting.value)) continue;
const old_value = setting.value;
setting.value = newSetting.value;
updatedSettings.push({ category_id: category.category, setting_id: setting.id, value: setting.value, old_value });
this.settingUpdated(category.category, setting.id, setting.value, old_value);
}
}
this.saveConfiguration();
return this.settingsUpdated(updatedSettings);
}
settingUpdated(category_id, setting_id, value, old_value) {
const event = new SettingUpdatedEvent({ category_id, setting_id, value, old_value });
this.events.emit('setting-updated', event);
this.events.emit(`setting-updated_{$category_id}_${setting_id}`, event);
}
settingsUpdated(updatedSettings) {
const event = new SettingsUpdatedEvent({ settings: updatedSettings.map(s => new SettingUpdatedEvent(s)) });
this.events.emit('settings-updated', event);
} }
async saveConfiguration() { async saveConfiguration() {
window.testConfig = new ContentConfig(this.config);
try { try {
const config = new ContentConfig(this.config).strip();
await FileUtils.writeFile(`${this.pluginPath}/user.config.json`, JSON.stringify({ await FileUtils.writeFile(`${this.pluginPath}/user.config.json`, JSON.stringify({
enabled: this.enabled, enabled: this.enabled,
config config: this.settings.strip().settings
})); }));
this.settings.setSaved();
} catch (err) { } catch (err) {
console.error(`Plugin ${this.id} configuration failed to save`, err);
throw err; throw err;
} }
} }

View File

@ -13,7 +13,7 @@ import Globals from './globals';
import CssEditor from './csseditor'; import CssEditor from './csseditor';
import Events from './events'; import Events from './events';
import { Utils, FileUtils, ClientLogger as Logger } from 'common'; import { Utils, FileUtils, ClientLogger as Logger } from 'common';
import { SettingUpdatedEvent } from 'structs'; import { SettingsSet, SettingUpdatedEvent } from 'structs';
import path from 'path'; import path from 'path';
export default class { export default class {
@ -25,24 +25,22 @@ export default class {
const user_config = await FileUtils.readJsonFromFile(settingsPath); const user_config = await FileUtils.readJsonFromFile(settingsPath);
const { settings, scss, css_editor_bounds } = user_config; const { settings, scss, css_editor_bounds } = user_config;
this.settings = defaultSettings; this.settings = defaultSettings.map(set => {
const newSet = new SettingsSet(set);
for (let newSet of settings) { newSet.merge(settings.find(s => s.id === newSet.id));
let set = this.settings.find(s => s.id === newSet.id); newSet.setSaved();
if (!set) continue; newSet.on('setting-updated', event => {
const { category, setting, value, old_value } = event;
for (let newCategory of newSet.settings) { Logger.log('Settings', `${newSet.id}/${category.id}/${setting.id} was changed from ${old_value} to ${value}`);
let category = set.settings.find(c => c.category === newCategory.category); Events.emit('setting-updated', event);
if (!category) continue; Events.emit(`setting-updated-${newSet.id}_${category.id}_${setting.id}`, event);
});
for (let newSetting of newCategory.settings) { newSet.on('settings-updated', async (event) => {
let setting = category.settings.find(s => s.id === newSetting.id); await this.saveSettings();
if (!setting) continue; Events.emit('settings-updated', event);
});
setting.value = newSetting.value; return newSet;
} });
}
}
CssEditor.updateScss(scss, true); CssEditor.updateScss(scss, true);
CssEditor.editor_bounds = css_editor_bounds || {}; CssEditor.editor_bounds = css_editor_bounds || {};
@ -59,22 +57,7 @@ export default class {
const settingsPath = path.resolve(this.dataPath, 'user.settings.json'); const settingsPath = path.resolve(this.dataPath, 'user.settings.json');
await FileUtils.writeJsonToFile(settingsPath, { await FileUtils.writeJsonToFile(settingsPath, {
settings: this.getSettings.map(set => { settings: this.getSettings.map(set => set.strip()),
return {
id: set.id,
settings: set.settings.map(category => {
return {
category: category.category,
settings: category.settings.map(setting => {
return {
id: setting.id,
value: setting.value
};
})
};
})
};
}),
scss: CssEditor.scss, scss: CssEditor.scss,
css_editor_bounds: { css_editor_bounds: {
width: CssEditor.editor_bounds.width, width: CssEditor.editor_bounds.width,
@ -83,10 +66,14 @@ export default class {
y: CssEditor.editor_bounds.y y: CssEditor.editor_bounds.y
} }
}); });
for (let set of this.getSettings) {
set.setSaved();
}
} catch (err) { } catch (err) {
// There was an error loading settings // There was an error saving settings
// This probably means that the user doesn't have any settings yet
Logger.err('Settings', err); Logger.err('Settings', err);
throw err;
} }
} }
@ -94,76 +81,37 @@ export default class {
return this.getSettings.find(s => s.id === set_id); return this.getSettings.find(s => s.id === set_id);
} }
static get core() { return this.getSet('core') }
static get ui() { return this.getSet('ui') }
static get emotes() { return this.getSet('emotes') }
static get security() { return this.getSet('security') }
static getCategory(set_id, category_id) { static getCategory(set_id, category_id) {
const set = this.getSet(set_id); const set = this.getSet(set_id);
if (!set) return; return set ? set.getCategory(category_id) : undefined;
return set.settings.find(c => c.category === category_id);
} }
static getSetting(set_id, category_id, setting_id) { static getSetting(set_id, category_id, setting_id) {
const category = this.getCategory(set_id, category_id); const set = this.getSet(set_id);
if (!category) return; return set ? set.getSetting(category_id, setting_id) : undefined;
return category.settings.find(s => s.id === setting_id);
} }
static get(set_id, category_id, setting_id) { static get(set_id, category_id, setting_id) {
const setting = this.getSetting(set_id, category_id, setting_id); const set = this.getSet(set_id);
return setting ? setting.value : undefined; return set ? set.get(category_id, setting_id) : undefined;
} }
static mergeSettings(set_id, newSettings, settingsUpdated) { static async mergeSettings(set_id, newSettings) {
const set = this.getSet(set_id); const set = this.getSet(set_id);
if (!set) return; if (!set) return;
const updatedSettings = [];
for (let newCategory of newSettings) { return await set.merge(newSettings);
let category = set.settings.find(c => c.category === newCategory.category);
for (let newSetting of newCategory.settings) {
let setting = category.settings.find(s => s.id === newSetting.id);
if (Utils.compare(setting.value, newSetting.value)) continue;
let old_value = setting.value;
setting.value = newSetting.value;
updatedSettings.push({ set_id: set.id, category_id: category.category, setting_id: setting.id, value: setting.value, old_value });
this.settingUpdated(set.id, category.category, setting.id, setting.value, old_value);
}
}
this.saveSettings();
return settingsUpdated ? settingsUpdated(updatedSettings) : updatedSettings;
} }
static setSetting(set_id, category_id, setting_id, value) { static setSetting(set_id, category_id, setting_id, value) {
for (let set of this.getSettings) { const setting = this.getSetting(set_id, category_id, setting_id);
if (set.id !== set_id) continue; if (!setting) throw {message: `Tried to set ${set_id}/${category_id}/${setting_id}, which doesn't exist`};
setting.value = value;
for (let category of set.settings) {
if (category.category !== category_id) continue;
for (let setting of category.settings) {
if (setting.id !== setting_id) continue;
if (Utils.compare(setting.value, value)) return true;
let old_value = setting.value;
setting.value = value;
this.settingUpdated(set_id, category_id, setting_id, value, old_value);
return true;
}
}
}
return false;
}
static settingUpdated(set_id, category_id, setting_id, value, old_value) {
Logger.log('Settings', `${set_id}/${category_id}/${setting_id} was changed from ${old_value} to ${value}`);
const event = new SettingUpdatedEvent({ set_id, category_id, setting_id, value, old_value });
Events.emit('setting-updated', event);
Events.emit(`setting-updated-${set_id}_{$category_id}_${setting_id}`, event);
} }
static get getSettings() { static get getSettings() {

View File

@ -42,6 +42,10 @@ export default class Theme {
this.saveSettings = this.saveSettings.bind(this); this.saveSettings = this.saveSettings.bind(this);
this.enable = this.enable.bind(this); this.enable = this.enable.bind(this);
this.disable = this.disable.bind(this); this.disable = this.disable.bind(this);
this.settings.on('setting-updated', event => this.events.emit('setting-updated', event));
this.settings.on('settings-updated', event => this.events.emit('settings-updated', event));
this.settings.on('settings-updated', event => this.saveConfiguration());
} }
get configs() { return this.__themeInternals.configs } get configs() { return this.__themeInternals.configs }
@ -61,7 +65,8 @@ export default class Theme {
get themePath() { return this.paths.contentPath } get themePath() { return this.paths.contentPath }
get dirName() { return this.paths.dirName } get dirName() { return this.paths.dirName }
get enabled() { return this.userConfig.enabled } get enabled() { return this.userConfig.enabled }
get config() { return this.userConfig.config || [] } get settings() { return this.userConfig.config }
get config() { return this.settings.settings }
get themeConfig() { return this.config } get themeConfig() { return this.config }
get css() { return this.userConfig.css } get css() { return this.userConfig.css }
get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new ThemeEvents(this)) } get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new ThemeEvents(this)) }
@ -71,47 +76,24 @@ export default class Theme {
} }
async saveSettings(newSettings) { async saveSettings(newSettings) {
const updatedSettings = []; const updatedSettings = this.settings.merge(newSettings);
for (let newCategory of newSettings) {
const category = this.config.find(c => c.category === newCategory.category);
for (let newSetting of newCategory.settings) {
const setting = category.settings.find(s => s.id === newSetting.id);
if (Utils.compare(setting.value, newSetting.value)) continue;
const old_value = setting.value;
setting.value = newSetting.value;
updatedSettings.push({ category_id: category.category, setting_id: setting.id, value: setting.value, old_value });
this.settingUpdated(category.category, setting.id, setting.value, old_value);
}
}
// As the theme's configuration has changed it needs recompiling // As the theme's configuration has changed it needs recompiling
// When the compiled CSS has been save it will also save the configuration // When the compiled CSS has been saved it will also save the configuration
await this.recompile(); await this.recompile();
return this.settingsUpdated(updatedSettings); return this.settingsUpdated(updatedSettings);
} }
settingUpdated(category_id, setting_id, value, old_value) {
const event = new SettingUpdatedEvent({ category_id, setting_id, value, old_value });
this.events.emit('setting-updated', event);
this.events.emit(`setting-updated_{$category_id}_${setting_id}`, event);
}
settingsUpdated(updatedSettings) {
const event = new SettingsUpdatedEvent({ settings: updatedSettings.map(s => new SettingUpdatedEvent(s)) });
this.events.emit('settings-updated', event);
}
async saveConfiguration() { async saveConfiguration() {
try { try {
const config = new ContentConfig(this.config).strip();
await FileUtils.writeFile(`${this.themePath}/user.config.json`, JSON.stringify({ await FileUtils.writeFile(`${this.themePath}/user.config.json`, JSON.stringify({
enabled: this.enabled, enabled: this.enabled,
config, config: this.settings.strip().settings,
css: this.css css: this.css
})); }));
this.settings.setSaved();
} catch (err) { } catch (err) {
throw err; throw err;
} }

View File

@ -101,67 +101,9 @@ export default class ThemeManager extends ContentManager {
static async parseSetting(setting) { static async parseSetting(setting) {
const { type, id, value } = setting; const { type, id, value } = setting;
const name = id.replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-'); const name = id.replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-');
const scss = await setting.toSCSS();
if (type === 'colour' || type === 'color') { if (scss) return [name, scss];
return [name, value];
}
if (type === 'array') {
const items = JSON.parse(JSON.stringify(value)) || [];
const settings_json = JSON.stringify(setting.settings);
for (let item of items) {
const settings = JSON.parse(settings_json);
for (let category of settings) {
const newCategory = item.settings.find(c => c.category === category.category);
for (let setting of category.settings) {
const newSetting = newCategory.settings.find(s => s.id === setting.id);
setting.value = setting.old_value = newSetting.value;
setting.changed = false;
}
}
item.settings = settings;
}
console.log('items', items);
// Final comma ensures the variable is a list
const maps = [];
for (let item of items)
maps.push(await this.getConfigAsSCSSMap(item.settings));
return [name, maps.length ? maps.join(', ') + ',' : '()'];
}
if (type === 'file' && Array.isArray(value)) {
if (!value || !value.length) return [name, '()'];
const files = [];
for (let filepath of value) {
const buffer = await FileUtils.readFileBuffer(path.resolve(setting.path, filepath));
const type = await FileUtils.getFileType(buffer);
files.push(`(data: ${this.toSCSSString(buffer.toString('base64'))}, type: ${this.toSCSSString(type.mime)}, url: ${this.toSCSSString(await FileUtils.toDataURI(buffer, type.mime))})`);
}
return [name, files.length ? files.join(', ') : '()'];
}
if (type === 'slider') {
return [name, value * setting.multi || 1];
}
if (type === 'dropdown' || type === 'radio') {
return [name, setting.options.find(opt => opt.id === value).value];
}
if (typeof value === 'boolean' || typeof value === 'number') {
return [name, value];
}
if (typeof value === 'string') {
return [name, this.toSCSSString(value)];
}
} }
static toSCSSString(value) { static toSCSSString(value) {

View File

@ -13,15 +13,27 @@ import Event from './event';
export default class SettingUpdatedEvent extends Event { export default class SettingUpdatedEvent extends Event {
get set() { get set() {
return this.args.set_id; return this.args.set;
}
get set_id() {
return this.args.set.id;
} }
get category() { get category() {
return this.args.category_id; return this.args.category;
}
get category_id() {
return this.args.category.id;
} }
get setting() { get setting() {
return this.args.setting_id; return this.args.setting;
}
get setting_id() {
return this.args.setting.id;
} }
get value() { get value() {

View File

@ -21,7 +21,7 @@
</div> </div>
</div> </div>
<div class="bd-flex bd-flex-col bd-pluginsView"> <div class="bd-flex bd-flex-col bd-pluginsview">
<div v-if="local" class="bd-flex bd-flex-grow bd-flex-col bd-plugins-container bd-local-plugins"> <div v-if="local" class="bd-flex bd-flex-grow bd-flex-col bd-plugins-container bd-local-plugins">
<PluginCard v-for="plugin in localPlugins" :plugin="plugin" :key="plugin.id" :togglePlugin="() => togglePlugin(plugin)" :reloadPlugin="() => reloadPlugin(plugin)" :deletePlugin="e => deletePlugin(plugin, e.shiftKey)" :showSettings="() => showSettings(plugin)" /> <PluginCard v-for="plugin in localPlugins" :plugin="plugin" :key="plugin.id" :togglePlugin="() => togglePlugin(plugin)" :reloadPlugin="() => reloadPlugin(plugin)" :deletePlugin="e => deletePlugin(plugin, e.shiftKey)" :showSettings="() => showSettings(plugin)" />
</div> </div>

View File

@ -13,7 +13,7 @@
<div class="bd-settings-schemes" v-if="schemes && schemes.length"> <div class="bd-settings-schemes" v-if="schemes && schemes.length">
<div class="bd-settings-schemes-container"> <div class="bd-settings-schemes-container">
<template v-for="scheme in schemes"> <template v-for="scheme in schemes">
<div class="bd-settings-scheme" :class="{'bd-active': checkSchemeActive(scheme)}" @click="() => setActiveScheme(scheme)"> <div class="bd-settings-scheme" :class="{'bd-active': scheme.isActive(settings)}" @click="() => scheme.applyTo(settings)">
<div class="bd-settings-scheme-icon" :style="{'background-image': `url(&quot;${scheme.icon_url}&quot;)`}"></div> <div class="bd-settings-scheme-icon" :style="{'background-image': `url(&quot;${scheme.icon_url}&quot;)`}"></div>
<div class="bd-settings-scheme-name" v-if="scheme.name">{{ scheme.name }}</div> <div class="bd-settings-scheme-name" v-if="scheme.name">{{ scheme.name }}</div>
<div class="bd-settings-scheme-hint" v-if="scheme.hint">{{ scheme.hint }}</div> <div class="bd-settings-scheme-hint" v-if="scheme.hint">{{ scheme.hint }}</div>
@ -23,7 +23,7 @@
</div> </div>
<div class="bd-settings-categories"> <div class="bd-settings-categories">
<template v-for="category in settings"> <template v-for="category in settings.categories">
<div class="bd-settings-category"> <div class="bd-settings-category">
<div v-if="category.category === 'default' || !category.type"> <div v-if="category.category === 'default' || !category.type">
<Setting v-for="setting in category.settings" :key="setting.id" :setting="setting" :change="v => settingChange(category, setting, v)" /> <Setting v-for="setting in category.settings" :key="setting.id" :setting="setting" :change="v => settingChange(category, setting, v)" />
@ -53,47 +53,15 @@
import Drawer from '../common/Drawer.vue'; import Drawer from '../common/Drawer.vue';
export default { export default {
props: ['settings', 'schemes', 'change'], props: ['settings', 'schemes'],
components: { components: {
Setting, Setting,
Drawer Drawer
}, },
methods: { methods: {
checkSchemeActive(scheme) {
for (let schemeCategory of scheme.settings) {
const category = this.settings.find(c => c.category === schemeCategory.category);
if (!category) return false;
for (let schemeSetting of schemeCategory.settings) {
const setting = category.settings.find(s => s.id === schemeSetting.id);
if (!setting || !Utils.compare(setting.value, schemeSetting.value)) return false;
}
}
return true;
},
setActiveScheme(scheme) {
for (let schemeCategory of scheme.settings) {
const category = this.settings.find(c => c.category === schemeCategory.category);
if (!category) {
console.err(`Category ${schemeCategory.category} does not exist`);
continue;
}
for (let schemeSetting of schemeCategory.settings) {
const setting = category.settings.find(s => s.id === schemeSetting.id);
if (!setting) {
console.err(`Setting ${schemeCategory.category}/${schemeSetting.id} does not exist`);
continue;
}
this.change(category.category, setting.id, schemeSetting.value);
}
}
},
settingChange(category, setting, value) { settingChange(category, setting, value) {
if (setting.disabled) return; if (setting.disabled) return;
this.change(category.category, setting.id, value); setting.value = value;
} }
} }
} }

View File

@ -21,7 +21,7 @@
</div> </div>
</div> </div>
<div class="bd-flex bd-flex-col bd-themesView"> <div class="bd-flex bd-flex-col bd-themesview">
<div v-if="local" class="bd-flex bd-flex-grow bd-flex-col bd-themes-container bd-local-themes"> <div v-if="local" class="bd-flex bd-flex-grow bd-flex-col bd-themes-container bd-local-themes">
<ThemeCard v-for="theme in localThemes" :theme="theme" :key="theme.id" :toggleTheme="() => toggleTheme(theme)" :reloadTheme="e => reloadTheme(theme, e.shiftKey)" :showSettings="() => showSettings(theme)" :deleteTheme="e => deleteTheme(theme, e.shiftKey)" /> <ThemeCard v-for="theme in localThemes" :theme="theme" :key="theme.id" :toggleTheme="() => toggleTheme(theme)" :reloadTheme="e => reloadTheme(theme, e.shiftKey)" :showSettings="() => showSettings(theme)" :deleteTheme="e => deleteTheme(theme, e.shiftKey)" />
</div> </div>

View File

@ -11,7 +11,7 @@
<template> <template>
<div class="bd-settings-modal" :class="{'bd-edited': changed}"> <div class="bd-settings-modal" :class="{'bd-edited': changed}">
<Modal :headerText="modal.headertext" :close="modal.close" :class="{'bd-modal-out': modal.closing}"> <Modal :headerText="modal.headertext" :close="modal.close" :class="{'bd-modal-out': modal.closing}">
<SettingsPanel :settings="configCache" :schemes="modal.schemes" :change="settingChange" slot="body" class="bd-settings-modal-body" /> <SettingsPanel :settings="settings" :schemes="modal.schemes" slot="body" class="bd-settings-modal-body" />
<div slot="footer" class="bd-footer-alert" :class="{'bd-active': changed, 'bd-warn': warnclose}" :style="{pointerEvents: changed ? 'all' : 'none'}"> <div slot="footer" class="bd-footer-alert" :class="{'bd-active': changed, 'bd-warn': warnclose}" :style="{pointerEvents: changed ? 'all' : 'none'}">
<div class="bd-footer-alert-text">Unsaved changes</div> <div class="bd-footer-alert-text">Unsaved changes</div>
<div class="bd-button bd-reset-button bd-tp" :class="{'bd-disabled': saving}" @click="resetSettings">Reset</div> <div class="bd-button bd-reset-button bd-tp" :class="{'bd-disabled': saving}" @click="resetSettings">Reset</div>
@ -34,9 +34,8 @@
props: ['modal'], props: ['modal'],
data() { data() {
return { return {
changed: false,
warnclose: false, warnclose: false,
configCache: [], settings: null,
closing: false, closing: false,
saving: false saving: false
} }
@ -45,41 +44,32 @@
Modal, Modal,
SettingsPanel SettingsPanel
}, },
computed: {
changed() {
return this.settings.categories.find(category => category.changed);
}
},
methods: { methods: {
checkForChanges() { // settingChange(category_id, setting_id, value) {
let changed = false; // console.log(`Setting ${category_id}/${setting_id} to ${value}`, this.configCache);
for (let category of this.configCache) { //
const cat = this.modal.settings.find(c => c.category === category.category); // const category = this.configCache.find(c => c.category === category_id);
for (let setting of category.settings) { // if (!category) return;
if (!Utils.compare(cat.settings.find(s => s.id === setting.id).value, setting.value)) { //
changed = true; // const setting = category.settings.find(s => s.id === setting_id);
Vue.set(setting, 'changed', true); // if (!setting) return;
} else { //
Vue.set(setting, 'changed', false); // setting.value = value;
} //
} // // this.changed = this.checkForChanges();
} // this.$forceUpdate();
return changed; // },
},
settingChange(category_id, setting_id, value) {
const category = this.configCache.find(c => c.category === category_id);
if (!category) return;
const setting = category.settings.find(s => s.id === setting_id);
if (!setting) return;
setting.value = value;
this.changed = this.checkForChanges();
this.$forceUpdate();
},
async saveSettings() { async saveSettings() {
if (this.saving) return; if (this.saving) return;
this.saving = true; this.saving = true;
try { try {
await this.modal.saveSettings(this.configCache); if (this.modal.saveSettings) await this.modal.saveSettings(this.settings);
this.configCache = JSON.parse(JSON.stringify(this.modal.settings)); // this.cloneSettings();
this.changed = false;
} catch (err) { } catch (err) {
// TODO Display error that settings failed to save // TODO Display error that settings failed to save
console.log(err); console.log(err);
@ -88,9 +78,14 @@
}, },
resetSettings() { resetSettings() {
if (this.saving) return; if (this.saving) return;
this.configCache = JSON.parse(JSON.stringify(this.modal.settings)); // this.configCache = JSON.parse(JSON.stringify(this.modal.settings));
this.changed = false; // this.changed = false;
this.cloneSettings();
this.$forceUpdate(); this.$forceUpdate();
},
cloneSettings() {
console.log('Cloning settings');
this.settings = this.modal.dont_clone ? this.modal.settings : this.modal.settings.clone();
} }
}, },
created() { created() {
@ -100,11 +95,19 @@
setTimeout(() => this.warnclose = false, 400); setTimeout(() => this.warnclose = false, 400);
throw {message: 'Settings have been changed'}; throw {message: 'Settings have been changed'};
} }
} };
this.modal.settings.on('settings-updated', this.cloneSettings);
},
destroyed() {
this.modal.settings.off('settings-updated', this.cloneSettings);
}, },
beforeMount() { beforeMount() {
this.configCache = JSON.parse(JSON.stringify(this.modal.settings)); // this.configCache = JSON.parse(JSON.stringify(this.modal.settings));
this.changed = this.checkForChanges(); // this.changed = this.checkForChanges();
console.log(this);
this.cloneSettings();
// this.settings = this.modal.dont_clone ? this.modal.settings : this.modal.settings.clone();
} }
} }
</script> </script>

View File

@ -12,11 +12,11 @@
<div class="bd-form-settingsarray" :class="{'bd-form-settingsarray-inline': setting.inline}"> <div class="bd-form-settingsarray" :class="{'bd-form-settingsarray-inline': setting.inline}">
<div class="bd-title"> <div class="bd-title">
<h3>{{ setting.text }}</h3> <h3>{{ setting.text }}</h3>
<button class="bd-button bd-button-primary" :class="{'bd-disabled': setting.disabled || setting.max && items.length >= setting.max}" @click="() => addItem(!setting.inline)">Add</button> <button class="bd-button bd-button-primary" :class="{'bd-disabled': setting.disabled || setting.max && setting.items.length >= setting.max}" @click="() => addItem(!setting.inline)">Add</button>
</div> </div>
<div class="bd-hint">{{ setting.hint }}</div> <div class="bd-hint">{{ setting.hint }}</div>
<div class="bd-settingsarray-items"> <div class="bd-settingsarray-items">
<div class="bd-settingsarray-item" v-for="(item, index) in items"> <div class="bd-settingsarray-item" v-for="(item, index) in setting.items">
<div class="bd-settingsarray-item-marker">{{ index + 1 }}</div> <div class="bd-settingsarray-item-marker">{{ index + 1 }}</div>
<SettingsPanel class="bd-settingsarray-item-contents" v-if="setting.inline" :settings="item.settings" :change="(c, s, v) => changeInItem(item, c, s, v)" /> <SettingsPanel class="bd-settingsarray-item-contents" v-if="setting.inline" :settings="item.settings" :change="(c, s, v) => changeInItem(item, c, s, v)" />
@ -28,7 +28,7 @@
<div class="bd-settingsarray-item-controls"> <div class="bd-settingsarray-item-controls">
<span class="bd-settingsarray-open" v-if="typeof setting.allow_external !== 'undefined' ? setting.allow_external || !setting.inline : true" @click="() => showModal(item, index)"><MiOpenInNew v-if="setting.inline" /><MiSettings v-else /></span> <span class="bd-settingsarray-open" v-if="typeof setting.allow_external !== 'undefined' ? setting.allow_external || !setting.inline : true" @click="() => showModal(item, index)"><MiOpenInNew v-if="setting.inline" /><MiSettings v-else /></span>
<span class="bd-settingsarray-remove" :class="{'bd-disabled': setting.disabled || setting.min && items.length <= setting.min}" @click="() => removeItem(item)"><MiMinus /></span> <span class="bd-settingsarray-remove" :class="{'bd-disabled': setting.disabled || setting.min && setting.items.length <= setting.min}" @click="() => removeItem(item)"><MiMinus /></span>
</div> </div>
</div> </div>
</div> </div>
@ -47,29 +47,26 @@
components: { components: {
MiSettings, MiOpenInNew, MiMinus MiSettings, MiOpenInNew, MiMinus
}, },
data() {
return {
items: []
};
},
watch: { watch: {
setting(value) { setting(value) {
// this.setting was changed // this.setting was changed
this.reloadSettings();
} }
}, },
methods: { methods: {
addItem(openModal) { addItem(openModal) {
if (this.setting.disabled || this.setting.max && this.items.length >= this.setting.max) return; if (this.setting.disabled || this.setting.max && this.setting.items.length >= this.setting.max) return;
const item = { settings: this.getItemSettings({}) }; // const item = { settings: this.getItemSettings({}) };
if (openModal) this.showModal(item, this.items.length); const item = this.setting.addItem();
this.items.push(item); if (openModal) this.showModal(item, this.setting.items.length);
this.update(); // this.update();
// this.$forceUpdate();
}, },
removeItem(item) { removeItem(item) {
if (this.setting.disabled || this.setting.min && this.items.length <= this.setting.min) return; if (this.setting.disabled || this.setting.min && this.setting.items.length <= this.setting.min) return;
this.items = this.items.filter(i => i !== item); // this.setting.items = this.setting.items.filter(i => i !== item);
this.update(); this.setting.removeItem(item);
// this.update();
this.$forceUpdate();
}, },
changeInItem(item, category_id, setting_id, value) { changeInItem(item, category_id, setting_id, value) {
console.log('Setting', item, category_id, setting_id, 'to', value); console.log('Setting', item, category_id, setting_id, 'to', value);
@ -82,50 +79,15 @@
setting.value = value; setting.value = value;
setting.changed = !Utils.compare(setting.value, setting.old_value); setting.changed = !Utils.compare(setting.value, setting.old_value);
this.update(); // this.update();
},
update() {
this.change(this.items.map(item => ({
settings: item.settings ? item.settings.map(category => ({
category: category.category,
settings: category.settings.map(setting => ({
id: setting.id,
value: setting.value
}))
})) : []
})));
}, },
showModal(item, index) { showModal(item, index) {
Modals.settings(this.setting.headertext ? this.setting.headertext.replace(/%n/, index + 1) : this.setting.text + ` #${index + 1}`, item.settings, this.setting.schemes, () => this.update()); Modals.settings(item, this.setting.headertext ? this.setting.headertext.replace(/%n/, index + 1) : this.setting.text + ` #${index + 1}`);
},
getItemSettings(item) {
const settings = JSON.parse(JSON.stringify(this.setting.settings));
const newSettings = item.settings || [];
for (let newCategory of newSettings) {
const category = settings.find(c => c.category === newCategory.category);
if (!category) continue;
for (let newSetting of newCategory.settings) {
const setting = category.settings.find(s => s.id === newSetting.id);
if (!setting) continue;
setting.value = setting.old_value = newSetting.value;
setting.changed = false;
}
}
return settings;
},
reloadSettings() {
this.items = JSON.parse(JSON.stringify(this.setting.value)) || [];
this.items = this.items.map(item => ({ settings: this.getItemSettings(item) }));
} }
}, },
beforeCreate() { beforeCreate() {
// https://vuejs.org/v2/guide/components.html#Circular-References-Between-Components // https://vuejs.org/v2/guide/components.html#Circular-References-Between-Components
this.$options.components.SettingsPanel = SettingsPanel; this.$options.components.SettingsPanel = SettingsPanel;
},
beforeMount() {
this.reloadSettings();
} }
} }
</script> </script>

View File

@ -12,7 +12,7 @@
<div class="bd-form-dropdown"> <div class="bd-form-dropdown">
<div class="bd-title"> <div class="bd-title">
<h3 v-if="setting.text">{{setting.text}}</h3> <h3 v-if="setting.text">{{setting.text}}</h3>
<Dropdown v-if="!setting.fullwidth" :options="setting.options" :selected="setting.value" :disabled="setting.disabled" :change="change" /> <Dropdown v-if="!setting.fullwidth" :options="setting.options" :selected="setting.args.value" :disabled="setting.disabled" :change="change" />
</div> </div>
<div class="bd-hint">{{setting.hint}}</div> <div class="bd-hint">{{setting.hint}}</div>
<Dropdown v-if="setting.fullwidth" :options="setting.options" :selected="setting.value" :disabled="setting.disabled" :change="change" /> <Dropdown v-if="setting.fullwidth" :options="setting.options" :selected="setting.value" :disabled="setting.disabled" :change="change" />
@ -25,6 +25,25 @@
props: ['setting', 'change'], props: ['setting', 'change'],
components: { components: {
Dropdown Dropdown
},
data() {
return {
active: false
};
},
methods: {
selectOption(option) {
this.active = false;
this.change(option.id);
}
},
mounted() {
document.addEventListener("click", e => {
let options = this.$refs.options;
if (options && !options.contains(e.target) && options !== e.target) {
this.active = false;
}
});
} }
} }
</script> </script>

View File

@ -123,27 +123,11 @@ export default class {
} }
} }
static settings(headertext, settings, schemes, settingsUpdated, settingUpdated, saveSettings) { static settings(settings, headertext, saveSettings) {
return this.add({ return this.add({
headertext, settings, schemes, headertext, settings, schemes: settings.schemes,
saveSettings: saveSettings ? saveSettings : newSettings => { saveSettings: saveSettings ? saveSettings : newSettings => {
const updatedSettings = []; return settings.merge(newSettings);
for (let newCategory of newSettings) {
let category = settings.find(c => c.category === newCategory.category);
for (let newSetting of newCategory.settings) {
let setting = category.settings.find(s => s.id === newSetting.id);
if (Utils.compare(setting.value, newSetting.value)) continue;
let old_value = setting.value;
setting.value = newSetting.value;
updatedSettings.push({ category_id: category.category, setting_id: setting.id, value: setting.value, old_value });
if (settingUpdated) settingUpdated(category.category, setting.id, setting.value, old_value);
}
}
return settingsUpdated ? settingsUpdated(updatedSettings) : updatedSettings;
} }
}, SettingsModal); }, SettingsModal);
} }
@ -151,11 +135,12 @@ export default class {
static internalSettings(set_id) { static internalSettings(set_id) {
const set = Settings.getSet(set_id); const set = Settings.getSet(set_id);
if (!set) return; if (!set) return;
return this.settings(set.headertext, set.settings, set.schemes, null, null, newSettings => Settings.mergeSettings(set.id, newSettings)); // return this.settings(set, set.headertext, newSettings => Settings.mergeSettings(set.id, newSettings));
return this.settings(set, set.headertext);
} }
static contentSettings(content) { static contentSettings(content) {
return this.settings(content.name + ' Settings', content.config, content.configSchemes, null, null, content.saveSettings.bind(content)); return this.settings(content.settings, content.name + ' Settings');
} }
static get stack() { static get stack() {