/* BetterDiscordApp Core JavaScript * Version: 1.78 * Author: Jiiks | http://jiiks.net * Date: 27/08/2015 - 16:36 * Last Update: 01/05/2016 * https://github.com/Jiiks/BetterDiscordApp */ /* Localstorage fix */ (function() { let __fs = window.require("fs"); let __process = window.require("process"); let __platform = __process.platform; let __dataPath = (__platform === "win32" ? __process.env.APPDATA : __platform === "darwin" ? __process.env.HOME + "/Library/Preferences" : process.env.HOME + "/.config") + "/BetterDiscord/"; let localStorageFile = "localStorage.json"; let __data = {}; if (__fs.existsSync(`${__dataPath}${localStorageFile}`)) { try { __data = JSON.parse(__fs.readFileSync(`${__dataPath}${localStorageFile}`)); } catch (err) { console.log(err); } } else if (__fs.existsSync(localStorageFile)) { try { __data = JSON.parse(__fs.readFileSync(localStorageFile)); } catch (err) { console.log(err); } } var __ls = __data; __ls.setItem = function(i, v) { __ls[i] = v; this.save(); }; __ls.getItem = function(i) { return __ls[i] || null; }; __ls.save = function() { __fs.writeFileSync(`${__dataPath}${localStorageFile}`, JSON.stringify(this), null, 4); }; var __proxy = new Proxy(__ls, { set: function(target, name, val) { __ls[name] = val; __ls.save(); }, get: function(target, name) { return __ls[name] || null; } }); window.localStorage = __proxy; })(); (() => { let v2Loader = document.createElement("div"); v2Loader.className = "bd-loaderv2"; v2Loader.title = "BandagedBD is loading..."; document.body.appendChild(v2Loader); })(); /* global DiscordNative:false */ var DataStore = (() => { const fs = require("fs"); const path = require("path"); const releaseChannel = DiscordNative.globals.releaseChannel; return new class DataStore { constructor() { this.data = {settings: {stable: {}, canary: {}, ptb: {}}}; this.pluginData = {}; } initialize() { try { if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4)); else this.data = require(this.BDFile); if (!fs.existsSync(this.settingsFile)) return; let settings = require(this.settingsFile); fs.unlinkSync(this.settingsFile); if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings}); else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings); this.setBDData("settings", settings); } catch (err) { BdApi.alert("Corrupt Storage", "The bd storage has somehow become corrupt. You may either try to salvage the file or delete it then reload."); } } get BDFile() {return this._BDFile || (this._BDFile = path.resolve(bdConfig.dataPath, "bdstorage.json"));} get settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(bdConfig.dataPath, "bdsettings.json"));} getPluginFile(pluginName) {return path.resolve(ContentManager.pluginsFolder, pluginName + ".config.json");} getSettingGroup(key) { return this.data.settings[releaseChannel][key] || null; } setSettingGroup(key, data) { this.data.settings[releaseChannel][key] = data; fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4)); } getBDData(key) { return this.data[key] || ""; } setBDData(key, value) { this.data[key] = value; fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4)); } getPluginData(pluginName, key) { if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key] || undefined; if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined; this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName))); return this.pluginData[pluginName][key] || undefined; } setPluginData(pluginName, key, value) { if (value === undefined) return; if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {}; this.pluginData[pluginName][key] = value; fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4)); } deletePluginData(pluginName, key) { if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {}; delete this.pluginData[pluginName][key]; fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4)); } }; })(); var BDEvents = (() => { const EventEmitter = require("events"); return new class BDEvents extends EventEmitter { dispatch(eventName, ...args) {this.emit(eventName, ...args);} off(eventName, eventAction) {this.removeListener(eventName, eventAction);} }; })(); window.bdStorage = class bdPluginStorage { static get(key) { Utils.warn("[Deprecation Notice] Please use BdApi.getBDData(). bdStorage may be removed in future versions."); return DataStore.getBDData(key); } static set(key, data) { Utils.warn("[Deprecation Notice] Please use BdApi.setBDData(). bdStorage may be removed in future versions."); DataStore.setBDData(key, data); } }; window.bdPluginStorage = class bdPluginStorage { static get(pluginName, key) { Utils.warn(`[Deprecation Notice] Please use BdApi.loadData() or BdApi.getData(). bdPluginStorage may be removed in future versions.`); return DataStore.getPluginData(pluginName, key) || null; } static set(pluginName, key, data) { Utils.warn("[Deprecation Notice] Please use BdApi.saveData() or BdApi.setData(). bdPluginStorage may be removed in future versions."); if (typeof(data) === "undefined") return Utils.warn("Trying to set undefined value in plugin " + pluginName); DataStore.setPluginData(pluginName, key, data); } static delete(pluginName, key) { Utils.warn("[Deprecation Notice] Please use BdApi.deleteData(). bdPluginStorage may be removed in future versions."); DataStore.deletePluginData(pluginName, key); } }; var settingsPanel, emoteModule, quickEmoteMenu, voiceMode, pluginModule, themeModule, dMode, publicServersModule; var minSupportedVersion = "0.3.0"; var bbdVersion = "0.2.3"; var mainCore; var settings = { "Save logs locally": {id: "bda-gs-0", info: "Saves chat logs locally", implemented: false, hidden: false, cat: "core"}, "Public Servers": {id: "bda-gs-1", info: "Display public servers button", implemented: true, hidden: false, cat: "core"}, "Minimal Mode": {id: "bda-gs-2", info: "Hide elements and reduce the size of elements.", implemented: true, hidden: false, cat: "core"}, "Voice Mode": {id: "bda-gs-4", info: "Only show voice chat", implemented: true, hidden: false, cat: "core"}, "Hide Channels": {id: "bda-gs-3", info: "Hide channels in minimal mode", implemented: true, hidden: false, cat: "core"}, "Dark Mode": {id: "bda-gs-5", info: "Make certain elements dark by default(wip)", implemented: true, hidden: false, cat: "core"}, "Override Default Emotes": {id: "bda-es-5", info: "Override default emotes", implemented: false, hidden: false, cat: "core"}, "Voice Disconnect": {id: "bda-dc-0", info: "Disconnect from voice server when closing Discord", implemented: true, hidden: false, cat: "core"}, "Custom css live update": {id: "bda-css-0", info: "", implemented: true, hidden: true, cat: "core"}, "Custom css auto udpate": {id: "bda-css-1", info: "", implemented: true, hidden: true, cat: "core"}, "24 Hour Timestamps": {id: "bda-gs-6", info: "Replace 12hr timestamps with proper ones", implemented: true, hidden: false, cat: "core"}, "Coloured Text": {id: "bda-gs-7", info: "Make text colour the same as role colour", implemented: true, hidden: false, cat: "core"}, "BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: true, hidden: false, cat: "core"}, "Developer Mode": {id: "bda-gs-8", info: "Developer Mode", implemented: true, hidden: false, cat: "core"}, "Content Error Modal": {id: "fork-ps-1", info: "Shows a modal with plugin/theme errors", implemented: true, hidden: false, cat: "fork"}, "Show Toasts": {id: "fork-ps-2", info: "Shows a small notification for important information", implemented: true, hidden: false, cat: "fork"}, "Scroll To Settings": {id: "fork-ps-3", info: "Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)", implemented: true, hidden: false, cat: "fork"}, "Animate On Hover": {id: "fork-es-2", info: "Only animate the emote modifiers on hover", implemented: true, hidden: false, cat: "fork"}, "Copy Selector": {id: "fork-dm-1", info: "Adds a \"Copy Selector\" option to context menus when developer mode is active", implemented: true, hidden: false, cat: "fork"}, "Download Emotes": {id: "fork-es-3", info: "Download emotes when the cache is expired", implemented: true, hidden: false, cat: "fork"}, "Normalize Classes": {id: "fork-ps-4", info: "Adds stable classes to elements to help themes. (e.g. adds .da-channels to .channels-Ie2l6A)", implemented: true, hidden: false, cat: "fork"}, "Automatic Loading": {id: "fork-ps-5", info: "Automatically loads, reloads, and unloads plugins and themes", implemented: true, hidden: false, cat: "fork"}, "Enable Transparency": {id: "fork-wp-1", info: "Enables the main window to be see-through (requires restart)", implemented: true, hidden: false, cat: "fork"}, "Window Frame": {id: "fork-wp-2", info: "Adds the native os window frame to the main window", implemented: false, hidden: true, cat: "fork"}, "Twitch Emotes": {id: "bda-es-7", info: "Show Twitch emotes", implemented: true, hidden: false, cat: "emote"}, "FrankerFaceZ Emotes": {id: "bda-es-1", info: "Show FrankerFaceZ Emotes", implemented: true, hidden: false, cat: "emote"}, "BetterTTV Emotes": {id: "bda-es-2", info: "Show BetterTTV Emotes", implemented: true, hidden: false, cat: "emote"}, "Emote Menu": {id: "bda-es-0", info: "Show Twitch/Favourite emotes in emote menu", implemented: true, hidden: false, cat: "emote"}, "Emoji Menu": {id: "bda-es-9", info: "Show Discord emoji menu", implemented: true, hidden: false, cat: "emote"}, "Emote Autocomplete": {id: "bda-es-3", info: "Autocomplete emote commands", implemented: false, hidden: false, cat: "emote"}, "Emote Auto Capitalization": {id: "bda-es-4", info: "Autocapitalize emote commands", implemented: true, hidden: false, cat: "emote"}, "Show Names": {id: "bda-es-6", info: "Show emote names on hover", implemented: true, hidden: false, cat: "emote"}, "Show emote modifiers": {id: "bda-es-8", info: "Enable emote mods (flip, spin, pulse, spin2, spin3, 1spin, 2spin, 3spin, tr, bl, br, shake, shake2, shake3, flap)", implemented: true, hidden: false, cat: "emote"}, }; var defaultCookie = { "bda-gs-0": false, "bda-gs-1": true, "bda-gs-2": false, "bda-gs-3": false, "bda-gs-4": false, "bda-gs-5": true, "bda-gs-6": false, "bda-gs-7": false, "bda-gs-8": false, "bda-es-0": true, "bda-es-1": true, "bda-es-2": true, "bda-es-3": false, "bda-es-4": false, "bda-es-5": true, "bda-es-6": true, "bda-es-7": true, "bda-gs-b": false, "bda-es-8": true, "bda-dc-0": false, "bda-css-0": false, "bda-css-1": false, "bda-es-9": true, "fork-dm-1": false, "fork-ps-1": true, "fork-ps-2": true, "fork-ps-3": true, "fork-ps-4": true, "fork-ps-5": true, "fork-es-2": false, "fork-es-3": true, "fork-wp-1": false, "fork-wp-2": false }; var settingsCookie = {}; var bdpluginErrors = [], bdthemeErrors = []; // define for backwards compatibility var bdConfig = null; function Core(config) { window.bdConfig = config; } var classNormalizer; Core.prototype.init = async function() { if (bdConfig.version < minSupportedVersion) { this.alert("Not Supported", "BetterDiscord v" + bdConfig.version + " (your version)" + " is not supported by the latest js (" + bbdVersion + ").

