Better merging of array setting items

This commit is contained in:
Samuel Elliott 2018-03-06 00:49:10 +00:00
parent da5e0ac99c
commit 72dd93503a
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
6 changed files with 195 additions and 29 deletions

View File

@ -14,6 +14,7 @@ import Setting from './basesetting';
import SettingsSet from '../settingsset';
import SettingsCategory from '../settingscategory';
import SettingsScheme from '../settingsscheme';
import { SettingsUpdatedEvent } from 'structs';
export default class ArraySetting extends Setting {
@ -108,21 +109,27 @@ export default class ArraySetting extends Setting {
* @param {SettingsSet} item Values to merge into the new set (optional)
* @return {SettingsSet} The new set
*/
addItem(item) {
const newItem = this.createItem(item);
this.args.items.push(newItem);
this.updateValue();
return newItem;
async addItem(_item) {
const item = this.createItem(_item);
this.args.items.push(item);
await this.updateValue();
await this.emit('item-added', { item });
return item;
}
/**
* Removes a set from this array setting.
* This ignores the minimum value.
* @param {SettingsSet} item The set to remove
* @return {Promise}
*/
removeItem(item) {
async removeItem(item) {
this.args.items = this.items.filter(i => i !== item);
this.updateValue();
await this.updateValue();
await this.emit('item-removed', { item });
}
/**
@ -135,24 +142,84 @@ export default class ArraySetting extends Setting {
return item;
const set = new SettingsSet({
id: item ? item.args ? item.args.id : item.id : Math.random(),
settings: Utils.deepclone(this.settings),
schemes: this.schemes
}, item ? item.args || item : undefined);
set.setSaved();
set.on('settings-updated', () => this.updateValue());
set.on('settings-updated', async event => {
await this.emit('item-updated', { item: set, event, updatedSettings: event.updatedSettings });
if (event.args.updating_array !== this) await this.updateValue();
});
return set;
}
/**
* Function to be called after the value changes.
* This can be overridden by other settings types.
* This function is used when the value needs to be updated synchronously (basically just in the constructor - so there won't be any events to emit anyway).
* @param {SettingUpdatedEvent} updatedSetting
*/
setValueHookSync(updatedSetting) {
this.args.items = updatedSetting.value ? updatedSetting.value.map(item => this.createItem(item)) : [];
}
/**
* Function to be called after the value changes.
* This can be overridden by other settings types.
* @param {SettingUpdatedEvent} updatedSetting
*/
setValueHook(updatedSetting) {
this.args.items = updatedSetting.value ? updatedSetting.value.map(item => this.createItem(item)) : [];
async setValueHook(updatedSetting) {
const newItems = [];
let error;
for (let newItem of updatedSetting.value) {
try {
const item = this.items.find(i => i.id && i.id === newItem.id);
if (item) {
// Merge the new item into the original item
newItems.push(item);
const updatedSettings = await item.merge(newItem, false);
if (!updatedSettings.length) continue;
const event = new SettingsUpdatedEvent({
updatedSettings,
updating_array: this
});
await item.emit('settings-updated', event);
// await this.emit('item-updated', { item, event, updatedSettings });
} else {
// Add a new item
const item = this.createItem(newItem);
newItems.push(item);
await this.emit('item-added', { item });
}
} catch (e) { error = e; }
}
for (let item of this.items) {
if (newItems.includes(item)) continue;
try {
// Item removed
await this.emit('item-removed', { item });
} catch (e) { error = e; }
}
this.args.items = newItems;
// We can't throw anything before the items array is updated, otherwise the array setting would be in an inconsistent state where the values in this.items wouldn't match the values in this.value
if (error) throw error;
}
// emit(...args) {
// console.log('Emitting event', args[0], 'with data', args[1]);
// return this.emitter.emit(...args);
// }
/**
* Updates the value of this array setting.
* This only exists for use by array settings.

View File

@ -105,9 +105,9 @@ export default class Setting {
* Merges a setting into this setting without emitting events (and therefore synchronously).
* This only exists for use by the constructor and SettingsCategory.
*/
_merge(newSetting) {
_merge(newSetting, hook = true) {
const value = newSetting.args ? newSetting.args.value : newSetting.value;
return this._setValue(value);
return this._setValue(value, hook);
}
/**
@ -116,12 +116,13 @@ export default class Setting {
* @return {Promise}
*/
async merge(newSetting, emit_multi = true, emit = true) {
const updatedSettings = this._merge(newSetting);
const updatedSettings = this._merge(newSetting, false);
if (!updatedSettings.length) return [];
const updatedSetting = updatedSettings[0];
await this.setValueHook(updatedSettings[0]);
if (emit)
await this.emit('setting-updated', updatedSetting);
await this.emit('setting-updated', updatedSettings[0]);
if (emit_multi)
await this.emit('settings-updated', new SettingsUpdatedEvent({
@ -135,7 +136,7 @@ export default class Setting {
* Sets the value of this setting.
* This only exists for use by the constructor and SettingsCategory.
*/
_setValue(value) {
_setValue(value, hook = true) {
const old_value = this.args.value;
if (Utils.compare(value, old_value)) return [];
this.args.value = value;
@ -146,7 +147,8 @@ export default class Setting {
value, old_value
});
this.setValueHook(updatedSetting);
if (hook)
this.setValueHookSync(updatedSetting);
return [updatedSetting];
}
@ -156,7 +158,8 @@ export default class Setting {
* This can be overridden by other settings types.
* @param {SettingUpdatedEvent} updatedSetting
*/
setValueHook(updatedSetting) {}
async setValueHook(updatedSetting) {}
setValueHookSync(updatedSetting) {}
/**
* Sets the value of this setting.
@ -164,9 +167,11 @@ export default class Setting {
* @return {Promise}
*/
async setValue(value, emit_multi = true, emit = true) {
const updatedSettings = this._setValue(value);
const updatedSettings = this._setValue(value, false);
if (!updatedSettings.length) return [];
await this.setValueHook(updatedSettings[0]);
if (emit)
await this.emit('setting-updated', updatedSettings[0]);

View File

@ -48,14 +48,14 @@
MiSettings, MiOpenInNew, MiMinus
},
methods: {
addItem(openModal) {
async addItem(openModal) {
if (this.setting.disabled || this.setting.max && this.setting.items.length >= this.setting.max) return;
const item = this.setting.addItem();
if (openModal) this.showModal(item, this.setting.items.length);
const item = await this.setting.addItem();
if (openModal) this.showModal(item, this.setting.items.length - 1);
},
removeItem(item) {
async removeItem(item) {
if (this.setting.disabled || this.setting.min && this.setting.items.length <= this.setting.min) return;
this.setting.removeItem(item);
await this.setting.removeItem(item);
},
showModal(item, index) {
Modals.settings(item, this.setting.headertext ? this.setting.headertext.replace(/%n/, index + 1) : this.setting.text + ` #${index + 1}`);

View File

@ -0,0 +1,84 @@
{
"info": {
"id": "example-plugin-4",
"name": "Example Plugin 4",
"authors": [
"Samuel Elliott"
],
"version": 1.0,
"description": "Plugin for testing array setting events as the first example plugin has a lot of stuff in it now."
},
"main": "index.js",
"type": "plugin",
"defaultConfig": [
{
"category": "default",
"settings": [
{
"id": "array-1",
"type": "array",
"text": "Test settings array",
"settings": [
{
"category": "default",
"settings": [
{
"id": "default-0",
"type": "bool",
"value": false,
"text": "Bool Test Setting 3",
"hint": "Bool Test Setting Hint 3"
},
{
"id": "default-1",
"type": "text",
"value": "defaultValue",
"text": "Text Test Setting",
"hint": "Text Test Setting Hint"
}
]
}
],
"schemes": [
{
"id": "scheme-1",
"name": "Test scheme",
"icon_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Cow_female_black_white.jpg/220px-Cow_female_black_white.jpg",
"settings": [
{
"category": "default",
"settings": [
{
"id": "default-0",
"value": true
}
]
}
]
},
{
"id": "scheme-2",
"name": "Another test scheme",
"icon_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Cow_female_black_white.jpg/220px-Cow_female_black_white.jpg",
"settings": [
{
"category": "default",
"settings": [
{
"id": "default-0",
"value": false
},
{
"id": "default-1",
"value": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Cow_female_black_white.jpg/220px-Cow_female_black_white.jpg"
}
]
}
]
}
]
}
]
}
]
}

View File

@ -0,0 +1,10 @@
module.exports = (Plugin, { Logger }) => class extends Plugin {
onstart() {
// Some array event examples
const arraySetting = this.settings.getSetting('default', 'array-1');
Logger.log('Array setting', arraySetting);
arraySetting.on('item-added', event => Logger.log('Item', event.item, 'was added to the array setting'));
arraySetting.on('item-updated', event => Logger.log('Item', event.item, 'of the array setting was updated', event));
arraySetting.on('item-removed', event => Logger.log('Item', event.item, 'removed from the array setting'));
}
};

View File

@ -14,7 +14,7 @@ module.exports = (Plugin, Api, Vendor, Dependencies) => {
Events.subscribe('TEST_EVENT', this.eventTest);
Logger.log('onStart');
Logger.log(`Plugin setting "default-0" value: ${this.getSetting('default-0')}`);
Logger.log(`Plugin setting "default-0" value: ${this.settings.get('default-0')}`);
this.events.on('setting-updated', event => {
console.log('Received plugin setting update:', event);
});
@ -92,14 +92,14 @@ module.exports = (Plugin, Api, Vendor, Dependencies) => {
test1() { return 'It works!'; }
test2() { return 'This works too!'; }
settingChanged(category, setting_id, value) {
settingChanged(event) {
if (!this.enabled) return;
Logger.log(`${category}/${setting_id} changed to ${value}`);
Logger.log(`${event.category_id}/${event.setting_id} changed to ${event.value}`);
}
settingsChanged(settings) {
settingsChanged(event) {
if (!this.enabled) return;
Logger.log([ 'Settings updated', settings ]);
Logger.log([ 'Settings updated', event.updatedSettings ]);
}
get settingscomponent() {