From da5e0ac99cdc23a8dc2d93a7ad4111b0c1f348cb Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Tue, 6 Mar 2018 00:47:34 +0000 Subject: [PATCH] Add live updating settings sets and categories --- .../src/structs/settings/settingscategory.js | 89 +++++++++++++-- client/src/structs/settings/settingsset.js | 102 ++++++++++++++++-- client/src/ui/modals.js | 2 +- 3 files changed, 170 insertions(+), 23 deletions(-) diff --git a/client/src/structs/settings/settingscategory.js b/client/src/structs/settings/settingscategory.js index 4ac07453..6db59b37 100644 --- a/client/src/structs/settings/settingscategory.js +++ b/client/src/structs/settings/settingscategory.js @@ -9,9 +9,12 @@ */ import Setting from './setting'; +import BaseSetting from './types/basesetting'; import { ClientLogger as Logger, AsyncEventEmitter } from 'common'; import { SettingUpdatedEvent, SettingsUpdatedEvent } from 'structs'; +let instances = 0; + export default class SettingsCategory { constructor(args, ...merge) { @@ -24,17 +27,10 @@ export default class SettingsCategory { this._merge(newCategory); } + this._eventsKey = instances++; + 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('settings-updated', ({ updatedSettings }) => this.emit('settings-updated', new SettingsUpdatedEvent({ - updatedSettings: updatedSettings.map(updatedSetting => new SettingUpdatedEvent(Object.assign({ - category: this, category_id: this.id - }, updatedSetting))) - }))); + this._bindSettingEvents(setting); } } @@ -83,6 +79,77 @@ export default class SettingsCategory { return false; } + /** + * Binds events to a category. + * This only exists for use by the constructor and settingsset.addCategory. + */ + _bindSettingEvents(setting) { + setting.on('setting-updated', setting[this._eventsKey + '_settingscategory_event_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', setting[this._eventsKey + '_settingscategory_event_settings-updated'] = ({ updatedSettings }) => this.emit('settings-updated', new SettingsUpdatedEvent({ + updatedSettings: updatedSettings.map(updatedSetting => new SettingUpdatedEvent(Object.assign({ + category: this, category_id: this.id + }, updatedSetting))) + }))); + } + + /** + * Dynamically adds a setting to this category. + * @param {Setting} category The setting to add to this category + * @param {Number} index The index to add the setting at (optional) + * @return {Promise} + */ + async addSetting(setting, index) { + if (this.settings.find(s => s === setting)) return; + + if (!(setting instanceof BaseSetting)) + setting = new Setting(setting); + + if (this.getSetting(setting.id)) + throw {message: 'A setting with this ID already exists.'}; + + this._bindSettingEvents(setting); + if (index === undefined) index = this.settings.length; + this.settings.splice(index, 0, setting); + + const event = { + category, category_id: category.id, + setting, setting_id: setting.id, + at_index: index + }; + + await setting.emit('added-to', event); + await this.emit('added-setting', event); + return setting; + } + + /** + * Dynamically removes a setting from this category. + * @param {Setting} setting The setting to remove from this category + * @return {Promise} + */ + async removeSetting(setting) { + setting.off('setting-updated', setting[this._eventsKey + '_settingscategory_event_setting-updated']); + setting.off('settings-updated', setting[this._eventsKey + '_settingscategory_event_settings-updated']); + + let index; + while ((index = this.settings.findIndex(c => c === category)) > -1) { + this.settings.splice(index, 0); + } + + const event = { + set: this, set_id: this.id, + category, category_id: category.id, + from_index: index + }; + + await category.emit('removed-from', event); + await this.emit('removed-category', event); + } + /** * Returns the first setting where calling {function} returns true. * @param {Function} function A function to call to filter settings @@ -107,7 +174,7 @@ export default class SettingsCategory { * @return {Setting} */ getSetting(id) { - return this.findSetting(setting => setting.id === id); + return this.find(setting => setting.id === id); } /** diff --git a/client/src/structs/settings/settingsset.js b/client/src/structs/settings/settingsset.js index e488f6ef..9a7c55f6 100644 --- a/client/src/structs/settings/settingsset.js +++ b/client/src/structs/settings/settingsset.js @@ -14,6 +14,8 @@ import { ClientLogger as Logger, AsyncEventEmitter } from 'common'; import { SettingUpdatedEvent, SettingsUpdatedEvent } from 'structs'; import { Modals } from 'ui'; +let instances = 0; + export default class SettingsSet { constructor(args, ...merge) { @@ -27,18 +29,10 @@ export default class SettingsSet { this._merge(newSet); } + this._eventsKey = instances++; + 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, - setting, setting_id: setting.id, - value, old_value - }))); - category.on('settings-updated', ({ updatedSettings }) => this.emit('settings-updated', new SettingsUpdatedEvent({ - updatedSettings: updatedSettings.map(updatedSetting => new SettingUpdatedEvent(Object.assign({ - set: this, set_id: this.id - }, updatedSetting))) - }))); + this._bindCategoryEvents(category); } } @@ -101,6 +95,92 @@ export default class SettingsSet { return false; } + /** + * Binds events to a category. + * This only exists for use by the constructor and settingsset.addCategory. + */ + _bindCategoryEvents(category) { + category.on('setting-updated', category[this._eventsKey + '_settingsset_event_setting-updated'] = ({ setting, value, old_value }) => this.emit('setting-updated', new SettingUpdatedEvent({ + set: this, set_id: this.id, + category, category_id: category.id, + setting, setting_id: setting.id, + value, old_value + }))); + category.on('settings-updated', category[this._eventsKey + '_settingsset_event_settings-updated'] = ({ updatedSettings }) => this.emit('settings-updated', new SettingsUpdatedEvent({ + updatedSettings: updatedSettings.map(updatedSetting => new SettingUpdatedEvent(Object.assign({ + set: this, set_id: this.id + }, updatedSetting))) + }))); + category.on('added-setting', category[this._eventsKey + '_settingsset_event_added-setting'] = ({ setting, at_index }) => this.emit('added-setting', { + set: this, set_id: this.id, + category, category_id: category.id, + setting, setting_id: setting.id, + at_index + })); + category.on('removed-setting', category[this._eventsKey + '_settingsset_event_removed-setting'] = ({ setting, from_index }) => this.emit('removed-setting', { + set: this, set_id: this.id, + category, category_id: category.id, + setting, setting_id: setting.id, + from_index + })); + } + + /** + * Dynamically adds a category to this set. + * @param {SettingsCategory} category The category to add to this set + * @param {Number} index The index to add the category at (optional) + * @return {Promise} + */ + async addCategory(category, index) { + if (this.categories.find(c => c === category)) return; + + if (!(category instanceof SettingsCategory)) + category = new SettingsCategory(category); + + if (this.getCategory(category.id)) + throw {message: 'A category with this ID already exists.'}; + + this._bindCategoryEvents(category); + if (index === undefined) index = this.categories.length; + this.categories.splice(index, 0, category); + + const event = { + set: this, set_id: this.id, + category, category_id: category.id, + at_index: index + }; + + await category.emit('added-to', event); + await this.emit('added-category', event); + return category; + } + + /** + * Dynamically removes a category from this set. + * @param {SettingsCategory} category The category to remove from this set + * @return {Promise} + */ + async removeCategory(category) { + category.off('setting-updated', category[this._eventsKey + '_settingsset_event_setting-updated']); + category.off('settings-updated', category[this._eventsKey + '_settingsset_event_settings-updated']); + category.off('added-setting', category[this._eventsKey + '_settingsset_event_added-setting']); + category.off('removed-setting', category[this._eventsKey + '_settingsset_event_removed-setting']); + + let index; + while ((index = this.categories.findIndex(c => c === category)) > -1) { + this.categories.splice(index, 0); + } + + const event = { + set: this, set_id: this.id, + category, category_id: category.id, + from_index: index + }; + + await category.emit('removed-from', event); + await this.emit('removed-category', event); + } + /** * Returns the first category where calling {function} returns true. * @param {Function} function A function to call to filter categories diff --git a/client/src/ui/modals.js b/client/src/ui/modals.js index 5166ebdb..ce2e6a98 100644 --- a/client/src/ui/modals.js +++ b/client/src/ui/modals.js @@ -8,7 +8,7 @@ * LICENSE file in the root directory of this source tree. */ -import { Utils, FileUtils, AsyncEventEmitter } from 'common'; +import { Utils, FileUtils, ClientLogger as Logger, AsyncEventEmitter } from 'common'; import { Settings, Events, PluginManager, ThemeManager } from 'modules'; import BaseModal from './components/common/Modal.vue'; import BasicModal from './components/bd/modals/BasicModal.vue';