Add generic settings modal and recursive compare

This commit is contained in:
Samuel Elliott 2018-02-14 01:40:47 +00:00
parent 6b96bdbc3b
commit 4b9084bee4
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
7 changed files with 208 additions and 10 deletions

View File

@ -8,7 +8,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { FileUtils } from 'common';
import { Utils, FileUtils } from 'common';
import { Modals } from 'ui';
export default class Plugin {
@ -54,13 +54,18 @@ export default class Plugin {
}
async saveSettings(newSettings) {
for (let category of newSettings) {
const oldCategory = this.pluginConfig.find(c => c.category === category.category);
for (let setting of category.settings) {
const oldSetting = oldCategory.settings.find(s => s.id === setting.id);
if (oldSetting.value === setting.value) continue;
oldSetting.value = setting.value;
if (this.settingChanged) this.settingChanged(category.category, setting.id, setting.value);
const updatedSettings = [];
for (let newCategory of newSettings) {
const category = this.pluginConfig.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;
let 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);
}
}

View File

@ -5,3 +5,4 @@
@import './basic-modal.scss';
@import './error-modal.scss';
@import './settings-modal.scss';

View File

@ -0,0 +1,32 @@
.bd-settings-modal {
.bd-modal .bd-modal-body {
padding: 0;
}
.bd-modal .bd-modal-footer {
.bd-ok {
width: 100px;
}
}
.bd-settings-modal-body {
padding: 0 15px;
margin: 0 0 74px;
.bd-switch-wrapper {
width: 40px;
height: 20px;
.bd-switch::before {
width: 14px;
height: 14px;
}
}
}
&.bd-edited {
.bd-scroller::-webkit-scrollbar-track {
margin-bottom: 74px;
}
}
}

View File

@ -38,7 +38,6 @@
};
},
created() {
console.log(this);
Events.on('bd-refresh-modals', this.eventListener = () => {
this.$forceUpdate();
});

View File

@ -0,0 +1,114 @@
/**
* BetterDiscord Settings Modal Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
<template>
<div class="bd-settings-modal" :class="{'bd-edited': changed}">
<Modal :headerText="modal.headertext" :close="attemptToClose" :class="{'bd-modal-out': modal.closing}">
<SettingsPanel :settings="configCache" :change="settingChange" slot="body" class="bd-settings-modal-body" />
<div slot="footer" class="bd-footer-alert" :class="{'bd-active': changed, 'bd-warn': warnclose}">
<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-ok" :class="{'bd-disabled': saving}" @click="saveSettings">
<div v-if="saving" class="bd-spinner-7"></div>
<template v-else>Save Changes</template>
</div>
</div>
</Modal>
</div>
</template>
<script>
// Imports
import Vue from 'vue';
import { Modal } from '../../common';
import SettingsPanel from '../SettingsPanel.vue';
import { Utils } from 'common';
export default {
props: ['modal'],
data() {
return {
changed: false,
warnclose: false,
configCache: [],
closing: false,
saving: false
}
},
components: {
Modal,
SettingsPanel
},
methods: {
checkForChanges() {
let changed = false;
for (let category of this.configCache) {
const cat = this.modal.settings.find(c => c.category === category.category);
for (let setting of category.settings) {
if (!Utils.compare(cat.settings.find(s => s.id === setting.id).value, setting.value)) {
changed = true;
Vue.set(setting, 'changed', true);
} else {
Vue.set(setting, 'changed', false);
}
}
}
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() {
if (this.saving) return;
this.saving = true;
try {
await this.modal.saveSettings(this.configCache);
this.configCache = JSON.parse(JSON.stringify(this.modal.settings));
this.changed = false;
} catch (err) {
// TODO Display error that settings failed to save
console.log(err);
}
this.saving = false;
},
resetSettings() {
if (this.saving) return;
this.configCache = JSON.parse(JSON.stringify(this.modal.settings));
this.changed = false;
this.$forceUpdate();
},
attemptToClose(e) {
if (!this.changed) {
this.closing = true;
setTimeout(() => {
this.modal.close();
}, 200);
return;
}
this.warnclose = true;
setTimeout(() => {
this.warnclose = false;
}, 400);
}
},
beforeMount() {
this.configCache = JSON.parse(JSON.stringify(this.modal.settings));
this.changed = this.checkForChanges();
}
}
</script>

View File

@ -8,12 +8,13 @@
* LICENSE file in the root directory of this source tree.
*/
import { FileUtils } from 'common';
import { Utils, FileUtils } from 'common';
import { Events, PluginManager, ThemeManager } from 'modules';
import BasicModal from './components/bd/modals/BasicModal.vue';
import ErrorModal from './components/bd/modals/ErrorModal.vue';
import PluginSettingsModal from './components/bd/modals/PluginSettingsModal.vue';
import ThemeSettingsModal from './components/bd/modals/ThemeSettingsModal.vue';
import SettingsModal from './components/bd/modals/SettingsModal.vue';
export default class {
@ -78,6 +79,31 @@ export default class {
});
}
static settings(headertext, settings, settingsUpdated, settingUpdated, saveSettings) {
return this.add({
headertext, settings,
saveSettings: saveSettings ? saveSettings : newSettings => {
const updatedSettings = [];
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);
}
static pluginSettings(plugin) {
return this.add({ plugin }, PluginSettingsModal);
}

View File

@ -45,6 +45,27 @@ export class Utils {
});
return camelCased;
}
static compare(value1, value2) {
// Check to see if value1 and value2 contain the same data
if (typeof value1 !== typeof value2) return false;
if (value1 === null && value2 === null) return true;
if (value1 === null || value2 === null) return false;
if (typeof value1 === 'object' || typeof value1 === 'array') {
// Loop through the object and check if everything's the same
let value1array = typeof value1 === 'array' ? value1 : Object.keys(value1);
let value2array = typeof value2 === 'array' ? value2 : Object.keys(value2);
if (value1array.length !== value2array.length) return false;
for (let key in value1) {
if (!this.compare(value1[key], value2[key])) return false;
}
} else if (value1 !== value2) return false;
// value1 and value2 contain the same data
return true;
}
}
export class FileUtils {