Please download the latest version from GitHub"); return; } if (bdConfig.updater.LatestVersion > bdConfig.version) { this.alert("Update Available", ` An update for BandagedBD is available (${bdConfig.updater.LatestVersion})! Please Reinstall!

Download Installer `); } Utils.log("Initializing Settings"); this.initSettings(); classNormalizer = new ClassNormalizer(); emoteModule = new EmoteModule(); quickEmoteMenu = new QuickEmoteMenu(); Utils.log("Initializing EmoteModule"); window.emotePromise = emoteModule.init().then(() => { emoteModule.initialized = true; Utils.log("Initializing QuickEmoteMenu"); quickEmoteMenu.init(); }); publicServersModule = new V2_PublicServers(); voiceMode = new VoiceMode(); dMode = new devMode(); this.injectExternals(); await this.checkForGuilds(); Utils.log("Updating Settings"); settingsPanel = new V2_SettingsPanel(); settingsPanel.updateSettings(); Utils.log("Loading Plugins"); pluginModule = new PluginModule(); pluginModule.loadPlugins(); Utils.log("Loading Themes"); themeModule = new ThemeModule(); themeModule.loadThemes(); $("#customcss").detach().appendTo(document.head); window.addEventListener("beforeunload", function() { if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click(); }); publicServersModule.initialize(); emoteModule.autoCapitalize(); Utils.log("Removing Loading Icon"); document.getElementsByClassName("bd-loaderv2")[0].remove(); Utils.log("Initializing Main Observer"); this.initObserver(); // Show loading errors if (settingsCookie["fork-ps-1"]) { Utils.log("Collecting Startup Errors"); this.showContentErrors({plugins: bdpluginErrors, themes: bdthemeErrors}); } if (!DataStore.getBDData("RNMAnnouncement")) { DataStore.setBDData("RNMAnnouncement", true); this.alert("Significant Changes", ` The lastest release of BBD has made a lot of improvements including being able to automatically load, unload, and reload plugins and themes.

