From 086b6253bc23c25e9762e91f6352fb03d0e3849d Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Fri, 16 Mar 2018 00:54:50 +0000 Subject: [PATCH] Add guild selection setting and developer mode option --- client/src/data/user.settings.default.json | 7 +- client/src/index.js | 65 ++++---- client/src/structs/settings/setting.js | 2 + client/src/structs/settings/types/array.js | 5 - client/src/structs/settings/types/guild.js | 151 ++++++++++++++++++ .../styles/partials/generic/forms/guilds.scss | 51 ++++++ .../styles/partials/generic/forms/index.scss | 3 +- .../styles/partials/generic/forms/main.scss | 1 + client/src/ui/components/bd/setting/Guild.vue | 85 ++++++++++ .../src/ui/components/bd/setting/Setting.vue | 3 + common/modules/utils.js | 13 ++ tests/plugins/Example 4/config.json | 9 ++ 12 files changed, 353 insertions(+), 42 deletions(-) create mode 100644 client/src/structs/settings/types/guild.js create mode 100644 client/src/styles/partials/generic/forms/guilds.scss create mode 100644 client/src/ui/components/bd/setting/Guild.vue diff --git a/client/src/data/user.settings.default.json b/client/src/data/user.settings.default.json index be4d3676..49efdfdc 100644 --- a/client/src/data/user.settings.default.json +++ b/client/src/data/user.settings.default.json @@ -47,10 +47,9 @@ { "id": "developer-mode", "type": "bool", - "text": "Developer Mode", - "hint": "BetterDiscord developer mode", - "value": false, - "disabled": true + "text": "Developer mode", + "hint": "Adds some of BetterDiscord's internal modules to `global._bd`.", + "value": false }, { "id": "ignore-content-manager-errors", diff --git a/client/src/index.js b/client/src/index.js index 63b9e68e..7c178387 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -19,33 +19,37 @@ const DEV = true; class BetterDiscord { constructor() { - window.BDDEVMODE = function () { - if (!DEV) return null; - return window._bd = { - DOM, - BdUI, - Modals, - Reflection, - Patcher, - Vendor, - Events, - CssEditor, - Globals, - ExtModuleManager, - PluginManager, - ThemeManager, - ModuleManager, - WebpackModules, - Settings, - Database, - ReactComponents, - DiscordApi, - Logger, - ClientIPC, - Utils, - EmoteModule - } - } + this._bd = { + DOM, + BdUI, + Modals, + Reflection, + Patcher, + Vendor, + Events, + CssEditor, + Globals, + ExtModuleManager, + PluginManager, + ThemeManager, + ModuleManager, + WebpackModules, + Settings, + Database, + ReactComponents, + DiscordApi, + Logger, + ClientIPC, + Utils, + EmoteModule + }; + + const developermode = Settings.getSetting('core', 'advanced', 'developer-mode'); + if (developermode.value) window._bd = this._bd; + developermode.on('setting-updated', event => { + if (event.value) window._bd = this._bd; + else if (window._bd) delete window._bd; + }); DOM.injectStyle(BdCss, 'bdmain'); this.globalReady = this.globalReady.bind(this); @@ -79,16 +83,13 @@ class BetterDiscord { this.vueInstance = BdUI.injectUi(); this.init(); } + } if (window.BetterDiscord) { Logger.log('main', 'Attempting to inject again?'); } else { let instance = null; - // eslint-disable-next-line no-inner-declarations - function init() { - instance = new BetterDiscord(); - } - Events.on('autopatcher', init); + Events.on('autopatcher', () => instance = new BetterDiscord()); ReactAutoPatcher.autoPatch().then(() => Events.emit('autopatcher')); } diff --git a/client/src/structs/settings/setting.js b/client/src/structs/settings/setting.js index 7785e03b..de2ffcc4 100644 --- a/client/src/structs/settings/setting.js +++ b/client/src/structs/settings/setting.js @@ -19,6 +19,7 @@ import SliderSetting from './types/slider'; import ColourSetting from './types/colour'; import KeybindSetting from './types/keybind'; import FileSetting from './types/file'; +import GuildSetting from './types/guild'; import ArraySetting from './types/array'; import CustomSetting from './types/custom'; @@ -38,6 +39,7 @@ export default class Setting { else if (args.type === 'colour') return new ColourSetting(args, ...merge); else if (args.type === 'keybind') return new KeybindSetting(args, ...merge); else if (args.type === 'file') return new FileSetting(args, ...merge); + else if (args.type === 'guild') return new GuildSetting(args, ...merge); else if (args.type === 'array') return new ArraySetting(args, ...merge); else if (args.type === 'custom') return new CustomSetting(args, ...merge); else throw {message: `Setting type ${args.type} unknown`}; diff --git a/client/src/structs/settings/types/array.js b/client/src/structs/settings/types/array.js index 6b128864..11fcd6b4 100644 --- a/client/src/structs/settings/types/array.js +++ b/client/src/structs/settings/types/array.js @@ -215,11 +215,6 @@ export default class ArraySetting extends Setting { 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. diff --git a/client/src/structs/settings/types/guild.js b/client/src/structs/settings/types/guild.js new file mode 100644 index 00000000..0bfce9e4 --- /dev/null +++ b/client/src/structs/settings/types/guild.js @@ -0,0 +1,151 @@ +/** + * BetterDiscord Guild Setting Struct + * 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. + */ + +import { DiscordApi } from 'modules'; +import { Utils, ClientLogger as Logger } from 'common'; +import Setting from './basesetting'; + +export default class GuildSetting extends Setting { + + constructor(args, ...merge) { + super(args, ...merge); + + this.args.guilds = this.value ? this.value.map(guild_id => DiscordApi.guilds.get({ id: guild_id })) : []; + } + + /** + * The value to use when the setting doesn't have a value. + */ + get defaultValue() { + return []; + } + + /** + * An array of currently selected guilds. + */ + get guilds() { + return this.args.guilds; + } + + /** + * The minimum amount of guilds the user may select. + * This only restricts removing guilds when there is less or equal guilds than this, and does not ensure that this number of guilds actually exists. + */ + get min() { + return this.args.min || 0; + } + + /** + * The maximum amount of guilds the user may select. + */ + get max() { + return this.args.max || null; + } + + /** + * Adds a guild to the list of selected guilds. + * @param {Number} guild_id The ID of the guild to add + * @return {Promise} + */ + addGuild(guild_id) { + return this.setValue(this.value.concat([guild_id])); + + // if (this.guilds.find(g => g && g.id === guild_id)) return; + // const guild = DiscordApi.guilds.get({ id: guild_id }); + // this.guilds.push(guild); + // await this.emit('guild-added', guild); + // return await this.setValue(this.value.concat([guild_id])); + } + + /** + * Removes a guild from the list of selected guilds. + * @param {Number} guild_id The ID of the guild to remove + * @return {Promise} + */ + removeGuild(guild_id) { + return this.setValue(this.value.filter(g => g !== guild_id)); + + // const guild = this.guilds.find(g => g && g.id === guild_id); + // const value = Utils.removeFromArray(this.value.slice(0), guild_id); + // if (guild) { + // Utils.filterArray(this.guilds, g => !g || g.id !== guild_id); + // await this.emit('guild-removed', guild); + // } + // return await this.setValue(value); + } + + /** + * 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.guilds = updatedSetting.value ? updatedSetting.value.map(guild_id => DiscordApi.guilds.get({ id: guild_id })) : []; + } + + /** + * Function to be called after the value changes. + * This can be overridden by other settings types. + * @param {SettingUpdatedEvent} updatedSetting + */ + async setValueHook(updatedSetting) { + const newGuilds = []; + let error; + + for (let newGuild of updatedSetting.value) { + try { + const guild = updatedSetting.old_value.find(g => g === newGuild); + + if (guild) { + // Guild was already selected + newGuilds.push(guild); + } else { + // Add a new guild + Logger.log('GuildSetting', ['Adding guild', newGuild, 'to', this]); + newGuilds.push(newGuild); + await this.emit('guild-added', { id: newGuild }); + } + } catch (e) { error = e; } + } + + for (let guild_id of updatedSetting.old_value) { + if (newGuilds.find(g => g === guild_id)) continue; + + try { + // Guild removed + Logger.log('GuildSetting', ['Removing guild', guild_id, 'from', this]); + await this.emit('guild-removed', { id: guild_id }); + } catch (e) { error = e; } + } + + this.args.guilds = newGuilds.map(id => DiscordApi.guilds.get({ id })); + + // We can't throw anything before the guilds array is updated, otherwise the guild setting would be in an inconsistent state where the values in this.guilds wouldn't match the values in this.value + if (error) throw error; + } + + /** + * Returns a representation of this setting's value in SCSS. + * @return {String|Promise} + */ + async toSCSS() { + if (!this.value || !this.value.length) return '()'; + + const guilds = []; + for (let guild_id of this.value) { + if (guild_id) + guilds.push(guild_id); + } + + return guilds.length ? guilds.join(', ') : '()'; + } + +} diff --git a/client/src/styles/partials/generic/forms/guilds.scss b/client/src/styles/partials/generic/forms/guilds.scss new file mode 100644 index 00000000..89332163 --- /dev/null +++ b/client/src/styles/partials/generic/forms/guilds.scss @@ -0,0 +1,51 @@ +.bd-form-guildinput { + +} + +.bd-guilds { + // margin: 15px 0; + + .bd-guild { + $size: 36px; + + display: inline-block; + width: $size; + height: $size; + background-color: rgb(47, 49, 54); + background-size: cover; + margin: 10px 10px 0 0; + border-radius: 50%; + cursor: pointer; + transition: border-radius .4s cubic-bezier(0.5, 0, 0.27, 1.55), background-color .4s cubic-bezier(0.5, 0, 0.27, 1.55), box-shadow .4s cubic-bezier(0.5, 0, 0.27, 1.55); + + .bd-guild-text { + float: left; + display: block; + width: $size; + height: $size; + text-align: center; + vertical-align: middle; + line-height: $size; + font-size: 15px; + } + + &:hover { + border-radius: 30%; + background-color: $colbdblue; + + &.bd-guild-has-icon { + background-color: rgb(47, 49, 54); + // box-shadow: 0 0 1px #000000; + } + } + + &.bd-active { + background-color: $colbdblue; + + &.bd-guild-has-icon { + background-color: rgb(47, 49, 54); + box-shadow: 0 0 7px $colbdblue; + } + } + } +} diff --git a/client/src/styles/partials/generic/forms/index.scss b/client/src/styles/partials/generic/forms/index.scss index e6cef62f..a8334930 100644 --- a/client/src/styles/partials/generic/forms/index.scss +++ b/client/src/styles/partials/generic/forms/index.scss @@ -1,10 +1,11 @@ @import './main.scss'; @import './switches.scss'; @import './text.scss'; -@import './files.scss'; @import './dropdowns.scss'; @import './radios.scss'; @import './sliders.scss'; @import './colourpickers.scss'; @import './keybinds.scss'; +@import './files.scss'; +@import './guilds.scss'; @import './arrays.scss'; diff --git a/client/src/styles/partials/generic/forms/main.scss b/client/src/styles/partials/generic/forms/main.scss index 470e6379..10c63018 100644 --- a/client/src/styles/partials/generic/forms/main.scss +++ b/client/src/styles/partials/generic/forms/main.scss @@ -8,6 +8,7 @@ .bd-form-colourpicker, .bd-form-keybind, .bd-form-fileinput, +.bd-form-guildinput, .bd-form-settingsarray { .bd-title { display: flex; diff --git a/client/src/ui/components/bd/setting/Guild.vue b/client/src/ui/components/bd/setting/Guild.vue new file mode 100644 index 00000000..8f65576f --- /dev/null +++ b/client/src/ui/components/bd/setting/Guild.vue @@ -0,0 +1,85 @@ +/** + * BetterDiscord Setting Guild 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. +*/ + + + + diff --git a/client/src/ui/components/bd/setting/Setting.vue b/client/src/ui/components/bd/setting/Setting.vue index 147060c0..cc1250c5 100644 --- a/client/src/ui/components/bd/setting/Setting.vue +++ b/client/src/ui/components/bd/setting/Setting.vue @@ -20,6 +20,7 @@ +
@@ -37,6 +38,7 @@ import ColourSetting from './Colour.vue'; import KeybindSetting from './Keybind.vue'; import FileSetting from './File.vue'; + import GuildSetting from './Guild.vue'; import ArraySetting from './Array.vue'; import CustomSetting from './Custom.vue'; @@ -55,6 +57,7 @@ ColourSetting, KeybindSetting, FileSetting, + GuildSetting, ArraySetting, CustomSetting }, diff --git a/common/modules/utils.js b/common/modules/utils.js index 99aecfa2..b31f72c6 100644 --- a/common/modules/utils.js +++ b/common/modules/utils.js @@ -162,6 +162,19 @@ export class Utils { return object; } + static filterArray(array, filter) { + const indexes = []; + for (let index in array) { + if (!filter(array[index], index)) + indexes.push(index); + } + + for (let i in indexes) + array.splice(indexes[i] - i, 1); + + return array; + } + static removeFromArray(array, item) { let index; while ((index = array.indexOf(item)) > -1) diff --git a/tests/plugins/Example 4/config.json b/tests/plugins/Example 4/config.json index 1c06afe2..3f26c3aa 100644 --- a/tests/plugins/Example 4/config.json +++ b/tests/plugins/Example 4/config.json @@ -84,6 +84,15 @@ "value": "mod+.", "text": "Test Keybind Setting 1", "hint": "Test Keybind Setting Hint 1" + }, + { + "id": "guild-1", + "type": "guild", + "value": [ + "280806472928198656" + ], + "text": "Test guild setting", + "hint": "Test guild setting hint" } ] }