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.
+*/
+
+
+ {{ setting.text }}
+