If you had the RestartNoMore plugin, I suggest removing it (or turning off BBD's loader in settings) so things aren't being loaded multiple times. `); } }; Core.prototype.checkForGuilds = function() { return new Promise(resolve => { const checkForGuilds = function() { if (document.querySelectorAll(`.${BDV2.guildClasses.guilds} .${BDV2.guildClasses.guild}`).length > 0) return resolve(bdConfig.deferLoaded = true); setTimeout(checkForGuilds, 100); }; $(document).ready(function () { setTimeout(checkForGuilds, 100); }); }); }; Core.prototype.injectExternals = async function() { await Utils.injectJs("https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js"); if (require.original) window.require = require.original; }; Core.prototype.initSettings = function () { DataStore.initialize(); if (!DataStore.getSettingGroup("settings")) { settingsCookie = defaultCookie; this.saveSettings(); } else { this.loadSettings(); $(""); } $("#customcss").html(this.editor.session.getValue()).detach().appendTo(document.head); } saveCss() { DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue())); } } class V2C_CssEditor extends BDV2.reactComponent { constructor(props) { super(props); let self = this; self.props.lines = 0; self.setInitialState(); self.attach = self.attach.bind(self); self.detachedEditor = BDV2.react.createElement(V2C_CssEditorDetached, {attach: self.attach}); self.onClick = self.onClick.bind(self); self.updateCss = self.updateCss.bind(self); self.saveCss = self.saveCss.bind(self); self.detach = self.detach.bind(self); } setInitialState() { this.state = { detached: this.props.detached || BDV2.editorDetached }; } componentDidMount() { // this.updateLineCount(); this.editor = ace.edit("bd-customcss-editor"); this.editor.setTheme("ace/theme/monokai"); this.editor.session.setMode("ace/mode/css"); this.editor.setShowPrintMargin(false); this.editor.setFontSize(14); this.editor.on("change", () => { if (!settingsCookie["bda-css-0"]) return; this.saveCss(); this.updateCss(); }); } componentWillUnmount() { this.editor.destroy(); } componentDidUpdate(prevProps, prevState) { let self = this; if (prevState.detached && !self.state.detached) { BDV2.reactDom.unmountComponentAtNode(self.detachedRoot); } } codeMirror() { } get options() { return { lineNumbers: true, mode: "css", indentUnit: 4, theme: "material", scrollbarStyle: "simple" }; } get css() { let _ccss = DataStore.getBDData("bdcustomcss"); let ccss = ""; if (_ccss && _ccss !== "") { ccss = atob(_ccss); } return ccss; } updateLineCount() { let lineCount = this.refs.editor.value.split("\n").length; if (lineCount == this.props.lines) return; this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + "."; this.props.lines = lineCount; } render() { let self = this; let {detached} = self.state; return BDV2.react.createElement( "div", {className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default", style: {padding: "60px 40px 0px"}}, detached && BDV2.react.createElement( "div", {id: "editor-detached"}, BDV2.react.createElement(V2Components.SettingsTitle, {text: "Custom CSS Editor"}), BDV2.react.createElement( "h3", null, "Editor Detached" ), BDV2.react.createElement( "button", {className: "btn btn-primary", onClick: () => { self.attach(); }}, "Attach" ) ), !detached && BDV2.react.createElement( "div", null, BDV2.react.createElement(V2Components.SettingsTitle, {text: "Custom CSS Editor"}), BDV2.react.createElement("div", {className: "editor-wrapper"}, BDV2.react.createElement("div", {id: "bd-customcss-editor", className: "editor", ref: "editor"}, self.css) ), BDV2.react.createElement( "div", {id: "bd-customcss-attach-controls"}, BDV2.react.createElement( "ul", {className: "checkbox-group"}, BDV2.react.createElement(V2Components.Checkbox, {id: "live-update", text: "Live Update", onChange: this.onChange, checked: settingsCookie["bda-css-0"]}) ), BDV2.react.createElement( "div", {id: "bd-customcss-detach-controls-button"}, BDV2.react.createElement( "button", {style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => { self.onClick("update"); }}, "Update" ), BDV2.react.createElement( "button", {style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => { self.onClick("save"); }}, "Save" ), BDV2.react.createElement( "button", {style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => { self.onClick("detach"); }}, "Detach" ), BDV2.react.createElement( "span", {style: {fontSize: "10px", marginLeft: "5px"}}, "Unsaved changes are lost on detach" ) ) ) ) ); } onClick(arg) { let self = this; switch (arg) { case "update": self.updateCss(); break; case "save": self.saveCss(); break; case "detach": self.detach(); break; } } onChange(id, checked) { switch (id) { case "live-update": settingsCookie["bda-css-0"] = checked; mainCore.saveSettings(); break; } } updateCss() { if ($("#customcss").length == 0) { $("head").append(""); } $("#customcss").html(this.editor.session.getValue()).detach().appendTo(document.head); } saveCss() { DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue())); } detach() { let self = this; self.setState({ detached: true }); let droot = self.detachedRoot; if (!droot) { console.log("FAILED TO INJECT ROOT: .app"); return; } BDV2.reactDom.render(self.detachedEditor, droot); } get detachedRoot() { let _root = $("#bd-customcss-detach-container"); if (!_root.length) { if (!this.injectDetachedRoot()) return null; return this.detachedRoot; } return _root[0]; } injectDetachedRoot() { if (!$(".app").length) return false; $("
", { id: "bd-customcss-detach-container" }).insertAfter($(".app")); return true; } attach() { let self = this; self.setState({ detached: false }); } } class V2C_List extends BDV2.reactComponent { constructor(props) { super(props); } render() { return BDV2.react.createElement( "ul", {className: this.props.className}, this.props.children ); } } class V2C_ContentColumn extends BDV2.reactComponent { constructor(props) { super(props); } render() { return BDV2.react.createElement( "div", {className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"}, BDV2.react.createElement( "h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, this.props.title ), this.props.children ); } } class V2C_PluginCard extends BDV2.reactComponent { constructor(props) { super(props); let self = this; self.onChange = self.onChange.bind(self); self.showSettings = self.showSettings.bind(self); self.setInitialState(); self.hasSettings = typeof self.props.plugin.getSettingsPanel === "function"; self.settingsPanel = ""; this.reload = this.reload.bind(this); this.onReload = this.onReload.bind(this); } setInitialState() { this.state = { checked: pluginCookie[this.props.plugin.getName()], settings: false, reloads: 0 }; } // componentDidMount() { // BDEvents.on("plugin-reloaded", this.onReload); // } // componentWillUnmount() { // BDEvents.off("plugin-reloaded", this.onReload); // } onReload(pluginName) { if (pluginName !== this.props.plugin.getName()) return; this.setState({reloads: this.state.reloads + 1}); } componentDidUpdate() { if (this.state.settings) { if (typeof this.settingsPanel === "object") { this.refs.settingspanel.appendChild(this.settingsPanel); } if (!settingsCookie["fork-ps-3"]) return; var isHidden = (container, element) => { let cTop = container.scrollTop; let cBottom = cTop + container.clientHeight; let eTop = element.offsetTop; let eBottom = eTop + element.clientHeight; return (eTop < cTop || eBottom > cBottom); }; let self = $(BDV2.reactDom.findDOMNode(this)); let container = self.parents(".scroller"); if (!isHidden(container[0], self[0])) return; container.animate({ scrollTop: self.offset().top - container.offset().top + container.scrollTop() - 30 }, 300); } } reload() { const plugin = this.props.plugin.getName(); pluginModule.reloadPlugin(plugin); this.props.plugin = bdplugins[plugin].plugin; this.onReload(this.props.plugin.getName()); } render() { let self = this; let {plugin} = this.props; let name = plugin.getName(); let author = plugin.getAuthor(); let description = plugin.getDescription(); let version = plugin.getVersion(); let website = bdplugins[name].website; let source = bdplugins[name].source; if (this.state.settings) { try { self.settingsPanel = plugin.getSettingsPanel(); } catch (err) { Utils.err("Unable to get settings panel for " + plugin.getName() + ".", err); } return BDV2.react.createElement("li", {className: "settings-open ui-switch-item"}, BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => { this.refs.settingspanel.innerHTML = ""; self.setState({settings: false}); }}, BDV2.react.createElement(V2Components.XSvg, null) ), typeof self.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel"}), typeof self.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: self.settingsPanel}}) ); } return BDV2.react.createElement("li", {"data-name": name, "data-version": version, "className": "settings-closed ui-switch-item"}, BDV2.react.createElement("div", {className: "bda-header"}, BDV2.react.createElement("span", {className: "bda-header-title"}, BDV2.react.createElement("span", {className: "bda-name"}, name), " v", BDV2.react.createElement("span", {className: "bda-version"}, version), " by ", BDV2.react.createElement("span", {className: "bda-author"}, author) ), BDV2.react.createElement("div", {className: "bda-controls"}, !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}), BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}}, BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}), BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"}) ) ) ), BDV2.react.createElement("div", {className: "bda-description-wrap scroller-wrap fade"}, BDV2.react.createElement("div", {className: "bda-description scroller"}, description) ), (website || source || this.hasSettings) && BDV2.react.createElement("div", {className: "bda-footer"}, BDV2.react.createElement("span", {className: "bda-links"}, website && BDV2.react.createElement("a", {className: "bda-link bda-link-website", href: website, target: "_blank"}, "Website"), website && source && " | ", source && BDV2.react.createElement("a", {className: "bda-link bda-link-source", href: source, target: "_blank"}, "Source") ), this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bda-settings-button", disabled: !this.state.checked}, "Settings") ) ); } onChange() { this.setState({checked: !this.state.checked}); pluginModule.togglePlugin(this.props.plugin.getName()); } showSettings() { if (!this.hasSettings) return; this.setState({settings: true}); } } class V2C_ThemeCard extends BDV2.reactComponent { constructor(props) { super(props); this.setInitialState(); this.onChange = this.onChange.bind(this); this.reload = this.reload.bind(this); } setInitialState() { this.state = { checked: themeCookie[this.props.theme.name], reloads: 0 }; } // componentDidMount() { // BDEvents.on("theme-reloaded", this.onReload); // } // componentWillUnmount() { // BDEvents.off("theme-reloaded", this.onReload); // } onReload(themeName) { if (themeName !== this.props.theme.name) return; this.setState({reloads: this.state.reloads + 1}); } reload() { const theme = this.props.theme.name; const error = themeModule.reloadTheme(theme); if (error) mainCore.showToast(`Could not reload ${bdthemes[theme].name}. Check console for details.`, {type: "error"}); else mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been reloaded.`, {type: "success"}); // this.setState(this.state); this.props.theme = bdthemes[theme]; this.onReload(this.props.theme.name); } render() { let {theme} = this.props; let name = theme.name; let description = theme.description; let version = theme.version; let author = theme.author; let website = bdthemes[name].website; let source = bdthemes[name].source; return BDV2.react.createElement("li", {"data-name": name, "data-version": version, "className": "settings-closed ui-switch-item"}, BDV2.react.createElement("div", {className: "bda-header"}, BDV2.react.createElement("span", {className: "bda-header-title"}, BDV2.react.createElement("span", {className: "bda-name"}, name), " v", BDV2.react.createElement("span", {className: "bda-version"}, version), " by ", BDV2.react.createElement("span", {className: "bda-author"}, author) ), BDV2.react.createElement("div", {className: "bda-controls"}, !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}), BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}}, BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}), BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"}) ) ) ), BDV2.react.createElement("div", {className: "bda-description-wrap scroller-wrap fade"}, BDV2.react.createElement("div", {className: "bda-description scroller"}, description) ), (website || source) && BDV2.react.createElement("div", {className: "bda-footer"}, BDV2.react.createElement("span", {className: "bda-links"}, website && BDV2.react.createElement("a", {className: "bda-link", href: website, target: "_blank"}, "Website"), website && source && " | ", source && BDV2.react.createElement("a", {className: "bda-link", href: source, target: "_blank"}, "Source") ) ) ); } onChange() { this.setState({checked: !this.state.checked}); themeModule.toggleTheme(this.props.theme.name); } } class V2Cs_TabBar { static get Item() { return V2C_TabBarItem; } static get Header() { return V2C_TabBarHeader; } static get Separator() { return V2C_TabBarSeparator; } } class V2Components { static get SettingsPanel() { return V2C_SettingsPanel; } static get Switch() { return V2C_Switch; } static get Scroller() { return V2C_Scroller; } static get TabBar() { return V2Cs_TabBar; } static get SideBar() { return V2C_SideBar; } static get Tools() { return V2C_Tools; } static get SettingsTitle() { return V2C_SettingsTitle; } static get CssEditor() { return V2C_CssEditor; } static get Checkbox() { return V2C_Checkbox; } static get List() { return V2C_List; } static get PluginCard() { return V2C_PluginCard; } static get ThemeCard() { return V2C_ThemeCard; } static get ContentColumn() { return V2C_ContentColumn; } static get ReloadIcon() { return V2C_ReloadIcon; } static get XSvg() { return V2C_XSvg; } static get Layer() { return V2C_Layer; } static get SidebarView() { return V2C_SidebarView; } static get ServerCard() { return V2C_ServerCard; } static TooltipWrap(Component, options) { const {style = "black", side = "top", text = ""} = options; const id = BDV2.KeyGenerator(); return class extends BDV2.reactComponent { constructor(props) { super(props); this.onMouseEnter = this.onMouseEnter.bind(this); this.onMouseLeave = this.onMouseLeave.bind(this); } componentDidMount() { this.node = BDV2.reactDom.findDOMNode(this); this.node.addEventListener("mouseenter", this.onMouseEnter); this.node.addEventListener("mouseleave", this.onMouseLeave); } componentWillUnmount() { this.node.removeEventListener("mouseenter", this.onMouseEnter); this.node.removeEventListener("mouseleave", this.onMouseLeave); } onMouseEnter() { const {left, top, width, height} = this.node.getBoundingClientRect(); BDV2.Tooltips.show(id, { position: side, text: text, color: style, targetWidth: width, targetHeight: height, windowWidth: Utils.screenWidth, windowHeight: Utils.screenHeight, x: left, y: top }); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { const nodes = Array.from(mutation.removedNodes); const directMatch = nodes.indexOf(this.node) > -1; const parentMatch = nodes.some(parent => parent.contains(this.node)); if (directMatch || parentMatch) { this.onMouseLeave(); observer.disconnect(); } }); }); observer.observe(document.body, {subtree: true, childList: true}); } onMouseLeave() { BDV2.Tooltips.hide(id); } render() { return BDV2.react.createElement(Component, this.props); } }; } } class V2_SettingsPanel_Sidebar { constructor(onClick) { this.onClick = onClick; } get items() { return [{text: "Core", id: "core"}, {text: "Bandages", id: "fork"}, {text: "Emotes", id: "emotes"}, {text: "Custom CSS", id: "customcss"}, {text: "Plugins", id: "plugins"}, {text: "Themes", id: "themes"}]; } get component() { return BDV2.react.createElement( "span", null, BDV2.react.createElement(V2Components.SideBar, {onClick: this.onClick, headerText: "Bandaged BD", items: this.items}), BDV2.react.createElement( "div", {style: {fontSize: "12px", fontWeight: "600", color: "#72767d", padding: "2px 10px"}}, `BD v${bdConfig.version} by `, BDV2.react.createElement( "a", {href: "https://github.com/Jiiks/", target: "_blank"}, "Jiiks" ) ), BDV2.react.createElement( "div", {style: {fontSize: "12px", fontWeight: "600", color: "#72767d", padding: "2px 10px"}}, `BBD v${bbdVersion} by `, BDV2.react.createElement( "a", {href: "https://github.com/rauenzi/", target: "_blank"}, "Zerebos" ) ) ); } get root() { let _root = $("#bd-settings-sidebar"); if (!_root.length) { if (!this.injectRoot()) return null; return this.root; } return _root[0]; } injectRoot() { let changeLog = $("[class*='side-'] > [class*='item-']:not([class*=Danger])").last(); if (!changeLog.length) return false; $("", {id: "bd-settings-sidebar"}).insertBefore(changeLog.prev()); return true; } render() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: [class*='side-'] > [class*='item-']:not([class*=Danger])"); return; } BDV2.reactDom.render(this.component, root); Utils.onRemoved(root, () => { BDV2.reactDom.unmountComponentAtNode(root); }); } } class V2_SettingsPanel { constructor() { let self = this; self.sideBarOnClick = self.sideBarOnClick.bind(self); self.onChange = self.onChange.bind(self); self.updateSettings = this.updateSettings.bind(self); self.sidebar = new V2_SettingsPanel_Sidebar(self.sideBarOnClick); } get root() { let _root = $("#bd-settingspane-container"); if (!_root.length) { if (!this.injectRoot()) return null; return this.root; } return _root[0]; } injectRoot() { if (!$(".layer-3QrUeG .standardSidebarView-3F1I7i, .layer-3QrUeG .ui-standard-sidebar-view").length) return false; const root = $("
", { "class": "contentRegion-3nDuYy content-region", "id": "bd-settingspane-container" }); $(".layer-3QrUeG .standardSidebarView-3F1I7i, .layer-3QrUeG .ui-standard-sidebar-view").append(root); Utils.onRemoved(root[0], () => { BDV2.reactDom.unmountComponentAtNode(root[0]); }); return true; } get coreSettings() { return this.getSettings("core"); } get forkSettings() { return this.getSettings("fork"); } get emoteSettings() { return this.getSettings("emote"); } getSettings(category) { return Object.keys(settings).reduce((arr, key) => { let setting = settings[key]; if (setting.cat === category && setting.implemented && !setting.hidden) { setting.text = key; arr.push(setting); } return arr; }, []); } sideBarOnClick(id) { let self = this; $(".contentRegion-3nDuYy, .content-region").first().hide(); $(self.root).show(); switch (id) { case "core": self.renderCoreSettings(); break; case "fork": self.renderForkSettings(); break; case "emotes": self.renderEmoteSettings(); break; case "customcss": self.renderCustomCssEditor(); break; case "plugins": self.renderPluginPane(); break; case "themes": self.renderThemePane(); break; } } onClick() {} onChange(id, checked) { settingsCookie[id] = checked; this.updateSettings(); } updateSettings() { let _c = settingsCookie; if (_c["bda-es-0"]) { $("#twitchcord-button-container").show(); } else { $("#twitchcord-button-container").hide(); } if (_c["bda-gs-b"]) { $("body").addClass("bd-blue"); } else { $("body").removeClass("bd-blue"); } if (_c["bda-gs-2"]) { $("body").addClass("bd-minimal"); } else { $("body").removeClass("bd-minimal"); } if (_c["bda-gs-3"]) { $("body").addClass("bd-minimal-chan"); } else { $("body").removeClass("bd-minimal-chan"); } if (_c["bda-gs-1"]) { $("#bd-pub-li").show(); } else { $("#bd-pub-li").hide(); } if (_c["bda-gs-4"]) { voiceMode.enable(); } else { voiceMode.disable(); } if (_c["bda-gs-5"]) { $("#app-mount").addClass("bda-dark"); } else { $("#app-mount").removeClass("bda-dark"); } if (_c["bda-gs-6"]) { mainCore.inject24Hour(); } if (_c["bda-gs-7"]) { mainCore.injectColoredText(); } else { mainCore.removeColoredText(); } if (_c["fork-ps-4"]) classNormalizer.start(); else classNormalizer.stop(); if (_c["fork-ps-5"]) { ContentManager.watchContent("plugin"); ContentManager.watchContent("theme"); } else { ContentManager.unwatchContent("plugin"); ContentManager.unwatchContent("theme"); } const current = BdApi.getWindowPreference("transparent"); if (current != _c["fork-wp-1"]) { BdApi.setWindowPreference("transparent", _c["fork-wp-1"]); if (_c["fork-wp-1"]) BdApi.setWindowPreference("backgroundColor", null); else BdApi.setWindowPreference("backgroundColor", "#2f3136"); } /*if (_c["fork-wp-2"]) { const current = BdApi.getWindowPreference("frame"); if (current != _c["fork-wp-2"]) BdApi.setWindowPreference("frame", _c["fork-wp-2"]); }*/ if (_c["bda-gs-8"]) { dMode.enable(_c["fork-dm-1"]); } else { dMode.disable(); } mainCore.saveSettings(); } renderSidebar() { let self = this; $("[class*='side-'] > [class*='item-']").off("click.v2settingspanel").on("click.v2settingspanel", () => { BDV2.reactDom.unmountComponentAtNode(self.root); $(self.root).hide(); $(".contentRegion-3nDuYy, .content-region").first().show(); }); self.sidebar.render(); } get coreComponent() { return BDV2.react.createElement(V2Components.Scroller, {contentColumn: true, fade: true, dark: true, children: [BDV2.react.createElement(V2Components.SettingsPanel, {key: "cspanel", title: "Core Settings", onChange: this.onChange, settings: this.coreSettings}), BDV2.react.createElement(V2Components.Tools, {key: "tools"})]}); } get forkComponent() { return BDV2.react.createElement(V2Components.Scroller, { contentColumn: true, fade: true, dark: true, children: [ BDV2.react.createElement(V2Components.SettingsPanel, {key: "fspanel", title: "BandagedBD Settings", onChange: this.onChange, settings: this.forkSettings, button: { title: "Clear Emote Cache", onClick: () => { emoteModule.clearEmoteData(); emoteModule.init(); quickEmoteMenu.init(); } }}), BDV2.react.createElement(V2Components.Tools, {key: "tools"}) ] } ); } get emoteComponent() { return BDV2.react.createElement(V2Components.Scroller, {contentColumn: true, fade: true, dark: true, children: [BDV2.react.createElement(V2Components.SettingsPanel, {key: "espanel", title: "Emote Settings", onChange: this.onChange, settings: this.emoteSettings}), BDV2.react.createElement(V2Components.Tools, {key: "tools"})]}); } get customCssComponent() { return BDV2.react.createElement(V2Components.Scroller, {contentColumn: true, fade: true, dark: true, children: [BDV2.react.createElement(V2Components.CssEditor, {key: "csseditor"}), BDV2.react.createElement(V2Components.Tools, {key: "tools"})]}); } contentComponent(type) { const componentElement = type == "plugins" ? this.pluginsComponent : this.themesComponent; const prefix = type.replace("s", ""); const settingsList = this; class ContentList extends BDV2.react.Component { constructor(props) { super(props); this.onChange = this.onChange.bind(this); } componentDidMount() { BDEvents.on(`${prefix}-reloaded`, this.onChange); BDEvents.on(`${prefix}-loaded`, this.onChange); BDEvents.on(`${prefix}-unloaded`, this.onChange); } componentWillUnmount() { BDEvents.off(`${prefix}-reloaded`, this.onChange); BDEvents.off(`${prefix}-loaded`, this.onChange); BDEvents.off(`${prefix}-unloaded`, this.onChange); } onChange() { settingsList.sideBarOnClick(type); } render() {return componentElement;} } return BDV2.react.createElement(ContentList); } get pluginsComponent() { let plugins = Object.keys(bdplugins).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).reduce((arr, key) => { arr.push(BDV2.react.createElement(V2Components.PluginCard, {key: key, plugin: bdplugins[key].plugin}));return arr; }, []); let list = BDV2.react.createElement(V2Components.List, {key: "plugin-list", className: "bda-slist", children: plugins}); let refreshIcon = !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload Plugin List"}), {className: "bd-reload-header", size: "18px", onClick: async () => { pluginModule.updatePluginList(); this.sideBarOnClick("plugins"); }}); let pfBtn = BDV2.react.createElement("button", {key: "folder-button", className: "bd-pfbtn", onClick: () => { require("electron").shell.openItem(ContentManager.pluginsFolder); }}, "Open Plugin Folder"); let contentColumn = BDV2.react.createElement(V2Components.ContentColumn, {key: "pcolumn", title: "Plugins", children: [refreshIcon, pfBtn, list]}); return BDV2.react.createElement(V2Components.Scroller, {contentColumn: true, fade: true, dark: true, children: [contentColumn, BDV2.react.createElement(V2Components.Tools, {key: "tools"})]}); } get themesComponent() { let themes = Object.keys(bdthemes).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).reduce((arr, key) => { arr.push(BDV2.react.createElement(V2Components.ThemeCard, {key: key, theme: bdthemes[key]}));return arr; }, []); let list = BDV2.react.createElement(V2Components.List, {key: "theme-list", className: "bda-slist", children: themes}); let refreshIcon = !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload Theme List"}), {className: "bd-reload-header", size: "18px", onClick: async () => { themeModule.updateThemeList(); this.sideBarOnClick("themes"); }}); let tfBtn = BDV2.react.createElement("button", {key: "folder-button", className: "bd-pfbtn", onClick: () => { require("electron").shell.openItem(ContentManager.themesFolder); }}, "Open Theme Folder"); let contentColumn = BDV2.react.createElement(V2Components.ContentColumn, {key: "tcolumn", title: "Themes", children: [refreshIcon, tfBtn, list]}); return BDV2.react.createElement(V2Components.Scroller, {contentColumn: true, fade: true, dark: true, children: [contentColumn, BDV2.react.createElement(V2Components.Tools, {key: "tools"})]}); } renderCoreSettings() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i"); return; } BDV2.reactDom.render(this.coreComponent, root); } renderForkSettings() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i"); return; } BDV2.reactDom.render(this.forkComponent, root); } renderEmoteSettings() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i"); return; } BDV2.reactDom.render(this.emoteComponent, root); } renderCustomCssEditor() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i"); return; } BDV2.reactDom.render(this.customCssComponent, root); } renderPluginPane() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i"); return; } BDV2.reactDom.render(this.contentComponent("plugins"), root); } renderThemePane() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i"); return; } BDV2.reactDom.render(this.contentComponent("themes"), root); } } class V2C_Layer extends BDV2.reactComponent { constructor(props) { super(props); } componentDidMount() { $(window).on(`keyup.${this.props.id}`, e => { if (e.which === 27) { BDV2.reactDom.unmountComponentAtNode(this.refs.root.parentNode); } }); $(`#${this.props.id}`).animate({opacity: 1}, { step: function(now) { $(this).css("transform", `scale(${1.1 - 0.1 * now}) translateZ(0px)`); }, duration: 200, done: () => {$(`#${this.props.id}`).css("opacity", "").css("transform", "");} }); } componentWillUnmount() { $(window).off(`keyup.${this.props.id}`); $(`#${this.props.id}`).animate({opacity: 0}, { step: function(now) { $(this).css("transform", `scale(${1.1 - 0.1 * now}) translateZ(0px)`); }, duration: 200, done: () => {$(`#${this.props.rootId}`).remove();} }); $("[class*=\"layer-\"]").removeClass("publicServersOpen").animate({opacity: 1}, { step: function(now) { $(this).css("transform", `scale(${0.07 * now + 0.93}) translateZ(0px)`); }, duration: 200, done: () => {$("[class*=\"layer-\"]").css("opacity", "").css("transform", "");} }); } componentWillMount() { $("[class*=\"layer-\"]").addClass("publicServersOpen").animate({opacity: 0}, { step: function(now) { $(this).css("transform", `scale(${0.07 * now + 0.93}) translateZ(0px)`); }, duration: 200 }); } render() { return BDV2.react.createElement( "div", {className: "layer bd-layer layer-3QrUeG", id: this.props.id, ref: "root", style: {opacity: 0, transform: "scale(1.1) translateZ(0px)"}}, this.props.children ); } } class V2C_SidebarView extends BDV2.reactComponent { constructor(props) { super(props); } render() { let {sidebar, content, tools} = this.props.children; return BDV2.react.createElement( "div", {className: "standardSidebarView-3F1I7i ui-standard-sidebar-view"}, BDV2.react.createElement( "div", {className: "sidebarRegion-VFTUkN sidebar-region"}, BDV2.react.createElement(V2Components.Scroller, {key: "sidebarScroller", ref: "sidebarScroller", sidebar: true, fade: sidebar.fade || true, dark: sidebar.dark || true, children: sidebar.component}) ), BDV2.react.createElement("div", {className: "contentRegion-3nDuYy content-region"}, BDV2.react.createElement("div", {className: "contentTransitionWrap-3hqOEW content-transition-wrap"}, BDV2.react.createElement("div", {className: "scrollerWrap-2lJEkd firefoxFixScrollFlex-cnI2ix contentRegionScrollerWrap-3YZXdm content-region-scroller-wrap scrollerThemed-2oenus themeGhost-28MSn0 scrollerTrack-1ZIpsv"}, BDV2.react.createElement("div", {className: "scroller-2FKFPG firefoxFixScrollFlex-cnI2ix contentRegionScroller-26nc1e content-region-scroller scroller", ref: "contentScroller"}, BDV2.react.createElement("div", {className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"}, content.component), tools.component ) ) ) ) ); } } class V2_PublicServers { constructor() {} get component() { return BDV2.react.createElement(V2Components.Layer, {rootId: "pubslayerroot", id: "pubslayer", children: BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot"})}); } get root() { let _root = document.getElementById("pubslayerroot"); if (!_root) { if (!this.injectRoot()) return null; return this.root; } return _root; } injectRoot() { if (!$(".layers, .layers-3iHuyZ").length) return false; $(".layers, .layers-3iHuyZ").append($("
", { id: "pubslayerroot" })); return true; } render() { let root = this.root; if (!root) { console.log("FAILED TO LOCATE ROOT: .layers"); return; } BDV2.reactDom.render(this.component, root); } get button() { let btn = $("
", { "class": BDV2.guildClasses.guild, "id": "bd-pub-li", "css": { height: "20px", display: settingsCookie["bda-gs-1"] ? "" : "none" } }).append($("
", { "class": BDV2.guildClasses.guildInner, "css": { "height": "20px", "border-radius": "4px" } }).append($("", { }).append($("
", { text: "public", id: "bd-pub-button", css: { "line-height": "20px", "font-size": "12px" }, click: () => { this.render(); } })))); return btn; } initialize() { let guilds = $(`.${BDV2.guildClasses.guilds}>:first-child`); guilds.after(this.button); } } class V2C_ServerCard extends BDV2.reactComponent { constructor(props) { super(props); if (!this.props.server.iconUrl) this.props.server.iconUrl = this.props.fallback; this.state = { imageError: false, joined: this.props.guildList.includes(this.props.server.identifier) }; } render() { let {server} = this.props; return BDV2.react.createElement( "div", // cardPrimary-1Hv-to {className: `card-3Qj_Yx cardPrimary-1Hv-to marginBottom8-AtZOdT bd-server-card${server.pinned ? " bd-server-card-pinned" : ""}`}, // BDV2.react.createElement( // "div", // { className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2yIZo0 alignStretch-1hwxMa noWrap-3jynv6" }, BDV2.react.createElement("img", {ref: "img", className: "bd-server-image", src: server.iconUrl, onError: this.handleError.bind(this)}), BDV2.react.createElement( "div", {className: "flexChild-faoVW3 bd-server-content"}, BDV2.react.createElement( "div", {className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6 bd-server-header"}, BDV2.react.createElement( "h5", {className: "h5-18_1nd defaultColor-1_ajX0 margin-reset bd-server-name"}, server.name ), BDV2.react.createElement( "h5", {className: "h5-18_1nd defaultColor-1_ajX0 margin-reset bd-server-member-count"}, server.members, " Members" ) ), BDV2.react.createElement( "div", {className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6"}, BDV2.react.createElement( "div", {className: "scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y bd-server-description-container"}, BDV2.react.createElement( "div", {className: "scroller-2FKFPG scroller bd-server-description"}, server.description ) ) ), BDV2.react.createElement( "div", {className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6 bd-server-footer"}, BDV2.react.createElement( "div", {className: "flexChild-faoVW3 bd-server-tags", style: {flex: "1 1 auto"}}, server.categories.join(", ") ), this.state.joined && BDV2.react.createElement( "button", {type: "button", className: "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN colorGreen-29iAKY", style: {minHeight: "12px", marginTop: "4px", backgroundColor: "#3ac15c"}}, BDV2.react.createElement( "div", {className: "ui-button-contents"}, "Joined" ) ), server.error && BDV2.react.createElement( "button", {type: "button", className: "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN disabled-9aF2ug", style: {minHeight: "12px", marginTop: "4px", backgroundColor: "#c13a3a"}}, BDV2.react.createElement( "div", {className: "ui-button-contents"}, "Error" ) ), !server.error && !this.state.joined && BDV2.react.createElement( "button", {type: "button", className: "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN", style: {minHeight: "12px", marginTop: "4px"}, onClick: () => {this.join();}}, BDV2.react.createElement( "div", {className: "ui-button-contents"}, "Join" ) ) ) ) // ) ); } handleError() { this.props.server.iconUrl = this.props.fallback; this.setState({imageError: true}); } join() { this.props.join(this); //this.setState({joined: true}); } } class V2C_PublicServers extends BDV2.reactComponent { constructor(props) { super(props); this.setInitialState(); this.close = this.close.bind(this); this.changeCategory = this.changeCategory.bind(this); this.search = this.search.bind(this); this.searchKeyDown = this.searchKeyDown.bind(this); this.checkConnection = this.checkConnection.bind(this); this.join = this.join.bind(this); this.connect = this.connect.bind(this); this.GuildStore = BDV2.WebpackModules.findByUniqueProperties(["getGuilds"]); this.AvatarDefaults = BDV2.WebpackModules.findByUniqueProperties(["getUserAvatarURL", "DEFAULT_AVATARS"]); this.InviteActions = BDV2.WebpackModules.findByUniqueProperties(["acceptInvite"]); this.SortedGuildStore = BDV2.WebpackModules.findByUniqueProperties(["getSortedGuilds"]); } componentDidMount() { this.checkConnection(); } setInitialState() { this.state = { selectedCategory: -1, title: "Loading...", loading: true, servers: [], next: null, connection: { state: 0, user: null } }; } close() { BDV2.reactDom.unmountComponentAtNode(document.getElementById(this.props.rootId)); } search(query, clear) { let self = this; $.ajax({ method: "GET", url: `${self.endPoint}${query}${query ? "&schema=new" : "?schema=new"}`, success: data => { let servers = data.results.reduce((arr, server) => { server.joined = false; arr.push(server); // arr.push(); return arr; }, []); if (!clear) { servers = self.state.servers.concat(servers); } else { //servers.unshift(self.bdServer); } let end = data.size + data.from; data.next = `?from=${end}`; if (self.state.term) data.next += `&term=${self.state.term}`; if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`; if (end >= data.total) { end = data.total; data.next = null; } let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`; if (self.state.term) title += ` for ${self.state.term}`; self.setState({ loading: false, title: title, servers: servers, next: data.next }); if (clear) { //console.log(self); self.refs.sbv.refs.contentScroller.scrollTop = 0; } }, error: () => { self.setState({ loading: false, title: "Failed to load servers. Check console for details" }); } }); } join(serverCard) { if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code); $.ajax({ method: "GET", url: `${this.joinEndPoint}/${serverCard.props.server.identifier}`, headers: { "Accept": "application/json;", "Content-Type": "application/json;" , "x-discord-id": this.state.connection.user.id, "x-discord-token": this.state.connection.user.accessToken }, crossDomain: true, xhrFields: { withCredentials: true }, success: () => { serverCard.setState({joined: true}); } }); } connect() { let self = this; let options = self.windowOptions; options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2); options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2); self.joinWindow = new (window.require("electron").remote.BrowserWindow)(options); let sub = window.location.hostname.split(".")[0]; let url = self.connectEndPoint + (sub === "canary" || sub === "ptb" ? `/${sub}` : "") + "?betterDiscord"; self.joinWindow.webContents.on("did-navigate", (event, url) => { if (url != "https://join.discordservers.com/session") return; self.joinWindow.close(); self.checkConnection(); }); self.joinWindow.loadURL(url); } get windowOptions() { return { width: 500, height: 550, backgroundColor: "#282b30", show: true, resizable: false, maximizable: false, minimizable: false, alwaysOnTop: true, frame: false, center: false }; } get bdServer() { let server = { name: "BetterDiscord", online: "7500+", members: "20000+", categories: ["community", "programming", "support"], description: "Official BetterDiscord server for support etc", identifier: "86004744966914048", iconUrl: "https://cdn.discordapp.com/icons/86004744966914048/292e7f6bfff2b71dfd13e508a859aedd.webp", nativejoin: true, invite_code: "0Tmfo5ZbORCRqbAd", pinned: true }; let guildList = this.SortedGuildStore.guildPositions; let defaultList = this.AvatarDefaults.DEFAULT_AVATARS; return BDV2.react.createElement(V2Components.ServerCard, {server: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}); } get endPoint() { return "https://search.discordservers.com"; } get joinEndPoint() { return "https://join.discordservers.com"; } get connectEndPoint() { return "https://join.discordservers.com/connect"; } checkConnection() { let self = this; try { $.ajax({ method: "GET", url: `${self.joinEndPoint}/session`, headers: { "Accept": "application/json;", "Content-Type": "application/json;" }, crossDomain: true, xhrFields: { withCredentials: true }, success: data => { self.setState({ selectedCategory: 0, connection: { state: 2, user: data } }); self.search("", true); }, error: jqXHR => { if (jqXHR.status === 403 || jqXHR.status === 404) { //Not connected self.setState({ title: "Not connected to discordservers.com!", loading: true, selectedCategory: -1, connection: { state: 1, user: null } }); return; } } }); } catch (error) { self.setState({ title: "Not connected to discordservers.com!", loading: true, selectedCategory: -1, connection: { state: 1, user: null } }); } } render() { return BDV2.react.createElement(V2Components.SidebarView, {ref: "sbv", children: this.component}); } get component() { return { sidebar: { component: this.sidebar }, content: { component: this.content }, tools: { component: BDV2.react.createElement(V2Components.Tools, {key: "pt", ref: "tools", onClick: this.close}) } }; } get sidebar() { return BDV2.react.createElement( "div", {className: "sidebar", key: "ps"}, BDV2.react.createElement( "div", {className: "ui-tab-bar SIDE"}, BDV2.react.createElement( "div", {className: "ui-tab-bar-header", style: {fontSize: "16px"}}, "Public Servers" ), BDV2.react.createElement(V2Components.TabBar.Separator, null), this.searchInput, BDV2.react.createElement(V2Components.TabBar.Separator, null), BDV2.react.createElement(V2Components.TabBar.Header, {text: "Categories"}), this.categoryButtons.map((value, index) => { return BDV2.react.createElement(V2Components.TabBar.Item, {id: index, onClick: this.changeCategory, key: index, text: value, selected: this.state.selectedCategory === index}); }), BDV2.react.createElement(V2Components.TabBar.Separator, null), this.footer, this.connection ) ); } get searchInput() { return BDV2.react.createElement( "div", {className: "ui-form-item"}, BDV2.react.createElement( "div", {className: "ui-text-input flex-vertical", style: {width: "172px", marginLeft: "10px"}}, BDV2.react.createElement("input", {ref: "searchinput", onKeyDown: this.searchKeyDown, onChange: () => {}, type: "text", className: "input default", placeholder: "Search...", maxLength: "50"}) ) ); } searchKeyDown(e) { let self = this; if (self.state.loading || e.which !== 13) return; self.setState({ loading: true, title: "Loading...", term: e.target.value }); let query = `?term=${e.target.value}`; if (self.state.selectedCategory !== 0) { query += `&category=${self.categoryButtons[self.state.selectedCategory]}`; } self.search(query, true); } get categoryButtons() { return ["All", "FPS Games", "MMO Games", "Strategy Games", "Sports Games", "Puzzle Games", "Retro Games", "Party Games", "Tabletop Games", "Sandbox Games", "Simulation Games", "Community", "Language", "Programming", "Other"]; } changeCategory(id) { let self = this; if (self.state.loading) return; self.refs.searchinput.value = ""; self.setState({ loading: true, selectedCategory: id, title: "Loading...", term: null }); if (id === 0) { self.search("", true); return; } self.search(`?category=${self.categoryButtons[id]}`, true); } get content() { let self = this; let guildList = this.SortedGuildStore.guildPositions; let defaultList = this.AvatarDefaults.DEFAULT_AVATARS; if (self.state.connection.state === 1) return self.notConnected; return [BDV2.react.createElement( "div", {ref: "content", key: "pc", className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"}, BDV2.react.createElement(V2Components.SettingsTitle, {text: self.state.title}), self.bdServer, self.state.servers.map((server) => { return BDV2.react.createElement(V2Components.ServerCard, {key: server.identifier, server: server, join: self.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}); }), self.state.next && BDV2.react.createElement( "button", {type: "button", onClick: () => { if (self.state.loading) return;self.setState({loading: true}); self.search(self.state.next, false); }, className: "ui-button filled brand small grow", style: {width: "100%", marginTop: "10px", marginBottom: "10px"}}, BDV2.react.createElement( "div", {className: "ui-button-contents"}, self.state.loading ? "Loading" : "Load More" ) ), self.state.servers.length > 0 && BDV2.react.createElement(V2Components.SettingsTitle, {text: self.state.title}) )]; } get notConnected() { let self = this; //return BDV2.react.createElement(V2Components.SettingsTitle, { text: self.state.title }); return [BDV2.react.createElement( "div", {key: "ncc", ref: "content", className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"}, BDV2.react.createElement( "h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, "Not connected to discordservers.com!", BDV2.react.createElement( "button", { onClick: self.connect, type: "button", className: "ui-button filled brand small grow", style: { display: "inline-block", minHeight: "18px", marginLeft: "10px", lineHeight: "14px" } }, BDV2.react.createElement( "div", {className: "ui-button-contents"}, "Connect" ) ) ), self.bdServer )]; } get footer() { return BDV2.react.createElement( "div", {className: "ui-tab-bar-header"}, BDV2.react.createElement( "a", {href: "https://discordservers.com", target: "_blank"}, "Discordservers.com" ) ); } get connection() { let self = this; let {connection} = self.state; if (connection.state !== 2) return BDV2.react.createElement("span", null); return BDV2.react.createElement( "span", null, BDV2.react.createElement(V2Components.TabBar.Separator, null), BDV2.react.createElement( "span", {style: {color: "#b9bbbe", fontSize: "10px", marginLeft: "10px"}}, "Connected as: ", `${connection.user.username}#${connection.user.discriminator}` ), BDV2.react.createElement( "div", {style: {padding: "5px 10px 0 10px"}}, BDV2.react.createElement( "button", {style: {width: "100%", minHeight: "20px"}, type: "button", className: "ui-button filled brand small grow"}, BDV2.react.createElement( "div", {className: "ui-button-contents", onClick: self.connect}, "Reconnect" ) ) ) ); } }