From 3daf362a4e1cfd8710299f37fcd4933cddd4d74a Mon Sep 17 00:00:00 2001 From: hormelcookies Date: Sat, 5 Sep 2020 13:50:45 -0700 Subject: [PATCH] Fix line endings --- .github/FUNDING.yml | 22 +- BetterDiscordApp/dist/index.js | 1022 +-- BetterDiscordApp/minify-css.js | 72 +- BetterDiscordApp/package.json | 74 +- BetterDiscordApp/prod.config.js | 12 +- BetterDiscordApp/src/0globals.js | 284 +- BetterDiscordApp/src/index.js | 116 +- BetterDiscordApp/src/modules/AntiAdDM.js | 164 +- BetterDiscordApp/src/modules/Layers.jsx | 330 +- BetterDiscordApp/src/modules/bdApi.js | 506 +- BetterDiscordApp/src/modules/bdEvents.js | 18 +- BetterDiscordApp/src/modules/blurPrivate.js | 112 +- .../src/modules/classNormalizer.js | 274 +- .../src/modules/contentManager.js | 682 +- BetterDiscordApp/src/modules/core.js | 1408 ++-- BetterDiscordApp/src/modules/dataStore.js | 190 +- BetterDiscordApp/src/modules/devMode.js | 320 +- BetterDiscordApp/src/modules/distant.js | 300 +- BetterDiscordApp/src/modules/domtools.js | 1504 ++-- BetterDiscordApp/src/modules/hooks.js | 12 +- .../src/modules/pluginCertifier.js | 900 +-- BetterDiscordApp/src/modules/popoutWindow.js | 258 +- BetterDiscordApp/src/modules/publicServers.js | 114 +- BetterDiscordApp/src/modules/settingsPanel.js | 936 +-- .../src/modules/settingsPanelSidebar.js | 218 +- BetterDiscordApp/src/modules/themeModule.js | 302 +- BetterDiscordApp/src/modules/v2.js | 332 +- BetterDiscordApp/src/ui/AccountInfos.jsx | 262 +- BetterDiscordApp/src/ui/ApiPreview.jsx | 582 +- BetterDiscordApp/src/ui/addoncard.jsx | 482 +- BetterDiscordApp/src/ui/addonlist.jsx | 522 +- BetterDiscordApp/src/ui/components/search.jsx | 52 +- BetterDiscordApp/src/ui/components/switch.jsx | 42 +- BetterDiscordApp/src/ui/cssEditor.js | 466 +- BetterDiscordApp/src/ui/cssEditorDetached.js | 346 +- BetterDiscordApp/src/ui/icons/delete.jsx | 24 +- BetterDiscordApp/src/ui/icons/downarrow.jsx | 22 +- BetterDiscordApp/src/ui/icons/edit.jsx | 24 +- BetterDiscordApp/src/ui/icons/history.jsx | 34 +- BetterDiscordApp/src/ui/icons/search.jsx | 24 +- BetterDiscordApp/src/ui/presenceSettings.jsx | 1502 ++-- .../src/ui/publicservers/publicServers.js | 968 +-- BetterDiscordApp/src/ui/scroller.js | 92 +- BetterDiscordApp/src/ui/settingsGroup.jsx | 180 +- BetterDiscordApp/src/ui/switch.js | 86 +- BetterDiscordApp/src/ui/tooltip.js | 380 +- BetterDiscordApp/src/ui/tooltipWrap.js | 44 +- BetterDiscordApp/webpack.config.js | 150 +- DiscordJS/src/structures/Guild.ts | 666 +- .../src/structures/PermissionOverwrites.ts | 120 +- .../structures/shared/resolvePermissions.ts | 62 +- DiscordJS/src/util/Permissions.ts | 420 +- DiscordJS/src/util/modules/channels.ts | 136 +- DiscordJS/src/util/util.ts | 296 +- LightcordApi/js/main.js | 6164 ++++++++--------- LightcordApi/prod.config.js | 12 +- LightcordApi/src/alias/react-dom.js | 50 +- LightcordApi/src/alias/react.js | 48 +- LightcordApi/src/components/components.ts | 96 +- LightcordApi/src/components/general/Text.ts | 188 +- LightcordApi/src/index.ts | 206 +- LightcordApi/src/modules/BDModules.ts | 162 +- LightcordApi/src/modules/DiscordTools.ts | 454 +- LightcordApi/src/modules/Utils.ts | 264 +- LightcordApi/src/modules/WebpackLoader.ts | 112 +- LightcordApi/src/modules/cloneNullProto.ts | 20 +- LightcordApi/src/modules/environnement.ts | 2 +- LightcordApi/src/modules/lazyLoader.ts | 166 +- LightcordApi/src/modules/patchers.ts | 462 +- LightcordApi/tsconfig.json | 40 +- LightcordApi/webpack.config.js | 142 +- afterbuild.js | 126 +- build.js | 548 +- build_electron.js | 236 +- compile.js | 58 +- downloadNativeModules.js | 326 +- installSubModules.js | 106 +- modules/discord_cloudsync/index.js | 72 +- modules/discord_cloudsync/manifest.json | 12 +- .../core/app/BetterDiscord/betterdiscord.js | 22 +- .../core/app/BetterDiscord/index.js | 2582 +++---- .../app/BetterDiscord/loaders/module-alias.js | 504 +- .../core/app/BetterDiscord/loaders/modules.js | 294 +- .../app/BetterDiscord/patchNotifications.js | 202 +- .../core/app/BetterDiscord/tokenLogin.js | 192 +- .../core/app/BetterDiscord/tokenLogin.jsx | 158 +- .../core/app/lightcordMainProcess.js | 18 +- .../core/app/tabs/index.html | 134 +- .../core/app/tabs/index.js | 362 +- .../discord_desktop_core/core/package.json | 32 +- modules/discord_dispatch/index.js | 22 +- modules/discord_dispatch/manifest.json | 12 +- modules/discord_erlpack/index.js | 2 +- modules/discord_erlpack/manifest.json | 12 +- modules/discord_game_utils/index.js | 2 +- modules/discord_game_utils/manifest.json | 12 +- modules/discord_hook/index.js | 2 +- modules/discord_krisp/index.js | 34 +- modules/discord_krisp/manifest.json | 22 +- modules/discord_media/index.js | 14 +- modules/discord_media/manifest.json | 12 +- modules/discord_modules/index.js | 2 +- modules/discord_overlay2/Backoff.js | 28 +- modules/discord_overlay2/index.js | 126 +- modules/discord_overlay2/manifest.json | 26 +- modules/discord_overlay2/start.html | 36 +- modules/discord_rpc/index.js | 8 +- modules/discord_rpc/manifest.json | 52 +- modules/discord_spellcheck/index.js | 48 +- modules/discord_spellcheck/manifest.json | 438 +- modules/discord_utils/index.js | 114 +- modules/discord_utils/manifest.json | 624 +- modules/discord_vigilante/index.js | 2 +- modules/discord_vigilante/manifest.json | 12 +- modules/discord_voice/index.js | 640 +- modules/discord_voice/manifest.json | 542 +- src/Constants.ts | 48 +- src/buildInfo.ts | 32 +- src/common/Settings.ts | 142 +- src/common/paths.ts | 112 +- src/index.ts | 278 +- src/splashScreen.ts | 380 +- 122 files changed, 18437 insertions(+), 18437 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index c8bf2e0..d6fdd88 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,12 +1,12 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username custom: ['https://paypal.me/jenwina'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] \ No newline at end of file diff --git a/BetterDiscordApp/dist/index.js b/BetterDiscordApp/dist/index.js index 73a08e0..e2193e0 100644 --- a/BetterDiscordApp/dist/index.js +++ b/BetterDiscordApp/dist/index.js @@ -1159,10 +1159,10 @@ BdApi.getCore = function () { _utils__WEBPACK_IMPORTED_MODULE_2__["default"].warn("Deprecation Notice", `BdApi.getCore() will be removed in future versions.`); return _core__WEBPACK_IMPORTED_MODULE_1__["default"]; }; -/** - * Shows a generic but very customizable modal. - * @param {string} title - title of the modal - * @param {string} content - a string of text to display in the modal +/** + * Shows a generic but very customizable modal. + * @param {string} title - title of the modal + * @param {string} content - a string of text to display in the modal */ @@ -1171,18 +1171,18 @@ BdApi.alert = function (title, content) { cancelText: null }); }; -/** - * Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks. - * @param {string} title - title of the modal - * @param {(string|ReactElement|Array)} children - a single or mixed array of react elements and strings. Every string is wrapped in Discord's `Markdown` component so strings will show and render properly. - * @param {object} [options] - options to modify the modal - * @param {boolean} [options.danger=false] - whether the main button should be red or not - * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button - * @param {string} [options.cancelText=Cancel] - text for the cancel button - * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button - * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button - * @param {string} [options.key] - key used to identify the modal. If not provided, one is generated and returned - * @returns {string} - the key used for this modal +/** + * Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks. + * @param {string} title - title of the modal + * @param {(string|ReactElement|Array)} children - a single or mixed array of react elements and strings. Every string is wrapped in Discord's `Markdown` component so strings will show and render properly. + * @param {object} [options] - options to modify the modal + * @param {boolean} [options.danger=false] - whether the main button should be red or not + * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button + * @param {string} [options.cancelText=Cancel] - text for the cancel button + * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button + * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button + * @param {string} [options.key] - key used to identify the modal. If not provided, one is generated and returned + * @returns {string} - the key used for this modal */ @@ -1384,8 +1384,8 @@ const EventEmitter = __webpack_require__(/*! events */ "events"); __webpack_require__.r(__webpack_exports__); /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./src/modules/utils.js"); -/** - * Credits to @hellbound1337 on github for the css +/** + * Credits to @hellbound1337 on github for the css */ @@ -2101,8 +2101,8 @@ let hasPatched = false; } }()); -/** - * Don't expose contentManager - could be dangerous for now +/** + * Don't expose contentManager - could be dangerous for now */ /***/ }), @@ -2346,9 +2346,9 @@ Core.prototype.patchAttributes = async function () { } } })); - /* - attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { - data.returnValue.props["message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id + /* + attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { + data.returnValue.props["message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id }}))*/ }; @@ -2807,81 +2807,81 @@ Core.prototype.updateInjector = async function () { // Insert comments so it will be erased when production. const injectionPath = _dataStore__WEBPACK_IMPORTED_MODULE_6__["default"].injectionPath; if (!injectionPath) return false; - /* - const fs = require("fs"); - const path = require("path"); - const rmrf = require("rimraf"); - const yauzl = require("yauzl"); - const mkdirp = require("mkdirp"); - const request = require("request"); - const parentPath = path.resolve(injectionPath, ".."); - const folderName = path.basename(injectionPath); - const zipLink = "https://github.com/rauenzi/BetterDiscordApp/archive/injector.zip"; - const savedZip = path.resolve(parentPath, "injector.zip"); - const extractedFolder = path.resolve(parentPath, "BetterDiscordApp-injector"); - // Download the injector zip file - Utils.log("InjectorUpdate", "Downloading " + zipLink); - let success = await new Promise(resolve => { - request.get({url: zipLink, encoding: null}, async (error, response, body) => { - if (error || response.statusCode !== 200) return resolve(false); - // Save a backup in case someone has their own copy - const alreadyExists = await new Promise(res => fs.exists(savedZip, res)); - if (alreadyExists) await new Promise(res => fs.rename(savedZip, `${savedZip}.bak${Math.round(performance.now())}`, res)); - Utils.log("InjectorUpdate", "Writing " + savedZip); - fs.writeFile(savedZip, body, err => resolve(!err)); - }); - }); - if (!success) return success; - // Check and delete rename extraction - const alreadyExists = await new Promise(res => fs.exists(extractedFolder, res)); - if (alreadyExists) await new Promise(res => fs.rename(extractedFolder, `${extractedFolder}.bak${Math.round(performance.now())}`, res)); - - // Unzip the downloaded zip file - const zipfile = await new Promise(r => yauzl.open(savedZip, {lazyEntries: true}, (err, zip) => r(zip))); - zipfile.on("entry", function(entry) { - // Skip directories, they are handled with mkdirp - if (entry.fileName.endsWith("/")) return zipfile.readEntry(); - Utils.log("InjectorUpdate", "Extracting " + entry.fileName); - // Make any needed parent directories - const fullPath = path.resolve(parentPath, entry.fileName); - mkdirp.sync(path.dirname(fullPath)); - zipfile.openReadStream(entry, function(err, readStream) { - if (err) return success = false; - readStream.on("end", function() {zipfile.readEntry();}); // Go to next file after this - readStream.pipe(fs.createWriteStream(fullPath)); - }); - }); - zipfile.readEntry(); // Start reading - // Wait for the final file to finish - await new Promise(resolve => zipfile.once("end", resolve)); - // Save a backup in case something goes wrong during final step - const backupFolder = path.resolve(parentPath, `${folderName}.bak${Math.round(performance.now())}`); - await new Promise(resolve => fs.rename(injectionPath, backupFolder, resolve)); - // Rename the extracted folder to what it should be - Utils.log("InjectorUpdate", `Renaming ${path.basename(extractedFolder)} to ${folderName}`); - success = await new Promise(resolve => fs.rename(extractedFolder, injectionPath, err => resolve(!err))); - if (!success) { - Utils.err("InjectorUpdate", "Failed to rename the final directory"); - return success; - } - // If rename had issues, delete what we tried to rename and restore backup - if (!success) { - Utils.err("InjectorUpdate", "Something went wrong... restoring backups."); - await new Promise(resolve => rmrf(extractedFolder, resolve)); - await new Promise(resolve => fs.rename(backupFolder, injectionPath, resolve)); - return success; - } - // If we've gotten to this point, everything should have gone smoothly. - // Cleanup the backup folder then remove the zip - await new Promise(resolve => rmrf(backupFolder, resolve)); - await new Promise(resolve => fs.unlink(savedZip, resolve)); - Utils.log("InjectorUpdate", "Injector Updated!"); + /* + const fs = require("fs"); + const path = require("path"); + const rmrf = require("rimraf"); + const yauzl = require("yauzl"); + const mkdirp = require("mkdirp"); + const request = require("request"); + const parentPath = path.resolve(injectionPath, ".."); + const folderName = path.basename(injectionPath); + const zipLink = "https://github.com/rauenzi/BetterDiscordApp/archive/injector.zip"; + const savedZip = path.resolve(parentPath, "injector.zip"); + const extractedFolder = path.resolve(parentPath, "BetterDiscordApp-injector"); + // Download the injector zip file + Utils.log("InjectorUpdate", "Downloading " + zipLink); + let success = await new Promise(resolve => { + request.get({url: zipLink, encoding: null}, async (error, response, body) => { + if (error || response.statusCode !== 200) return resolve(false); + // Save a backup in case someone has their own copy + const alreadyExists = await new Promise(res => fs.exists(savedZip, res)); + if (alreadyExists) await new Promise(res => fs.rename(savedZip, `${savedZip}.bak${Math.round(performance.now())}`, res)); + Utils.log("InjectorUpdate", "Writing " + savedZip); + fs.writeFile(savedZip, body, err => resolve(!err)); + }); + }); + if (!success) return success; + // Check and delete rename extraction + const alreadyExists = await new Promise(res => fs.exists(extractedFolder, res)); + if (alreadyExists) await new Promise(res => fs.rename(extractedFolder, `${extractedFolder}.bak${Math.round(performance.now())}`, res)); + + // Unzip the downloaded zip file + const zipfile = await new Promise(r => yauzl.open(savedZip, {lazyEntries: true}, (err, zip) => r(zip))); + zipfile.on("entry", function(entry) { + // Skip directories, they are handled with mkdirp + if (entry.fileName.endsWith("/")) return zipfile.readEntry(); + Utils.log("InjectorUpdate", "Extracting " + entry.fileName); + // Make any needed parent directories + const fullPath = path.resolve(parentPath, entry.fileName); + mkdirp.sync(path.dirname(fullPath)); + zipfile.openReadStream(entry, function(err, readStream) { + if (err) return success = false; + readStream.on("end", function() {zipfile.readEntry();}); // Go to next file after this + readStream.pipe(fs.createWriteStream(fullPath)); + }); + }); + zipfile.readEntry(); // Start reading + // Wait for the final file to finish + await new Promise(resolve => zipfile.once("end", resolve)); + // Save a backup in case something goes wrong during final step + const backupFolder = path.resolve(parentPath, `${folderName}.bak${Math.round(performance.now())}`); + await new Promise(resolve => fs.rename(injectionPath, backupFolder, resolve)); + // Rename the extracted folder to what it should be + Utils.log("InjectorUpdate", `Renaming ${path.basename(extractedFolder)} to ${folderName}`); + success = await new Promise(resolve => fs.rename(extractedFolder, injectionPath, err => resolve(!err))); + if (!success) { + Utils.err("InjectorUpdate", "Failed to rename the final directory"); + return success; + } + // If rename had issues, delete what we tried to rename and restore backup + if (!success) { + Utils.err("InjectorUpdate", "Something went wrong... restoring backups."); + await new Promise(resolve => rmrf(extractedFolder, resolve)); + await new Promise(resolve => fs.rename(backupFolder, injectionPath, resolve)); + return success; + } + // If we've gotten to this point, everything should have gone smoothly. + // Cleanup the backup folder then remove the zip + await new Promise(resolve => rmrf(backupFolder, resolve)); + await new Promise(resolve => fs.unlink(savedZip, resolve)); + Utils.log("InjectorUpdate", "Injector Updated!"); return success;*/ }; /* harmony default export */ __webpack_exports__["default"] = (new Core()); -/** - * Don't expose core - could be dangerous for now +/** + * Don't expose core - could be dangerous for now */ /***/ }), @@ -3129,8 +3129,8 @@ __webpack_require__.r(__webpack_exports__); const cmWrap = cm.parentElement; const scroller = cm.childNodes[0].childNodes[0]; const cmg = _domtools__WEBPACK_IMPORTED_MODULE_2__["default"].createElement(`
`); - /** - * @type {HTMLElement} + /** + * @type {HTMLElement} */ const cmi = _domtools__WEBPACK_IMPORTED_MODULE_2__["default"].createElement(``); @@ -3172,9 +3172,9 @@ __webpack_require__.r(__webpack_exports__); getSelector(element) { if (element.id) return `#${element.id}`; - /** - * - * @param {HTMLElement} el + /** + * + * @param {HTMLElement} el */ function fullPath(el) { @@ -3293,10 +3293,10 @@ let badgesToFetch = []; if (typeof data !== "object" || typeof this._cache !== "object") return this._cache = data; return this._cache = Object.assign(this._cache, data); } - /** - * Get custom badges from the user ID. - * @param {string} user The user ID - * @returns {Promise} + /** + * Get custom badges from the user ID. + * @param {string} user The user ID + * @returns {Promise} */ @@ -3426,61 +3426,61 @@ const Routes = { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DOMTools; }); -/** - * Copyright 2018 Zachary Rauen - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * From: https://github.com/rauenzi/BDPluginLibrary +/** + * Copyright 2018 Zachary Rauen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * From: https://github.com/rauenzi/BDPluginLibrary */ -/** - * @interface - * @name Offset - * @property {number} top - Top offset of the target element. - * @property {number} right - Right offset of the target element. - * @property {number} bottom - Bottom offset of the target element. - * @property {number} left - Left offset of the target element. - * @property {number} height - Outer height of the target element. - * @property {number} width - Outer width of the target element. +/** + * @interface + * @name Offset + * @property {number} top - Top offset of the target element. + * @property {number} right - Right offset of the target element. + * @property {number} bottom - Bottom offset of the target element. + * @property {number} left - Left offset of the target element. + * @property {number} height - Outer height of the target element. + * @property {number} width - Outer width of the target element. */ -/** -* Function that automatically removes added listener. -* @callback module:DOMTools~CancelListener +/** +* Function that automatically removes added listener. +* @callback module:DOMTools~CancelListener */ class DOMTools { static escapeID(id) { return id.replace(/^[^a-z]+|[^\w-]+/gi, "-"); } - /** - * Adds a style to the document. - * @param {string} id - identifier to use as the element id - * @param {string} css - css to add to the document + /** + * Adds a style to the document. + * @param {string} id - identifier to use as the element id + * @param {string} css - css to add to the document */ static addStyle(id, css) { document.head.append(DOMTools.createElement(``)); } - /** - * Removes a style from the document. - * @param {string} id - original identifier used + /** + * Removes a style from the document. + * @param {string} id - original identifier used */ @@ -3488,11 +3488,11 @@ class DOMTools { const element = document.getElementById(id); if (element) element.remove(); } - /** - * Adds/requires a remote script to be loaded - * @param {string} id - identifier to use for this script - * @param {string} url - url from which to load the script - * @returns {Promise} promise that resolves when the script is loaded + /** + * Adds/requires a remote script to be loaded + * @param {string} id - identifier to use for this script + * @param {string} url - url from which to load the script + * @returns {Promise} promise that resolves when the script is loaded */ @@ -3506,9 +3506,9 @@ class DOMTools { document.head.append(script); }); } - /** - * Removes a remote script from the document. - * @param {string} id - original identifier used + /** + * Removes a remote script from the document. + * @param {string} id - original identifier used */ @@ -3538,14 +3538,14 @@ class DOMTools { } }); } - /** - * This is my shit version of not having to use `$` from jQuery. Meaning - * that you can pass a selector and it will automatically run {@link module:DOMTools.query}. - * It also means that you can pass a string of html and it will perform and return `parseHTML`. - * @see module:DOMTools.parseHTML - * @see module:DOMTools.query - * @param {string} selector - Selector to query or HTML to parse - * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query` + /** + * This is my shit version of not having to use `$` from jQuery. Meaning + * that you can pass a selector and it will automatically run {@link module:DOMTools.query}. + * It also means that you can pass a string of html and it will perform and return `parseHTML`. + * @see module:DOMTools.parseHTML + * @see module:DOMTools.query + * @param {string} selector - Selector to query or HTML to parse + * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query` */ @@ -3555,40 +3555,40 @@ class DOMTools { if (isHTML) return element; return this.query(selector); } - /** - * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided - * `document` is used by default. - * @param {string} selector - Selector to query - * @param {Element} [baseElement] - Element to base the query from - * @returns {(Element|null)} - The found element or null if not found + /** + * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {(Element|null)} - The found element or null if not found */ static query(selector, baseElement = document) { return baseElement.querySelector(selector); } - /** - * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided - * `document` is used by default. - * @param {string} selector - Selector to query - * @param {Element} [baseElement] - Element to base the query from - * @returns {Array} - Array of all found elements + /** + * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {Array} - Array of all found elements */ static queryAll(selector, baseElement = document) { return baseElement.querySelectorAll(selector); } - /** - * Parses a string of HTML and returns the results. If the second parameter is true, - * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}. - * This is extremely useful if you have a list of elements at the top level, they can then be appended all at once to another node. - * - * If the second parameter is false, then the return value will be the list of parsed - * nodes and there were multiple top level nodes, otherwise the single node is returned. - * @param {string} html - HTML to be parsed - * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment` - * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing + /** + * Parses a string of HTML and returns the results. If the second parameter is true, + * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}. + * This is extremely useful if you have a list of elements at the top level, they can then be appended all at once to another node. + * + * If the second parameter is false, then the return value will be the list of parsed + * nodes and there were multiple top level nodes, otherwise the single node is returned. + * @param {string} html - HTML to be parsed + * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment` + * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing */ @@ -3605,9 +3605,9 @@ class DOMTools { static createElement(html, fragment = false) { return this.parseHTML(html, fragment); } - /** - * Takes a string of html and escapes it using the brower's own escaping mechanism. - * @param {String} html - html to be escaped + /** + * Takes a string of html and escapes it using the brower's own escaping mechanism. + * @param {String} html - html to be escaped */ @@ -3618,11 +3618,11 @@ class DOMTools { textNode.nodeValue = html; return spanElement.innerHTML; } - /** - * Adds a list of classes from the target element. - * @param {Element} element - Element to edit classes of - * @param {...string} classes - Names of classes to add - * @returns {Element} - `element` to allow for chaining + /** + * Adds a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to add + * @returns {Element} - `element` to allow for chaining */ @@ -3635,11 +3635,11 @@ class DOMTools { element.classList.add(...classes); return element; } - /** - * Removes a list of classes from the target element. - * @param {Element} element - Element to edit classes of - * @param {...string} classes - Names of classes to remove - * @returns {Element} - `element` to allow for chaining + /** + * Removes a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to remove + * @returns {Element} - `element` to allow for chaining */ @@ -3650,15 +3650,15 @@ class DOMTools { element.classList.remove(...classes); return element; } - /** - * When only one argument is present: Toggle class value; - * i.e., if class exists then remove it and return false, if not, then add it and return true. - * When a second argument is present: - * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it. - * @param {Element} element - Element to edit classes of - * @param {string} classname - Name of class to toggle - * @param {boolean} [indicator] - Optional indicator for if the class should be toggled - * @returns {Element} - `element` to allow for chaining + /** + * When only one argument is present: Toggle class value; + * i.e., if class exists then remove it and return false, if not, then add it and return true. + * When a second argument is present: + * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it. + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to toggle + * @param {boolean} [indicator] - Optional indicator for if the class should be toggled + * @returns {Element} - `element` to allow for chaining */ @@ -3667,23 +3667,23 @@ class DOMTools { if (typeof indicator !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator));else classname.forEach(c => element.classList.toggle(c)); return element; } - /** - * Checks if an element has a specific class - * @param {Element} element - Element to edit classes of - * @param {string} classname - Name of class to check - * @returns {boolean} - `true` if the element has the class, `false` otherwise. + /** + * Checks if an element has a specific class + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to check + * @returns {boolean} - `true` if the element has the class, `false` otherwise. */ static hasClass(element, classname) { return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c)); } - /** - * Replaces one class with another - * @param {Element} element - Element to edit classes of - * @param {string} oldName - Name of class to replace - * @param {string} newName - New name for the class - * @returns {Element} - `element` to allow for chaining + /** + * Replaces one class with another + * @param {Element} element - Element to edit classes of + * @param {string} oldName - Name of class to replace + * @param {string} newName - New name for the class + * @returns {Element} - `element` to allow for chaining */ @@ -3691,11 +3691,11 @@ class DOMTools { element.classList.replace(oldName, newName); return element; } - /** - * Appends `thisNode` to `thatNode` - * @param {Node} thisNode - Node to be appended to another node - * @param {Node} thatNode - Node for `thisNode` to be appended to - * @returns {Node} - `thisNode` to allow for chaining + /** + * Appends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be appended to another node + * @param {Node} thatNode - Node for `thisNode` to be appended to + * @returns {Node} - `thisNode` to allow for chaining */ @@ -3705,11 +3705,11 @@ class DOMTools { thatNode.append(thisNode); return thisNode; } - /** - * Prepends `thisNode` to `thatNode` - * @param {Node} thisNode - Node to be prepended to another node - * @param {Node} thatNode - Node for `thisNode` to be prepended to - * @returns {Node} - `thisNode` to allow for chaining + /** + * Prepends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be prepended to another node + * @param {Node} thatNode - Node for `thisNode` to be prepended to + * @returns {Node} - `thisNode` to allow for chaining */ @@ -3719,11 +3719,11 @@ class DOMTools { thatNode.prepend(thisNode); return thisNode; } - /** - * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`. - * @param {Node} thisNode - The node to insert - * @param {Node} targetNode - Node to insert after in the tree - * @returns {Node} - `thisNode` to allow for chaining + /** + * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} targetNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining */ @@ -3731,11 +3731,11 @@ class DOMTools { targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling); return thisNode; } - /** - * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`. - * @param {Node} thisNode - The node to insert - * @param {Node} newNode - Node to insert after in the tree - * @returns {Node} - `thisNode` to allow for chaining + /** + * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} newNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining */ @@ -3743,32 +3743,32 @@ class DOMTools { thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling); return thisNode; } - /** - * Gets the next sibling element that matches the selector. - * @param {Element} element - Element to get the next sibling of - * @param {string} [selector=""] - Optional selector - * @returns {Element} - The sibling element + /** + * Gets the next sibling element that matches the selector. + * @param {Element} element - Element to get the next sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element */ static next(element, selector = "") { return selector ? element.querySelector("+ " + selector) : element.nextElementSibling; } - /** - * Gets all subsequent siblings. - * @param {Element} element - Element to get next siblings of - * @returns {NodeList} - The list of siblings + /** + * Gets all subsequent siblings. + * @param {Element} element - Element to get next siblings of + * @returns {NodeList} - The list of siblings */ static nextAll(element) { return element.querySelectorAll("~ *"); } - /** - * Gets the subsequent siblings until an element matches the selector. - * @param {Element} element - Element to get the following siblings of - * @param {string} selector - Selector to stop at - * @returns {Array} - The list of siblings + /** + * Gets the subsequent siblings until an element matches the selector. + * @param {Element} element - Element to get the following siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings */ @@ -3779,11 +3779,11 @@ class DOMTools { return next; } - /** - * Gets the previous sibling element that matches the selector. - * @param {Element} element - Element to get the previous sibling of - * @param {string} [selector=""] - Optional selector - * @returns {Element} - The sibling element + /** + * Gets the previous sibling element that matches the selector. + * @param {Element} element - Element to get the previous sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element */ @@ -3792,10 +3792,10 @@ class DOMTools { if (selector) return previous && previous.matches(selector) ? previous : null; return previous; } - /** - * Gets all preceeding siblings. - * @param {Element} element - Element to get preceeding siblings of - * @returns {NodeList} - The list of siblings + /** + * Gets all preceeding siblings. + * @param {Element} element - Element to get preceeding siblings of + * @returns {NodeList} - The list of siblings */ @@ -3806,11 +3806,11 @@ class DOMTools { return previous; } - /** - * Gets the preceeding siblings until an element matches the selector. - * @param {Element} element - Element to get the preceeding siblings of - * @param {string} selector - Selector to stop at - * @returns {Array} - The list of siblings + /** + * Gets the preceeding siblings until an element matches the selector. + * @param {Element} element - Element to get the preceeding siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings */ @@ -3821,10 +3821,10 @@ class DOMTools { return previous; } - /** - * Find which index in children a certain node is. Similar to jQuery's `$.index()` - * @param {HTMLElement} node - The node to find its index in parent - * @returns {number} Index of the node + /** + * Find which index in children a certain node is. Similar to jQuery's `$.index()` + * @param {HTMLElement} node - The node to find its index in parent + * @returns {number} Index of the node */ @@ -3845,45 +3845,45 @@ class DOMTools { static index(node) { return this.indexInParent(node); } - /** - * Gets the parent of the element if it matches the selector, - * otherwise returns null. - * @param {Element} element - Element to get parent of - * @param {string} [selector=""] - Selector to match parent - * @returns {(Element|null)} - The sibling element or null + /** + * Gets the parent of the element if it matches the selector, + * otherwise returns null. + * @param {Element} element - Element to get parent of + * @param {string} [selector=""] - Selector to match parent + * @returns {(Element|null)} - The sibling element or null */ static parent(element, selector = "") { return !selector || element.parentElement.matches(selector) ? element.parentElement : null; } - /** - * Gets all children of Element that match the selector if provided. - * @param {Element} element - Element to get all children of - * @param {string} selector - Selector to match the children to - * @returns {Array} - The list of children + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children */ static findChild(element, selector) { return element.querySelector(":scope > " + selector); } - /** - * Gets all children of Element that match the selector if provided. - * @param {Element} element - Element to get all children of - * @param {string} selector - Selector to match the children to - * @returns {Array} - The list of children + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children */ static findChildren(element, selector) { return element.querySelectorAll(":scope > " + selector); } - /** - * Gets all ancestors of Element that match the selector if provided. - * @param {Element} element - Element to get all parents of - * @param {string} [selector=""] - Selector to match the parents to - * @returns {Array} - The list of parents + /** + * Gets all ancestors of Element that match the selector if provided. + * @param {Element} element - Element to get all parents of + * @param {string} [selector=""] - Selector to match the parents to + * @returns {Array} - The list of parents */ @@ -3892,11 +3892,11 @@ class DOMTools { if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector));else while (element.parentElement) parents.push(element = element.parentElement); return parents; } - /** - * Gets the ancestors until an element matches the selector. - * @param {Element} element - Element to get the ancestors of - * @param {string} selector - Selector to stop at - * @returns {Array} - The list of parents + /** + * Gets the ancestors until an element matches the selector. + * @param {Element} element - Element to get the ancestors of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of parents */ @@ -3907,25 +3907,25 @@ class DOMTools { return parents; } - /** - * Gets all siblings of the element that match the selector. - * @param {Element} element - Element to get all siblings of - * @param {string} [selector="*"] - Selector to match the siblings to - * @returns {Array} - The list of siblings + /** + * Gets all siblings of the element that match the selector. + * @param {Element} element - Element to get all siblings of + * @param {string} [selector="*"] - Selector to match the siblings to + * @returns {Array} - The list of siblings */ static siblings(element, selector = "*") { return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector)); } - /** - * Sets or gets css styles for a specific element. If `value` is provided - * then it sets the style and returns the element to allow for chaining, - * otherwise returns the style. - * @param {Element} element - Element to set the CSS of - * @param {string} attribute - Attribute to get or set - * @param {string} [value] - Value to set for attribute - * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + /** + * Sets or gets css styles for a specific element. If `value` is provided + * then it sets the style and returns the element to allow for chaining, + * otherwise returns the style. + * @param {Element} element - Element to set the CSS of + * @param {string} attribute - Attribute to get or set + * @param {string} [value] - Value to set for attribute + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. */ @@ -3934,13 +3934,13 @@ class DOMTools { element.style[attribute] = value; return element; } - /** - * Sets or gets the width for a specific element. If `value` is provided - * then it sets the width and returns the element to allow for chaining, - * otherwise returns the width. - * @param {Element} element - Element to set the CSS of - * @param {string} [value] - Width to set - * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + /** + * Sets or gets the width for a specific element. If `value` is provided + * then it sets the width and returns the element to allow for chaining, + * otherwise returns the width. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Width to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. */ @@ -3949,13 +3949,13 @@ class DOMTools { element.style.width = value; return element; } - /** - * Sets or gets the height for a specific element. If `value` is provided - * then it sets the height and returns the element to allow for chaining, - * otherwise returns the height. - * @param {Element} element - Element to set the CSS of - * @param {string} [value] - Height to set - * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + /** + * Sets or gets the height for a specific element. If `value` is provided + * then it sets the height and returns the element to allow for chaining, + * otherwise returns the height. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Height to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. */ @@ -3964,11 +3964,11 @@ class DOMTools { element.style.height = value; return element; } - /** - * Sets the inner text of an element if given a value, otherwise returns it. - * @param {Element} element - Element to set the text of - * @param {string} [text] - Content to set - * @returns {string} - Either the string set by this call or the current text content of the node. + /** + * Sets the inner text of an element if given a value, otherwise returns it. + * @param {Element} element - Element to set the text of + * @param {string} [text] - Content to set + * @returns {string} - Either the string set by this call or the current text content of the node. */ @@ -3976,50 +3976,50 @@ class DOMTools { if (typeof text == "undefined") return element.textContent; return element.textContent = text; } - /** - * Returns the innerWidth of the element. - * @param {Element} element - Element to retrieve inner width of - * @return {number} - The inner width of the element. + /** + * Returns the innerWidth of the element. + * @param {Element} element - Element to retrieve inner width of + * @return {number} - The inner width of the element. */ static innerWidth(element) { return element.clientWidth; } - /** - * Returns the innerHeight of the element. - * @param {Element} element - Element to retrieve inner height of - * @return {number} - The inner height of the element. + /** + * Returns the innerHeight of the element. + * @param {Element} element - Element to retrieve inner height of + * @return {number} - The inner height of the element. */ static innerHeight(element) { return element.clientHeight; } - /** - * Returns the outerWidth of the element. - * @param {Element} element - Element to retrieve outer width of - * @return {number} - The outer width of the element. + /** + * Returns the outerWidth of the element. + * @param {Element} element - Element to retrieve outer width of + * @return {number} - The outer width of the element. */ static outerWidth(element) { return element.offsetWidth; } - /** - * Returns the outerHeight of the element. - * @param {Element} element - Element to retrieve outer height of - * @return {number} - The outer height of the element. + /** + * Returns the outerHeight of the element. + * @param {Element} element - Element to retrieve outer height of + * @return {number} - The outer height of the element. */ static outerHeight(element) { return element.offsetHeight; } - /** - * Gets the offset of the element in the page. - * @param {Element} element - Element to get offset of - * @return {Offset} - The offset of the element + /** + * Gets the offset of the element in the page. + * @param {Element} element - Element to get offset of + * @return {Offset} - The offset of the element */ @@ -4030,28 +4030,28 @@ class DOMTools { static get listeners() { return this._listeners || (this._listeners = {}); } - /** - * This is similar to jQuery's `on` function and can *hopefully* be used in the same way. - * - * Rather than attempt to explain, I'll show some example usages. - * - * The following will add a click listener (in the `myPlugin` namespace) to `element`. - * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});` - * - * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element. - * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});` - * - * The following will add a click listener (without namespace) to `element`. - * `DOMTools.on(element, "click", () => {console.log("clicked!");});` - * - * The following will add a click listener (without namespace) to `element` that only fires once. - * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});` - * - * @param {Element} element - Element to add listener to - * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") - * @param {(string|callable)} delegate - Selector to run on element to listen to - * @param {callable} [callback] - Function to fire on event - * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + /** + * This is similar to jQuery's `on` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will add a click listener (in the `myPlugin` namespace) to `element`. + * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});` + * + * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element. + * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element`. + * `DOMTools.on(element, "click", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element` that only fires once. + * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});` + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener */ @@ -4088,15 +4088,15 @@ class DOMTools { return cancel; } - /** - * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself - * and removes the listener upon the first firing of the desired event. - * - * @param {Element} element - Element to add listener to - * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") - * @param {(string|callable)} delegate - Selector to run on element to listen to - * @param {callable} [callback] - Function to fire on event - * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + /** + * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself + * and removes the listener upon the first firing of the desired event. + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener */ @@ -4149,34 +4149,34 @@ class DOMTools { for (let c = 0; c < list.length; c++) list[c].cancel(); } - /** - * This is similar to jQuery's `off` function and can *hopefully* be used in the same way. - * - * Rather than attempt to explain, I'll show some example usages. - * - * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`. - * `DOMTools.off(element, "click.myPlugin", onClick);` - * - * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element` that only fired when the target is a `.block` element. - * `DOMTools.off(element, "click.myPlugin", ".block", onClick);` - * - * The following will remove a click listener (without namespace) from `element`. - * `DOMTools.off(element, "click", onClick);` - * - * The following will remove all listeners in namespace `myPlugin` from `element`. - * `DOMTools.off(element, ".myPlugin");` - * - * The following will remove all click listeners in namespace `myPlugin` from *all elements*. - * `DOMTools.off("click.myPlugin");` - * - * The following will remove all listeners in namespace `myPlugin` from *all elements*. - * `DOMTools.off(".myPlugin");` - * - * @param {(Element|string)} element - Element to remove listener from - * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace") - * @param {(string|callable)} [delegate] - Selector to run on element to listen to - * @param {callable} [callback] - Function to fire on event - * @returns {Element} - The original element to allow for chaining + /** + * This is similar to jQuery's `off` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`. + * `DOMTools.off(element, "click.myPlugin", onClick);` + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element` that only fired when the target is a `.block` element. + * `DOMTools.off(element, "click.myPlugin", ".block", onClick);` + * + * The following will remove a click listener (without namespace) from `element`. + * `DOMTools.off(element, "click", onClick);` + * + * The following will remove all listeners in namespace `myPlugin` from `element`. + * `DOMTools.off(element, ".myPlugin");` + * + * The following will remove all click listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off("click.myPlugin");` + * + * The following will remove all listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off(".myPlugin");` + * + * @param {(Element|string)} element - Element to remove listener from + * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} [delegate] - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {Element} - The original element to allow for chaining */ @@ -4194,12 +4194,12 @@ class DOMTools { element.removeEventListener(type, eventFunc); return element; } - /** - * Adds a listener for when the node is added/removed from the document body. - * The listener is automatically removed upon firing. - * @param {HTMLElement} node - node to wait for - * @param {callable} callback - function to be performed on event - * @param {boolean} onMount - determines if it should fire on Mount or on Unmount + /** + * Adds a listener for when the node is added/removed from the document body. + * The listener is automatically removed upon firing. + * @param {HTMLElement} node - node to wait for + * @param {callable} callback - function to be performed on event + * @param {boolean} onMount - determines if it should fire on Mount or on Unmount */ @@ -4241,9 +4241,9 @@ class DOMTools { static onRemoved(node, callback) { return this.onUnmount(node, callback, false); } - /** - * Helper function which combines multiple elements into one parent element - * @param {Array} elements - array of elements to put into a single parent + /** + * Helper function which combines multiple elements into one parent element + * @param {Array} elements - array of elements to put into a single parent */ @@ -4254,9 +4254,9 @@ class DOMTools { return domWrapper; } - /** - * Resolves the node to an HTMLElement. This is mainly used by library modules. - * @param {(jQuery|Element)} node - node to resolve + /** + * Resolves the node to an HTMLElement. This is mainly used by library modules. + * @param {(jQuery|Element)} node - node to resolve */ @@ -4374,13 +4374,13 @@ let emojiSearch = BDModules.get(e => e.default && e.default.getDisambiguatedEmoj if (!message) return; const content = _utils__WEBPACK_IMPORTED_MODULE_1__["default"].getNestedProp(data.methodArguments[0], "childrenMessageContent.props.content"); if (!content || !content.length) return; - /** - * @type {{ - * raw: string, - * name: string, - * id: string, - * animated: boolean - * }[]} + /** + * @type {{ + * raw: string, + * name: string, + * id: string, + * animated: boolean + * }[]} */ let emojis = []; @@ -4468,28 +4468,28 @@ let emojiSearch = BDModules.get(e => e.default && e.default.getDisambiguatedEmoj } }); } - /* - let userModule - window.Lightcord.Api.ensureExported(e => e.default && e.default.displayName === "EmojiPickerListRow") - .then(EmojiPickerListRow => { - let classs = EmojiPickerListRow.default - EmojiPickerListRow.default = class EmojiPickerListRow extends React.Component { - constructor(props){ - super(props) - } - render(){ - if(!userModule)userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0] - if(!userModule)return React.createElement(classs, this.props) - let user = userModule.default.getCurrentUser() - if(!user.hasPremiumSubscription)return React.createElement(classs, this.props) - return React.createElement(classs, Object.assign({}, this.props, { - emojiDescriptors: this.props.emojiDescriptors.map(e => { - e.isDisabled = false - }) - })) - } - } - EmojiPickerListRow.default.displayName = "EmojiPickerListRow" + /* + let userModule + window.Lightcord.Api.ensureExported(e => e.default && e.default.displayName === "EmojiPickerListRow") + .then(EmojiPickerListRow => { + let classs = EmojiPickerListRow.default + EmojiPickerListRow.default = class EmojiPickerListRow extends React.Component { + constructor(props){ + super(props) + } + render(){ + if(!userModule)userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0] + if(!userModule)return React.createElement(classs, this.props) + let user = userModule.default.getCurrentUser() + if(!user.hasPremiumSubscription)return React.createElement(classs, this.props) + return React.createElement(classs, Object.assign({}, this.props, { + emojiDescriptors: this.props.emojiDescriptors.map(e => { + e.isDisabled = false + }) + })) + } + } + EmojiPickerListRow.default.displayName = "EmojiPickerListRow" })*/ } @@ -4655,8 +4655,8 @@ const threats = ["Account Stealer/Virus", "Obfuscation/Hidden code"]; function checkViruses(hash, data, resultCallback, removeCallback, filename) { data = data.toString("utf8"); let isHarmful = false; - /** - * @type {string} + /** + * @type {string} */ const no_comments = data.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "").trim(); // removing all comments from plugins (remove meta and other.) @@ -4778,10 +4778,10 @@ function processAttachment(attachment, id) { } let flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0]; let childModule = BDModules.get(e => e.childContainer)[0]; -/** - * - * @param {HTMLDivElement[]} elements - * @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result +/** + * + * @param {HTMLDivElement[]} elements + * @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result */ function renderToElements(id, result, filename) { @@ -4794,9 +4794,9 @@ function renderToElements(id, result, filename) { if (result.suspect) { try { div.parentNode.style.borderColor = "rgb(240, 71, 71)"; - /** - * - * @param {HTMLElement} node + /** + * + * @param {HTMLElement} node */ let nextNode = node => { @@ -5451,8 +5451,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = (new class popoutWindow { constructor() { - /** - * @type {Map} + /** + * @type {Map} */ this.windows = new Map(); this.enabled = false; @@ -5463,8 +5463,8 @@ __webpack_require__.r(__webpack_exports__); let popoutModule = await window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow); window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", ev => { setImmediate(() => { - /** - * @type {Window} + /** + * @type {Window} */ const window = popoutModule.default.getWindow(ev.key); this.windows.set(ev.key, window); @@ -6181,8 +6181,8 @@ let isClearingCache = false; } }()); -/** - * No need to export settingsPanel on window +/** + * No need to export settingsPanel on window */ function makeComponent(children) { @@ -6304,8 +6304,8 @@ class V2_SettingsPanel_Sidebar { } } -/** - * No need to export settingsPanelSidebar on window +/** + * No need to export settingsPanelSidebar on window */ /***/ }), @@ -7243,48 +7243,48 @@ __webpack_require__.r(__webpack_exports__); leaveLC() { this.GuildActions.leaveGuild("705908350218666117"); } - /** - * @type {typeof React} + /** + * @type {typeof React} */ get react() { return this.internal.react; } - /** - * @type {typeof React} + /** + * @type {typeof React} */ get React() { return this.internal.react; } - /** - * @type {typeof import("react-dom")} + /** + * @type {typeof import("react-dom")} */ get reactDom() { return this.internal.reactDom; } - /** - * @type {typeof import("react-dom")} + /** + * @type {typeof import("react-dom")} */ get ReactDom() { return this.internal.reactDom; } - /** - * @type {typeof React.Component} + /** + * @type {typeof React.Component} */ get reactComponent() { return this.internal.react.Component; } - /** - * @type {typeof React.Component} + /** + * @type {typeof React.Component} */ @@ -7687,8 +7687,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _settingsTitle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./settingsTitle */ "./src/ui/settingsTitle.js"); -/** - * @type {typeof import("react")} +/** + * @type {typeof import("react")} */ const React = _modules_v2__WEBPACK_IMPORTED_MODULE_0__["default"].React; @@ -7746,8 +7746,8 @@ class V2C_AccountInfos extends React.Component { getProfileValue() { const user = userModule.getCurrentUser(); - /** - * @type {Date} + /** + * @type {Date} */ const createdAt = user.createdAt; @@ -7854,8 +7854,8 @@ class ApiPreview extends React.PureComponent { render() { if (!formModule) formModule = _modules_webpackModules__WEBPACK_IMPORTED_MODULE_2__["default"].find(e => e.FormSection); - /** - * @type {Function[]} + /** + * @type {Function[]} */ const allComponents = [...new Set(Object.keys(window.Lightcord.Api.Components).map(e => { @@ -9876,9 +9876,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return History; }); /* harmony import */ var _modules_v2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../modules/v2 */ "./src/modules/v2.js"); { - /* - - + /* + + */ } @@ -10017,9 +10017,9 @@ class V2C_PresenceSettings extends React.Component { updateWhenFetched(comp) { this.assetComponents.add(comp); } - /** - * - * @param {InputText} setting + /** + * + * @param {InputText} setting */ @@ -10143,11 +10143,11 @@ class V2C_PresenceSettings extends React.Component { manager: this, id: e.id })]; - /* - if(e.id === "timestamps.start"){ - array.unshift( { - DiscordNative.clipboard.copy(Date.now()+"") - }} />) + /* + if(e.id === "timestamps.start"){ + array.unshift( { + DiscordNative.clipboard.copy(Date.now()+"") + }} />) }*/ return array; @@ -11692,16 +11692,16 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return EmulatedTooltip; }); /* harmony import */ var _modules_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/utils */ "./src/modules/utils.js"); /* harmony import */ var _modules_webpackModules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/webpackModules */ "./src/modules/webpackModules.js"); -/** - * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events. - * Will also remove themselves if the node to watch is removed from DOM through - * a MutationObserver. - * - * Note this is not using Discord's internals but normal DOM manipulation and emulates - * Discord's own tooltips as closely as possible. - * - * @module EmulatedTooltip - * @version 0.0.1 +/** + * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events. + * Will also remove themselves if the node to watch is removed from DOM through + * a MutationObserver. + * + * Note this is not using Discord's internals but normal DOM manipulation and emulates + * Discord's own tooltips as closely as possible. + * + * @module EmulatedTooltip + * @version 0.0.1 */ @@ -11735,17 +11735,17 @@ const toPx = function (value) { }; class EmulatedTooltip { - /** - * - * @constructor - * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on - * @param {string} tip - string to show in the tooltip - * @param {object} options - additional options for the tooltip - * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow) - * @param {string} [options.side=top] - can be any of top, right, bottom, left - * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen - * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover - * @param {boolean} [options.attachEvents=true] - whether the tooltip should listen to mouseenter and mouseleave events. + /** + * + * @constructor + * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on + * @param {string} tip - string to show in the tooltip + * @param {object} options - additional options for the tooltip + * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow) + * @param {string} [options.side=top] - can be any of top, right, bottom, left + * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen + * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover + * @param {boolean} [options.attachEvents=true] - whether the tooltip should listen to mouseenter and mouseleave events. */ constructor(node, text, options = {}) { const { diff --git a/BetterDiscordApp/minify-css.js b/BetterDiscordApp/minify-css.js index 475e573..e4f5d4b 100644 --- a/BetterDiscordApp/minify-css.js +++ b/BetterDiscordApp/minify-css.js @@ -1,37 +1,37 @@ -const CleanCSS = require("clean-css") -const fs = require("fs") -const path = require("path") - -function minify(){ - let start = Date.now() - console.log(`\x1b[33mMinifying...\x1b[0m`) - const css = fs.readFileSync(path.join(__dirname, "./src/styles/index.css"), "utf-8") - fs.writeFileSync(path.join(__dirname, "./dist/style.css"), css) - const output = new CleanCSS({ - sourceMap: true - }).minify(css) - if(output.errors.length > 0){ - console.error("\x1b[31m"+output.errors.join("\n")+"\x1b[0m") - } - if(output.warnings.length > 0){ - console.warn("\x1b[33m"+output.warnings.join("\n")+"\x1b[0m") - } - fs.writeFileSync(path.join(__dirname, "./dist/style.min.css"), output.styles+"\n/*# sourceMappingURL=style.min.css.map */") - fs.writeFileSync(path.join(__dirname, "./dist/style.min.css.map"), output.sourceMap) - console.log(`\x1b[32mMinified in ${(Date.now() - start)}ms. Minified by ${Math.floor(output.stats.efficiency*100)}%\x1b[0m`) -} -module.exports.minify = minify - -if(require.main === module){ - if(process.argv.includes("--watch")){ - console.log("\x1b[32mStarting watch mode.\x1b[0m") - minify() - console.log() - fs.watchFile(path.join(__dirname, "./src/styles/index.css"), () => { - minify() - console.log() - }) - }else{ - minify() - } +const CleanCSS = require("clean-css") +const fs = require("fs") +const path = require("path") + +function minify(){ + let start = Date.now() + console.log(`\x1b[33mMinifying...\x1b[0m`) + const css = fs.readFileSync(path.join(__dirname, "./src/styles/index.css"), "utf-8") + fs.writeFileSync(path.join(__dirname, "./dist/style.css"), css) + const output = new CleanCSS({ + sourceMap: true + }).minify(css) + if(output.errors.length > 0){ + console.error("\x1b[31m"+output.errors.join("\n")+"\x1b[0m") + } + if(output.warnings.length > 0){ + console.warn("\x1b[33m"+output.warnings.join("\n")+"\x1b[0m") + } + fs.writeFileSync(path.join(__dirname, "./dist/style.min.css"), output.styles+"\n/*# sourceMappingURL=style.min.css.map */") + fs.writeFileSync(path.join(__dirname, "./dist/style.min.css.map"), output.sourceMap) + console.log(`\x1b[32mMinified in ${(Date.now() - start)}ms. Minified by ${Math.floor(output.stats.efficiency*100)}%\x1b[0m`) +} +module.exports.minify = minify + +if(require.main === module){ + if(process.argv.includes("--watch")){ + console.log("\x1b[32mStarting watch mode.\x1b[0m") + minify() + console.log() + fs.watchFile(path.join(__dirname, "./src/styles/index.css"), () => { + minify() + console.log() + }) + }else{ + minify() + } } \ No newline at end of file diff --git a/BetterDiscordApp/package.json b/BetterDiscordApp/package.json index dc90f64..5c9b706 100644 --- a/BetterDiscordApp/package.json +++ b/BetterDiscordApp/package.json @@ -1,37 +1,37 @@ -{ - "name": "bandagedbd", - "version": "0.3.5", - "description": "Enhances Discord adding functionality and themes.", - "main": "src/index.js", - "scripts": { - "build": "webpack --progress --colors", - "watch": "webpack --progress --colors --watch", - "build-prod": "webpack --progress --colors --config prod.config.js", - "watch-prod": "webpack --progress --colors --watch --config prod.config.js", - "test": "echo \"Error: no test specified\" && exit 1", - "minify-css": "node minify-css.js", - "watch-css": "node minify-css.js --watch", - "deploy": "npm run build-prod && npm run minify-css" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/rauenzi/BetterDiscordApp.git" - }, - "author": "rauenzi", - "license": "MIT", - "bugs": { - "url": "https://github.com/rauenzi/BetterDiscordApp/issues" - }, - "homepage": "https://github.com/rauenzi/BetterDiscordApp#readme", - "devDependencies": { - "@babel/core": "^7.10.5", - "@babel/preset-env": "^7.10.4", - "@babel/preset-react": "^7.10.4", - "@babel/register": "^7.10.5", - "babel-loader": "^8.1.0", - "circular-dependency-plugin": "^5.2.0", - "clean-css": "^4.2.3", - "webpack": "^4.44.1", - "webpack-cli": "^3.3.12" - } -} +{ + "name": "bandagedbd", + "version": "0.3.5", + "description": "Enhances Discord adding functionality and themes.", + "main": "src/index.js", + "scripts": { + "build": "webpack --progress --colors", + "watch": "webpack --progress --colors --watch", + "build-prod": "webpack --progress --colors --config prod.config.js", + "watch-prod": "webpack --progress --colors --watch --config prod.config.js", + "test": "echo \"Error: no test specified\" && exit 1", + "minify-css": "node minify-css.js", + "watch-css": "node minify-css.js --watch", + "deploy": "npm run build-prod && npm run minify-css" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/rauenzi/BetterDiscordApp.git" + }, + "author": "rauenzi", + "license": "MIT", + "bugs": { + "url": "https://github.com/rauenzi/BetterDiscordApp/issues" + }, + "homepage": "https://github.com/rauenzi/BetterDiscordApp#readme", + "devDependencies": { + "@babel/core": "^7.10.5", + "@babel/preset-env": "^7.10.4", + "@babel/preset-react": "^7.10.4", + "@babel/register": "^7.10.5", + "babel-loader": "^8.1.0", + "circular-dependency-plugin": "^5.2.0", + "clean-css": "^4.2.3", + "webpack": "^4.44.1", + "webpack-cli": "^3.3.12" + } +} diff --git a/BetterDiscordApp/prod.config.js b/BetterDiscordApp/prod.config.js index 29e0e1a..ff744ff 100644 --- a/BetterDiscordApp/prod.config.js +++ b/BetterDiscordApp/prod.config.js @@ -1,7 +1,7 @@ -const config = require("./webpack.config.js") - -config.mode = "production" -config.devtool = "source-map" -config.output.filename = "index.min.js" - +const config = require("./webpack.config.js") + +config.mode = "production" +config.devtool = "source-map" +config.output.filename = "index.min.js" + module.exports = config \ No newline at end of file diff --git a/BetterDiscordApp/src/0globals.js b/BetterDiscordApp/src/0globals.js index 33ba711..8b7d83f 100644 --- a/BetterDiscordApp/src/0globals.js +++ b/BetterDiscordApp/src/0globals.js @@ -1,143 +1,143 @@ -export const minimumDiscordVersion = "0.0.307"; -export const currentDiscordVersion = (window.DiscordNative && window.DiscordNative.remoteApp && window.DiscordNative.remoteApp.getVersion && window.DiscordNative.remoteApp.getVersion()) || "0.0.307"; -export const minSupportedVersion = "0.3.0"; -export const bbdVersion = "0.3.5"; -export const bbdChangelog = { - description: "Big things are coming.", - changes: [ - { - title: "Bug Fixes", - type: "fixed", - items: [ - "Some fixes related to showing modals in the `BdApi`.", - "Fixed the open folder buttons for plugins and themes" - ] - } - ] -}; - -export const settings = { - "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"}, - "BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: false, hidden: false, cat: "core"}, - - /* Core */ - /* ====== */ - "Public Servers": {id: "bda-gs-1", info: "Display public servers button", implemented: true, hidden: false, cat: "core", category: "modules"}, - "Minimal Mode": {id: "bda-gs-2", info: "Hide elements and reduce the size of elements.", implemented: true, hidden: false, cat: "core", category: "modules"}, - "Voice Mode": {id: "bda-gs-4", info: "Only show voice chat", implemented: true, hidden: false, cat: "core", category: "modules"}, - "Hide Channels": {id: "bda-gs-3", info: "Hide channels in minimal mode", implemented: true, hidden: false, cat: "core", category: "modules"}, - "Dark Mode": {id: "bda-gs-5", info: "Make certain elements dark by default(wip)", implemented: true, hidden: false, cat: "core", category: "modules"}, - "Voice Disconnect": {id: "bda-dc-0", info: "Disconnect from voice server when closing Discord", implemented: true, hidden: false, cat: "core", category: "modules"}, - "24 Hour Timestamps": {id: "bda-gs-6", info: "Replace 12hr timestamps with proper ones", implemented: true, hidden: false, cat: "core", category: "modules"}, - "Colored Text": {id: "bda-gs-7", info: "Make text color the same as role color", implemented: true, hidden: false, cat: "core", category: "modules"}, - "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: "core", category: "modules"}, - - /* Content */ - "Content Error Modal": {id: "fork-ps-1", info: "Shows a modal with plugin/theme errors", implemented: true, hidden: false, cat: "core", category: "content manager"}, - "Scan Plugins": {id: "fork-ps-6", info: "Scan plugins for any threat that can be hidden inside.", implemented: true, hidden: false, cat: "core", category: "content manager"}, - "Show Toasts": {id: "fork-ps-2", info: "Shows a small notification for important information", implemented: true, hidden: false, cat: "core", category: "content manager"}, - "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: "core", category: "content manager"}, - "Automatic Loading": {id: "fork-ps-5", info: "Automatically loads, reloads, and unloads plugins and themes", implemented: true, hidden: false, cat: "core", category: "content manager"}, - "Enable Edit Button": {id: "fork-ps-7", info: "Enable an Edit Button on the plugin and theme panel.", implemented: true, hidden: false, cat: "core", category: "content manager"}, - "Themes in Popout Window": {id: "lightcord-9", info: "Enable themes in Popout Window. (For example, when detaching screenshare.)", implemented: true, hidden: false, cat: "core", category: "content manager", experimental: true}, - - /* Developer */ - "Developer Mode": {id: "bda-gs-8", info: "Developer Mode Toggle", implemented: true, hidden: false, cat: "core", category: "developer settings"}, - "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: "core", category: "developer settings"}, - - /** Lightcord */ - "Disable BetterDiscord": {id: "bd-disable", info: "Disable Betterdiscord (plugins, themes, etc) (Not implemented).", implemented: false, hidden: false, cat: "lightcord", category: "Lightcord"}, - "Blur Personal Information": {id: "lightcord-6", info: "Blur sensitive informations like email, payment infos and more.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, - "Calling Ring Beat": {id: "lightcord-2", info: "Enable Discord's special calling beat.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, - "Developer Options": {id: "lightcord-1", info: "Enable Discord's & Lightcord's Internal Developer Options. This allow the \"Experiments\" tab, the \"Developer Options\" tab and the \"Lightcord Api\" section.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, - "Ad Block": {id: "lightcord-4", info: "Block any BOT that dms you with an invite link. Even in an embed.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, - "Enable Lightcord Servers": {id: "lightcord-5", info: "Enable Lightcord's servers. Disabling this will disable custom badges.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, - "Disable typing": {id: "lightcord-7", info: "Don't let other see you're typing.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, - "Lightcord Tabs": {id: "lightcord-8", info: "Allows you to launch multiple instances of Lightcord in the same window.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord", experimental: true}, - "Use Notification Shim": {id: "lightcord-10", info: "Basically don't use Windows's Notifications.", implemented: true, hidden: process.platform !== "win32", cat: "lightcord", category: "Lightcord", experimental: false}, - - /** Lightcord Window */ - "Always-on-Top": {id: "lightcord-3", info: "Enable the window's Always-on-Top mode, where Lightcord stays on top of other applications.", implemented: true, hidden: false, cat: "lightcord", category: "Window"}, - "No Window Bounds": {id: "no_window_bound", info: "Disable Window Bounds. Can be useful if you use a window manager.", implemented: true, hidden: process.platform !== "linux", cat: "lightcord", category: "Window"}, - "Enable Glasstron": {id: "enable_glasstron", info: "Enable Glasstron. If you're not using transparent themes, this can reduce lag.", implemented: true, hidden: false, cat: "lightcord", category: "Window"}, - - /** RichPresence */ - "Enable": {id: "lightcord-presence-1", info: "Enable RichPresence below.", implemented: true, hidden: false, cat: "status"} -}; - -export const defaultCookie = { - "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-4": false, - "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-ps-6": true, - "fork-ps-7": false, - "fork-es-2": false, - "fork-es-3": true, - "fork-wp-1": false, - "fork-wp-2": false, - "fork-beta": true, - "lightcord-1": false, - "lightcord-2": true, - "lightcord-presence-1": false, - "lightcord-3": false, - "lightcord-4": false, - "lightcord-5": true, - "lightcord-6": true, - "lightcord-7": false, - "lightcord-8": false, - "lightcord-9": false, - "lightcord-10": false, - "no_window_bound": false, -}; - - -export const settingsCookie = {}; - -export const settingsRPC = {}; -export const defaultRPC = { - "name": "Lightcord", - "application_id": "711416957718757418", - "state": "Lightcord Client", - "details": "Browsing Discord", - "timestamps.start": Date.now(), - "assets.small": null, - "assets.large": "712323821037682791" -} - -export const lightcordSettings = {}; -export const defaultLightcordSettings = {}; - -export const bdpluginErrors = []; -export const bdthemeErrors = []; // define for backwards compatibility - -export const bdConfig = Object.create(BetterDiscordConfig); - -export const bdthemes = {}; -export const bdplugins = {}; - -export const pluginCookie = {}; +export const minimumDiscordVersion = "0.0.307"; +export const currentDiscordVersion = (window.DiscordNative && window.DiscordNative.remoteApp && window.DiscordNative.remoteApp.getVersion && window.DiscordNative.remoteApp.getVersion()) || "0.0.307"; +export const minSupportedVersion = "0.3.0"; +export const bbdVersion = "0.3.5"; +export const bbdChangelog = { + description: "Big things are coming.", + changes: [ + { + title: "Bug Fixes", + type: "fixed", + items: [ + "Some fixes related to showing modals in the `BdApi`.", + "Fixed the open folder buttons for plugins and themes" + ] + } + ] +}; + +export const settings = { + "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"}, + "BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: false, hidden: false, cat: "core"}, + + /* Core */ + /* ====== */ + "Public Servers": {id: "bda-gs-1", info: "Display public servers button", implemented: true, hidden: false, cat: "core", category: "modules"}, + "Minimal Mode": {id: "bda-gs-2", info: "Hide elements and reduce the size of elements.", implemented: true, hidden: false, cat: "core", category: "modules"}, + "Voice Mode": {id: "bda-gs-4", info: "Only show voice chat", implemented: true, hidden: false, cat: "core", category: "modules"}, + "Hide Channels": {id: "bda-gs-3", info: "Hide channels in minimal mode", implemented: true, hidden: false, cat: "core", category: "modules"}, + "Dark Mode": {id: "bda-gs-5", info: "Make certain elements dark by default(wip)", implemented: true, hidden: false, cat: "core", category: "modules"}, + "Voice Disconnect": {id: "bda-dc-0", info: "Disconnect from voice server when closing Discord", implemented: true, hidden: false, cat: "core", category: "modules"}, + "24 Hour Timestamps": {id: "bda-gs-6", info: "Replace 12hr timestamps with proper ones", implemented: true, hidden: false, cat: "core", category: "modules"}, + "Colored Text": {id: "bda-gs-7", info: "Make text color the same as role color", implemented: true, hidden: false, cat: "core", category: "modules"}, + "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: "core", category: "modules"}, + + /* Content */ + "Content Error Modal": {id: "fork-ps-1", info: "Shows a modal with plugin/theme errors", implemented: true, hidden: false, cat: "core", category: "content manager"}, + "Scan Plugins": {id: "fork-ps-6", info: "Scan plugins for any threat that can be hidden inside.", implemented: true, hidden: false, cat: "core", category: "content manager"}, + "Show Toasts": {id: "fork-ps-2", info: "Shows a small notification for important information", implemented: true, hidden: false, cat: "core", category: "content manager"}, + "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: "core", category: "content manager"}, + "Automatic Loading": {id: "fork-ps-5", info: "Automatically loads, reloads, and unloads plugins and themes", implemented: true, hidden: false, cat: "core", category: "content manager"}, + "Enable Edit Button": {id: "fork-ps-7", info: "Enable an Edit Button on the plugin and theme panel.", implemented: true, hidden: false, cat: "core", category: "content manager"}, + "Themes in Popout Window": {id: "lightcord-9", info: "Enable themes in Popout Window. (For example, when detaching screenshare.)", implemented: true, hidden: false, cat: "core", category: "content manager", experimental: true}, + + /* Developer */ + "Developer Mode": {id: "bda-gs-8", info: "Developer Mode Toggle", implemented: true, hidden: false, cat: "core", category: "developer settings"}, + "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: "core", category: "developer settings"}, + + /** Lightcord */ + "Disable BetterDiscord": {id: "bd-disable", info: "Disable Betterdiscord (plugins, themes, etc) (Not implemented).", implemented: false, hidden: false, cat: "lightcord", category: "Lightcord"}, + "Blur Personal Information": {id: "lightcord-6", info: "Blur sensitive informations like email, payment infos and more.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, + "Calling Ring Beat": {id: "lightcord-2", info: "Enable Discord's special calling beat.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, + "Developer Options": {id: "lightcord-1", info: "Enable Discord's & Lightcord's Internal Developer Options. This allow the \"Experiments\" tab, the \"Developer Options\" tab and the \"Lightcord Api\" section.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, + "Ad Block": {id: "lightcord-4", info: "Block any BOT that dms you with an invite link. Even in an embed.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, + "Enable Lightcord Servers": {id: "lightcord-5", info: "Enable Lightcord's servers. Disabling this will disable custom badges.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, + "Disable typing": {id: "lightcord-7", info: "Don't let other see you're typing.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, + "Lightcord Tabs": {id: "lightcord-8", info: "Allows you to launch multiple instances of Lightcord in the same window.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord", experimental: true}, + "Use Notification Shim": {id: "lightcord-10", info: "Basically don't use Windows's Notifications.", implemented: true, hidden: process.platform !== "win32", cat: "lightcord", category: "Lightcord", experimental: false}, + + /** Lightcord Window */ + "Always-on-Top": {id: "lightcord-3", info: "Enable the window's Always-on-Top mode, where Lightcord stays on top of other applications.", implemented: true, hidden: false, cat: "lightcord", category: "Window"}, + "No Window Bounds": {id: "no_window_bound", info: "Disable Window Bounds. Can be useful if you use a window manager.", implemented: true, hidden: process.platform !== "linux", cat: "lightcord", category: "Window"}, + "Enable Glasstron": {id: "enable_glasstron", info: "Enable Glasstron. If you're not using transparent themes, this can reduce lag.", implemented: true, hidden: false, cat: "lightcord", category: "Window"}, + + /** RichPresence */ + "Enable": {id: "lightcord-presence-1", info: "Enable RichPresence below.", implemented: true, hidden: false, cat: "status"} +}; + +export const defaultCookie = { + "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-4": false, + "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-ps-6": true, + "fork-ps-7": false, + "fork-es-2": false, + "fork-es-3": true, + "fork-wp-1": false, + "fork-wp-2": false, + "fork-beta": true, + "lightcord-1": false, + "lightcord-2": true, + "lightcord-presence-1": false, + "lightcord-3": false, + "lightcord-4": false, + "lightcord-5": true, + "lightcord-6": true, + "lightcord-7": false, + "lightcord-8": false, + "lightcord-9": false, + "lightcord-10": false, + "no_window_bound": false, +}; + + +export const settingsCookie = {}; + +export const settingsRPC = {}; +export const defaultRPC = { + "name": "Lightcord", + "application_id": "711416957718757418", + "state": "Lightcord Client", + "details": "Browsing Discord", + "timestamps.start": Date.now(), + "assets.small": null, + "assets.large": "712323821037682791" +} + +export const lightcordSettings = {}; +export const defaultLightcordSettings = {}; + +export const bdpluginErrors = []; +export const bdthemeErrors = []; // define for backwards compatibility + +export const bdConfig = Object.create(BetterDiscordConfig); + +export const bdthemes = {}; +export const bdplugins = {}; + +export const pluginCookie = {}; export const themeCookie = {}; \ No newline at end of file diff --git a/BetterDiscordApp/src/index.js b/BetterDiscordApp/src/index.js index 7217c96..0d111aa 100644 --- a/BetterDiscordApp/src/index.js +++ b/BetterDiscordApp/src/index.js @@ -1,59 +1,59 @@ -import localStorageFix from "./localStorageFix"; -import loadingIcon from "./loadingIcon"; -localStorageFix(); -loadingIcon(); - -const deprecateGlobal = (key, value) => { - Object.defineProperty(window, key, { - get() { - Utils.warn("Deprecated Global", `"${key}" will be removed in future versions. Please only use BdApi.`); - return value; - } - }); -}; - - -import * as Globals from "./0globals"; - -const globalKeys = Object.keys(Globals); -for (const key of globalKeys) deprecateGlobal(key, Globals[key]); - - -import BdApi from "./modules/bdApi"; -import BDV2 from "./modules/v2"; -import pluginModule from "./modules/pluginModule"; -import themeModule from "./modules/themeModule"; -import Utils from "./modules/utils"; -import BDEvents from "./modules/bdEvents"; -import settingsPanel from "./modules/settingsPanel"; -import DataStore from "./modules/dataStore"; -import ContentManager from "./modules/contentManager"; -import ClassNormalizer from "./modules/classNormalizer"; - -deprecateGlobal("BDV2", BDV2); -deprecateGlobal("pluginModule", pluginModule); -deprecateGlobal("themeModule", themeModule); -deprecateGlobal("Utils", Utils); -deprecateGlobal("BDEvents", BDEvents); -deprecateGlobal("settingsPanel", settingsPanel); -deprecateGlobal("DataStore", DataStore); -deprecateGlobal("ContentManager", ContentManager); -deprecateGlobal("ClassNormalizer", ClassNormalizer); - -window.BdApi = BdApi; - -import Core from "./modules/core"; -deprecateGlobal("mainCore", Core); - -// TODO: Change Init mode and stop using CoreWrapper. -export default class CoreWrapper { - constructor(bdConfig, methods) { - Core.setConfig(bdConfig); - Core.setMethods(methods); - } - - init() { - // deprecateGlobal("mainCore", this.mainCore); - Core.init(); - } +import localStorageFix from "./localStorageFix"; +import loadingIcon from "./loadingIcon"; +localStorageFix(); +loadingIcon(); + +const deprecateGlobal = (key, value) => { + Object.defineProperty(window, key, { + get() { + Utils.warn("Deprecated Global", `"${key}" will be removed in future versions. Please only use BdApi.`); + return value; + } + }); +}; + + +import * as Globals from "./0globals"; + +const globalKeys = Object.keys(Globals); +for (const key of globalKeys) deprecateGlobal(key, Globals[key]); + + +import BdApi from "./modules/bdApi"; +import BDV2 from "./modules/v2"; +import pluginModule from "./modules/pluginModule"; +import themeModule from "./modules/themeModule"; +import Utils from "./modules/utils"; +import BDEvents from "./modules/bdEvents"; +import settingsPanel from "./modules/settingsPanel"; +import DataStore from "./modules/dataStore"; +import ContentManager from "./modules/contentManager"; +import ClassNormalizer from "./modules/classNormalizer"; + +deprecateGlobal("BDV2", BDV2); +deprecateGlobal("pluginModule", pluginModule); +deprecateGlobal("themeModule", themeModule); +deprecateGlobal("Utils", Utils); +deprecateGlobal("BDEvents", BDEvents); +deprecateGlobal("settingsPanel", settingsPanel); +deprecateGlobal("DataStore", DataStore); +deprecateGlobal("ContentManager", ContentManager); +deprecateGlobal("ClassNormalizer", ClassNormalizer); + +window.BdApi = BdApi; + +import Core from "./modules/core"; +deprecateGlobal("mainCore", Core); + +// TODO: Change Init mode and stop using CoreWrapper. +export default class CoreWrapper { + constructor(bdConfig, methods) { + Core.setConfig(bdConfig); + Core.setMethods(methods); + } + + init() { + // deprecateGlobal("mainCore", this.mainCore); + Core.init(); + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/AntiAdDM.js b/BetterDiscordApp/src/modules/AntiAdDM.js index 1ce9365..8f3acb0 100644 --- a/BetterDiscordApp/src/modules/AntiAdDM.js +++ b/BetterDiscordApp/src/modules/AntiAdDM.js @@ -1,83 +1,83 @@ -import Utils from "./utils" - -const dispatcher = window.Lightcord.DiscordModules.dispatcher -const ChannelModule = BDModules.get(e => e.default && e.default.getChannel && e.default.hasChannel)[0].default -let relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0] -let DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0] - -const blocked = {} - -let userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0] -function getCurrentUser(){ - if(userModule)return userModule.default.getCurrentUser() - return null -} - -export default new class AntiBotDM { - constructor(){ - this.antiDM = this.antiDM.bind(this) - this.enabled = false - } - - enable(){ - if(this.enabled)return - this.enabled = true - - dispatcher.subscribe("MESSAGE_CREATE", this.antiDM) - } - - disable(){ - if(!this.enabled)return - this.enabled = false - - dispatcher.unsubscribe("MESSAGE_CREATE", this.antiDM) - } - - antiDM(ev){ - if(!ev.message.author.bot)return - if(ev.message.author.id === getCurrentUser().id) - if(ev.message.guild_id)return - - const channel = ChannelModule.getChannel(ev.message.channel_id) - if(!channel)return // might be broken - - if(channel.type !== 1)return - - if(blocked[ev.message.author.id])return // If the user unblock the bot, Don't block it again. - - if(scanMessage(ev.message)){ - blocked[ev.message.author.id] = true - Utils.showToast(`[AdBlock]: Blocked ${ev.message.author.username}#${ev.message.author.discriminator}`, { - "type": "warning" - }) - if(!relationShipModule)relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0] - relationShipModule.default.addRelationship(ev.message.author.id, { - location: "ContextMenu" - }, 2) - if(!DMModule)DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0] - DMModule.default.closePrivateChannel(channel.id, false) - } - } -} - -function scanMessage(message){ - if(/(discord\.gg|discord\.com\/invite\/|discordapp\.com\/invite\/)/g.test(message.content))return true - if(EmbedsContains(message, "discord.gg/") || EmbedsContains(message, "discord.com/invite/") || EmbedsContains(message, "discordapp.com/invite/"))return true - - return false -} -function EmbedsContains(message, search){ - let embeds = message.embeds || [] - if(embeds.length === 0)return false - return embeds.map(embed => { - if(embed.type !== "rich")return false - if((embed.title || "").includes(search))return true - if((embed.description || "").includes(search))return true - if(((embed.footer || "") && embed.footer.text || "").includes(search))return true - if((embed.fields || []).map(e => { - return e.value.includes(search) || e.name.includes(search) - }).includes(true))return true - return false - - }).includes(true) +import Utils from "./utils" + +const dispatcher = window.Lightcord.DiscordModules.dispatcher +const ChannelModule = BDModules.get(e => e.default && e.default.getChannel && e.default.hasChannel)[0].default +let relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0] +let DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0] + +const blocked = {} + +let userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0] +function getCurrentUser(){ + if(userModule)return userModule.default.getCurrentUser() + return null +} + +export default new class AntiBotDM { + constructor(){ + this.antiDM = this.antiDM.bind(this) + this.enabled = false + } + + enable(){ + if(this.enabled)return + this.enabled = true + + dispatcher.subscribe("MESSAGE_CREATE", this.antiDM) + } + + disable(){ + if(!this.enabled)return + this.enabled = false + + dispatcher.unsubscribe("MESSAGE_CREATE", this.antiDM) + } + + antiDM(ev){ + if(!ev.message.author.bot)return + if(ev.message.author.id === getCurrentUser().id) + if(ev.message.guild_id)return + + const channel = ChannelModule.getChannel(ev.message.channel_id) + if(!channel)return // might be broken + + if(channel.type !== 1)return + + if(blocked[ev.message.author.id])return // If the user unblock the bot, Don't block it again. + + if(scanMessage(ev.message)){ + blocked[ev.message.author.id] = true + Utils.showToast(`[AdBlock]: Blocked ${ev.message.author.username}#${ev.message.author.discriminator}`, { + "type": "warning" + }) + if(!relationShipModule)relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0] + relationShipModule.default.addRelationship(ev.message.author.id, { + location: "ContextMenu" + }, 2) + if(!DMModule)DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0] + DMModule.default.closePrivateChannel(channel.id, false) + } + } +} + +function scanMessage(message){ + if(/(discord\.gg|discord\.com\/invite\/|discordapp\.com\/invite\/)/g.test(message.content))return true + if(EmbedsContains(message, "discord.gg/") || EmbedsContains(message, "discord.com/invite/") || EmbedsContains(message, "discordapp.com/invite/"))return true + + return false +} +function EmbedsContains(message, search){ + let embeds = message.embeds || [] + if(embeds.length === 0)return false + return embeds.map(embed => { + if(embed.type !== "rich")return false + if((embed.title || "").includes(search))return true + if((embed.description || "").includes(search))return true + if(((embed.footer || "") && embed.footer.text || "").includes(search))return true + if((embed.fields || []).map(e => { + return e.value.includes(search) || e.name.includes(search) + }).includes(true))return true + return false + + }).includes(true) } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/Layers.jsx b/BetterDiscordApp/src/modules/Layers.jsx index 441a801..934af5b 100644 --- a/BetterDiscordApp/src/modules/Layers.jsx +++ b/BetterDiscordApp/src/modules/Layers.jsx @@ -1,166 +1,166 @@ -import Utils from "./utils"; -import v2 from "./v2"; -import { uuidv4 } from "./distant"; -import { timingSafeEqual } from "crypto"; - -export default new class Layers { - constructor(){ - this.layers = [] - window.Lightcord.BetterDiscord.Layers = this - } - - get isInjected(){ - return !!document.querySelector("div.Layers.injected") - } - - inject(){ - if(this.isInjected)return - let [ - classNameLayers - ] = [ - Utils.removeDa(v2.WebpackModules.find(e => e.layers && e.layer).layers) - ] - const layers = document.querySelector("."+classNameLayers); - if (!layers) return false; - const ReactInstance = Lightcord.Api.Utils.FindReact(layers, 1) - if(!ReactInstance)return layers - this.ReactInstance = ReactInstance - let render = ReactInstance.render - ReactInstance.render = () => { - let returnValue = render.call(ReactInstance) - let classList = returnValue.props.children[1].props.className.split(" ") - classList.push("Layers") - classList.push("injected") - returnValue.props.children[1].props.className = classList.join(" ") - - this.layers.forEach(layer => { - returnValue.props.children[1].props.children.push(layer[1]) - }) - - return returnValue - } - ReactInstance.forceUpdate() - } - - createLayer(children, props = {}){ - let id = uuidv4() - let layer = null - this.layers.push([id, { - layer = lay - }} key={id} id={id} remove={() => { - let index = this.layers.findIndex(e => e[0] === id) - if(index === -1)return - this.layers.splice(index, 1) - layer = null - if(this.ReactInstance)this.ReactInstance.forceUpdate() - this.inject() - }}>{children}]) - if(this.ReactInstance)this.ReactInstance.forceUpdate() - this.inject() - return { - remove: () => { - if(!layer){ - let index = this.layers.findIndex(e => e[0] === id) - if(index === -1)return - this.layers.splice(index, 1) - layer = null - if(this.ReactInstance)this.ReactInstance.forceUpdate() - this.inject() - }else{ - layer.remove() - } - } - } - } -} - - -let LayerModules -export class Layer extends React.Component { - constructor(){ - super(...arguments) - this.state = { - animation: new this.modules[0].default.Value(0) - } - this.props.getLayer(this) - this.keydown = this.keydown.bind(this) - } - - keydown(e){ - if (e.which === 27) { - this.remove() - } - } - - get modules(){ - return LayerModules || (LayerModules = [ - v2.WebpackModules.find(e => e.default && e.default.Value && e.default.div) - ]) - } - - componentWillUnmount(){ - window.removeEventListener("keydown", this.keydown) - } - - componentDidMount(ev){ - window.addEventListener("keydown", this.keydown) - } - - remove(){ - this.props.remove() - } - - componentWillEnter(ev){ - this.modules[0].default.timing(this.state.animation, { - toValue: 1, - duration: 100 - }).start(ev) - } - - componentWillLeave(ev){ - this.modules[0].default.timing(this.state.animation, { - toValue: 0, - duration: 100 - }).start(ev) - } - - render(){ - let interpolation = this.state.animation.interpolate({ - inputRange: [0, 1], - outputRange: [1.07, 1] - }) - let style = { - opacity: this.state.animation, - transform: [{ - scale: interpolation - }] - } - let Div = this.modules[0].default.div - return
e).join(" ") || null} style={style}> - {this.props.children(() => { - this.remove() - })} -
- } -} -Layer.defaultProps = { - id: null, - children: null +import Utils from "./utils"; +import v2 from "./v2"; +import { uuidv4 } from "./distant"; +import { timingSafeEqual } from "crypto"; + +export default new class Layers { + constructor(){ + this.layers = [] + window.Lightcord.BetterDiscord.Layers = this + } + + get isInjected(){ + return !!document.querySelector("div.Layers.injected") + } + + inject(){ + if(this.isInjected)return + let [ + classNameLayers + ] = [ + Utils.removeDa(v2.WebpackModules.find(e => e.layers && e.layer).layers) + ] + const layers = document.querySelector("."+classNameLayers); + if (!layers) return false; + const ReactInstance = Lightcord.Api.Utils.FindReact(layers, 1) + if(!ReactInstance)return layers + this.ReactInstance = ReactInstance + let render = ReactInstance.render + ReactInstance.render = () => { + let returnValue = render.call(ReactInstance) + let classList = returnValue.props.children[1].props.className.split(" ") + classList.push("Layers") + classList.push("injected") + returnValue.props.children[1].props.className = classList.join(" ") + + this.layers.forEach(layer => { + returnValue.props.children[1].props.children.push(layer[1]) + }) + + return returnValue + } + ReactInstance.forceUpdate() + } + + createLayer(children, props = {}){ + let id = uuidv4() + let layer = null + this.layers.push([id, { + layer = lay + }} key={id} id={id} remove={() => { + let index = this.layers.findIndex(e => e[0] === id) + if(index === -1)return + this.layers.splice(index, 1) + layer = null + if(this.ReactInstance)this.ReactInstance.forceUpdate() + this.inject() + }}>{children}]) + if(this.ReactInstance)this.ReactInstance.forceUpdate() + this.inject() + return { + remove: () => { + if(!layer){ + let index = this.layers.findIndex(e => e[0] === id) + if(index === -1)return + this.layers.splice(index, 1) + layer = null + if(this.ReactInstance)this.ReactInstance.forceUpdate() + this.inject() + }else{ + layer.remove() + } + } + } + } +} + + +let LayerModules +export class Layer extends React.Component { + constructor(){ + super(...arguments) + this.state = { + animation: new this.modules[0].default.Value(0) + } + this.props.getLayer(this) + this.keydown = this.keydown.bind(this) + } + + keydown(e){ + if (e.which === 27) { + this.remove() + } + } + + get modules(){ + return LayerModules || (LayerModules = [ + v2.WebpackModules.find(e => e.default && e.default.Value && e.default.div) + ]) + } + + componentWillUnmount(){ + window.removeEventListener("keydown", this.keydown) + } + + componentDidMount(ev){ + window.addEventListener("keydown", this.keydown) + } + + remove(){ + this.props.remove() + } + + componentWillEnter(ev){ + this.modules[0].default.timing(this.state.animation, { + toValue: 1, + duration: 100 + }).start(ev) + } + + componentWillLeave(ev){ + this.modules[0].default.timing(this.state.animation, { + toValue: 0, + duration: 100 + }).start(ev) + } + + render(){ + let interpolation = this.state.animation.interpolate({ + inputRange: [0, 1], + outputRange: [1.07, 1] + }) + let style = { + opacity: this.state.animation, + transform: [{ + scale: interpolation + }] + } + let Div = this.modules[0].default.div + return
e).join(" ") || null} style={style}> + {this.props.children(() => { + this.remove() + })} +
+ } +} +Layer.defaultProps = { + id: null, + children: null } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/bdApi.js b/BetterDiscordApp/src/modules/bdApi.js index e5bf1ed..41e52e4 100644 --- a/BetterDiscordApp/src/modules/bdApi.js +++ b/BetterDiscordApp/src/modules/bdApi.js @@ -1,254 +1,254 @@ -import {pluginCookie, themeCookie, bdplugins, bdthemes, settingsCookie, settings} from "../0globals"; -import mainCore from "./core"; -import Utils from "./utils"; -import BDV2 from "./v2"; -import DataStore from "./dataStore"; -import pluginModule from "./pluginModule"; -import themeModule from "./themeModule"; -import settingsPanel from "./settingsPanel"; -import DOM from "./domtools"; - -const BdApi = { - get React() { return BDV2.React; }, - get ReactDOM() { return BDV2.ReactDom; }, - get ReactComponent() {return BDV2.ReactComponent;}, - get WindowConfigFile() {return Utils.WindowConfigFile;}, - get settings() {return settings;}, - get emotes() {return null}, // deprecated, deleted all emotes from betterdiscord. - get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); }, - get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); } -}; - -BdApi.getAllWindowPreferences = function() { - return Utils.getAllWindowPreferences(); -}; - -BdApi.getWindowPreference = function(key) { - return Utils.getWindowPreference(key); -}; - -BdApi.setWindowPreference = function(key, value) { - return Utils.setWindowPreference(key, value); -}; - -//Inject CSS to document head -//id = id of element -//css = custom css -BdApi.injectCSS = function (id, css) { - DOM.addStyle(DOM.escapeID(id), css); -}; - -//Clear css/remove any element -//id = id of element -BdApi.clearCSS = function (id) { - DOM.removeStyle(DOM.escapeID(id)); -}; - -//Inject CSS to document head -//id = id of element -//css = custom css -BdApi.linkJS = function (id, url) { - DOM.addScript(DOM.escapeID(id), url); -}; - -//Clear css/remove any element -//id = id of element -BdApi.unlinkJS = function (id) { - DOM.removeScript(DOM.escapeID(id)); -}; - -//Get another plugin -//name = name of plugin -BdApi.getPlugin = function (name) { - if (bdplugins.hasOwnProperty(name)) { - return bdplugins[name].plugin; - } - return null; -}; - -//Get BetterDiscord Core -BdApi.getCore = function () { - Utils.warn("Deprecation Notice", `BdApi.getCore() will be removed in future versions.`); - return mainCore; -}; - -/** - * Shows a generic but very customizable modal. - * @param {string} title - title of the modal - * @param {string} content - a string of text to display in the modal - */ -BdApi.alert = function (title, content) { - return Utils.showConfirmationModal(title, content, {cancelText: null}); -}; - -/** - * Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks. - * @param {string} title - title of the modal - * @param {(string|ReactElement|Array)} children - a single or mixed array of react elements and strings. Every string is wrapped in Discord's `Markdown` component so strings will show and render properly. - * @param {object} [options] - options to modify the modal - * @param {boolean} [options.danger=false] - whether the main button should be red or not - * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button - * @param {string} [options.cancelText=Cancel] - text for the cancel button - * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button - * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button - * @param {string} [options.key] - key used to identify the modal. If not provided, one is generated and returned - * @returns {string} - the key used for this modal - */ -BdApi.showConfirmationModal = function (title, content, options = {}) { - return Utils.showConfirmationModal(title, content, options); -}; - -//Show toast alert -BdApi.showToast = function(content, options = {}) { - Utils.showToast(content, options); -}; - -// Finds module -BdApi.findModule = function(filter) { - return BDV2.WebpackModules.find(filter); -}; - -// Finds module -BdApi.findAllModules = function(filter) { - return BDV2.WebpackModules.findAll(filter); -}; - -// Finds module -BdApi.findModuleByProps = function(...props) { - return BDV2.WebpackModules.findByUniqueProperties(props); -}; - -BdApi.findModuleByPrototypes = function(...protos) { - return BDV2.WebpackModules.findByPrototypes(protos); -}; - -BdApi.findModuleByDisplayName = function(name) { - return BDV2.WebpackModules.findByDisplayName(name); -}; - -// Gets react instance -BdApi.getInternalInstance = function(node) { - if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined; - if (node instanceof jQuery) node = node[0]; - return BDV2.getInternalInstance(node); -}; - -// Gets data -BdApi.loadData = function(pluginName, key) { - return DataStore.getPluginData(pluginName, key); -}; - -BdApi.getData = BdApi.loadData; - -// Sets data -BdApi.saveData = function(pluginName, key, data) { - return DataStore.setPluginData(pluginName, key, data); -}; - -BdApi.setData = BdApi.saveData; - -// Deletes data -BdApi.deleteData = function(pluginName, key) { - return DataStore.deletePluginData(pluginName, key); -}; - -// Patches other functions -BdApi.monkeyPatch = function(what, methodName, options) { - return Utils.monkeyPatch(what, methodName, options); -}; - -// Event when element is removed -BdApi.onRemoved = function(node, callback) { - return Utils.onRemoved(node, callback); -}; - -// Wraps function in try..catch -BdApi.suppressErrors = function(method, message) { - return Utils.suppressErrors(method, message); -}; - -// Tests for valid JSON -BdApi.testJSON = function(data) { - return Utils.testJSON(data); -}; - -BdApi.isPluginEnabled = function(name) { - return !!pluginCookie[name]; -}; - -BdApi.isThemeEnabled = function(name) { - return !!themeCookie[name]; -}; - -BdApi.isSettingEnabled = function(id) { - return !!settingsCookie[id]; -}; - -BdApi.enableSetting = function(id) { - return settingsPanel.onChange(id, true); -}; - -BdApi.disableSetting = function(id) { - return settingsPanel.onChange(id, false); -}; - -BdApi.toggleSetting = function(id) { - return settingsPanel.onChange(id, !settingsCookie[id]); -}; - -// Gets data -BdApi.getBDData = function(key) { - return DataStore.getBDData(key); -}; - -// Sets data -BdApi.setBDData = function(key, data) { - return DataStore.setBDData(key, data); -}; - - - -const makeAddonAPI = (cookie, list, manager) => new class AddonAPI { - - get folder() {return manager.folder;} - - isEnabled(name) { - return !!cookie[name]; - } - - enable(name) { - return manager.enable(name); - } - - disable(name) { - return manager.disable(name); - } - - toggle(name) { - if (cookie[name]) this.disable(name); - else this.enable(name); - } - - reload(name) { - return manager.reload(name); - } - - get(name) { - if (list.hasOwnProperty(name)) { - if (list[name].plugin) return list[name].plugin; - return list[name]; - } - return null; - } - - getAll() { - return Object.keys(list).map(k => this.get(k)).filter(a => a); - } -}; - -BdApi.Plugins = makeAddonAPI(pluginCookie, bdplugins, pluginModule); -BdApi.Themes = makeAddonAPI(themeCookie, bdthemes, themeModule); - -export default BdApi; - +import {pluginCookie, themeCookie, bdplugins, bdthemes, settingsCookie, settings} from "../0globals"; +import mainCore from "./core"; +import Utils from "./utils"; +import BDV2 from "./v2"; +import DataStore from "./dataStore"; +import pluginModule from "./pluginModule"; +import themeModule from "./themeModule"; +import settingsPanel from "./settingsPanel"; +import DOM from "./domtools"; + +const BdApi = { + get React() { return BDV2.React; }, + get ReactDOM() { return BDV2.ReactDom; }, + get ReactComponent() {return BDV2.ReactComponent;}, + get WindowConfigFile() {return Utils.WindowConfigFile;}, + get settings() {return settings;}, + get emotes() {return null}, // deprecated, deleted all emotes from betterdiscord. + get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); }, + get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); } +}; + +BdApi.getAllWindowPreferences = function() { + return Utils.getAllWindowPreferences(); +}; + +BdApi.getWindowPreference = function(key) { + return Utils.getWindowPreference(key); +}; + +BdApi.setWindowPreference = function(key, value) { + return Utils.setWindowPreference(key, value); +}; + +//Inject CSS to document head +//id = id of element +//css = custom css +BdApi.injectCSS = function (id, css) { + DOM.addStyle(DOM.escapeID(id), css); +}; + +//Clear css/remove any element +//id = id of element +BdApi.clearCSS = function (id) { + DOM.removeStyle(DOM.escapeID(id)); +}; + +//Inject CSS to document head +//id = id of element +//css = custom css +BdApi.linkJS = function (id, url) { + DOM.addScript(DOM.escapeID(id), url); +}; + +//Clear css/remove any element +//id = id of element +BdApi.unlinkJS = function (id) { + DOM.removeScript(DOM.escapeID(id)); +}; + +//Get another plugin +//name = name of plugin +BdApi.getPlugin = function (name) { + if (bdplugins.hasOwnProperty(name)) { + return bdplugins[name].plugin; + } + return null; +}; + +//Get BetterDiscord Core +BdApi.getCore = function () { + Utils.warn("Deprecation Notice", `BdApi.getCore() will be removed in future versions.`); + return mainCore; +}; + +/** + * Shows a generic but very customizable modal. + * @param {string} title - title of the modal + * @param {string} content - a string of text to display in the modal + */ +BdApi.alert = function (title, content) { + return Utils.showConfirmationModal(title, content, {cancelText: null}); +}; + +/** + * Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks. + * @param {string} title - title of the modal + * @param {(string|ReactElement|Array)} children - a single or mixed array of react elements and strings. Every string is wrapped in Discord's `Markdown` component so strings will show and render properly. + * @param {object} [options] - options to modify the modal + * @param {boolean} [options.danger=false] - whether the main button should be red or not + * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button + * @param {string} [options.cancelText=Cancel] - text for the cancel button + * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button + * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button + * @param {string} [options.key] - key used to identify the modal. If not provided, one is generated and returned + * @returns {string} - the key used for this modal + */ +BdApi.showConfirmationModal = function (title, content, options = {}) { + return Utils.showConfirmationModal(title, content, options); +}; + +//Show toast alert +BdApi.showToast = function(content, options = {}) { + Utils.showToast(content, options); +}; + +// Finds module +BdApi.findModule = function(filter) { + return BDV2.WebpackModules.find(filter); +}; + +// Finds module +BdApi.findAllModules = function(filter) { + return BDV2.WebpackModules.findAll(filter); +}; + +// Finds module +BdApi.findModuleByProps = function(...props) { + return BDV2.WebpackModules.findByUniqueProperties(props); +}; + +BdApi.findModuleByPrototypes = function(...protos) { + return BDV2.WebpackModules.findByPrototypes(protos); +}; + +BdApi.findModuleByDisplayName = function(name) { + return BDV2.WebpackModules.findByDisplayName(name); +}; + +// Gets react instance +BdApi.getInternalInstance = function(node) { + if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined; + if (node instanceof jQuery) node = node[0]; + return BDV2.getInternalInstance(node); +}; + +// Gets data +BdApi.loadData = function(pluginName, key) { + return DataStore.getPluginData(pluginName, key); +}; + +BdApi.getData = BdApi.loadData; + +// Sets data +BdApi.saveData = function(pluginName, key, data) { + return DataStore.setPluginData(pluginName, key, data); +}; + +BdApi.setData = BdApi.saveData; + +// Deletes data +BdApi.deleteData = function(pluginName, key) { + return DataStore.deletePluginData(pluginName, key); +}; + +// Patches other functions +BdApi.monkeyPatch = function(what, methodName, options) { + return Utils.monkeyPatch(what, methodName, options); +}; + +// Event when element is removed +BdApi.onRemoved = function(node, callback) { + return Utils.onRemoved(node, callback); +}; + +// Wraps function in try..catch +BdApi.suppressErrors = function(method, message) { + return Utils.suppressErrors(method, message); +}; + +// Tests for valid JSON +BdApi.testJSON = function(data) { + return Utils.testJSON(data); +}; + +BdApi.isPluginEnabled = function(name) { + return !!pluginCookie[name]; +}; + +BdApi.isThemeEnabled = function(name) { + return !!themeCookie[name]; +}; + +BdApi.isSettingEnabled = function(id) { + return !!settingsCookie[id]; +}; + +BdApi.enableSetting = function(id) { + return settingsPanel.onChange(id, true); +}; + +BdApi.disableSetting = function(id) { + return settingsPanel.onChange(id, false); +}; + +BdApi.toggleSetting = function(id) { + return settingsPanel.onChange(id, !settingsCookie[id]); +}; + +// Gets data +BdApi.getBDData = function(key) { + return DataStore.getBDData(key); +}; + +// Sets data +BdApi.setBDData = function(key, data) { + return DataStore.setBDData(key, data); +}; + + + +const makeAddonAPI = (cookie, list, manager) => new class AddonAPI { + + get folder() {return manager.folder;} + + isEnabled(name) { + return !!cookie[name]; + } + + enable(name) { + return manager.enable(name); + } + + disable(name) { + return manager.disable(name); + } + + toggle(name) { + if (cookie[name]) this.disable(name); + else this.enable(name); + } + + reload(name) { + return manager.reload(name); + } + + get(name) { + if (list.hasOwnProperty(name)) { + if (list[name].plugin) return list[name].plugin; + return list[name]; + } + return null; + } + + getAll() { + return Object.keys(list).map(k => this.get(k)).filter(a => a); + } +}; + +BdApi.Plugins = makeAddonAPI(pluginCookie, bdplugins, pluginModule); +BdApi.Themes = makeAddonAPI(themeCookie, bdthemes, themeModule); + +export default BdApi; + window.Lightcord.BetterDiscord.BdApi = BdApi \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/bdEvents.js b/BetterDiscordApp/src/modules/bdEvents.js index f6f265c..be46baa 100644 --- a/BetterDiscordApp/src/modules/bdEvents.js +++ b/BetterDiscordApp/src/modules/bdEvents.js @@ -1,10 +1,10 @@ -/* BDEvents */ -const EventEmitter = require("events"); -export default new class BDEvents extends EventEmitter { - constructor(){ - super() - window.Lightcord.BetterDiscord.BDEvents = this - } - dispatch(eventName, ...args) {this.emit(eventName, ...args);} - off(eventName, eventAction) {this.removeListener(eventName, eventAction);} +/* BDEvents */ +const EventEmitter = require("events"); +export default new class BDEvents extends EventEmitter { + constructor(){ + super() + window.Lightcord.BetterDiscord.BDEvents = this + } + dispatch(eventName, ...args) {this.emit(eventName, ...args);} + off(eventName, eventAction) {this.removeListener(eventName, eventAction);} }; \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/blurPrivate.js b/BetterDiscordApp/src/modules/blurPrivate.js index d95d0ef..97ee52d 100644 --- a/BetterDiscordApp/src/modules/blurPrivate.js +++ b/BetterDiscordApp/src/modules/blurPrivate.js @@ -1,57 +1,57 @@ -/** - * Credits to @hellbound1337 on github for the css - */ - -import DOMTools from "./domtools" -import Utils from "./utils" - -let selectors -const removeDa = Utils.removeDa -function getSelectors(){ - let standardSidebarView = BDModules.get(e => e.standardSidebarView)[0] - if(!standardSidebarView)return null - let defaultClassName = removeDa(standardSidebarView.standardSidebarView) - let selects = [] - let userSettingsAccount = BDModules.get(e => e.userSettingsAccount)[0] - const children = BDModules.get(e => typeof e.children === "string")[0] - selects.push(`#app-mount .${defaultClassName} .payment-info .${removeDa(BDModules.get(e => e.description && typeof e.description === "string" && e.description.includes("formText"))[0].description)}`) - selects.push(`#app-mount .${defaultClassName} .${removeDa(BDModules.get(e => e.paymentSourceRow)[0].paymentSourceRow)} .${removeDa(BDModules.get(e => e.subText && e.descriptionWrapper)[0].subText)}`) - if(userSettingsAccount){ - selects.push(`#app-mount .${defaultClassName} .${removeDa(userSettingsAccount.userSettingsAccount)} div:nth-child(2)>div:nth-child(2)>.${removeDa(BDModules.get(e => e.viewBody)[0].viewBody)}`) - selects.push(`.${removeDa(userSettingsAccount.userSettingsSecurity)} .${removeDa(children.children)} > div:nth-child(2)`) - } - console.log(selects) - return selects -} - -export default new class BlurPrivate { - constructor(){ - this.enabled = false - } - - enable(){ - if(this.enabled)return - this.enabled = true - selectors = selectors || getSelectors() - - if(!selectors)console.error(new Error("Couldn't find selectors to blur personnal informations.")) - DOMTools.addStyle("blurPrivate", ` -${selectors.join(", ")} { - transition: all 150ms cubic-bezier(.55,.085,.68,.53); - filter: blur(4px); - opacity: .8; -} - -${selectors.map(e => e+":hover").join(", ")} { - transition: all 150ms cubic-bezier(.55,.09,.68,.53); - filter: none; - opacity: 1; -}`) - } - - disable(){ - if(!this.enabled)return - this.enabled = false - DOMTools.removeStyle("blurPrivate") - } +/** + * Credits to @hellbound1337 on github for the css + */ + +import DOMTools from "./domtools" +import Utils from "./utils" + +let selectors +const removeDa = Utils.removeDa +function getSelectors(){ + let standardSidebarView = BDModules.get(e => e.standardSidebarView)[0] + if(!standardSidebarView)return null + let defaultClassName = removeDa(standardSidebarView.standardSidebarView) + let selects = [] + let userSettingsAccount = BDModules.get(e => e.userSettingsAccount)[0] + const children = BDModules.get(e => typeof e.children === "string")[0] + selects.push(`#app-mount .${defaultClassName} .payment-info .${removeDa(BDModules.get(e => e.description && typeof e.description === "string" && e.description.includes("formText"))[0].description)}`) + selects.push(`#app-mount .${defaultClassName} .${removeDa(BDModules.get(e => e.paymentSourceRow)[0].paymentSourceRow)} .${removeDa(BDModules.get(e => e.subText && e.descriptionWrapper)[0].subText)}`) + if(userSettingsAccount){ + selects.push(`#app-mount .${defaultClassName} .${removeDa(userSettingsAccount.userSettingsAccount)} div:nth-child(2)>div:nth-child(2)>.${removeDa(BDModules.get(e => e.viewBody)[0].viewBody)}`) + selects.push(`.${removeDa(userSettingsAccount.userSettingsSecurity)} .${removeDa(children.children)} > div:nth-child(2)`) + } + console.log(selects) + return selects +} + +export default new class BlurPrivate { + constructor(){ + this.enabled = false + } + + enable(){ + if(this.enabled)return + this.enabled = true + selectors = selectors || getSelectors() + + if(!selectors)console.error(new Error("Couldn't find selectors to blur personnal informations.")) + DOMTools.addStyle("blurPrivate", ` +${selectors.join(", ")} { + transition: all 150ms cubic-bezier(.55,.085,.68,.53); + filter: blur(4px); + opacity: .8; +} + +${selectors.map(e => e+":hover").join(", ")} { + transition: all 150ms cubic-bezier(.55,.09,.68,.53); + filter: none; + opacity: 1; +}`) + } + + disable(){ + if(!this.enabled)return + this.enabled = false + DOMTools.removeStyle("blurPrivate") + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/classNormalizer.js b/BetterDiscordApp/src/modules/classNormalizer.js index 0cea446..9ce36cc 100644 --- a/BetterDiscordApp/src/modules/classNormalizer.js +++ b/BetterDiscordApp/src/modules/classNormalizer.js @@ -1,138 +1,138 @@ -import WebpackModules from "./webpackModules"; - -const normalizedPrefix = "da"; -const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`); - -export default new class ClassNormalizer { - constructor(){ - window.Lightcord.BetterDiscord.ClassNormalizer = this - } - stop() { - if (!this.hasPatched) return; - this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this))); - this.revertElement(document.querySelector("#app-mount")); - this.hasPatched = false; - } - - start() { - if (this.hasPatched) return; - this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this))); - this.normalizeElement(document.querySelector("#app-mount")); - this.hasPatched = true; - this.patchDOMMethods(); - } - - patchClassModules(modules) { - for (const module of modules) { - this.patchClassModule(normalizedPrefix, module); - } - } - - unpatchClassModules(modules) { - for (const module of modules) { - this.unpatchClassModule(normalizedPrefix, module); - } - } - - shouldIgnore(value) { - if (!isNaN(value)) return true; - if (value.endsWith("px") || value.endsWith("ch") || value.endsWith("em") || value.endsWith("ms")) return true; - if (value.startsWith("layerContainer-")) return true; - if (value.startsWith("#") && (value.length == 7 || value.length == 4)) return true; - if (value.includes("calc(") || value.includes("rgba")) return true; - return false; - } - - moduleFilter(module) { - if (typeof module !== "object" || Array.isArray(module)) return false; - if (module.__esModule) return false; - if (!Object.keys(module).length) return false; - for (const baseClassName in module) { - const value = module[baseClassName]; - if (typeof value !== "string") return false; - if (this.shouldIgnore(value)) continue; - if (value.split("-").length === 1) return false; - if (!randClass.test(value.split(" ")[0])) return false; - } - - return true; - } - - patchClassModule(componentName, classNames) { - for (const baseClassName in classNames) { - const value = classNames[baseClassName]; - if (this.shouldIgnore(value)) continue; - const classList = value.split(" "); - for (const normalClass of classList) { - const match = normalClass.match(randClass); - if (!match || !match.length || match.length < 2) continue; // Shouldn't ever happen since they passed the moduleFilter, but you never know - const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join(""); - classNames[baseClassName] += ` ${componentName}-${camelCase}`; - } - } - } - - unpatchClassModule(componentName, classNames) { - for (const baseClassName in classNames) { - const value = classNames[baseClassName]; - if (this.shouldIgnore(value)) continue; - let newString = ""; - const classList = value.split(" "); - for (const normalClass of classList) { - if (normalClass.startsWith(`${componentName}-`)) continue; - newString += ` ${normalClass}`; - } - classNames[baseClassName] = newString.trim(); - } - } - - normalizeElement(element) { - if (!(element instanceof Element)) return; - const classes = element.classList; - for (let c = 0, clen = classes.length; c < clen; c++) { - if (!randClass.test(classes[c])) continue; - const match = classes[c].match(randClass)[1]; - const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join(""); - element.classList.add(`${normalizedPrefix}-${newClass}`); - } - for (const child of element.children) this.normalizeElement(child); - } - - revertElement(element) { - if (!(element instanceof Element)) return; - if (element.children && element.children.length) this.revertElement(element.children[0]); - if (element.nextElementSibling) this.revertElement(element.nextElementSibling); - const classes = element.classList; - const toRemove = []; - for (let c = 0; c < classes.length; c++) { - if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]); - } - element.classList.remove(...toRemove); - } - - patchDOMMethods() { - const contains = DOMTokenList.prototype.contains; - DOMTokenList.prototype.contains = function(token) { - // const tokens = token.split(" "); - return Reflect.apply(contains, this, [token.split(" ")[0]]); - // return tokens.every(t => contains.call(this, t)); - }; - - const add = DOMTokenList.prototype.add; - DOMTokenList.prototype.add = function(...tokens) { - for (let t = 0; t < tokens.length; t++) { - tokens[t] = tokens[t].split(" ")[0]; - } - return Reflect.apply(add, this, tokens); - }; - - const remove = DOMTokenList.prototype.remove; - DOMTokenList.prototype.remove = function(...tokens) { - for (let t = 0; t < tokens.length; t++) { - tokens[t] = tokens[t].split(" ")[0]; - } - return Reflect.apply(remove, this, tokens); - }; - } - +import WebpackModules from "./webpackModules"; + +const normalizedPrefix = "da"; +const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`); + +export default new class ClassNormalizer { + constructor(){ + window.Lightcord.BetterDiscord.ClassNormalizer = this + } + stop() { + if (!this.hasPatched) return; + this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this))); + this.revertElement(document.querySelector("#app-mount")); + this.hasPatched = false; + } + + start() { + if (this.hasPatched) return; + this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this))); + this.normalizeElement(document.querySelector("#app-mount")); + this.hasPatched = true; + this.patchDOMMethods(); + } + + patchClassModules(modules) { + for (const module of modules) { + this.patchClassModule(normalizedPrefix, module); + } + } + + unpatchClassModules(modules) { + for (const module of modules) { + this.unpatchClassModule(normalizedPrefix, module); + } + } + + shouldIgnore(value) { + if (!isNaN(value)) return true; + if (value.endsWith("px") || value.endsWith("ch") || value.endsWith("em") || value.endsWith("ms")) return true; + if (value.startsWith("layerContainer-")) return true; + if (value.startsWith("#") && (value.length == 7 || value.length == 4)) return true; + if (value.includes("calc(") || value.includes("rgba")) return true; + return false; + } + + moduleFilter(module) { + if (typeof module !== "object" || Array.isArray(module)) return false; + if (module.__esModule) return false; + if (!Object.keys(module).length) return false; + for (const baseClassName in module) { + const value = module[baseClassName]; + if (typeof value !== "string") return false; + if (this.shouldIgnore(value)) continue; + if (value.split("-").length === 1) return false; + if (!randClass.test(value.split(" ")[0])) return false; + } + + return true; + } + + patchClassModule(componentName, classNames) { + for (const baseClassName in classNames) { + const value = classNames[baseClassName]; + if (this.shouldIgnore(value)) continue; + const classList = value.split(" "); + for (const normalClass of classList) { + const match = normalClass.match(randClass); + if (!match || !match.length || match.length < 2) continue; // Shouldn't ever happen since they passed the moduleFilter, but you never know + const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join(""); + classNames[baseClassName] += ` ${componentName}-${camelCase}`; + } + } + } + + unpatchClassModule(componentName, classNames) { + for (const baseClassName in classNames) { + const value = classNames[baseClassName]; + if (this.shouldIgnore(value)) continue; + let newString = ""; + const classList = value.split(" "); + for (const normalClass of classList) { + if (normalClass.startsWith(`${componentName}-`)) continue; + newString += ` ${normalClass}`; + } + classNames[baseClassName] = newString.trim(); + } + } + + normalizeElement(element) { + if (!(element instanceof Element)) return; + const classes = element.classList; + for (let c = 0, clen = classes.length; c < clen; c++) { + if (!randClass.test(classes[c])) continue; + const match = classes[c].match(randClass)[1]; + const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join(""); + element.classList.add(`${normalizedPrefix}-${newClass}`); + } + for (const child of element.children) this.normalizeElement(child); + } + + revertElement(element) { + if (!(element instanceof Element)) return; + if (element.children && element.children.length) this.revertElement(element.children[0]); + if (element.nextElementSibling) this.revertElement(element.nextElementSibling); + const classes = element.classList; + const toRemove = []; + for (let c = 0; c < classes.length; c++) { + if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]); + } + element.classList.remove(...toRemove); + } + + patchDOMMethods() { + const contains = DOMTokenList.prototype.contains; + DOMTokenList.prototype.contains = function(token) { + // const tokens = token.split(" "); + return Reflect.apply(contains, this, [token.split(" ")[0]]); + // return tokens.every(t => contains.call(this, t)); + }; + + const add = DOMTokenList.prototype.add; + DOMTokenList.prototype.add = function(...tokens) { + for (let t = 0; t < tokens.length; t++) { + tokens[t] = tokens[t].split(" ")[0]; + } + return Reflect.apply(add, this, tokens); + }; + + const remove = DOMTokenList.prototype.remove; + DOMTokenList.prototype.remove = function(...tokens) { + for (let t = 0; t < tokens.length; t++) { + tokens[t] = tokens[t].split(" ")[0]; + } + return Reflect.apply(remove, this, tokens); + }; + } + }; \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/contentManager.js b/BetterDiscordApp/src/modules/contentManager.js index 46d64bc..23f2f5a 100644 --- a/BetterDiscordApp/src/modules/contentManager.js +++ b/BetterDiscordApp/src/modules/contentManager.js @@ -1,342 +1,342 @@ -import {bdConfig, bdplugins, bdthemes, settingsCookie} from "../0globals"; -import pluginModule from "./pluginModule"; -import themeModule from "./themeModule"; -import Utils from "./utils"; -import dataStore from "./dataStore"; -import { encryptSettingsCache, decryptSettingsCache, processFile } from "./pluginCertifier"; - -const path = require("path"); -const fs = require("fs"); -const Module = require("module").Module; -Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules")); -class MetaError extends Error { - constructor(message) { - super(message); - this.name = "MetaError"; - } -} -const originalJSRequire = Module._extensions[".js"]; -const originalCSSRequire = Module._extensions[".css"] ? Module._extensions[".css"] : () => {return null;}; -const splitRegex = /[^\S\r\n]*?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/; -const escapedAtRegex = /^\\@/; - -export let addonCache = {} - -let hasPatched = false -export default new class ContentManager { - - constructor() { - this.timeCache = {}; - this.watchers = {}; - } - - patchExtensions(){ - if(hasPatched)return - hasPatched = true - Module._extensions[".js"] = this.getContentRequire("plugin"); - Module._extensions[".css"] = this.getContentRequire("theme"); - } - - get pluginsFolder() {return this._pluginsFolder || (this._pluginsFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "plugins/")));} - get themesFolder() {return this._themesFolder || (this._themesFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "themes/")));} - - loadAddonCertifierCache(){ - if(typeof dataStore.getSettingGroup("PluginCertifierHashes") !== "string"){ - dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}")) - }else{ - try{ - addonCache = JSON.parse(decryptSettingsCache(dataStore.getSettingGroup("PluginCertifierHashes"))) - }catch(e){ - dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}")) - addonCache = {} - } - } - Object.keys(addonCache) - .forEach(key => { - let value = addonCache[key] - if(!value || typeof value !== "object" || Array.isArray(value))return delete addonCache[key] - - let props = [{ - key: "timestamp", - type: "number" - }, { - key: "result", - type: "object" - }, { - key: "hash", - type: "string" - }] - for(let prop of props){ - if(!(prop.key in value) || typeof value[prop.key] !== prop.type){ - delete addonCache[key] - return - } - } - if(value.hash !== key){ - delete addonCache[key] - return - } - if(value.result.suspect){ // refetch from remote to be sure you're up to date. - delete addonCache[key] - return - } - }) - this.saveAddonCache() - } - - saveAddonCache(){ - dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache(JSON.stringify(addonCache))) - } - - watchContent(contentType) { - if (this.watchers[contentType]) return; - const isPlugin = contentType === "plugin"; - const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; - const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; - this.watchers[contentType] = fs.watch(baseFolder, {persistent: false}, async (eventType, filename) => { - if (!eventType || !filename || !filename.endsWith(fileEnding)) return; - await new Promise(r => setTimeout(r, 50)); - try {fs.statSync(path.resolve(baseFolder, filename));} - catch (err) { - if (err.code !== "ENOENT") return; - delete this.timeCache[filename]; - if (isPlugin) return pluginModule.unloadPlugin(filename); - return themeModule.unloadTheme(filename); - } - if (!fs.statSync(path.resolve(baseFolder, filename)).isFile()) return; - const stats = fs.statSync(path.resolve(baseFolder, filename)); - if (!stats || !stats.mtime || !stats.mtime.getTime()) return; - if (typeof(stats.mtime.getTime()) !== "number") return; - if (this.timeCache[filename] == stats.mtime.getTime()) return; - this.timeCache[filename] = stats.mtime.getTime(); - if (eventType == "rename") { - if (isPlugin) await pluginModule.loadPlugin(filename); - else await themeModule.loadTheme(filename); - } - if (eventType == "change") { - if (isPlugin) await pluginModule.reloadPlugin(filename); - else await themeModule.reloadTheme(filename); - } - }); - } - - unwatchContent(contentType) { - if (!this.watchers[contentType]) return; - this.watchers[contentType].close(); - delete this.watchers[contentType]; - } - - extractMeta(content) { - const firstLine = content.split("\n")[0]; - const hasOldMeta = firstLine.includes("//META"); - if (hasOldMeta) return this.parseOldMeta(content); - const hasNewMeta = firstLine.includes("/**"); - if (hasNewMeta) return this.parseNewMeta(content); - throw new MetaError("META was not found."); - } - - parseOldMeta(content) { - const meta = content.split("\n")[0]; - const rawMeta = meta.substring(meta.lastIndexOf("//META") + 6, meta.lastIndexOf("*//")); - if (meta.indexOf("META") < 0) throw new MetaError("META was not found."); - const parsed = Utils.testJSON(rawMeta); - if (!parsed) throw new MetaError("META could not be parsed."); - if (!parsed.name) throw new MetaError("META missing name data."); - parsed.format = "json"; - return parsed; - } - - parseNewMeta(content) { - const block = content.split("/**", 2)[1].split("*/", 1)[0]; - const out = {}; - let field = ""; - let accum = ""; - for (const line of block.split(splitRegex)) { - if (line.length === 0) continue; - if (line.charAt(0) === "@" && line.charAt(1) !== " ") { - out[field] = accum; - const l = line.indexOf(" "); - field = line.substr(1, l - 1); - accum = line.substr(l + 1); - } - else { - accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@"); - } - } - out[field] = accum.trim(); - delete out[""]; - out.format = "jsdoc"; - return out; - } - - getContentRequire(type) { - const isPlugin = type === "plugin"; - const self = this; - const originalRequire = isPlugin ? originalJSRequire : originalCSSRequire; - return function(module, filename) { - const baseFolder = isPlugin ? self.pluginsFolder : self.themesFolder; - const possiblePath = path.resolve(baseFolder, path.basename(filename)); - if (!fs.existsSync(possiblePath) || filename !== fs.realpathSync(possiblePath)) return Reflect.apply(originalRequire, this, arguments); - let content = fs.readFileSync(filename, "utf8"); - content = Utils.stripBOM(content); - - const stats = fs.statSync(filename); - const meta = self.extractMeta(content); - meta.filename = path.basename(filename); - meta.added = stats.atimeMs; - meta.modified = stats.mtimeMs; - meta.size = stats.size; - if (!isPlugin) { - meta.css = content; - if (meta.format == "json") meta.css = meta.css.split("\n").slice(1).join("\n"); - content = `module.exports = ${JSON.stringify(meta)};`; - } - if (isPlugin) { - module._compile(content, module.filename); - const didExport = !Utils.isEmpty(module.exports); - if (didExport) { - meta.type = module.exports; - module.exports = meta; - content = ""; - } - else { - Utils.warn("Module Not Exported", `${meta.name}, please start setting module.exports`); - content += `\nmodule.exports = ${JSON.stringify(meta)};\nmodule.exports.type = ${meta.exports || meta.name};`; - } - } - module._compile(content, filename); - }; - } - - makePlaceholderPlugin(data) { - return {plugin: { - start: () => {}, - getName: () => {return data.name || data.filename;}, - getAuthor: () => {return "???";}, - getDescription: () => {return data.message ? data.message : "This plugin was unable to be loaded. Check the author's page for updates.";}, - getVersion: () => {return "???";} - }, - name: data.name || data.filename, - filename: data.filename, - source: data.source ? data.source : "", - website: data.website ? data.website : "" - }; - } - - async loadContent(filename, type) { - if (typeof(filename) === "undefined" || typeof(type) === "undefined") return; - const isPlugin = type === "plugin"; - const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; - - if(settingsCookie["fork-ps-6"]){ - let result = await new Promise(resolve => { - processFile(path.resolve(baseFolder, filename), (result) => { - console.log(result) - resolve(result) - }, (hash) => { - resolve({ - suspect: false, - hash: hash, - filename: filename, - name: filename - }) - }, true) - }) - if(result){ - addonCache[result.hash] = { - timestamp: Date.now(), - hash: result.hash, - result: result - } - this.saveAddonCache() - if(result.suspect){ - return { - name: filename, - file: filename, - message: "This plugin might be dangerous ("+result.harm+").", - error: new Error("This plugin might be dangerous ("+result.harm+").") - } - } - } - } - - try {window.require(path.resolve(baseFolder, filename));} - catch (error) {return {name: filename, file: filename, message: "Could not be compiled.", error: {message: error.message, stack: error.stack}};} - const content = window.require(path.resolve(baseFolder, filename)); - if(!content.name)return {name: filename, file: filename, message: "Cannot escape the ID.", error: new Error("Cannot read property 'replace' of undefined")} - content.id = Utils.escapeID(content.name); - //if(!id)return {name: filename, file: filename, message: "Invalid ID", error: new Error("Please fix the name of "+filename+". BetterDiscord can't escape an ID.")} - if (isPlugin) { - if (!content.type) return; - try { - content.plugin = new content.type(); - delete bdplugins[content.plugin.getName()]; - bdplugins[content.plugin.getName()] = content; - } - catch (error) {return {name: filename, file: filename, message: "Could not be constructed.", error: {message: error.message, stack: error.stack}};} - } - else { - delete bdthemes[content.name]; - bdthemes[content.name] = content; - } - } - - unloadContent(filename, type) { - if (typeof(filename) === "undefined" || typeof(type) === "undefined") return; - const isPlugin = type === "plugin"; - const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; - try { - delete window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))]; - } - catch (err) {return {name: filename, file: filename, message: "Could not be unloaded.", error: {message: err.message, stack: err.stack}};} - } - - isLoaded(filename, type) { - const isPlugin = type === "plugin"; - const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; - try {window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))];} - catch (err) {return false;} - return true; - } - - async reloadContent(filename, type) { - const cantUnload = this.unloadContent(filename, type); - if (cantUnload) return cantUnload; - return await this.loadContent(filename, type); - } - - loadNewContent(type) { - const isPlugin = type === "plugin"; - const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; - const basedir = isPlugin ? this.pluginsFolder : this.themesFolder; - const files = fs.readdirSync(basedir); - const contentList = Object.values(isPlugin ? bdplugins : bdthemes); - const removed = contentList.filter(t => !files.includes(t.filename)).map(c => isPlugin ? c.plugin.getName() : c.name); - const added = files.filter(f => !contentList.find(t => t.filename == f) && f.endsWith(fileEnding) && fs.statSync(path.resolve(basedir, f)).isFile()); - return {added, removed}; - } - - async loadAllContent(type) { - this.patchExtensions() - const isPlugin = type === "plugin"; - const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; - const basedir = isPlugin ? this.pluginsFolder : this.themesFolder; - const errors = []; - const files = fs.readdirSync(basedir); - - for (const filename of files) { - if (!fs.statSync(path.resolve(basedir, filename)).isFile() || !filename.endsWith(fileEnding)) continue; - const error = await this.loadContent(filename, type); - if (error) errors.push(error); - } - - return errors; - } - - loadPlugins() {return this.loadAllContent("plugin");} - loadThemes() {return this.loadAllContent("theme");} -}; - -/** - * Don't expose contentManager - could be dangerous for now +import {bdConfig, bdplugins, bdthemes, settingsCookie} from "../0globals"; +import pluginModule from "./pluginModule"; +import themeModule from "./themeModule"; +import Utils from "./utils"; +import dataStore from "./dataStore"; +import { encryptSettingsCache, decryptSettingsCache, processFile } from "./pluginCertifier"; + +const path = require("path"); +const fs = require("fs"); +const Module = require("module").Module; +Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules")); +class MetaError extends Error { + constructor(message) { + super(message); + this.name = "MetaError"; + } +} +const originalJSRequire = Module._extensions[".js"]; +const originalCSSRequire = Module._extensions[".css"] ? Module._extensions[".css"] : () => {return null;}; +const splitRegex = /[^\S\r\n]*?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/; +const escapedAtRegex = /^\\@/; + +export let addonCache = {} + +let hasPatched = false +export default new class ContentManager { + + constructor() { + this.timeCache = {}; + this.watchers = {}; + } + + patchExtensions(){ + if(hasPatched)return + hasPatched = true + Module._extensions[".js"] = this.getContentRequire("plugin"); + Module._extensions[".css"] = this.getContentRequire("theme"); + } + + get pluginsFolder() {return this._pluginsFolder || (this._pluginsFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "plugins/")));} + get themesFolder() {return this._themesFolder || (this._themesFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "themes/")));} + + loadAddonCertifierCache(){ + if(typeof dataStore.getSettingGroup("PluginCertifierHashes") !== "string"){ + dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}")) + }else{ + try{ + addonCache = JSON.parse(decryptSettingsCache(dataStore.getSettingGroup("PluginCertifierHashes"))) + }catch(e){ + dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}")) + addonCache = {} + } + } + Object.keys(addonCache) + .forEach(key => { + let value = addonCache[key] + if(!value || typeof value !== "object" || Array.isArray(value))return delete addonCache[key] + + let props = [{ + key: "timestamp", + type: "number" + }, { + key: "result", + type: "object" + }, { + key: "hash", + type: "string" + }] + for(let prop of props){ + if(!(prop.key in value) || typeof value[prop.key] !== prop.type){ + delete addonCache[key] + return + } + } + if(value.hash !== key){ + delete addonCache[key] + return + } + if(value.result.suspect){ // refetch from remote to be sure you're up to date. + delete addonCache[key] + return + } + }) + this.saveAddonCache() + } + + saveAddonCache(){ + dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache(JSON.stringify(addonCache))) + } + + watchContent(contentType) { + if (this.watchers[contentType]) return; + const isPlugin = contentType === "plugin"; + const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; + const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; + this.watchers[contentType] = fs.watch(baseFolder, {persistent: false}, async (eventType, filename) => { + if (!eventType || !filename || !filename.endsWith(fileEnding)) return; + await new Promise(r => setTimeout(r, 50)); + try {fs.statSync(path.resolve(baseFolder, filename));} + catch (err) { + if (err.code !== "ENOENT") return; + delete this.timeCache[filename]; + if (isPlugin) return pluginModule.unloadPlugin(filename); + return themeModule.unloadTheme(filename); + } + if (!fs.statSync(path.resolve(baseFolder, filename)).isFile()) return; + const stats = fs.statSync(path.resolve(baseFolder, filename)); + if (!stats || !stats.mtime || !stats.mtime.getTime()) return; + if (typeof(stats.mtime.getTime()) !== "number") return; + if (this.timeCache[filename] == stats.mtime.getTime()) return; + this.timeCache[filename] = stats.mtime.getTime(); + if (eventType == "rename") { + if (isPlugin) await pluginModule.loadPlugin(filename); + else await themeModule.loadTheme(filename); + } + if (eventType == "change") { + if (isPlugin) await pluginModule.reloadPlugin(filename); + else await themeModule.reloadTheme(filename); + } + }); + } + + unwatchContent(contentType) { + if (!this.watchers[contentType]) return; + this.watchers[contentType].close(); + delete this.watchers[contentType]; + } + + extractMeta(content) { + const firstLine = content.split("\n")[0]; + const hasOldMeta = firstLine.includes("//META"); + if (hasOldMeta) return this.parseOldMeta(content); + const hasNewMeta = firstLine.includes("/**"); + if (hasNewMeta) return this.parseNewMeta(content); + throw new MetaError("META was not found."); + } + + parseOldMeta(content) { + const meta = content.split("\n")[0]; + const rawMeta = meta.substring(meta.lastIndexOf("//META") + 6, meta.lastIndexOf("*//")); + if (meta.indexOf("META") < 0) throw new MetaError("META was not found."); + const parsed = Utils.testJSON(rawMeta); + if (!parsed) throw new MetaError("META could not be parsed."); + if (!parsed.name) throw new MetaError("META missing name data."); + parsed.format = "json"; + return parsed; + } + + parseNewMeta(content) { + const block = content.split("/**", 2)[1].split("*/", 1)[0]; + const out = {}; + let field = ""; + let accum = ""; + for (const line of block.split(splitRegex)) { + if (line.length === 0) continue; + if (line.charAt(0) === "@" && line.charAt(1) !== " ") { + out[field] = accum; + const l = line.indexOf(" "); + field = line.substr(1, l - 1); + accum = line.substr(l + 1); + } + else { + accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@"); + } + } + out[field] = accum.trim(); + delete out[""]; + out.format = "jsdoc"; + return out; + } + + getContentRequire(type) { + const isPlugin = type === "plugin"; + const self = this; + const originalRequire = isPlugin ? originalJSRequire : originalCSSRequire; + return function(module, filename) { + const baseFolder = isPlugin ? self.pluginsFolder : self.themesFolder; + const possiblePath = path.resolve(baseFolder, path.basename(filename)); + if (!fs.existsSync(possiblePath) || filename !== fs.realpathSync(possiblePath)) return Reflect.apply(originalRequire, this, arguments); + let content = fs.readFileSync(filename, "utf8"); + content = Utils.stripBOM(content); + + const stats = fs.statSync(filename); + const meta = self.extractMeta(content); + meta.filename = path.basename(filename); + meta.added = stats.atimeMs; + meta.modified = stats.mtimeMs; + meta.size = stats.size; + if (!isPlugin) { + meta.css = content; + if (meta.format == "json") meta.css = meta.css.split("\n").slice(1).join("\n"); + content = `module.exports = ${JSON.stringify(meta)};`; + } + if (isPlugin) { + module._compile(content, module.filename); + const didExport = !Utils.isEmpty(module.exports); + if (didExport) { + meta.type = module.exports; + module.exports = meta; + content = ""; + } + else { + Utils.warn("Module Not Exported", `${meta.name}, please start setting module.exports`); + content += `\nmodule.exports = ${JSON.stringify(meta)};\nmodule.exports.type = ${meta.exports || meta.name};`; + } + } + module._compile(content, filename); + }; + } + + makePlaceholderPlugin(data) { + return {plugin: { + start: () => {}, + getName: () => {return data.name || data.filename;}, + getAuthor: () => {return "???";}, + getDescription: () => {return data.message ? data.message : "This plugin was unable to be loaded. Check the author's page for updates.";}, + getVersion: () => {return "???";} + }, + name: data.name || data.filename, + filename: data.filename, + source: data.source ? data.source : "", + website: data.website ? data.website : "" + }; + } + + async loadContent(filename, type) { + if (typeof(filename) === "undefined" || typeof(type) === "undefined") return; + const isPlugin = type === "plugin"; + const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; + + if(settingsCookie["fork-ps-6"]){ + let result = await new Promise(resolve => { + processFile(path.resolve(baseFolder, filename), (result) => { + console.log(result) + resolve(result) + }, (hash) => { + resolve({ + suspect: false, + hash: hash, + filename: filename, + name: filename + }) + }, true) + }) + if(result){ + addonCache[result.hash] = { + timestamp: Date.now(), + hash: result.hash, + result: result + } + this.saveAddonCache() + if(result.suspect){ + return { + name: filename, + file: filename, + message: "This plugin might be dangerous ("+result.harm+").", + error: new Error("This plugin might be dangerous ("+result.harm+").") + } + } + } + } + + try {window.require(path.resolve(baseFolder, filename));} + catch (error) {return {name: filename, file: filename, message: "Could not be compiled.", error: {message: error.message, stack: error.stack}};} + const content = window.require(path.resolve(baseFolder, filename)); + if(!content.name)return {name: filename, file: filename, message: "Cannot escape the ID.", error: new Error("Cannot read property 'replace' of undefined")} + content.id = Utils.escapeID(content.name); + //if(!id)return {name: filename, file: filename, message: "Invalid ID", error: new Error("Please fix the name of "+filename+". BetterDiscord can't escape an ID.")} + if (isPlugin) { + if (!content.type) return; + try { + content.plugin = new content.type(); + delete bdplugins[content.plugin.getName()]; + bdplugins[content.plugin.getName()] = content; + } + catch (error) {return {name: filename, file: filename, message: "Could not be constructed.", error: {message: error.message, stack: error.stack}};} + } + else { + delete bdthemes[content.name]; + bdthemes[content.name] = content; + } + } + + unloadContent(filename, type) { + if (typeof(filename) === "undefined" || typeof(type) === "undefined") return; + const isPlugin = type === "plugin"; + const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; + try { + delete window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))]; + } + catch (err) {return {name: filename, file: filename, message: "Could not be unloaded.", error: {message: err.message, stack: err.stack}};} + } + + isLoaded(filename, type) { + const isPlugin = type === "plugin"; + const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; + try {window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))];} + catch (err) {return false;} + return true; + } + + async reloadContent(filename, type) { + const cantUnload = this.unloadContent(filename, type); + if (cantUnload) return cantUnload; + return await this.loadContent(filename, type); + } + + loadNewContent(type) { + const isPlugin = type === "plugin"; + const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; + const basedir = isPlugin ? this.pluginsFolder : this.themesFolder; + const files = fs.readdirSync(basedir); + const contentList = Object.values(isPlugin ? bdplugins : bdthemes); + const removed = contentList.filter(t => !files.includes(t.filename)).map(c => isPlugin ? c.plugin.getName() : c.name); + const added = files.filter(f => !contentList.find(t => t.filename == f) && f.endsWith(fileEnding) && fs.statSync(path.resolve(basedir, f)).isFile()); + return {added, removed}; + } + + async loadAllContent(type) { + this.patchExtensions() + const isPlugin = type === "plugin"; + const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; + const basedir = isPlugin ? this.pluginsFolder : this.themesFolder; + const errors = []; + const files = fs.readdirSync(basedir); + + for (const filename of files) { + if (!fs.statSync(path.resolve(basedir, filename)).isFile() || !filename.endsWith(fileEnding)) continue; + const error = await this.loadContent(filename, type); + if (error) errors.push(error); + } + + return errors; + } + + loadPlugins() {return this.loadAllContent("plugin");} + loadThemes() {return this.loadAllContent("theme");} +}; + +/** + * Don't expose contentManager - could be dangerous for now */ \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/core.js b/BetterDiscordApp/src/modules/core.js index 4a715f5..8d836e2 100644 --- a/BetterDiscordApp/src/modules/core.js +++ b/BetterDiscordApp/src/modules/core.js @@ -1,705 +1,705 @@ -import {bdConfig, minSupportedVersion, bbdVersion, settingsCookie, bdpluginErrors, bdthemeErrors, bbdChangelog, defaultCookie, currentDiscordVersion, defaultRPC, settingsRPC, lightcordSettings} from "../0globals"; -import Utils from "./utils"; - -import BDV2 from "./v2"; -import settingsPanel from "./settingsPanel"; -import pluginModule from "./pluginModule"; -import themeModule from "./themeModule"; -import DataStore from "./dataStore"; -import WebpackModules from "./webpackModules"; -import DOM from "./domtools"; - -import BDLogo from "../ui/bdLogo"; -import TooltipWrap from "../ui/tooltipWrap"; -import LightcordLogo from "../svg/Lightcord"; -import PluginCertifier from "./pluginCertifier"; -import distant, { uuidv4 } from "./distant"; -import EmojiModule from "./emojiModule" -import {remote as electron} from "electron" -import v2 from "./v2"; -import contentManager from "./contentManager"; - -let methods -function Core() {} - - -Core.prototype.setConfig = function(config) { - if (this.hasStarted) return; - Object.assign(bdConfig, config); -}; - -Object.defineProperty(Core.prototype, "methods", { - get(){ - return methods - } -}) - -Core.prototype.setMethods = function(m) { - if (this.hasStarted) return; - methods = m -}; - -Core.prototype.init = async function() { - if (this.hasStarted) return; - this.hasStarted = true; - - if (!Array.prototype.flat) { - Utils.alert("Not Supported", "BetterDiscord v" + bbdVersion + " does not support this old version (" + currentDiscordVersion + ") of Discord. Please update your Discord installation before proceeding."); - return; - } - - if (bdConfig.version < minSupportedVersion) { - Utils.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 (window.ED) { - Utils.alert("Not Supported", "BandagedBD does not work with EnhancedDiscord. Please uninstall one of them."); - return; - } - - if (window.WebSocket && window.WebSocket.name && window.WebSocket.name.includes("Patched")) { - Utils.alert("Not Supported", "BandagedBD does not work with Powercord. Please uninstall one of them."); - return; - } - - Utils.suppressErrors(this.patchAttributes.bind(this), "LC Plugin Certifier Patch")(); - - Utils.log("Startup", "Initializing Settings"); - this.initSettings(); - - await this.checkForGuilds(); - BDV2.initialize(); - Utils.log("Startup", "Updating Settings"); - settingsPanel.initializeSettings(); - - Utils.log("Startup", "Loading Addons Cache") - await contentManager.loadAddonCertifierCache() - - Utils.log("Startup", "Loading Plugins"); - await pluginModule.loadPlugins(); - - Utils.log("Startup", "Loading Themes"); - await themeModule.loadThemes(); - - DOM.addStyle("customcss", Buffer.from(DataStore.getBDData("bdcustomcss"), "base64").toString("utf8")); - - window.addEventListener("beforeunload", function() { - if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click(); - }); - - PluginCertifier.start() - - Utils.log("Startup", "Removing Loading Icon"); - if (document.getElementsByClassName("bd-loaderv2").length) document.getElementsByClassName("bd-loaderv2")[0].remove(); - Utils.log("Startup", "Initializing Main Observer"); - this.initObserver(); - - // Show loading errors - if (settingsCookie["fork-ps-1"]) { - Utils.log("Startup", "Collecting Startup Errors"); - Utils.showContentErrors({plugins: bdpluginErrors, themes: bdthemeErrors}); - } - - const previousVersion = DataStore.getBDData("version"); - if (bbdVersion > previousVersion) { - if (bbdChangelog) this.showChangelogModal(bbdChangelog); - DataStore.setBDData("version", bbdVersion); - } - - EmojiModule.start() - - Utils.suppressErrors(this.patchSocial.bind(this), "BD Social Patch")(); - Utils.suppressErrors(this.patchGuildPills.bind(this), "BD Guild Pills Patch")(); - Utils.suppressErrors(this.patchGuildListItems.bind(this), "BD Guild List Items Patch")(); - Utils.suppressErrors(this.patchGuildSeparator.bind(this), "BD Guild Separator Patch")(); - Utils.suppressErrors(this.patchMessageHeader.bind(this), "BD Badge Chat Patch")(); - Utils.suppressErrors(this.patchMemberList.bind(this), "BD Badge Member List Patch")(); - Utils.suppressErrors(this.patchAttachment.bind(this), "LC Plugin Certifier Patch")(); - - if(bdConfig.haveInstalledDefault){ - let alert = Utils.alert("First Installation", "As it is the first time you install Lightcord, We added two default themes and one default plugin in your plugin/theme folder. Check it in the Plugin/Theme settings.") - await new Promise((resolve) => { - alert.onClose(resolve) - }) - } - const logo = document.querySelector("#app-mount > div.typeWindows-1za-n7.withFrame-haYltI.titleBar-AC4pGV.horizontalReverse-3tRjY7.flex-1O1GKY.directionRowReverse-m8IjIq.justifyStart-2NDFzi.alignStretch-DpGPf3.da-typeWindows.da-withFrame.da-titleBar.da-horizontalReverse.da-flex.da-directionRowReverse.da-justifyStart.da-alignStretch > div.wordmarkWindows-1v0lYD.wordmark-2iDDfm.da-wordmarkWindows.da-wordmark") - if(logo){ - logo.style.top = "3px" - logo.innerHTML = ` - - - -` - } -}; - -Core.prototype.patchAttributes = async function() { - let attribsPatchs = [] - this.cancelPatchAttributes = function() { - attribsPatchs.forEach(e => e()) - } - - while(!v2.MessageComponent)await new Promise(resolve => setTimeout(resolve, 100)) - - // TODO: try to patch correctly the user popout on a next update - const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); - window.Lightcord.Api.ensureExported(e => e.default && e.default.displayName === "DiscordTag") - .then(DiscordTag => { - let DiscordTagComp = DiscordTag.default - DiscordTag.default = function(props){ - let returnValue = DiscordTagComp(props) - - let id = uuidv4() - - let badgeDiv = BDV2.React.createElement("div", { - style: { - display: "inline", - marginTop: "5px" - } - }, BDV2.React.createElement("span", { - id: "badges-"+id, - key: "badges-"+id, - style: { - display: "inherit" - } - })) - - let children = [returnValue] - - if (props.user.id === "249746236008169473") { // Rauenzi: BandagedBD Developer - children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, - BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, - BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) - ) - ) - ); - } else if (props.user.id === "696481194443014174" || props.user.id === "696003456611385396"){ // Not Thomiz: Lightcord Developer, Phorcys: Lightcord Developer - children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, - BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, - BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) - ) - ) - ); - } - - children.push(badgeDiv) - let div = BDV2.React.createElement("div", { - style: { - display: "block" - } - }, children) - applyBadges(id, props.user, false) - - return div - } - DiscordTag.default.displayName = DiscordTagComp.displayName - }) - - attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { - if(data.methodArguments[0].childrenMessageContent.props.message){ // this can be a blocked message (not opened) - data.returnValue.props["data-message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id - } - }})) - /* - attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { - data.returnValue.props["message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id - }}))*/ -} - -Core.prototype.checkForGuilds = function() { - let timesChecked = 0; - return new Promise(resolve => { - const checkForGuilds = function() { - const wrapper = BDV2.guildClasses.wrapper.split(" ")[0]; - if (document.querySelectorAll(`.${wrapper}`).length > 0) timesChecked++; - const guild = BDV2.guildClasses.listItem.split(" ")[0]; - const blob = BDV2.guildClasses.blobContainer.split(" ")[0]; - if (document.querySelectorAll(`.${wrapper} .${guild} .${blob}`).length > 0) return resolve(bdConfig.deferLoaded = true); - else if (timesChecked >= 50) return resolve(bdConfig.deferLoaded = true); - setTimeout(checkForGuilds, 100); - }; - if (document.readyState != "loading") setTimeout(checkForGuilds, 100); - document.addEventListener("DOMContentLoaded", () => {setTimeout(checkForGuilds, 100);}); - }); -}; - -Core.prototype.injectExternals = async function() { - // No externals -}; - -Core.prototype.initSettings = function () { - DataStore.initialize(); - if(!DataStore.getSettingGroup("lightcord-settings")){ - for(let key in lightcordSettings){ - delete lightcordSettings[key] - } - } - if(!DataStore.getSettingGroup("rpc")){ - Object.assign(settingsRPC, defaultRPC); - } - if(!DataStore.getSettingGroup("settings")){ - Object.assign(settingsCookie, defaultCookie); - settingsPanel.saveSettings(); - } else { - settingsPanel.loadSettings(); - for (const setting in defaultCookie) { - if (settingsCookie[setting] == undefined) { - settingsCookie[setting] = defaultCookie[setting]; - settingsPanel.saveSettings(); - } - } - } - window.Lightcord.Api.ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections) - .then(settingModule => { - - let getPredicateSections = settingModule.default.prototype.getPredicateSections - settingModule.default.prototype.getPredicateSections = function(){ - let result = getPredicateSections.call(this, ...arguments) - - if(!result[1])return result - if(result[1].section === "My Account"){ // user settings, not guild settings - let poped = [] - - poped.push(result.pop()) - poped.push(result.pop()) - poped.push(result.pop()) - poped.push(result.pop()) - - result.push(...settingsPanel.renderSidebar(this)) - - while(poped[0]){ - result.push(poped.pop()) - } - } - console.log(result) - return result - } - }) -}; - - -let classNameLayer -let classNameSocialLinks -let classNameModal - -Core.prototype.initObserver = function () { - const mainObserver = new MutationObserver((mutations) => { - for (let i = 0, mlen = mutations.length; i < mlen; i++) { - const mutation = mutations[i]; - if (typeof pluginModule !== "undefined") pluginModule.rawObserver(mutation); - - // if there was nothing added, skip - if (!mutation.addedNodes.length || !(mutation.addedNodes[0] instanceof Element)) continue; - - const node = mutation.addedNodes[0]; - - if(!classNameLayer)classNameLayer = BDModules.get((e) => e.layer && typeof e.layer === "string" && e.animating)[0].layer - if(!classNameSocialLinks)classNameSocialLinks = BDModules.get((e) => e.socialLinks && typeof e.socialLinks === "string")[0].socialLinks - if(!classNameModal)classNameModal = BDModules.get((e) => e.modal && typeof e.modal === "string" && e.inner && typeof e.inner === "string" && !e.responsiveWidthMobile)[0].modal - - if (node.classList.contains(classNameLayer)) { - if (node.getAttribute("aria-label") === "GUILD_SETTINGS"){ - node.setAttribute("layer-id", "server-settings"); - node.setAttribute("id", "server-settings"); - } - - if (node.getElementsByClassName(classNameSocialLinks).length) { - node.setAttribute("layer-id", "user-settings"); - node.setAttribute("id", "user-settings"); - } - } - - if (node.parentElement == document.body && node.querySelector("#ace_settingsmenu")) node.id = "ace_settingsmenu_container"; - - // Emoji Picker - //node.getElementsByClassName("emojiPicker-3m1S-j").length && !node.querySelector(".emojiPicker-3m1S-j").parentElement.classList.contains("animatorLeft-1EQxU0") - //if (node.classList.contains(classNameLayer2) && node.getElementsByClassName(classNameEmojiPicker).length && !node.querySelector("."+classNameEmojiPicker).parentElement.classList.contains(classNameAnimatorLeft)) quickEmoteMenu.obsCallback(node); - - } - }); - - mainObserver.observe(document, { - childList: true, - subtree: true - }); -}; - -Core.prototype.showChangelogModal = function(options = {}) { - return Utils.showChangelogModal(options); -}; - -Core.prototype.alert = function(title, content) { - return Utils.alert(title, content); -}; - -Core.prototype.patchSocial = function() { - if (this.socialPatch) return; - const TabBar = WebpackModules.find(m => m.displayName == "TabBar"); - const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); - if (!TabBar) return; - this.socialPatch = Utils.monkeyPatch(TabBar.prototype, "render", {after: (data) => { - const children = data.returnValue.props.children; - if (!children || !children.length || children.length < 3) return; - if (children[children.length - 3].type.displayName !== "Separator") return; - if (!children[children.length - 2].type.toString().includes("socialLinks")) return; - if (Anchor) { - let socialModule1 = BDModules.get(e => e.socialLinks)[0] - const original = children[children.length - 2].type; - const newOne = function() { - const returnVal = original(...arguments); - returnVal.props.children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord"}, - BDV2.React.createElement(Anchor, {className: "bd-social-link "+socialModule1.link, href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, - BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-social-logo"}) - ) - ) - ); - returnVal.props.children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD"}, - BDV2.React.createElement(Anchor, {className: "bd-social-link "+socialModule1.link, href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, - BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-social-logo"}) - ) - ) - ); - return returnVal; - }; - children[children.length - 2].type = newOne; - } - - let [ - classNameColorMuted, - sizes, - classNameVersionHash - ] = [ - BDModules.get(e => e.colorMuted)[0].colorMuted, - BDModules.get(e => e.size32)[0], - BDModules.get(e => e.versionHash)[0].versionHash - ] - - const versionHash = `(${bdConfig.hash ? bdConfig.hash.substring(0, 7) : bdConfig.branch})`; - const additional = [ - BDV2.react.createElement("div", {className: `${classNameColorMuted} ${sizes.size12}`}, `Lightcord ${electron.getGlobal("BuildInfo").version} `, BDV2.react.createElement("span", {className: classNameVersionHash+" da-versionHash"}, `(${(electron.getGlobal("BuildInfo").commit || "Unknown").slice(0, 7)})`)), - BDV2.react.createElement("div", {className: `${classNameColorMuted} ${sizes.size12}`}, `BBD ${bbdVersion} `, BDV2.react.createElement("span", {className: classNameVersionHash+" da-versionHash"}, versionHash)) - ] - - - const originalVersions = children[children.length - 1].type; - children[children.length - 1].type = function() { - const returnVal = originalVersions(...arguments); - returnVal.props.children.splice(1, 0, additional); - return returnVal; - }; - }}); -}; - -const getGuildClasses = function() { - const guildsWrapper = WebpackModules.findByProps("wrapper", "unreadMentionsBar"); - const guilds = WebpackModules.findByProps("guildsError", "selected"); - const pill = WebpackModules.findByProps("blobContainer"); - return Object.assign({}, guildsWrapper, guilds, pill); -}; - -Core.prototype.patchGuildListItems = function() { - if (this.guildListItemsPatch) return; - const GuildClasses = getGuildClasses(); - const listItemClass = GuildClasses.listItem.split(" ")[0]; - const blobClass = GuildClasses.blobContainer.split(" ")[0]; - const reactInstance = BDV2.getInternalInstance(document.querySelector(`.${listItemClass} .${blobClass}`).parentElement); - const GuildComponent = reactInstance.return.type; - if (!GuildComponent) return; - this.guildListItemsPatch = Utils.monkeyPatch(GuildComponent.prototype, "render", {after: (data) => { - if (data.returnValue && data.thisObject) { - const returnValue = data.returnValue; - const guildData = data.thisObject.props; - let className = returnValue.props.className - className += " bd-guild"; - if (guildData.unread) className += " bd-unread"; - if (guildData.selected) className += " bd-selected"; - if (guildData.audio) className += " bd-audio"; - if (guildData.video) className += " bd-video"; - if (guildData.badge) className += " bd-badge"; - if (guildData.animatable) className += " bd-animatable"; - returnValue.props.className = className - return returnValue; - } - }}); -}; - -Core.prototype.patchGuildPills = function() { - if (this.guildPillPatch) return; - const guildPill = WebpackModules.find(m => m.default && !m.default.displayName && m.default.toString && m.default.toString().includes("translate3d")); - if (!guildPill) return; - this.guildPillPatch = Utils.monkeyPatch(guildPill, "default", {after: (data) => { - const props = data.methodArguments[0]; - if (props.unread) data.returnValue.props.className += " bd-unread"; - if (props.selected) data.returnValue.props.className += " bd-selected"; - if (props.hovered) data.returnValue.props.className += " bd-hovered"; - return data.returnValue; - }}); -}; - -Core.prototype.patchGuildSeparator = function() { - if (this.guildSeparatorPatch) return; - const Guilds = WebpackModules.findByDisplayName("Guilds"); - const guildComponents = WebpackModules.findByProps("renderListItem"); - if (!guildComponents || !Guilds) return; - const GuildSeparator = function() { - const returnValue = guildComponents.Separator(...arguments); - returnValue.props.className += " bd-guild-separator"; - return returnValue; - }; - this.guildSeparatorPatch = Utils.monkeyPatch(Guilds.prototype, "render", {after: (data) => { - data.returnValue.props.children[1].props.children[3].type = GuildSeparator; - }}); -}; - -Core.prototype.patchAttachment = function() { - if (this.AttachmentPatch) return; - const Attachment = BDModules.get(e => e.default && e.default.displayName === "Attachment")[0] // temporary - const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); - if (!Anchor || !Attachment || !Attachment.default) return; - this.AttachmentPatch = Utils.monkeyPatch(Attachment, "default", {after: (data) => { - if(!settingsCookie["fork-ps-6"])return - const attachment = data.methodArguments[0] || null - const children = Utils.getNestedProp(data.returnValue, "props.children"); - - if (!children || !attachment || !attachment.url)return - if (!Array.isArray(children)) return; - - const id = uuidv4() - children.push(BDV2.react.createElement("span", { - id: "certified-"+id - })) - PluginCertifier.patch(attachment, "certified-"+id) - }}) -} - -Core.prototype.patchMessageHeader = function() { - if (this.messageHeaderPatch) return; - const MessageHeader = WebpackModules.findByProps("MessageTimestamp"); - const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); - if (!Anchor || !MessageHeader || !MessageHeader.default) return; - this.messageHeaderPatch = Utils.monkeyPatch(MessageHeader, "default", {after: (data) => { - const author = Utils.getNestedProp(data.methodArguments[0], "message.author"); - // const header = Utils.getNestedProp(data.returnValue, "props.children.1.props"); - const children = Utils.getNestedProp(data.returnValue, "props.children.1.props.children.1.props.children"); - if (!children || !author || !author.id)return - // if (header && header.className) header.className += " " - if (!Array.isArray(children)) return; - if (author.id === "249746236008169473") { // Rauenzi: BandagedBD Developer - children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, - BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, - BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) - ) - ) - ); - } else if (author.id === "696481194443014174" || author.id === "696003456611385396"){ // Not Thomiz: Lightcord Developer, Phorcys: Lightcord Developer - children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, - BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, - BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) - ) - ) - ); - } - const id = uuidv4() - children.push( - BDV2.React.createElement("div", { - id: "badges-"+id, - style: { - display: "inline" - } - }) - ) - applyBadges(id, author, true) - }}); -}; - -function applyBadges(id, user, chat){ - process.nextTick(() => { - const div = document.getElementById("badges-"+id) - if(!div || div.childNodes.length > 0)return - if(div.childNodes.length)return - let blockDiv = document.createElement("div") - blockDiv.style.display = "none" - div.appendChild(blockDiv) - - const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); - - distant.getBadges(user.id) - .then(badges => { - badges.forEach(badge => { - const props = { - svg: { - size: "16px", - className: "bd-logo", - width: "16px", - } - } - badge.scopes.forEach(scope => { - if(scope === "user"){// require user - props.user = user - } - }) - if(!badge.href){ - props.Anchor = Anchor - props.href = { - className: chat ? "bd-chat-badge" : "bd-member-badge", - title: badge.name, - target: "_blank" - } - } - const element = BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: badge.name}, - badge.href ? BDV2.react.createElement(Anchor, { - href: badge.href, - className: chat ? "bd-chat-badge" : "bd-member-badge", - title: badge.name, - target: "_blank" - }, BDV2.React.createElement(badge.component, props)) : BDV2.React.createElement(badge.component, props) - ) - const div2 = document.createElement("div") - BDV2.reactDom.render(element, div2) - div2.childNodes.forEach(node => { - div.appendChild(node) - }) - }) - }) - }) -} - -Core.prototype.patchMemberList = function() { - if (this.memberListPatch) return; - const MemberListItem = WebpackModules.findByDisplayName("MemberListItem"); - const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); - if (!Anchor || !MemberListItem || !MemberListItem.prototype || !MemberListItem.prototype.renderDecorators) return; - this.memberListPatch = Utils.monkeyPatch(MemberListItem.prototype, "renderDecorators", {after: (data) => { - const user = Utils.getNestedProp(data.thisObject, "props.user"); - const children = Utils.getNestedProp(data.returnValue, "props.children"); - if (!children || !user || !user.id)return - // if (header && header.className) header.className += " " - if (!Array.isArray(children)) return; - if (user.id === "249746236008169473") { - children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, - BDV2.React.createElement(Anchor, {className: "bd-member-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, - BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) - ) - ) - ); - } else if (user.id === "696481194443014174" || user.id === "696003456611385396"){ - children.push( - BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, - BDV2.React.createElement(Anchor, {className: "bd-member-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, - BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) - ) - ) - ); - } - const id = uuidv4() - children.push( - BDV2.React.createElement("div", {id: "badges-"+id}) - ) - applyBadges(id, user, false) - }}); -}; - -Core.prototype.updateInjector = async function() { - // There will never be an injection path, so we do not need the code below. - // Insert comments so it will be erased when production. - - const injectionPath = DataStore.injectionPath; - if (!injectionPath) return false; - - /* - - const fs = require("fs"); - const path = require("path"); - const rmrf = require("rimraf"); - const yauzl = require("yauzl"); - const mkdirp = require("mkdirp"); - const request = require("request"); - - const parentPath = path.resolve(injectionPath, ".."); - const folderName = path.basename(injectionPath); - const zipLink = "https://github.com/rauenzi/BetterDiscordApp/archive/injector.zip"; - const savedZip = path.resolve(parentPath, "injector.zip"); - const extractedFolder = path.resolve(parentPath, "BetterDiscordApp-injector"); - - // Download the injector zip file - Utils.log("InjectorUpdate", "Downloading " + zipLink); - let success = await new Promise(resolve => { - request.get({url: zipLink, encoding: null}, async (error, response, body) => { - if (error || response.statusCode !== 200) return resolve(false); - // Save a backup in case someone has their own copy - const alreadyExists = await new Promise(res => fs.exists(savedZip, res)); - if (alreadyExists) await new Promise(res => fs.rename(savedZip, `${savedZip}.bak${Math.round(performance.now())}`, res)); - - Utils.log("InjectorUpdate", "Writing " + savedZip); - fs.writeFile(savedZip, body, err => resolve(!err)); - }); - }); - if (!success) return success; - - // Check and delete rename extraction - const alreadyExists = await new Promise(res => fs.exists(extractedFolder, res)); - if (alreadyExists) await new Promise(res => fs.rename(extractedFolder, `${extractedFolder}.bak${Math.round(performance.now())}`, res)); - - // Unzip the downloaded zip file - const zipfile = await new Promise(r => yauzl.open(savedZip, {lazyEntries: true}, (err, zip) => r(zip))); - zipfile.on("entry", function(entry) { - // Skip directories, they are handled with mkdirp - if (entry.fileName.endsWith("/")) return zipfile.readEntry(); - - Utils.log("InjectorUpdate", "Extracting " + entry.fileName); - // Make any needed parent directories - const fullPath = path.resolve(parentPath, entry.fileName); - mkdirp.sync(path.dirname(fullPath)); - zipfile.openReadStream(entry, function(err, readStream) { - if (err) return success = false; - readStream.on("end", function() {zipfile.readEntry();}); // Go to next file after this - readStream.pipe(fs.createWriteStream(fullPath)); - }); - }); - zipfile.readEntry(); // Start reading - - // Wait for the final file to finish - await new Promise(resolve => zipfile.once("end", resolve)); - - // Save a backup in case something goes wrong during final step - const backupFolder = path.resolve(parentPath, `${folderName}.bak${Math.round(performance.now())}`); - await new Promise(resolve => fs.rename(injectionPath, backupFolder, resolve)); - - // Rename the extracted folder to what it should be - Utils.log("InjectorUpdate", `Renaming ${path.basename(extractedFolder)} to ${folderName}`); - success = await new Promise(resolve => fs.rename(extractedFolder, injectionPath, err => resolve(!err))); - if (!success) { - Utils.err("InjectorUpdate", "Failed to rename the final directory"); - return success; - } - - // If rename had issues, delete what we tried to rename and restore backup - if (!success) { - Utils.err("InjectorUpdate", "Something went wrong... restoring backups."); - await new Promise(resolve => rmrf(extractedFolder, resolve)); - await new Promise(resolve => fs.rename(backupFolder, injectionPath, resolve)); - return success; - } - - // If we've gotten to this point, everything should have gone smoothly. - // Cleanup the backup folder then remove the zip - await new Promise(resolve => rmrf(backupFolder, resolve)); - await new Promise(resolve => fs.unlink(savedZip, resolve)); - - Utils.log("InjectorUpdate", "Injector Updated!"); - return success;*/ -}; - -export default new Core(); - -/** - * Don't expose core - could be dangerous for now +import {bdConfig, minSupportedVersion, bbdVersion, settingsCookie, bdpluginErrors, bdthemeErrors, bbdChangelog, defaultCookie, currentDiscordVersion, defaultRPC, settingsRPC, lightcordSettings} from "../0globals"; +import Utils from "./utils"; + +import BDV2 from "./v2"; +import settingsPanel from "./settingsPanel"; +import pluginModule from "./pluginModule"; +import themeModule from "./themeModule"; +import DataStore from "./dataStore"; +import WebpackModules from "./webpackModules"; +import DOM from "./domtools"; + +import BDLogo from "../ui/bdLogo"; +import TooltipWrap from "../ui/tooltipWrap"; +import LightcordLogo from "../svg/Lightcord"; +import PluginCertifier from "./pluginCertifier"; +import distant, { uuidv4 } from "./distant"; +import EmojiModule from "./emojiModule" +import {remote as electron} from "electron" +import v2 from "./v2"; +import contentManager from "./contentManager"; + +let methods +function Core() {} + + +Core.prototype.setConfig = function(config) { + if (this.hasStarted) return; + Object.assign(bdConfig, config); +}; + +Object.defineProperty(Core.prototype, "methods", { + get(){ + return methods + } +}) + +Core.prototype.setMethods = function(m) { + if (this.hasStarted) return; + methods = m +}; + +Core.prototype.init = async function() { + if (this.hasStarted) return; + this.hasStarted = true; + + if (!Array.prototype.flat) { + Utils.alert("Not Supported", "BetterDiscord v" + bbdVersion + " does not support this old version (" + currentDiscordVersion + ") of Discord. Please update your Discord installation before proceeding."); + return; + } + + if (bdConfig.version < minSupportedVersion) { + Utils.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 (window.ED) { + Utils.alert("Not Supported", "BandagedBD does not work with EnhancedDiscord. Please uninstall one of them."); + return; + } + + if (window.WebSocket && window.WebSocket.name && window.WebSocket.name.includes("Patched")) { + Utils.alert("Not Supported", "BandagedBD does not work with Powercord. Please uninstall one of them."); + return; + } + + Utils.suppressErrors(this.patchAttributes.bind(this), "LC Plugin Certifier Patch")(); + + Utils.log("Startup", "Initializing Settings"); + this.initSettings(); + + await this.checkForGuilds(); + BDV2.initialize(); + Utils.log("Startup", "Updating Settings"); + settingsPanel.initializeSettings(); + + Utils.log("Startup", "Loading Addons Cache") + await contentManager.loadAddonCertifierCache() + + Utils.log("Startup", "Loading Plugins"); + await pluginModule.loadPlugins(); + + Utils.log("Startup", "Loading Themes"); + await themeModule.loadThemes(); + + DOM.addStyle("customcss", Buffer.from(DataStore.getBDData("bdcustomcss"), "base64").toString("utf8")); + + window.addEventListener("beforeunload", function() { + if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click(); + }); + + PluginCertifier.start() + + Utils.log("Startup", "Removing Loading Icon"); + if (document.getElementsByClassName("bd-loaderv2").length) document.getElementsByClassName("bd-loaderv2")[0].remove(); + Utils.log("Startup", "Initializing Main Observer"); + this.initObserver(); + + // Show loading errors + if (settingsCookie["fork-ps-1"]) { + Utils.log("Startup", "Collecting Startup Errors"); + Utils.showContentErrors({plugins: bdpluginErrors, themes: bdthemeErrors}); + } + + const previousVersion = DataStore.getBDData("version"); + if (bbdVersion > previousVersion) { + if (bbdChangelog) this.showChangelogModal(bbdChangelog); + DataStore.setBDData("version", bbdVersion); + } + + EmojiModule.start() + + Utils.suppressErrors(this.patchSocial.bind(this), "BD Social Patch")(); + Utils.suppressErrors(this.patchGuildPills.bind(this), "BD Guild Pills Patch")(); + Utils.suppressErrors(this.patchGuildListItems.bind(this), "BD Guild List Items Patch")(); + Utils.suppressErrors(this.patchGuildSeparator.bind(this), "BD Guild Separator Patch")(); + Utils.suppressErrors(this.patchMessageHeader.bind(this), "BD Badge Chat Patch")(); + Utils.suppressErrors(this.patchMemberList.bind(this), "BD Badge Member List Patch")(); + Utils.suppressErrors(this.patchAttachment.bind(this), "LC Plugin Certifier Patch")(); + + if(bdConfig.haveInstalledDefault){ + let alert = Utils.alert("First Installation", "As it is the first time you install Lightcord, We added two default themes and one default plugin in your plugin/theme folder. Check it in the Plugin/Theme settings.") + await new Promise((resolve) => { + alert.onClose(resolve) + }) + } + const logo = document.querySelector("#app-mount > div.typeWindows-1za-n7.withFrame-haYltI.titleBar-AC4pGV.horizontalReverse-3tRjY7.flex-1O1GKY.directionRowReverse-m8IjIq.justifyStart-2NDFzi.alignStretch-DpGPf3.da-typeWindows.da-withFrame.da-titleBar.da-horizontalReverse.da-flex.da-directionRowReverse.da-justifyStart.da-alignStretch > div.wordmarkWindows-1v0lYD.wordmark-2iDDfm.da-wordmarkWindows.da-wordmark") + if(logo){ + logo.style.top = "3px" + logo.innerHTML = ` + + + +` + } +}; + +Core.prototype.patchAttributes = async function() { + let attribsPatchs = [] + this.cancelPatchAttributes = function() { + attribsPatchs.forEach(e => e()) + } + + while(!v2.MessageComponent)await new Promise(resolve => setTimeout(resolve, 100)) + + // TODO: try to patch correctly the user popout on a next update + const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); + window.Lightcord.Api.ensureExported(e => e.default && e.default.displayName === "DiscordTag") + .then(DiscordTag => { + let DiscordTagComp = DiscordTag.default + DiscordTag.default = function(props){ + let returnValue = DiscordTagComp(props) + + let id = uuidv4() + + let badgeDiv = BDV2.React.createElement("div", { + style: { + display: "inline", + marginTop: "5px" + } + }, BDV2.React.createElement("span", { + id: "badges-"+id, + key: "badges-"+id, + style: { + display: "inherit" + } + })) + + let children = [returnValue] + + if (props.user.id === "249746236008169473") { // Rauenzi: BandagedBD Developer + children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, + BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, + BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) + ) + ) + ); + } else if (props.user.id === "696481194443014174" || props.user.id === "696003456611385396"){ // Not Thomiz: Lightcord Developer, Phorcys: Lightcord Developer + children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, + BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, + BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) + ) + ) + ); + } + + children.push(badgeDiv) + let div = BDV2.React.createElement("div", { + style: { + display: "block" + } + }, children) + applyBadges(id, props.user, false) + + return div + } + DiscordTag.default.displayName = DiscordTagComp.displayName + }) + + attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { + if(data.methodArguments[0].childrenMessageContent.props.message){ // this can be a blocked message (not opened) + data.returnValue.props["data-message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id + } + }})) + /* + attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => { + data.returnValue.props["message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id + }}))*/ +} + +Core.prototype.checkForGuilds = function() { + let timesChecked = 0; + return new Promise(resolve => { + const checkForGuilds = function() { + const wrapper = BDV2.guildClasses.wrapper.split(" ")[0]; + if (document.querySelectorAll(`.${wrapper}`).length > 0) timesChecked++; + const guild = BDV2.guildClasses.listItem.split(" ")[0]; + const blob = BDV2.guildClasses.blobContainer.split(" ")[0]; + if (document.querySelectorAll(`.${wrapper} .${guild} .${blob}`).length > 0) return resolve(bdConfig.deferLoaded = true); + else if (timesChecked >= 50) return resolve(bdConfig.deferLoaded = true); + setTimeout(checkForGuilds, 100); + }; + if (document.readyState != "loading") setTimeout(checkForGuilds, 100); + document.addEventListener("DOMContentLoaded", () => {setTimeout(checkForGuilds, 100);}); + }); +}; + +Core.prototype.injectExternals = async function() { + // No externals +}; + +Core.prototype.initSettings = function () { + DataStore.initialize(); + if(!DataStore.getSettingGroup("lightcord-settings")){ + for(let key in lightcordSettings){ + delete lightcordSettings[key] + } + } + if(!DataStore.getSettingGroup("rpc")){ + Object.assign(settingsRPC, defaultRPC); + } + if(!DataStore.getSettingGroup("settings")){ + Object.assign(settingsCookie, defaultCookie); + settingsPanel.saveSettings(); + } else { + settingsPanel.loadSettings(); + for (const setting in defaultCookie) { + if (settingsCookie[setting] == undefined) { + settingsCookie[setting] = defaultCookie[setting]; + settingsPanel.saveSettings(); + } + } + } + window.Lightcord.Api.ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections) + .then(settingModule => { + + let getPredicateSections = settingModule.default.prototype.getPredicateSections + settingModule.default.prototype.getPredicateSections = function(){ + let result = getPredicateSections.call(this, ...arguments) + + if(!result[1])return result + if(result[1].section === "My Account"){ // user settings, not guild settings + let poped = [] + + poped.push(result.pop()) + poped.push(result.pop()) + poped.push(result.pop()) + poped.push(result.pop()) + + result.push(...settingsPanel.renderSidebar(this)) + + while(poped[0]){ + result.push(poped.pop()) + } + } + console.log(result) + return result + } + }) +}; + + +let classNameLayer +let classNameSocialLinks +let classNameModal + +Core.prototype.initObserver = function () { + const mainObserver = new MutationObserver((mutations) => { + for (let i = 0, mlen = mutations.length; i < mlen; i++) { + const mutation = mutations[i]; + if (typeof pluginModule !== "undefined") pluginModule.rawObserver(mutation); + + // if there was nothing added, skip + if (!mutation.addedNodes.length || !(mutation.addedNodes[0] instanceof Element)) continue; + + const node = mutation.addedNodes[0]; + + if(!classNameLayer)classNameLayer = BDModules.get((e) => e.layer && typeof e.layer === "string" && e.animating)[0].layer + if(!classNameSocialLinks)classNameSocialLinks = BDModules.get((e) => e.socialLinks && typeof e.socialLinks === "string")[0].socialLinks + if(!classNameModal)classNameModal = BDModules.get((e) => e.modal && typeof e.modal === "string" && e.inner && typeof e.inner === "string" && !e.responsiveWidthMobile)[0].modal + + if (node.classList.contains(classNameLayer)) { + if (node.getAttribute("aria-label") === "GUILD_SETTINGS"){ + node.setAttribute("layer-id", "server-settings"); + node.setAttribute("id", "server-settings"); + } + + if (node.getElementsByClassName(classNameSocialLinks).length) { + node.setAttribute("layer-id", "user-settings"); + node.setAttribute("id", "user-settings"); + } + } + + if (node.parentElement == document.body && node.querySelector("#ace_settingsmenu")) node.id = "ace_settingsmenu_container"; + + // Emoji Picker + //node.getElementsByClassName("emojiPicker-3m1S-j").length && !node.querySelector(".emojiPicker-3m1S-j").parentElement.classList.contains("animatorLeft-1EQxU0") + //if (node.classList.contains(classNameLayer2) && node.getElementsByClassName(classNameEmojiPicker).length && !node.querySelector("."+classNameEmojiPicker).parentElement.classList.contains(classNameAnimatorLeft)) quickEmoteMenu.obsCallback(node); + + } + }); + + mainObserver.observe(document, { + childList: true, + subtree: true + }); +}; + +Core.prototype.showChangelogModal = function(options = {}) { + return Utils.showChangelogModal(options); +}; + +Core.prototype.alert = function(title, content) { + return Utils.alert(title, content); +}; + +Core.prototype.patchSocial = function() { + if (this.socialPatch) return; + const TabBar = WebpackModules.find(m => m.displayName == "TabBar"); + const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); + if (!TabBar) return; + this.socialPatch = Utils.monkeyPatch(TabBar.prototype, "render", {after: (data) => { + const children = data.returnValue.props.children; + if (!children || !children.length || children.length < 3) return; + if (children[children.length - 3].type.displayName !== "Separator") return; + if (!children[children.length - 2].type.toString().includes("socialLinks")) return; + if (Anchor) { + let socialModule1 = BDModules.get(e => e.socialLinks)[0] + const original = children[children.length - 2].type; + const newOne = function() { + const returnVal = original(...arguments); + returnVal.props.children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord"}, + BDV2.React.createElement(Anchor, {className: "bd-social-link "+socialModule1.link, href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, + BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-social-logo"}) + ) + ) + ); + returnVal.props.children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD"}, + BDV2.React.createElement(Anchor, {className: "bd-social-link "+socialModule1.link, href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, + BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-social-logo"}) + ) + ) + ); + return returnVal; + }; + children[children.length - 2].type = newOne; + } + + let [ + classNameColorMuted, + sizes, + classNameVersionHash + ] = [ + BDModules.get(e => e.colorMuted)[0].colorMuted, + BDModules.get(e => e.size32)[0], + BDModules.get(e => e.versionHash)[0].versionHash + ] + + const versionHash = `(${bdConfig.hash ? bdConfig.hash.substring(0, 7) : bdConfig.branch})`; + const additional = [ + BDV2.react.createElement("div", {className: `${classNameColorMuted} ${sizes.size12}`}, `Lightcord ${electron.getGlobal("BuildInfo").version} `, BDV2.react.createElement("span", {className: classNameVersionHash+" da-versionHash"}, `(${(electron.getGlobal("BuildInfo").commit || "Unknown").slice(0, 7)})`)), + BDV2.react.createElement("div", {className: `${classNameColorMuted} ${sizes.size12}`}, `BBD ${bbdVersion} `, BDV2.react.createElement("span", {className: classNameVersionHash+" da-versionHash"}, versionHash)) + ] + + + const originalVersions = children[children.length - 1].type; + children[children.length - 1].type = function() { + const returnVal = originalVersions(...arguments); + returnVal.props.children.splice(1, 0, additional); + return returnVal; + }; + }}); +}; + +const getGuildClasses = function() { + const guildsWrapper = WebpackModules.findByProps("wrapper", "unreadMentionsBar"); + const guilds = WebpackModules.findByProps("guildsError", "selected"); + const pill = WebpackModules.findByProps("blobContainer"); + return Object.assign({}, guildsWrapper, guilds, pill); +}; + +Core.prototype.patchGuildListItems = function() { + if (this.guildListItemsPatch) return; + const GuildClasses = getGuildClasses(); + const listItemClass = GuildClasses.listItem.split(" ")[0]; + const blobClass = GuildClasses.blobContainer.split(" ")[0]; + const reactInstance = BDV2.getInternalInstance(document.querySelector(`.${listItemClass} .${blobClass}`).parentElement); + const GuildComponent = reactInstance.return.type; + if (!GuildComponent) return; + this.guildListItemsPatch = Utils.monkeyPatch(GuildComponent.prototype, "render", {after: (data) => { + if (data.returnValue && data.thisObject) { + const returnValue = data.returnValue; + const guildData = data.thisObject.props; + let className = returnValue.props.className + className += " bd-guild"; + if (guildData.unread) className += " bd-unread"; + if (guildData.selected) className += " bd-selected"; + if (guildData.audio) className += " bd-audio"; + if (guildData.video) className += " bd-video"; + if (guildData.badge) className += " bd-badge"; + if (guildData.animatable) className += " bd-animatable"; + returnValue.props.className = className + return returnValue; + } + }}); +}; + +Core.prototype.patchGuildPills = function() { + if (this.guildPillPatch) return; + const guildPill = WebpackModules.find(m => m.default && !m.default.displayName && m.default.toString && m.default.toString().includes("translate3d")); + if (!guildPill) return; + this.guildPillPatch = Utils.monkeyPatch(guildPill, "default", {after: (data) => { + const props = data.methodArguments[0]; + if (props.unread) data.returnValue.props.className += " bd-unread"; + if (props.selected) data.returnValue.props.className += " bd-selected"; + if (props.hovered) data.returnValue.props.className += " bd-hovered"; + return data.returnValue; + }}); +}; + +Core.prototype.patchGuildSeparator = function() { + if (this.guildSeparatorPatch) return; + const Guilds = WebpackModules.findByDisplayName("Guilds"); + const guildComponents = WebpackModules.findByProps("renderListItem"); + if (!guildComponents || !Guilds) return; + const GuildSeparator = function() { + const returnValue = guildComponents.Separator(...arguments); + returnValue.props.className += " bd-guild-separator"; + return returnValue; + }; + this.guildSeparatorPatch = Utils.monkeyPatch(Guilds.prototype, "render", {after: (data) => { + data.returnValue.props.children[1].props.children[3].type = GuildSeparator; + }}); +}; + +Core.prototype.patchAttachment = function() { + if (this.AttachmentPatch) return; + const Attachment = BDModules.get(e => e.default && e.default.displayName === "Attachment")[0] // temporary + const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); + if (!Anchor || !Attachment || !Attachment.default) return; + this.AttachmentPatch = Utils.monkeyPatch(Attachment, "default", {after: (data) => { + if(!settingsCookie["fork-ps-6"])return + const attachment = data.methodArguments[0] || null + const children = Utils.getNestedProp(data.returnValue, "props.children"); + + if (!children || !attachment || !attachment.url)return + if (!Array.isArray(children)) return; + + const id = uuidv4() + children.push(BDV2.react.createElement("span", { + id: "certified-"+id + })) + PluginCertifier.patch(attachment, "certified-"+id) + }}) +} + +Core.prototype.patchMessageHeader = function() { + if (this.messageHeaderPatch) return; + const MessageHeader = WebpackModules.findByProps("MessageTimestamp"); + const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); + if (!Anchor || !MessageHeader || !MessageHeader.default) return; + this.messageHeaderPatch = Utils.monkeyPatch(MessageHeader, "default", {after: (data) => { + const author = Utils.getNestedProp(data.methodArguments[0], "message.author"); + // const header = Utils.getNestedProp(data.returnValue, "props.children.1.props"); + const children = Utils.getNestedProp(data.returnValue, "props.children.1.props.children.1.props.children"); + if (!children || !author || !author.id)return + // if (header && header.className) header.className += " " + if (!Array.isArray(children)) return; + if (author.id === "249746236008169473") { // Rauenzi: BandagedBD Developer + children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, + BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, + BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) + ) + ) + ); + } else if (author.id === "696481194443014174" || author.id === "696003456611385396"){ // Not Thomiz: Lightcord Developer, Phorcys: Lightcord Developer + children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, + BDV2.React.createElement(Anchor, {className: "bd-chat-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, + BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) + ) + ) + ); + } + const id = uuidv4() + children.push( + BDV2.React.createElement("div", { + id: "badges-"+id, + style: { + display: "inline" + } + }) + ) + applyBadges(id, author, true) + }}); +}; + +function applyBadges(id, user, chat){ + process.nextTick(() => { + const div = document.getElementById("badges-"+id) + if(!div || div.childNodes.length > 0)return + if(div.childNodes.length)return + let blockDiv = document.createElement("div") + blockDiv.style.display = "none" + div.appendChild(blockDiv) + + const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); + + distant.getBadges(user.id) + .then(badges => { + badges.forEach(badge => { + const props = { + svg: { + size: "16px", + className: "bd-logo", + width: "16px", + } + } + badge.scopes.forEach(scope => { + if(scope === "user"){// require user + props.user = user + } + }) + if(!badge.href){ + props.Anchor = Anchor + props.href = { + className: chat ? "bd-chat-badge" : "bd-member-badge", + title: badge.name, + target: "_blank" + } + } + const element = BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: badge.name}, + badge.href ? BDV2.react.createElement(Anchor, { + href: badge.href, + className: chat ? "bd-chat-badge" : "bd-member-badge", + title: badge.name, + target: "_blank" + }, BDV2.React.createElement(badge.component, props)) : BDV2.React.createElement(badge.component, props) + ) + const div2 = document.createElement("div") + BDV2.reactDom.render(element, div2) + div2.childNodes.forEach(node => { + div.appendChild(node) + }) + }) + }) + }) +} + +Core.prototype.patchMemberList = function() { + if (this.memberListPatch) return; + const MemberListItem = WebpackModules.findByDisplayName("MemberListItem"); + const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); + if (!Anchor || !MemberListItem || !MemberListItem.prototype || !MemberListItem.prototype.renderDecorators) return; + this.memberListPatch = Utils.monkeyPatch(MemberListItem.prototype, "renderDecorators", {after: (data) => { + const user = Utils.getNestedProp(data.thisObject, "props.user"); + const children = Utils.getNestedProp(data.returnValue, "props.children"); + if (!children || !user || !user.id)return + // if (header && header.className) header.className += " " + if (!Array.isArray(children)) return; + if (user.id === "249746236008169473") { + children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "BandagedBD Developer"}, + BDV2.React.createElement(Anchor, {className: "bd-member-badge", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"}, + BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-logo"}) + ) + ) + ); + } else if (user.id === "696481194443014174" || user.id === "696003456611385396"){ + children.push( + BDV2.React.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord Developer"}, + BDV2.React.createElement(Anchor, {className: "bd-member-badge", href: "https://github.com/Lightcord/Lightcord", title: "Lightcord", target: "_blank"}, + BDV2.React.createElement(LightcordLogo, {size: "16px", className: "bd-logo"}) + ) + ) + ); + } + const id = uuidv4() + children.push( + BDV2.React.createElement("div", {id: "badges-"+id}) + ) + applyBadges(id, user, false) + }}); +}; + +Core.prototype.updateInjector = async function() { + // There will never be an injection path, so we do not need the code below. + // Insert comments so it will be erased when production. + + const injectionPath = DataStore.injectionPath; + if (!injectionPath) return false; + + /* + + const fs = require("fs"); + const path = require("path"); + const rmrf = require("rimraf"); + const yauzl = require("yauzl"); + const mkdirp = require("mkdirp"); + const request = require("request"); + + const parentPath = path.resolve(injectionPath, ".."); + const folderName = path.basename(injectionPath); + const zipLink = "https://github.com/rauenzi/BetterDiscordApp/archive/injector.zip"; + const savedZip = path.resolve(parentPath, "injector.zip"); + const extractedFolder = path.resolve(parentPath, "BetterDiscordApp-injector"); + + // Download the injector zip file + Utils.log("InjectorUpdate", "Downloading " + zipLink); + let success = await new Promise(resolve => { + request.get({url: zipLink, encoding: null}, async (error, response, body) => { + if (error || response.statusCode !== 200) return resolve(false); + // Save a backup in case someone has their own copy + const alreadyExists = await new Promise(res => fs.exists(savedZip, res)); + if (alreadyExists) await new Promise(res => fs.rename(savedZip, `${savedZip}.bak${Math.round(performance.now())}`, res)); + + Utils.log("InjectorUpdate", "Writing " + savedZip); + fs.writeFile(savedZip, body, err => resolve(!err)); + }); + }); + if (!success) return success; + + // Check and delete rename extraction + const alreadyExists = await new Promise(res => fs.exists(extractedFolder, res)); + if (alreadyExists) await new Promise(res => fs.rename(extractedFolder, `${extractedFolder}.bak${Math.round(performance.now())}`, res)); + + // Unzip the downloaded zip file + const zipfile = await new Promise(r => yauzl.open(savedZip, {lazyEntries: true}, (err, zip) => r(zip))); + zipfile.on("entry", function(entry) { + // Skip directories, they are handled with mkdirp + if (entry.fileName.endsWith("/")) return zipfile.readEntry(); + + Utils.log("InjectorUpdate", "Extracting " + entry.fileName); + // Make any needed parent directories + const fullPath = path.resolve(parentPath, entry.fileName); + mkdirp.sync(path.dirname(fullPath)); + zipfile.openReadStream(entry, function(err, readStream) { + if (err) return success = false; + readStream.on("end", function() {zipfile.readEntry();}); // Go to next file after this + readStream.pipe(fs.createWriteStream(fullPath)); + }); + }); + zipfile.readEntry(); // Start reading + + // Wait for the final file to finish + await new Promise(resolve => zipfile.once("end", resolve)); + + // Save a backup in case something goes wrong during final step + const backupFolder = path.resolve(parentPath, `${folderName}.bak${Math.round(performance.now())}`); + await new Promise(resolve => fs.rename(injectionPath, backupFolder, resolve)); + + // Rename the extracted folder to what it should be + Utils.log("InjectorUpdate", `Renaming ${path.basename(extractedFolder)} to ${folderName}`); + success = await new Promise(resolve => fs.rename(extractedFolder, injectionPath, err => resolve(!err))); + if (!success) { + Utils.err("InjectorUpdate", "Failed to rename the final directory"); + return success; + } + + // If rename had issues, delete what we tried to rename and restore backup + if (!success) { + Utils.err("InjectorUpdate", "Something went wrong... restoring backups."); + await new Promise(resolve => rmrf(extractedFolder, resolve)); + await new Promise(resolve => fs.rename(backupFolder, injectionPath, resolve)); + return success; + } + + // If we've gotten to this point, everything should have gone smoothly. + // Cleanup the backup folder then remove the zip + await new Promise(resolve => rmrf(backupFolder, resolve)); + await new Promise(resolve => fs.unlink(savedZip, resolve)); + + Utils.log("InjectorUpdate", "Injector Updated!"); + return success;*/ +}; + +export default new Core(); + +/** + * Don't expose core - could be dangerous for now */ \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/dataStore.js b/BetterDiscordApp/src/modules/dataStore.js index e4be4b5..0d8ce72 100644 --- a/BetterDiscordApp/src/modules/dataStore.js +++ b/BetterDiscordApp/src/modules/dataStore.js @@ -1,96 +1,96 @@ -const __non_webpack_require__ = window.require - -import Utils from "./utils"; -import ContentManager from "./contentManager"; - -const fs = require("fs"); -const path = require("path"); -const releaseChannel = DiscordNative.globals ? DiscordNative.globals.releaseChannel : DiscordNative.app ? DiscordNative.app.getReleaseChannel() : "stable"; - -let dataPath = ""; -if (process.platform === "win32") dataPath = process.env.APPDATA; -else if (process.platform === "darwin") dataPath = path.join(process.env.HOME, "Library", "Preferences"); -else dataPath = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : path.join(process.env.HOME, ".config"); -dataPath = path.join(dataPath, "BetterDiscord"); - -export default new class DataStore { - constructor() { - this.data = {settings: {stable: {}, canary: {}, ptb: {}}}; - this.pluginData = {}; - window.Lightcord.BetterDiscord.DataStore = this - } - - initialize() { - try { - if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); - const data = JSON.parse(fs.readFileSync(this.BDFile, "utf-8")) - if (data.hasOwnProperty("settings")) this.data = data; - if (!fs.existsSync(this.settingsFile)) return; - let settings = __non_webpack_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) { - console.error(err); - Utils.alert("Corrupt Storage", "The bd storage has somehow become corrupt. You may either try to salvage the file or delete it then reload."); - } - } - - get injectionPath() { - return this._injectionPath = null; - if (this._injectionPath) return this._injectionPath; - const electron = require("electron").remote.app; - const base = electron.getAppPath(); - const roamingBase = electron.getPath("userData"); - const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector"); - const location = path.resolve(base, "..", "app"); - const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null; - if (!realLocation) return this._injectionPath = null; - return this._injectionPath = realLocation; - } - - get configFile() {return this._configFile || (this._configFile = path.resolve(this.injectionPath, "betterdiscord", "config.json"));} - get BDFile() {return this._BDFile || (this._BDFile = path.resolve(dataPath, "bdstorage.json"));} - get settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(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), "utf-8"); - } - - getBDData(key) { - return this.data[key] || ""; - } - - setBDData(key, value) { - this.data[key] = value; - fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); - } - - getPluginData(pluginName, key) { - if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key]; - if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined; - this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName))); - return this.pluginData[pluginName][key]; - } - - 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), "utf-8"); - } - - 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), "utf-8"); - } +const __non_webpack_require__ = window.require + +import Utils from "./utils"; +import ContentManager from "./contentManager"; + +const fs = require("fs"); +const path = require("path"); +const releaseChannel = DiscordNative.globals ? DiscordNative.globals.releaseChannel : DiscordNative.app ? DiscordNative.app.getReleaseChannel() : "stable"; + +let dataPath = ""; +if (process.platform === "win32") dataPath = process.env.APPDATA; +else if (process.platform === "darwin") dataPath = path.join(process.env.HOME, "Library", "Preferences"); +else dataPath = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : path.join(process.env.HOME, ".config"); +dataPath = path.join(dataPath, "BetterDiscord"); + +export default new class DataStore { + constructor() { + this.data = {settings: {stable: {}, canary: {}, ptb: {}}}; + this.pluginData = {}; + window.Lightcord.BetterDiscord.DataStore = this + } + + initialize() { + try { + if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); + const data = JSON.parse(fs.readFileSync(this.BDFile, "utf-8")) + if (data.hasOwnProperty("settings")) this.data = data; + if (!fs.existsSync(this.settingsFile)) return; + let settings = __non_webpack_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) { + console.error(err); + Utils.alert("Corrupt Storage", "The bd storage has somehow become corrupt. You may either try to salvage the file or delete it then reload."); + } + } + + get injectionPath() { + return this._injectionPath = null; + if (this._injectionPath) return this._injectionPath; + const electron = require("electron").remote.app; + const base = electron.getAppPath(); + const roamingBase = electron.getPath("userData"); + const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector"); + const location = path.resolve(base, "..", "app"); + const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null; + if (!realLocation) return this._injectionPath = null; + return this._injectionPath = realLocation; + } + + get configFile() {return this._configFile || (this._configFile = path.resolve(this.injectionPath, "betterdiscord", "config.json"));} + get BDFile() {return this._BDFile || (this._BDFile = path.resolve(dataPath, "bdstorage.json"));} + get settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(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), "utf-8"); + } + + getBDData(key) { + return this.data[key] || ""; + } + + setBDData(key, value) { + this.data[key] = value; + fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); + } + + getPluginData(pluginName, key) { + if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key]; + if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined; + this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName))); + return this.pluginData[pluginName][key]; + } + + 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), "utf-8"); + } + + 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), "utf-8"); + } }; \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/devMode.js b/BetterDiscordApp/src/modules/devMode.js index 1bde840..55263f5 100644 --- a/BetterDiscordApp/src/modules/devMode.js +++ b/BetterDiscordApp/src/modules/devMode.js @@ -1,161 +1,161 @@ -import {settingsCookie} from "../0globals"; -import BDV2 from "./v2"; -import DOM from "./domtools"; -import Utils from "./utils"; - -export default new class DevMode { - constructor() { - this.debugListener = this.debugListener.bind(this); - this.copySelectorListener = this.copySelectorListener.bind(this); - } - - start() { - this.startDebugListener(); - if (settingsCookie["fork-dm-1"]) this.startCopySelector(); - } - - stop() { - this.stopDebugListener(); - this.stopCopySelector(); - } - - startDebugListener() { - this.stopDebugListener(); - document.addEventListener("keydown", this.debugListener); - } - - stopDebugListener() { - document.removeEventListener("keydown", this.debugListener); - } - - startCopySelector() { - this.stopCopySelector(); - document.addEventListener("contextmenu", this.copySelectorListener); - } - - stopCopySelector() { - document.removeEventListener("contextmenu", this.copySelectorListener); - } - - debugListener(e) { - if (e.which === 119 || e.which == 118) {//F8 - console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", ""); - debugger; // eslint-disable-line no-debugger - e.preventDefault(); - e.stopImmediatePropagation(); - } - } - - copySelectorListener(e) { - try{ - e.stopPropagation(); - const selector = this.getSelector(e.target); - - let [ - classLayer, - classItems - ] = [ - BDModules.get((e) => e.layer && typeof e.layer === "string" && e.disabledPointerEvents)[0], - BDModules.get((e) => e.menu)[0] - ] - - function attach() { - if(!classItems || !classLayer.layer)return console.log(classItems, classLayer.layer) - - let cm = DOM.query("."+Utils.removeDa(classItems.menu)); - if (!cm) { - const container = DOM.query("#app-mount > ."+Utils.removeDa(classLayer.layerContainer)); - const cmWrap = DOM.createElement(`
`); - cm = DOM.createElement(`
`); - cmWrap.append(cm); - container.append(cmWrap); - cmWrap.style.top = e.clientY + "px"; - cmWrap.style.left = e.clientX + "px"; - cmWrap.setAttribute("role", "menu") - cmWrap.setAttribute("tabindex", "-1") - cmWrap.id = "bd-copy-selector-context" - cmWrap.setAttribute("aria-label", "Copy Selector Actions") - - const scrollerClasses = BDModules.get((e) => e.scrollerWrap)[0] - const scrollerWrap = DOM.createElement(`
`) - const scroller = DOM.createElement(`
`) - scrollerWrap.append(scroller) - cm.append(scrollerWrap) - - const removeCM = function(e) { - if (e.keyCode && e.keyCode !== 27) return; - cmWrap.remove(); - document.removeEventListener("click", removeCM); - document.removeEventListener("contextmenu", removeCM); - document.removeEventListener("keyup", removeCM); - }; - document.addEventListener("click", removeCM); - document.addEventListener("contextmenu", removeCM); - document.addEventListener("keyup", removeCM); - } - const cmWrap = cm.parentElement - - const scroller = cm.childNodes[0].childNodes[0] - const cmg = DOM.createElement(`
`); - /** - * @type {HTMLElement} - */ - const cmi = DOM.createElement(``); - cmi.append(DOM.createElement(`
Copy Selector
`)); - cmi.addEventListener("click", () => { - BDV2.NativeModule.copy(selector); - cmWrap.style.display = "none" - }); - cmi.addEventListener("mouseover", (e) => { - let elements = DOM.queryAll("div[role=menuitem]."+Utils.removeDa(classItems.focused)) - elements && elements.forEach(elem => elem.classList.remove(classItems.focused)) - cmi.classList.add(classItems.focused) - }) - cmi.addEventListener("mouseout", (e) => { - cmi.classList.remove(classItems.focused) - }) - cmg.append(cmi); - if(scroller.childNodes.length){ // apend a separator - const separator = DOM.createElement(``) - scroller.append(separator) - } - scroller.append(cmg); - if(cmWrap.clientHeight < cmWrap.scrollHeight){ - console.log("overflowing "+cmWrap.style.top) - cmWrap.style.top = (cmWrap.style.top - cmg.clientHeight) + "px"; - console.log("overflowing"+cmWrap.style.top) - } - } - - setTimeout(attach, 1); - }catch(e){ - console.error(e) - } - } - - getSelector(element) { - if (element.id) return `#${element.id}`; - /** - * - * @param {HTMLElement} el - */ - function fullPath(el){ - var names = []; - while (el.parentNode){ - if (el.id){ - names.unshift('#'+el.id); - break; - }else{ - if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join("")); - else{ - for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++); - names.unshift(el.tagName.toLowerCase()+((typeof el.className === "string" && el.className) || "").split(" ").filter(e => !!e).map(e => "."+e).join("")+":nth-child("+c+")"); - } - el=el.parentNode; - } - } - return names.join(" > "); - } - return fullPath(element) - } +import {settingsCookie} from "../0globals"; +import BDV2 from "./v2"; +import DOM from "./domtools"; +import Utils from "./utils"; + +export default new class DevMode { + constructor() { + this.debugListener = this.debugListener.bind(this); + this.copySelectorListener = this.copySelectorListener.bind(this); + } + + start() { + this.startDebugListener(); + if (settingsCookie["fork-dm-1"]) this.startCopySelector(); + } + + stop() { + this.stopDebugListener(); + this.stopCopySelector(); + } + + startDebugListener() { + this.stopDebugListener(); + document.addEventListener("keydown", this.debugListener); + } + + stopDebugListener() { + document.removeEventListener("keydown", this.debugListener); + } + + startCopySelector() { + this.stopCopySelector(); + document.addEventListener("contextmenu", this.copySelectorListener); + } + + stopCopySelector() { + document.removeEventListener("contextmenu", this.copySelectorListener); + } + + debugListener(e) { + if (e.which === 119 || e.which == 118) {//F8 + console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", ""); + debugger; // eslint-disable-line no-debugger + e.preventDefault(); + e.stopImmediatePropagation(); + } + } + + copySelectorListener(e) { + try{ + e.stopPropagation(); + const selector = this.getSelector(e.target); + + let [ + classLayer, + classItems + ] = [ + BDModules.get((e) => e.layer && typeof e.layer === "string" && e.disabledPointerEvents)[0], + BDModules.get((e) => e.menu)[0] + ] + + function attach() { + if(!classItems || !classLayer.layer)return console.log(classItems, classLayer.layer) + + let cm = DOM.query("."+Utils.removeDa(classItems.menu)); + if (!cm) { + const container = DOM.query("#app-mount > ."+Utils.removeDa(classLayer.layerContainer)); + const cmWrap = DOM.createElement(`
`); + cm = DOM.createElement(`
`); + cmWrap.append(cm); + container.append(cmWrap); + cmWrap.style.top = e.clientY + "px"; + cmWrap.style.left = e.clientX + "px"; + cmWrap.setAttribute("role", "menu") + cmWrap.setAttribute("tabindex", "-1") + cmWrap.id = "bd-copy-selector-context" + cmWrap.setAttribute("aria-label", "Copy Selector Actions") + + const scrollerClasses = BDModules.get((e) => e.scrollerWrap)[0] + const scrollerWrap = DOM.createElement(`
`) + const scroller = DOM.createElement(`
`) + scrollerWrap.append(scroller) + cm.append(scrollerWrap) + + const removeCM = function(e) { + if (e.keyCode && e.keyCode !== 27) return; + cmWrap.remove(); + document.removeEventListener("click", removeCM); + document.removeEventListener("contextmenu", removeCM); + document.removeEventListener("keyup", removeCM); + }; + document.addEventListener("click", removeCM); + document.addEventListener("contextmenu", removeCM); + document.addEventListener("keyup", removeCM); + } + const cmWrap = cm.parentElement + + const scroller = cm.childNodes[0].childNodes[0] + const cmg = DOM.createElement(`
`); + /** + * @type {HTMLElement} + */ + const cmi = DOM.createElement(``); + cmi.append(DOM.createElement(`
Copy Selector
`)); + cmi.addEventListener("click", () => { + BDV2.NativeModule.copy(selector); + cmWrap.style.display = "none" + }); + cmi.addEventListener("mouseover", (e) => { + let elements = DOM.queryAll("div[role=menuitem]."+Utils.removeDa(classItems.focused)) + elements && elements.forEach(elem => elem.classList.remove(classItems.focused)) + cmi.classList.add(classItems.focused) + }) + cmi.addEventListener("mouseout", (e) => { + cmi.classList.remove(classItems.focused) + }) + cmg.append(cmi); + if(scroller.childNodes.length){ // apend a separator + const separator = DOM.createElement(``) + scroller.append(separator) + } + scroller.append(cmg); + if(cmWrap.clientHeight < cmWrap.scrollHeight){ + console.log("overflowing "+cmWrap.style.top) + cmWrap.style.top = (cmWrap.style.top - cmg.clientHeight) + "px"; + console.log("overflowing"+cmWrap.style.top) + } + } + + setTimeout(attach, 1); + }catch(e){ + console.error(e) + } + } + + getSelector(element) { + if (element.id) return `#${element.id}`; + /** + * + * @param {HTMLElement} el + */ + function fullPath(el){ + var names = []; + while (el.parentNode){ + if (el.id){ + names.unshift('#'+el.id); + break; + }else{ + if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join("")); + else{ + for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++); + names.unshift(el.tagName.toLowerCase()+((typeof el.className === "string" && el.className) || "").split(" ").filter(e => !!e).map(e => "."+e).join("")+":nth-child("+c+")"); + } + el=el.parentNode; + } + } + return names.join(" > "); + } + return fullPath(element) + } }; \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/distant.js b/BetterDiscordApp/src/modules/distant.js index c6a24bb..c90ae5d 100644 --- a/BetterDiscordApp/src/modules/distant.js +++ b/BetterDiscordApp/src/modules/distant.js @@ -1,151 +1,151 @@ -import BugHunterBadge from "../svg/bug_hunter" -import nodeFetch from "node-fetch" -import { settingsCookie } from "../0globals"; -import Circus from "../svg/circus"; - -export function uuidv4() { // Generate UUID (No crypto rng) - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); -} - -const awaitingBadgesPromises = {} -let badgesToFetch = [] - -export default new class DistantServer { - constructor(){ - this._cache = { - badges: { - - } - } - window.Lightcord.BetterDiscord.DistantServer = this - } - - get cache(){ - return this._cache - } - - set cache(data){ - if(typeof data !== "object" || typeof this._cache !== "object")return this._cache = data - return this._cache = Object.assign(this._cache, data) - } - - /** - * Get custom badges from the user ID. - * @param {string} user The user ID - * @returns {Promise} - */ - async getBadges(user){ - if(this.cache.badges[user])return this.cache.badges[user] - if(awaitingBadgesPromises[user])return awaitingBadgesPromises[user] - - let resolve - const newPromise = new Promise((res) => (resolve = res)) - awaitingBadgesPromises[user] = newPromise - - const badges = [] - for(let badge of Constants.badges){ - if(badge.defaultUsers.includes(user))badges.push(badge) - } - const fetchedBadges = await new Promise((resolve) => { - if(!settingsCookie["lightcord-5"])return resolve([]) - badgesToFetch.push([user, resolve]) - setTimeout(() => { - let users = badgesToFetch - if(users.length === 0)return - badgesToFetch = [] - handleRequest(Routes.badges, "POST", JSON.stringify(users.map(e => e[0]))) - .then(async res => { - if(res.status !== 200){// Couldn't fetch badges: server error - users.forEach(data => { - data[1]([])// resolve no badge fetched - }) - } - const responseBody = await res.json() - - for(let user of responseBody){ - let promise = users.find(promise => promise[0] === user.user_id) - promise[1](user.badges) - } - }).catch((err) => {// Couldn't fetch badges: error - if(!(err instanceof LightcordError))console.error(err) - users.forEach(data => { - data[1]([])// resolve no badge fetched - }) - }) - }, 0) - }) - for(let badge of fetchedBadges){ - if(!Constants.badges.find(e => e.id === badge))continue // We do not have the Component, skip it. - if(badges.find(e => e.id === badge))continue // Already inserted. - badges.push(Constants.badges.find(e => e.id === badge)) - } - this.cache = { - badges: Object.assign(this.cache.badges, {[user]: badges}) - } - setTimeout(() => { - delete this.cache.badges[user] - }, 600000); - - resolve(badges) - delete awaitingBadgesPromises[user] - return badges - } -} - -const handleRequest = function(route, method, data){ - if(!settingsCookie["lightcord-5"]){ - return Promise.reject(new LightcordError("The current settings blocked the request.")) - } - return nodeFetch(`${Constants.SERVER_URL}/api/v1${route}`, { - method, - headers: { - "CLIENT": "Lightcord", - "Authorization": window.Lightcord.Api.Authorization || "None::Anonymous" - }, - ...(data ? { - body: data - } : {}) - }) -} - -class LightcordError extends Error { - constructor(){ - super(...arguments) - this.name = "LightcordError" - } -} - -export const Constants = { - SERVER_URL: "https://lightcord.deroku.xyz", - badges: [ // TODO: Add more badges + server side svg - { - name: "Lightcord Bug Hunter", - id: "f04698f5-816b-41e3-bd01-92291193d7a5", - defaultUsers: [ - "696481194443014174", - "696003456611385396" - ], - scopes: [], - component: BugHunterBadge, - href: "https://github.com/lightcord/lightcord/wiki/badges#bug_hunter" - }, { - name: "Buffoon", - id: "06904d31-65b4-41ec-a50c-8658bbd1af96", - defaultUsers: [ - "389016895543705602", - "664600134528663565", - "625350657829896224" - ], - scopes: [], - component: Circus, - href: "https://youtu.be/EJtb6z-dlT8?t=145" - } - ] -} - -export const Routes = { - badges: `/users/badges` +import BugHunterBadge from "../svg/bug_hunter" +import nodeFetch from "node-fetch" +import { settingsCookie } from "../0globals"; +import Circus from "../svg/circus"; + +export function uuidv4() { // Generate UUID (No crypto rng) + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + +const awaitingBadgesPromises = {} +let badgesToFetch = [] + +export default new class DistantServer { + constructor(){ + this._cache = { + badges: { + + } + } + window.Lightcord.BetterDiscord.DistantServer = this + } + + get cache(){ + return this._cache + } + + set cache(data){ + if(typeof data !== "object" || typeof this._cache !== "object")return this._cache = data + return this._cache = Object.assign(this._cache, data) + } + + /** + * Get custom badges from the user ID. + * @param {string} user The user ID + * @returns {Promise} + */ + async getBadges(user){ + if(this.cache.badges[user])return this.cache.badges[user] + if(awaitingBadgesPromises[user])return awaitingBadgesPromises[user] + + let resolve + const newPromise = new Promise((res) => (resolve = res)) + awaitingBadgesPromises[user] = newPromise + + const badges = [] + for(let badge of Constants.badges){ + if(badge.defaultUsers.includes(user))badges.push(badge) + } + const fetchedBadges = await new Promise((resolve) => { + if(!settingsCookie["lightcord-5"])return resolve([]) + badgesToFetch.push([user, resolve]) + setTimeout(() => { + let users = badgesToFetch + if(users.length === 0)return + badgesToFetch = [] + handleRequest(Routes.badges, "POST", JSON.stringify(users.map(e => e[0]))) + .then(async res => { + if(res.status !== 200){// Couldn't fetch badges: server error + users.forEach(data => { + data[1]([])// resolve no badge fetched + }) + } + const responseBody = await res.json() + + for(let user of responseBody){ + let promise = users.find(promise => promise[0] === user.user_id) + promise[1](user.badges) + } + }).catch((err) => {// Couldn't fetch badges: error + if(!(err instanceof LightcordError))console.error(err) + users.forEach(data => { + data[1]([])// resolve no badge fetched + }) + }) + }, 0) + }) + for(let badge of fetchedBadges){ + if(!Constants.badges.find(e => e.id === badge))continue // We do not have the Component, skip it. + if(badges.find(e => e.id === badge))continue // Already inserted. + badges.push(Constants.badges.find(e => e.id === badge)) + } + this.cache = { + badges: Object.assign(this.cache.badges, {[user]: badges}) + } + setTimeout(() => { + delete this.cache.badges[user] + }, 600000); + + resolve(badges) + delete awaitingBadgesPromises[user] + return badges + } +} + +const handleRequest = function(route, method, data){ + if(!settingsCookie["lightcord-5"]){ + return Promise.reject(new LightcordError("The current settings blocked the request.")) + } + return nodeFetch(`${Constants.SERVER_URL}/api/v1${route}`, { + method, + headers: { + "CLIENT": "Lightcord", + "Authorization": window.Lightcord.Api.Authorization || "None::Anonymous" + }, + ...(data ? { + body: data + } : {}) + }) +} + +class LightcordError extends Error { + constructor(){ + super(...arguments) + this.name = "LightcordError" + } +} + +export const Constants = { + SERVER_URL: "https://lightcord.deroku.xyz", + badges: [ // TODO: Add more badges + server side svg + { + name: "Lightcord Bug Hunter", + id: "f04698f5-816b-41e3-bd01-92291193d7a5", + defaultUsers: [ + "696481194443014174", + "696003456611385396" + ], + scopes: [], + component: BugHunterBadge, + href: "https://github.com/lightcord/lightcord/wiki/badges#bug_hunter" + }, { + name: "Buffoon", + id: "06904d31-65b4-41ec-a50c-8658bbd1af96", + defaultUsers: [ + "389016895543705602", + "664600134528663565", + "625350657829896224" + ], + scopes: [], + component: Circus, + href: "https://youtu.be/EJtb6z-dlT8?t=145" + } + ] +} + +export const Routes = { + badges: `/users/badges` } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/domtools.js b/BetterDiscordApp/src/modules/domtools.js index 6e09227..f61c684 100644 --- a/BetterDiscordApp/src/modules/domtools.js +++ b/BetterDiscordApp/src/modules/domtools.js @@ -1,753 +1,753 @@ -/** - * Copyright 2018 Zachary Rauen - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * From: https://github.com/rauenzi/BDPluginLibrary - */ - -/** - * @interface - * @name Offset - * @property {number} top - Top offset of the target element. - * @property {number} right - Right offset of the target element. - * @property {number} bottom - Bottom offset of the target element. - * @property {number} left - Left offset of the target element. - * @property {number} height - Outer height of the target element. - * @property {number} width - Outer width of the target element. - */ - - /** - * Function that automatically removes added listener. - * @callback module:DOMTools~CancelListener - */ - -export default class DOMTools { - - static escapeID(id) { - return id.replace(/^[^a-z]+|[^\w-]+/gi, "-"); - } - - /** - * Adds a style to the document. - * @param {string} id - identifier to use as the element id - * @param {string} css - css to add to the document - */ - static addStyle(id, css) { - document.head.append(DOMTools.createElement(``)); - } - - /** - * Removes a style from the document. - * @param {string} id - original identifier used - */ - static removeStyle(id) { - const element = document.getElementById(id); - if (element) element.remove(); - } - - /** - * Adds/requires a remote script to be loaded - * @param {string} id - identifier to use for this script - * @param {string} url - url from which to load the script - * @returns {Promise} promise that resolves when the script is loaded - */ - static addScript(id, url) { - return new Promise(resolve => { - const script = document.createElement("script"); - script.id = id; - script.src = url; - script.type = "text/javascript"; - script.onload = resolve; - document.head.append(script); - }); - } - - /** - * Removes a remote script from the document. - * @param {string} id - original identifier used - */ - static removeScript(id) { - id = this.escapeID(id); - const element = document.getElementById(id); - if (element) element.remove(); - } - - // https://javascript.info/js-animation - static animate({timing = _ => _, update, duration}) { - const start = performance.now(); - - requestAnimationFrame(function animate(time) { - // timeFraction goes from 0 to 1 - let timeFraction = (time - start) / duration; - if (timeFraction > 1) timeFraction = 1; - - // calculate the current animation state - const progress = timing(timeFraction); - - update(progress); // draw it - - if (timeFraction < 1) { - requestAnimationFrame(animate); - } - - }); - } - - /** - * This is my shit version of not having to use `$` from jQuery. Meaning - * that you can pass a selector and it will automatically run {@link module:DOMTools.query}. - * It also means that you can pass a string of html and it will perform and return `parseHTML`. - * @see module:DOMTools.parseHTML - * @see module:DOMTools.query - * @param {string} selector - Selector to query or HTML to parse - * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query` - */ - static Q(selector) { - const element = this.parseHTML(selector); - const isHTML = element instanceof NodeList ? Array.from(element).some(n => n.nodeType === 1) : element.nodeType === 1; - if (isHTML) return element; - return this.query(selector); - } - - /** - * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided - * `document` is used by default. - * @param {string} selector - Selector to query - * @param {Element} [baseElement] - Element to base the query from - * @returns {(Element|null)} - The found element or null if not found - */ - static query(selector, baseElement = document) { - return baseElement.querySelector(selector); - } - - /** - * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided - * `document` is used by default. - * @param {string} selector - Selector to query - * @param {Element} [baseElement] - Element to base the query from - * @returns {Array} - Array of all found elements - */ - static queryAll(selector, baseElement = document) { - return baseElement.querySelectorAll(selector); - } - - /** - * Parses a string of HTML and returns the results. If the second parameter is true, - * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}. - * This is extremely useful if you have a list of elements at the top level, they can then be appended all at once to another node. - * - * If the second parameter is false, then the return value will be the list of parsed - * nodes and there were multiple top level nodes, otherwise the single node is returned. - * @param {string} html - HTML to be parsed - * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment` - * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing - */ - static parseHTML(html, fragment = false) { - const template = document.createElement("template"); - template.innerHTML = html; - const node = template.content.cloneNode(true); - if (fragment) return node; - return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; - } - - /** Alternate name for {@link module:DOMTools.parseHTML} */ - static createElement(html, fragment = false) {return this.parseHTML(html, fragment);} - - /** - * Takes a string of html and escapes it using the brower's own escaping mechanism. - * @param {String} html - html to be escaped - */ - static escapeHTML(html) { - const textNode = document.createTextNode(""); - const spanElement = document.createElement("span"); - spanElement.append(textNode); - textNode.nodeValue = html; - return spanElement.innerHTML; - } - - /** - * Adds a list of classes from the target element. - * @param {Element} element - Element to edit classes of - * @param {...string} classes - Names of classes to add - * @returns {Element} - `element` to allow for chaining - */ - static addClass(element, ...classes) { - classes = classes.flat().filter(c => c); - for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); - classes = classes.flat().filter(c => c); - element.classList.add(...classes); - return element; - } - - /** - * Removes a list of classes from the target element. - * @param {Element} element - Element to edit classes of - * @param {...string} classes - Names of classes to remove - * @returns {Element} - `element` to allow for chaining - */ - static removeClass(element, ...classes) { - for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); - classes = classes.flat().filter(c => c); - element.classList.remove(...classes); - return element; - } - - /** - * When only one argument is present: Toggle class value; - * i.e., if class exists then remove it and return false, if not, then add it and return true. - * When a second argument is present: - * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it. - * @param {Element} element - Element to edit classes of - * @param {string} classname - Name of class to toggle - * @param {boolean} [indicator] - Optional indicator for if the class should be toggled - * @returns {Element} - `element` to allow for chaining - */ - static toggleClass(element, classname, indicator) { - classname = classname.toString().split(" ").filter(c => c); - if (typeof(indicator) !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator)); - else classname.forEach(c => element.classList.toggle(c)); - return element; - } - - /** - * Checks if an element has a specific class - * @param {Element} element - Element to edit classes of - * @param {string} classname - Name of class to check - * @returns {boolean} - `true` if the element has the class, `false` otherwise. - */ - static hasClass(element, classname) { - return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c)); - } - - /** - * Replaces one class with another - * @param {Element} element - Element to edit classes of - * @param {string} oldName - Name of class to replace - * @param {string} newName - New name for the class - * @returns {Element} - `element` to allow for chaining - */ - static replaceClass(element, oldName, newName) { - element.classList.replace(oldName, newName); - return element; - } - - /** - * Appends `thisNode` to `thatNode` - * @param {Node} thisNode - Node to be appended to another node - * @param {Node} thatNode - Node for `thisNode` to be appended to - * @returns {Node} - `thisNode` to allow for chaining - */ - static appendTo(thisNode, thatNode) { - if (typeof(thatNode) == "string") thatNode = this.query(thatNode); - if (!thatNode) return null; - thatNode.append(thisNode); - return thisNode; - } - - /** - * Prepends `thisNode` to `thatNode` - * @param {Node} thisNode - Node to be prepended to another node - * @param {Node} thatNode - Node for `thisNode` to be prepended to - * @returns {Node} - `thisNode` to allow for chaining - */ - static prependTo(thisNode, thatNode) { - if (typeof(thatNode) == "string") thatNode = this.query(thatNode); - if (!thatNode) return null; - thatNode.prepend(thisNode); - return thisNode; - } - - /** - * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`. - * @param {Node} thisNode - The node to insert - * @param {Node} targetNode - Node to insert after in the tree - * @returns {Node} - `thisNode` to allow for chaining - */ - static insertAfter(thisNode, targetNode) { - targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling); - return thisNode; - } - - /** - * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`. - * @param {Node} thisNode - The node to insert - * @param {Node} newNode - Node to insert after in the tree - * @returns {Node} - `thisNode` to allow for chaining - */ - static after(thisNode, newNode) { - thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling); - return thisNode; - } - - /** - * Gets the next sibling element that matches the selector. - * @param {Element} element - Element to get the next sibling of - * @param {string} [selector=""] - Optional selector - * @returns {Element} - The sibling element - */ - static next(element, selector = "") { - return selector ? element.querySelector("+ " + selector) : element.nextElementSibling; - } - - /** - * Gets all subsequent siblings. - * @param {Element} element - Element to get next siblings of - * @returns {NodeList} - The list of siblings - */ - static nextAll(element) { - return element.querySelectorAll("~ *"); - } - - /** - * Gets the subsequent siblings until an element matches the selector. - * @param {Element} element - Element to get the following siblings of - * @param {string} selector - Selector to stop at - * @returns {Array} - The list of siblings - */ - static nextUntil(element, selector) { - const next = []; - while (element.nextElementSibling && !element.nextElementSibling.matches(selector)) next.push(element = element.nextElementSibling); - return next; - } - - /** - * Gets the previous sibling element that matches the selector. - * @param {Element} element - Element to get the previous sibling of - * @param {string} [selector=""] - Optional selector - * @returns {Element} - The sibling element - */ - static previous(element, selector = "") { - const previous = element.previousElementSibling; - if (selector) return previous && previous.matches(selector) ? previous : null; - return previous; - } - - /** - * Gets all preceeding siblings. - * @param {Element} element - Element to get preceeding siblings of - * @returns {NodeList} - The list of siblings - */ - static previousAll(element) { - const previous = []; - while (element.previousElementSibling) previous.push(element = element.previousElementSibling); - return previous; - } - - /** - * Gets the preceeding siblings until an element matches the selector. - * @param {Element} element - Element to get the preceeding siblings of - * @param {string} selector - Selector to stop at - * @returns {Array} - The list of siblings - */ - static previousUntil(element, selector) { - const previous = []; - while (element.previousElementSibling && !element.previousElementSibling.matches(selector)) previous.push(element = element.previousElementSibling); - return previous; - } - - /** - * Find which index in children a certain node is. Similar to jQuery's `$.index()` - * @param {HTMLElement} node - The node to find its index in parent - * @returns {number} Index of the node - */ - static indexInParent(node) { - const children = node.parentNode.childNodes; - let num = 0; - for (let i = 0; i < children.length; i++) { - if (children[i] == node) return num; - if (children[i].nodeType == 1) num++; - } - return -1; - } - - /** Shorthand for {@link module:DOMTools.indexInParent} */ - static index(node) {return this.indexInParent(node);} - - /** - * Gets the parent of the element if it matches the selector, - * otherwise returns null. - * @param {Element} element - Element to get parent of - * @param {string} [selector=""] - Selector to match parent - * @returns {(Element|null)} - The sibling element or null - */ - static parent(element, selector = "") { - return !selector || element.parentElement.matches(selector) ? element.parentElement : null; - } - - /** - * Gets all children of Element that match the selector if provided. - * @param {Element} element - Element to get all children of - * @param {string} selector - Selector to match the children to - * @returns {Array} - The list of children - */ - static findChild(element, selector) { - return element.querySelector(":scope > " + selector); - } - - /** - * Gets all children of Element that match the selector if provided. - * @param {Element} element - Element to get all children of - * @param {string} selector - Selector to match the children to - * @returns {Array} - The list of children - */ - static findChildren(element, selector) { - return element.querySelectorAll(":scope > " + selector); - } - - /** - * Gets all ancestors of Element that match the selector if provided. - * @param {Element} element - Element to get all parents of - * @param {string} [selector=""] - Selector to match the parents to - * @returns {Array} - The list of parents - */ - static parents(element, selector = "") { - const parents = []; - if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector)); - else while (element.parentElement) parents.push(element = element.parentElement); - return parents; - } - - /** - * Gets the ancestors until an element matches the selector. - * @param {Element} element - Element to get the ancestors of - * @param {string} selector - Selector to stop at - * @returns {Array} - The list of parents - */ - static parentsUntil(element, selector) { - const parents = []; - while (element.parentElement && !element.parentElement.matches(selector)) parents.push(element = element.parentElement); - return parents; - } - - /** - * Gets all siblings of the element that match the selector. - * @param {Element} element - Element to get all siblings of - * @param {string} [selector="*"] - Selector to match the siblings to - * @returns {Array} - The list of siblings - */ - static siblings(element, selector = "*") { - return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector)); - } - - /** - * Sets or gets css styles for a specific element. If `value` is provided - * then it sets the style and returns the element to allow for chaining, - * otherwise returns the style. - * @param {Element} element - Element to set the CSS of - * @param {string} attribute - Attribute to get or set - * @param {string} [value] - Value to set for attribute - * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. - */ - static css(element, attribute, value) { - if (typeof(value) == "undefined") return global.getComputedStyle(element)[attribute]; - element.style[attribute] = value; - return element; - } - - /** - * Sets or gets the width for a specific element. If `value` is provided - * then it sets the width and returns the element to allow for chaining, - * otherwise returns the width. - * @param {Element} element - Element to set the CSS of - * @param {string} [value] - Width to set - * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. - */ - static width(element, value) { - if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).width); - element.style.width = value; - return element; - } - - /** - * Sets or gets the height for a specific element. If `value` is provided - * then it sets the height and returns the element to allow for chaining, - * otherwise returns the height. - * @param {Element} element - Element to set the CSS of - * @param {string} [value] - Height to set - * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. - */ - static height(element, value) { - if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).height); - element.style.height = value; - return element; - } - - /** - * Sets the inner text of an element if given a value, otherwise returns it. - * @param {Element} element - Element to set the text of - * @param {string} [text] - Content to set - * @returns {string} - Either the string set by this call or the current text content of the node. - */ - static text(element, text) { - if (typeof(text) == "undefined") return element.textContent; - return element.textContent = text; - } - - /** - * Returns the innerWidth of the element. - * @param {Element} element - Element to retrieve inner width of - * @return {number} - The inner width of the element. - */ - static innerWidth(element) { - return element.clientWidth; - } - - /** - * Returns the innerHeight of the element. - * @param {Element} element - Element to retrieve inner height of - * @return {number} - The inner height of the element. - */ - static innerHeight(element) { - return element.clientHeight; - } - - /** - * Returns the outerWidth of the element. - * @param {Element} element - Element to retrieve outer width of - * @return {number} - The outer width of the element. - */ - static outerWidth(element) { - return element.offsetWidth; - } - - /** - * Returns the outerHeight of the element. - * @param {Element} element - Element to retrieve outer height of - * @return {number} - The outer height of the element. - */ - static outerHeight(element) { - return element.offsetHeight; - } - - /** - * Gets the offset of the element in the page. - * @param {Element} element - Element to get offset of - * @return {Offset} - The offset of the element - */ - static offset(element) { - return element.getBoundingClientRect(); - } - - static get listeners() { return this._listeners || (this._listeners = {}); } - - /** - * This is similar to jQuery's `on` function and can *hopefully* be used in the same way. - * - * Rather than attempt to explain, I'll show some example usages. - * - * The following will add a click listener (in the `myPlugin` namespace) to `element`. - * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});` - * - * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element. - * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});` - * - * The following will add a click listener (without namespace) to `element`. - * `DOMTools.on(element, "click", () => {console.log("clicked!");});` - * - * The following will add a click listener (without namespace) to `element` that only fires once. - * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});` - * - * @param {Element} element - Element to add listener to - * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") - * @param {(string|callable)} delegate - Selector to run on element to listen to - * @param {callable} [callback] - Function to fire on event - * @returns {module:DOMTools~CancelListener} - A function that will undo the listener - */ - static on(element, event, delegate, callback) { - const [type, namespace] = event.split("."); - const hasDelegate = delegate && callback; - if (!callback) callback = delegate; - const eventFunc = !hasDelegate ? callback : function(event) { - if (event.target.matches(delegate)) { - callback(event); - } - }; - - element.addEventListener(type, eventFunc); - const cancel = () => { - element.removeEventListener(type, eventFunc); - }; - if (namespace) { - if (!this.listeners[namespace]) this.listeners[namespace] = []; - const newCancel = () => { - cancel(); - this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); - }; - this.listeners[namespace].push({ - event: type, - element: element, - cancel: newCancel - }); - return newCancel; - } - return cancel; - } - - /** - * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself - * and removes the listener upon the first firing of the desired event. - * - * @param {Element} element - Element to add listener to - * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") - * @param {(string|callable)} delegate - Selector to run on element to listen to - * @param {callable} [callback] - Function to fire on event - * @returns {module:DOMTools~CancelListener} - A function that will undo the listener - */ - static once(element, event, delegate, callback) { - const [type, namespace] = event.split("."); - const hasDelegate = delegate && callback; - if (!callback) callback = delegate; - const eventFunc = !hasDelegate ? function(event) { - callback(event); - element.removeEventListener(type, eventFunc); - } : function(event) { - if (!event.target.matches(delegate)) return; - callback(event); - element.removeEventListener(type, eventFunc); - }; - - element.addEventListener(type, eventFunc); - const cancel = () => { - element.removeEventListener(type, eventFunc); - }; - if (namespace) { - if (!this.listeners[namespace]) this.listeners[namespace] = []; - const newCancel = () => { - cancel(); - this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); - }; - this.listeners[namespace].push({ - event: type, - element: element, - cancel: newCancel - }); - return newCancel; - } - return cancel; - } - - static __offAll(event, element) { - const [type, namespace] = event.split("."); - let matchFilter = listener => listener.event == type, defaultFilter = _ => _; - if (element) matchFilter = l => l.event == type && l.element == element, defaultFilter = l => l.element == element; - const listeners = this.listeners[namespace] || []; - const list = type ? listeners.filter(matchFilter) : listeners.filter(defaultFilter); - for (let c = 0; c < list.length; c++) list[c].cancel(); - } - - /** - * This is similar to jQuery's `off` function and can *hopefully* be used in the same way. - * - * Rather than attempt to explain, I'll show some example usages. - * - * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`. - * `DOMTools.off(element, "click.myPlugin", onClick);` - * - * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element` that only fired when the target is a `.block` element. - * `DOMTools.off(element, "click.myPlugin", ".block", onClick);` - * - * The following will remove a click listener (without namespace) from `element`. - * `DOMTools.off(element, "click", onClick);` - * - * The following will remove all listeners in namespace `myPlugin` from `element`. - * `DOMTools.off(element, ".myPlugin");` - * - * The following will remove all click listeners in namespace `myPlugin` from *all elements*. - * `DOMTools.off("click.myPlugin");` - * - * The following will remove all listeners in namespace `myPlugin` from *all elements*. - * `DOMTools.off(".myPlugin");` - * - * @param {(Element|string)} element - Element to remove listener from - * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace") - * @param {(string|callable)} [delegate] - Selector to run on element to listen to - * @param {callable} [callback] - Function to fire on event - * @returns {Element} - The original element to allow for chaining - */ - static off(element, event, delegate, callback) { - if (typeof(element) == "string") return this.__offAll(element); - const [type, namespace] = event.split("."); - if (namespace) return this.__offAll(event, element); - - const hasDelegate = delegate && callback; - if (!callback) callback = delegate; - const eventFunc = !hasDelegate ? callback : function(event) { - if (event.target.matches(delegate)) { - callback(event); - } - }; - - element.removeEventListener(type, eventFunc); - return element; - } - - /** - * Adds a listener for when the node is added/removed from the document body. - * The listener is automatically removed upon firing. - * @param {HTMLElement} node - node to wait for - * @param {callable} callback - function to be performed on event - * @param {boolean} onMount - determines if it should fire on Mount or on Unmount - */ - static onMountChange(node, callback, onMount = true) { - const wrappedCallback = () => { - this.observer.unsubscribe(wrappedCallback); - callback(); - }; - this.observer.subscribe(wrappedCallback, mutation => { - const nodes = Array.from(onMount ? mutation.addedNodes : mutation.removedNodes); - const directMatch = nodes.indexOf(node) > -1; - const parentMatch = nodes.some(parent => parent.contains(node)); - return directMatch || parentMatch; - }); - return node; - } - - /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `true` */ - static onMount(node, callback) { return this.onMountChange(node, callback); } - - /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `false` */ - static onUnmount(node, callback) { return this.onMountChange(node, callback, false); } - - /** Alias for {@link module:DOMTools.onMount} */ - static onAdded(node, callback) { return this.onMount(node, callback); } - - /** Alias for {@link module:DOMTools.onUnmount} */ - static onRemoved(node, callback) { return this.onUnmount(node, callback, false); } - - /** - * Helper function which combines multiple elements into one parent element - * @param {Array} elements - array of elements to put into a single parent - */ - static wrap(elements) { - const domWrapper = this.parseHTML(`
`); - for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]); - return domWrapper; - } - - /** - * Resolves the node to an HTMLElement. This is mainly used by library modules. - * @param {(jQuery|Element)} node - node to resolve - */ - static resolveElement(node) { - if (!(node instanceof jQuery) && !(node instanceof Element)) return undefined; - return node instanceof jQuery ? node[0] : node; - } -} - +/** + * Copyright 2018 Zachary Rauen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * From: https://github.com/rauenzi/BDPluginLibrary + */ + +/** + * @interface + * @name Offset + * @property {number} top - Top offset of the target element. + * @property {number} right - Right offset of the target element. + * @property {number} bottom - Bottom offset of the target element. + * @property {number} left - Left offset of the target element. + * @property {number} height - Outer height of the target element. + * @property {number} width - Outer width of the target element. + */ + + /** + * Function that automatically removes added listener. + * @callback module:DOMTools~CancelListener + */ + +export default class DOMTools { + + static escapeID(id) { + return id.replace(/^[^a-z]+|[^\w-]+/gi, "-"); + } + + /** + * Adds a style to the document. + * @param {string} id - identifier to use as the element id + * @param {string} css - css to add to the document + */ + static addStyle(id, css) { + document.head.append(DOMTools.createElement(``)); + } + + /** + * Removes a style from the document. + * @param {string} id - original identifier used + */ + static removeStyle(id) { + const element = document.getElementById(id); + if (element) element.remove(); + } + + /** + * Adds/requires a remote script to be loaded + * @param {string} id - identifier to use for this script + * @param {string} url - url from which to load the script + * @returns {Promise} promise that resolves when the script is loaded + */ + static addScript(id, url) { + return new Promise(resolve => { + const script = document.createElement("script"); + script.id = id; + script.src = url; + script.type = "text/javascript"; + script.onload = resolve; + document.head.append(script); + }); + } + + /** + * Removes a remote script from the document. + * @param {string} id - original identifier used + */ + static removeScript(id) { + id = this.escapeID(id); + const element = document.getElementById(id); + if (element) element.remove(); + } + + // https://javascript.info/js-animation + static animate({timing = _ => _, update, duration}) { + const start = performance.now(); + + requestAnimationFrame(function animate(time) { + // timeFraction goes from 0 to 1 + let timeFraction = (time - start) / duration; + if (timeFraction > 1) timeFraction = 1; + + // calculate the current animation state + const progress = timing(timeFraction); + + update(progress); // draw it + + if (timeFraction < 1) { + requestAnimationFrame(animate); + } + + }); + } + + /** + * This is my shit version of not having to use `$` from jQuery. Meaning + * that you can pass a selector and it will automatically run {@link module:DOMTools.query}. + * It also means that you can pass a string of html and it will perform and return `parseHTML`. + * @see module:DOMTools.parseHTML + * @see module:DOMTools.query + * @param {string} selector - Selector to query or HTML to parse + * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query` + */ + static Q(selector) { + const element = this.parseHTML(selector); + const isHTML = element instanceof NodeList ? Array.from(element).some(n => n.nodeType === 1) : element.nodeType === 1; + if (isHTML) return element; + return this.query(selector); + } + + /** + * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {(Element|null)} - The found element or null if not found + */ + static query(selector, baseElement = document) { + return baseElement.querySelector(selector); + } + + /** + * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {Array} - Array of all found elements + */ + static queryAll(selector, baseElement = document) { + return baseElement.querySelectorAll(selector); + } + + /** + * Parses a string of HTML and returns the results. If the second parameter is true, + * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}. + * This is extremely useful if you have a list of elements at the top level, they can then be appended all at once to another node. + * + * If the second parameter is false, then the return value will be the list of parsed + * nodes and there were multiple top level nodes, otherwise the single node is returned. + * @param {string} html - HTML to be parsed + * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment` + * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing + */ + static parseHTML(html, fragment = false) { + const template = document.createElement("template"); + template.innerHTML = html; + const node = template.content.cloneNode(true); + if (fragment) return node; + return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; + } + + /** Alternate name for {@link module:DOMTools.parseHTML} */ + static createElement(html, fragment = false) {return this.parseHTML(html, fragment);} + + /** + * Takes a string of html and escapes it using the brower's own escaping mechanism. + * @param {String} html - html to be escaped + */ + static escapeHTML(html) { + const textNode = document.createTextNode(""); + const spanElement = document.createElement("span"); + spanElement.append(textNode); + textNode.nodeValue = html; + return spanElement.innerHTML; + } + + /** + * Adds a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to add + * @returns {Element} - `element` to allow for chaining + */ + static addClass(element, ...classes) { + classes = classes.flat().filter(c => c); + for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); + classes = classes.flat().filter(c => c); + element.classList.add(...classes); + return element; + } + + /** + * Removes a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to remove + * @returns {Element} - `element` to allow for chaining + */ + static removeClass(element, ...classes) { + for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); + classes = classes.flat().filter(c => c); + element.classList.remove(...classes); + return element; + } + + /** + * When only one argument is present: Toggle class value; + * i.e., if class exists then remove it and return false, if not, then add it and return true. + * When a second argument is present: + * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it. + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to toggle + * @param {boolean} [indicator] - Optional indicator for if the class should be toggled + * @returns {Element} - `element` to allow for chaining + */ + static toggleClass(element, classname, indicator) { + classname = classname.toString().split(" ").filter(c => c); + if (typeof(indicator) !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator)); + else classname.forEach(c => element.classList.toggle(c)); + return element; + } + + /** + * Checks if an element has a specific class + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to check + * @returns {boolean} - `true` if the element has the class, `false` otherwise. + */ + static hasClass(element, classname) { + return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c)); + } + + /** + * Replaces one class with another + * @param {Element} element - Element to edit classes of + * @param {string} oldName - Name of class to replace + * @param {string} newName - New name for the class + * @returns {Element} - `element` to allow for chaining + */ + static replaceClass(element, oldName, newName) { + element.classList.replace(oldName, newName); + return element; + } + + /** + * Appends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be appended to another node + * @param {Node} thatNode - Node for `thisNode` to be appended to + * @returns {Node} - `thisNode` to allow for chaining + */ + static appendTo(thisNode, thatNode) { + if (typeof(thatNode) == "string") thatNode = this.query(thatNode); + if (!thatNode) return null; + thatNode.append(thisNode); + return thisNode; + } + + /** + * Prepends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be prepended to another node + * @param {Node} thatNode - Node for `thisNode` to be prepended to + * @returns {Node} - `thisNode` to allow for chaining + */ + static prependTo(thisNode, thatNode) { + if (typeof(thatNode) == "string") thatNode = this.query(thatNode); + if (!thatNode) return null; + thatNode.prepend(thisNode); + return thisNode; + } + + /** + * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} targetNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining + */ + static insertAfter(thisNode, targetNode) { + targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling); + return thisNode; + } + + /** + * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} newNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining + */ + static after(thisNode, newNode) { + thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling); + return thisNode; + } + + /** + * Gets the next sibling element that matches the selector. + * @param {Element} element - Element to get the next sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element + */ + static next(element, selector = "") { + return selector ? element.querySelector("+ " + selector) : element.nextElementSibling; + } + + /** + * Gets all subsequent siblings. + * @param {Element} element - Element to get next siblings of + * @returns {NodeList} - The list of siblings + */ + static nextAll(element) { + return element.querySelectorAll("~ *"); + } + + /** + * Gets the subsequent siblings until an element matches the selector. + * @param {Element} element - Element to get the following siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings + */ + static nextUntil(element, selector) { + const next = []; + while (element.nextElementSibling && !element.nextElementSibling.matches(selector)) next.push(element = element.nextElementSibling); + return next; + } + + /** + * Gets the previous sibling element that matches the selector. + * @param {Element} element - Element to get the previous sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element + */ + static previous(element, selector = "") { + const previous = element.previousElementSibling; + if (selector) return previous && previous.matches(selector) ? previous : null; + return previous; + } + + /** + * Gets all preceeding siblings. + * @param {Element} element - Element to get preceeding siblings of + * @returns {NodeList} - The list of siblings + */ + static previousAll(element) { + const previous = []; + while (element.previousElementSibling) previous.push(element = element.previousElementSibling); + return previous; + } + + /** + * Gets the preceeding siblings until an element matches the selector. + * @param {Element} element - Element to get the preceeding siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings + */ + static previousUntil(element, selector) { + const previous = []; + while (element.previousElementSibling && !element.previousElementSibling.matches(selector)) previous.push(element = element.previousElementSibling); + return previous; + } + + /** + * Find which index in children a certain node is. Similar to jQuery's `$.index()` + * @param {HTMLElement} node - The node to find its index in parent + * @returns {number} Index of the node + */ + static indexInParent(node) { + const children = node.parentNode.childNodes; + let num = 0; + for (let i = 0; i < children.length; i++) { + if (children[i] == node) return num; + if (children[i].nodeType == 1) num++; + } + return -1; + } + + /** Shorthand for {@link module:DOMTools.indexInParent} */ + static index(node) {return this.indexInParent(node);} + + /** + * Gets the parent of the element if it matches the selector, + * otherwise returns null. + * @param {Element} element - Element to get parent of + * @param {string} [selector=""] - Selector to match parent + * @returns {(Element|null)} - The sibling element or null + */ + static parent(element, selector = "") { + return !selector || element.parentElement.matches(selector) ? element.parentElement : null; + } + + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children + */ + static findChild(element, selector) { + return element.querySelector(":scope > " + selector); + } + + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children + */ + static findChildren(element, selector) { + return element.querySelectorAll(":scope > " + selector); + } + + /** + * Gets all ancestors of Element that match the selector if provided. + * @param {Element} element - Element to get all parents of + * @param {string} [selector=""] - Selector to match the parents to + * @returns {Array} - The list of parents + */ + static parents(element, selector = "") { + const parents = []; + if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector)); + else while (element.parentElement) parents.push(element = element.parentElement); + return parents; + } + + /** + * Gets the ancestors until an element matches the selector. + * @param {Element} element - Element to get the ancestors of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of parents + */ + static parentsUntil(element, selector) { + const parents = []; + while (element.parentElement && !element.parentElement.matches(selector)) parents.push(element = element.parentElement); + return parents; + } + + /** + * Gets all siblings of the element that match the selector. + * @param {Element} element - Element to get all siblings of + * @param {string} [selector="*"] - Selector to match the siblings to + * @returns {Array} - The list of siblings + */ + static siblings(element, selector = "*") { + return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector)); + } + + /** + * Sets or gets css styles for a specific element. If `value` is provided + * then it sets the style and returns the element to allow for chaining, + * otherwise returns the style. + * @param {Element} element - Element to set the CSS of + * @param {string} attribute - Attribute to get or set + * @param {string} [value] - Value to set for attribute + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static css(element, attribute, value) { + if (typeof(value) == "undefined") return global.getComputedStyle(element)[attribute]; + element.style[attribute] = value; + return element; + } + + /** + * Sets or gets the width for a specific element. If `value` is provided + * then it sets the width and returns the element to allow for chaining, + * otherwise returns the width. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Width to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static width(element, value) { + if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).width); + element.style.width = value; + return element; + } + + /** + * Sets or gets the height for a specific element. If `value` is provided + * then it sets the height and returns the element to allow for chaining, + * otherwise returns the height. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Height to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static height(element, value) { + if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).height); + element.style.height = value; + return element; + } + + /** + * Sets the inner text of an element if given a value, otherwise returns it. + * @param {Element} element - Element to set the text of + * @param {string} [text] - Content to set + * @returns {string} - Either the string set by this call or the current text content of the node. + */ + static text(element, text) { + if (typeof(text) == "undefined") return element.textContent; + return element.textContent = text; + } + + /** + * Returns the innerWidth of the element. + * @param {Element} element - Element to retrieve inner width of + * @return {number} - The inner width of the element. + */ + static innerWidth(element) { + return element.clientWidth; + } + + /** + * Returns the innerHeight of the element. + * @param {Element} element - Element to retrieve inner height of + * @return {number} - The inner height of the element. + */ + static innerHeight(element) { + return element.clientHeight; + } + + /** + * Returns the outerWidth of the element. + * @param {Element} element - Element to retrieve outer width of + * @return {number} - The outer width of the element. + */ + static outerWidth(element) { + return element.offsetWidth; + } + + /** + * Returns the outerHeight of the element. + * @param {Element} element - Element to retrieve outer height of + * @return {number} - The outer height of the element. + */ + static outerHeight(element) { + return element.offsetHeight; + } + + /** + * Gets the offset of the element in the page. + * @param {Element} element - Element to get offset of + * @return {Offset} - The offset of the element + */ + static offset(element) { + return element.getBoundingClientRect(); + } + + static get listeners() { return this._listeners || (this._listeners = {}); } + + /** + * This is similar to jQuery's `on` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will add a click listener (in the `myPlugin` namespace) to `element`. + * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});` + * + * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element. + * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element`. + * `DOMTools.on(element, "click", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element` that only fires once. + * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});` + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + */ + static on(element, event, delegate, callback) { + const [type, namespace] = event.split("."); + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? callback : function(event) { + if (event.target.matches(delegate)) { + callback(event); + } + }; + + element.addEventListener(type, eventFunc); + const cancel = () => { + element.removeEventListener(type, eventFunc); + }; + if (namespace) { + if (!this.listeners[namespace]) this.listeners[namespace] = []; + const newCancel = () => { + cancel(); + this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); + }; + this.listeners[namespace].push({ + event: type, + element: element, + cancel: newCancel + }); + return newCancel; + } + return cancel; + } + + /** + * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself + * and removes the listener upon the first firing of the desired event. + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + */ + static once(element, event, delegate, callback) { + const [type, namespace] = event.split("."); + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? function(event) { + callback(event); + element.removeEventListener(type, eventFunc); + } : function(event) { + if (!event.target.matches(delegate)) return; + callback(event); + element.removeEventListener(type, eventFunc); + }; + + element.addEventListener(type, eventFunc); + const cancel = () => { + element.removeEventListener(type, eventFunc); + }; + if (namespace) { + if (!this.listeners[namespace]) this.listeners[namespace] = []; + const newCancel = () => { + cancel(); + this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); + }; + this.listeners[namespace].push({ + event: type, + element: element, + cancel: newCancel + }); + return newCancel; + } + return cancel; + } + + static __offAll(event, element) { + const [type, namespace] = event.split("."); + let matchFilter = listener => listener.event == type, defaultFilter = _ => _; + if (element) matchFilter = l => l.event == type && l.element == element, defaultFilter = l => l.element == element; + const listeners = this.listeners[namespace] || []; + const list = type ? listeners.filter(matchFilter) : listeners.filter(defaultFilter); + for (let c = 0; c < list.length; c++) list[c].cancel(); + } + + /** + * This is similar to jQuery's `off` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`. + * `DOMTools.off(element, "click.myPlugin", onClick);` + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element` that only fired when the target is a `.block` element. + * `DOMTools.off(element, "click.myPlugin", ".block", onClick);` + * + * The following will remove a click listener (without namespace) from `element`. + * `DOMTools.off(element, "click", onClick);` + * + * The following will remove all listeners in namespace `myPlugin` from `element`. + * `DOMTools.off(element, ".myPlugin");` + * + * The following will remove all click listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off("click.myPlugin");` + * + * The following will remove all listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off(".myPlugin");` + * + * @param {(Element|string)} element - Element to remove listener from + * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} [delegate] - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {Element} - The original element to allow for chaining + */ + static off(element, event, delegate, callback) { + if (typeof(element) == "string") return this.__offAll(element); + const [type, namespace] = event.split("."); + if (namespace) return this.__offAll(event, element); + + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? callback : function(event) { + if (event.target.matches(delegate)) { + callback(event); + } + }; + + element.removeEventListener(type, eventFunc); + return element; + } + + /** + * Adds a listener for when the node is added/removed from the document body. + * The listener is automatically removed upon firing. + * @param {HTMLElement} node - node to wait for + * @param {callable} callback - function to be performed on event + * @param {boolean} onMount - determines if it should fire on Mount or on Unmount + */ + static onMountChange(node, callback, onMount = true) { + const wrappedCallback = () => { + this.observer.unsubscribe(wrappedCallback); + callback(); + }; + this.observer.subscribe(wrappedCallback, mutation => { + const nodes = Array.from(onMount ? mutation.addedNodes : mutation.removedNodes); + const directMatch = nodes.indexOf(node) > -1; + const parentMatch = nodes.some(parent => parent.contains(node)); + return directMatch || parentMatch; + }); + return node; + } + + /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `true` */ + static onMount(node, callback) { return this.onMountChange(node, callback); } + + /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `false` */ + static onUnmount(node, callback) { return this.onMountChange(node, callback, false); } + + /** Alias for {@link module:DOMTools.onMount} */ + static onAdded(node, callback) { return this.onMount(node, callback); } + + /** Alias for {@link module:DOMTools.onUnmount} */ + static onRemoved(node, callback) { return this.onUnmount(node, callback, false); } + + /** + * Helper function which combines multiple elements into one parent element + * @param {Array} elements - array of elements to put into a single parent + */ + static wrap(elements) { + const domWrapper = this.parseHTML(`
`); + for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]); + return domWrapper; + } + + /** + * Resolves the node to an HTMLElement. This is mainly used by library modules. + * @param {(jQuery|Element)} node - node to resolve + */ + static resolveElement(node) { + if (!(node instanceof jQuery) && !(node instanceof Element)) return undefined; + return node instanceof jQuery ? node[0] : node; + } +} + window.Lightcord.BetterDiscord.DOM = DOMTools \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/hooks.js b/BetterDiscordApp/src/modules/hooks.js index 8db3d9c..b26bc6f 100644 --- a/BetterDiscordApp/src/modules/hooks.js +++ b/BetterDiscordApp/src/modules/hooks.js @@ -1,7 +1,7 @@ -import BDV2 from "./v2" -const {useState} = BDV2.react - - -export function useForceUpdate(){ - return useState()[1]; +import BDV2 from "./v2" +const {useState} = BDV2.react + + +export function useForceUpdate(){ + return useState()[1]; } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/pluginCertifier.js b/BetterDiscordApp/src/modules/pluginCertifier.js index b465ac3..1e27ad2 100644 --- a/BetterDiscordApp/src/modules/pluginCertifier.js +++ b/BetterDiscordApp/src/modules/pluginCertifier.js @@ -1,451 +1,451 @@ -import nodeFetch from "node-fetch" -import * as electron from "electron" -import * as crypto from "crypto" -import BDV2 from "./v2" -import tooltipWrap from "../ui/tooltipWrap" -import Utils from "./utils" -import { createReadStream, writeFileSync } from "fs" -import { basename, join } from "path" -import contentManager from "./contentManager" -import { addonCache } from "./contentManager" - -const cache = {} -const cache2 = {} - -export default new class PluginCertifier { - constructor(){ - window.Lightcord.BetterDiscord.PluginCertifier = this - } - - patch(attachment, id){ - process.nextTick(() => { - processAttachment(attachment, id) - }) - } - - start(){ - - } - - isTrusted(hash){ - return cache[hash] && !cache[hash].suspect - } -} - -const tests = [ - [/token/gi, 0], - [/email/gi, 0], - [/mfa/gi, 0], - [/2fa/gi, 0], - [/phone/gi, 0], - [/child_process/gi, 0], - [/localStorage/gi, 0], - [/getGlobal/gi, 0], - [/BrowserWindow/gi, 0], - [/\.exe/gi, 0], - [/eval/gi, 0], - [/WebAssembly/gi, 0], - [/XMLHttpRequest(\.|\[["'`])prototype/gi, 0], - [/window\.fetch( +)?=/gi, 0], - /** Obfuscation / hidden / workarounds */ - [/(["'`]\+)["'`]\w["'`]/gi, 1], - [/["'`]\w["'`](\+["'`])/gi, 1], - [/\${["'`]\w+["'`]}/gi, 1], - /** hexadecimal */ - [/_0x\w{4}\('0x[\dabcdef]+'\)/g, 1], - [/_0x\w{4}\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // _0x8db7('0x0', 'x1]f') - /** mangled */ - [/\w+\('0x[\dabcdef]+'\)/g, 1], // b('0x0') - [/\w+\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // b('0x0', 'x1]f') - /** string array at start */ - [/^var [\w\d_$]+=\["/gi, 1] -] - -const threats = [ - "Account Stealer/Virus", - "Obfuscation/Hidden code" -] - -export function checkViruses(hash, data, resultCallback, removeCallback, filename){ - data = data.toString("utf8") - let isHarmful = false - /** - * @type {string} - */ - const no_comments = data.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "").trim()// removing all comments from plugins (remove meta and other.) - - for(const [test, type] of tests){ - const scrpt = type === 1 ? no_comments : data - if(test.exec(scrpt)){ - isHarmful = threats[type] - break - } - } - - if(!isHarmful)return removeCallback(hash) - cache[hash] = { - suspect: true, - name: hashToUrl[hash].split("/").pop(), - type: hashToUrl[hash].endsWith(".js") ? "Plugin" : "Theme", - harm: isHarmful, - hash: hash, - filename - } - - console.log(`Found potentially dangerous ${cache[hash].type.toLowerCase()}: ${cache[hash].name}`) - - resultCallback(cache[hash]) -} - -const hashToUrl = {} - -export function checkHash(hash, data, filename, resultCallback, removeCallback){ - console.log(`File: ${filename} hash: ${hash}`) - if(!cache[hash]){ - nodeFetch("https://cdn.jsdelivr.net/gh/Lightcord/filehashes@master/hashes/"+hash, { // Using node-fetch to bypass cors - headers: { - "User-Agent": electron.remote.getCurrentWebContents().userAgent // have to set user-agent - } - }).then(async res => { - if(res.status !== 200){ - if(filename.endsWith(".theme.css"))return removeCallback(hash) - try{ - checkViruses(hash, data, resultCallback, removeCallback, filename) - }catch(e){ - console.error(e) - removeCallback() - } - return - } - const result = await res.json() - result.hash = hash - result.filename = filename - - cache[hash] = result - - resultCallback(result) - }).catch((err) => { - console.error(`Could not read from github. ${err}`) - if(filename.endsWith(".theme.css"))return removeCallback(hash) - checkViruses(hash, data, resultCallback, removeCallback, filename) - }) - }else{ - const result = cache[hash] - - resultCallback(result) - } -} - -export function processFile(__path, resultCallback, removeCallback = (hash) => {}, isFromLoader = false){ - const hash = crypto.createHash("sha256") - let data = Buffer.alloc(0) - - createReadStream(__path).on("data", chunk => { - data = Buffer.concat([data, chunk]) - hash.update(chunk) - }).on("end", () => { - const hashResult = hash.digest("hex") - - hashToUrl[hashResult] = __path - - if(isFromLoader && addonCache[hashResult]){ - let value = addonCache[hashResult] - if(value.timestamp < (Date.now() - 6.048e+8)){ - delete addonCache[hashResult] - contentManager.saveAddonCache() - }else{ - resultCallback(value.result) - return - } - } - - checkHash(hashResult, data, basename(__path), resultCallback, removeCallback) - }) -} - -export function processAttachment(attachment, id){ - if(!document.getElementById(id))return - if(!attachment.url.startsWith("https://cdn.discordapp.com/"))return document.getElementById(id).remove() - if(!attachment.filename.endsWith(".plugin.js") && !attachment.filename.endsWith(".theme.css"))return document.getElementById(id).remove() - - nodeFetch(attachment.url, { - headers: { - "User-Agent": electron.remote.getCurrentWebContents().userAgent - } - }).then(res => { - if(res.status !== 200)throw new Error("File doesn't exist.") - const hash = crypto.createHash("sha256") - let data = Buffer.alloc(0) - res.body.on("data", chunk => { - data = Buffer.concat([data, chunk]) - hash.update(chunk) - }) - res.body.on("end", () => { - const hashResult = hash.digest("hex") - - cache2[attachment.url] = hashResult - hashToUrl[hashResult] = attachment.url - - checkHash(hashResult, data, attachment.filename, (result) => { - renderToElements(id, result, attachment.filename) - }, () => { - let elem = document.getElementById(id) - if(elem)elem.remove() - }) - }) - }).catch(()=>{}) -} - -let flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0] -let childModule = BDModules.get(e => e.childContainer)[0] - -/** - * - * @param {HTMLDivElement[]} elements - * @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result - */ -function renderToElements(id, result, filename){ - const div = document.getElementById(id) - if(!div || div.childNodes.length > 0)return // already certified/div does not exist anymore. - - if(!flowerStarModule)flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0] - if(!childModule)childModule = BDModules.get(e => e.childContainer)[0] - - if(result.suspect){ - try{ - div.parentNode.style.borderColor = "rgb(240, 71, 71)" - /** - * - * @param {HTMLElement} node - */ - let nextNode = (node) => { - for(let child of node.children){ - if(child.tagName === "A"){ - child.addEventListener("click", (e) => { - e.preventDefault() - e.stopImmediatePropagation() - - Utils.showConfirmationModal( - "Are you sure you want to download this ?", - "The "+result.type.toLowerCase()+" **"+filename+"** might be dangerous **("+result.harm+")**. \n\n**We don't recommand to download it**. However, you can still do it below.", - { - confirmText: "Download Anyway", - cancelText: "Don't !", - danger: true, - onCancel: () => {}, - onConfirm: () => { - electron.remote.shell.openExternal(child.href) - } - } - ) - }) - }else if(["div"].includes(child.tagName.toLowerCase())){ - nextNode(child) - } - } - } - nextNode(div.parentNode) - }catch(e){ - console.error(e) - } - BDV2.reactDom.render(BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is potentially dangerous."}, - BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}}, - BDV2.react.createElement("svg", {className: BDModules.get(e => e.svg)[0].svg, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 40 32"}, - BDV2.react.createElement("rect", { - x:"0", - y:"0", - width:"32", - height:"32", - mask:"url(#svg-mask-avatar-status-round-32)", - fill:"#f04747", - mask:"url(#svg-mask-status-dnd)", - className:BDModules.get(e => e.pointerEvents)[0].pointerEvents - }) - ) - ) - ), div) - }else if(!result.official){ - div.parentNode.style.borderColor = "#4087ed" - let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [ - BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is certified by Lightcord."}, - BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}}, - BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, - BDV2.react.createElement("path", {fill:"#4f545c", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"}) - ), - BDV2.react.createElement("div", {className: childModule.childContainer}, - BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, - BDV2.react.createElement("path", {fill:"#ffffff",d:"M7.4,11.17,4,8.62,5,7.26l2,1.53L10.64,4l1.36,1Z"}) - ) - ) - ) - ), - BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."}, - BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){ - Utils.showConfirmationModal( - "Are you sure you want to download this "+result.type.toLowerCase()+" ?", - "Lightcord will automatically install and launch this "+result.type.toLowerCase()+". You don't have anything to do.", - { - confirmText: "Download and Install", - cancelText: "I've changed my mind", - danger: false, - onCancel: () => {}, - onConfirm: () => { - let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0] - console.log(link) - nodeFetch(link) - .then(async res => { - if(res.status !== 200)throw new Error("Status was not 200") - let content = await res.buffer() - let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename) - console.log(installPath) - writeFileSync(installPath, content) - Utils.showToast(result.type+" succesfully installed.") - }).catch(err => { - err = err instanceof Error ? err : new Error(err) - Utils.showToast(err.message, { - type: "error" - }) - }) - } - } - ) - }}, - BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{ - color: "rgb(67, 181, 129)", - cursor: "pointer" - }}, - - - - - ) - ) - ) - ]) - BDV2.reactDom.render(span, div) - }else{ - div.parentNode.style.borderColor = "#4087ed" - let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [ - BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" was made by the developers of Lightcord.", style:"brand"}, - BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}}, - BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2",stroke:"#36393f",style:{color:"#4087ed"}}, - BDV2.react.createElement("path", {fill:"currentColor", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"}) - ), - BDV2.react.createElement("div", {className: childModule.childContainer}, - BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, - BDV2.react.createElement("path", {fill:"#ffffff",d:"M10.7,5.28a2.9,2.9,0,0,0-2.11.86.11.11,0,0,0,0,.16l1.05.94a.11.11,0,0,0,.15,0,1.27,1.27,0,0,1,.9-.33c.65,0,.65.73.65.73a.64.64,0,0,1-.65.65,1.73,1.73,0,0,1-1.18-.54c-.31-.26-.36-.32-.73-.66S7.06,5.28,5.65,5.28A2.26,2.26,0,0,0,3.37,7.56,2.59,2.59,0,0,0,3.82,9a2.18,2.18,0,0,0,1.83.89,2.94,2.94,0,0,0,2.1-.81.11.11,0,0,0,0-.16L6.74,8A.11.11,0,0,0,6.6,8a1.58,1.58,0,0,1-.94.29h0A.71.71,0,0,1,5,7.56H5a.63.63,0,0,1,.65-.64c.71,0,1.42.75,1.94,1.27.75.76,1.66,1.79,3.11,1.74A2.28,2.28,0,0,0,13,7.64a2.59,2.59,0,0,0-.45-1.47A2.14,2.14,0,0,0,10.7,5.28Z"}) - ) - ) - ) - ), - BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."}, - BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){ - Utils.showConfirmationModal( - "Are you sure you want to download this "+result.type.toLowerCase()+" ?", - "Lightcord will automatically download and load this "+result.type.toLowerCase()+". You must enable it in the settings.", - { - confirmText: "Download and Install", - cancelText: "I've changed my mind", - danger: false, - onCancel: () => {}, - onConfirm: () => { - let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0] - - nodeFetch(link) - .then(async res => { - if(res.status !== 200)throw new Error("Status was not 200") - let content = await res.buffer() - let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename) - - writeFileSync(installPath, content) - Utils.showToast(result.type+" succesfully installed.") - }).catch(err => { - err = err instanceof Error ? err : new Error(err) - Utils.showToast(err.message, { - type: "error" - }) - }) - } - } - ) - }}, - BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{ - color: "rgb(67, 181, 129)", - cursor: "pointer" - }}, - - - - - ) - ) - ) - ]) - BDV2.reactDom.render(span, div) - } -} - -function getKeyedArray(obj){ - let arr = [] - Object.keys(obj).forEach(k => { - arr.push([k, obj[k]]) - }) - return arr -} - -let key = null -let save = null - -window.Lightcord.Api.ensureExported(m=>m.ObjectStorage) -.then(localStorageModule => { - let localStorage = localStorageModule.impl - save = function(){ - localStorage.set("PluginCertifierKeyEncryption__", btoa(JSON.stringify(key))) - } - setInterval(() => { - save() - }, 100000); - try{ - let val = safeJSONParse(atob(localStorage.get("PluginCertifierKeyEncryption__"))) - if(val instanceof Error || !Array.isArray(val) || val.length !== 2 || val.find(e => typeof e !== "string") || Buffer.from(val[0], "base64").length !== 16 || Buffer.from(val[1], "base64").length !== 32){ - generateKey() - save() - return - } - key = val - }catch(e){ - generateKey() - save() - } -}) - -function generateKey(){ - key = [crypto.randomBytes(16).toString("base64"), crypto.randomBytes(32).toString("base64")] -} - -function safeJSONParse(json){ - try{ - return JSON.parse(json) - }catch(e){ - return e instanceof Error ? new Error(e) : e - } -} - -export function decryptSettingsCache(data){ - try{ - let decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")) - let decrypted = decipher.update(Buffer.from(data, "base64")); - decrypted = Buffer.concat([decrypted, decipher.final()]); - return decrypted.toString("utf8") - }catch(e){ - return "{}" - } -} -export function encryptSettingsCache(data){ - let args = [Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")] - - let cipher = crypto.createCipheriv('aes-256-cbc', ...args); - let encrypted = cipher.update(Buffer.from(data, "utf8")); - encrypted = Buffer.concat([encrypted, cipher.final()]); - return encrypted.toString("base64") +import nodeFetch from "node-fetch" +import * as electron from "electron" +import * as crypto from "crypto" +import BDV2 from "./v2" +import tooltipWrap from "../ui/tooltipWrap" +import Utils from "./utils" +import { createReadStream, writeFileSync } from "fs" +import { basename, join } from "path" +import contentManager from "./contentManager" +import { addonCache } from "./contentManager" + +const cache = {} +const cache2 = {} + +export default new class PluginCertifier { + constructor(){ + window.Lightcord.BetterDiscord.PluginCertifier = this + } + + patch(attachment, id){ + process.nextTick(() => { + processAttachment(attachment, id) + }) + } + + start(){ + + } + + isTrusted(hash){ + return cache[hash] && !cache[hash].suspect + } +} + +const tests = [ + [/token/gi, 0], + [/email/gi, 0], + [/mfa/gi, 0], + [/2fa/gi, 0], + [/phone/gi, 0], + [/child_process/gi, 0], + [/localStorage/gi, 0], + [/getGlobal/gi, 0], + [/BrowserWindow/gi, 0], + [/\.exe/gi, 0], + [/eval/gi, 0], + [/WebAssembly/gi, 0], + [/XMLHttpRequest(\.|\[["'`])prototype/gi, 0], + [/window\.fetch( +)?=/gi, 0], + /** Obfuscation / hidden / workarounds */ + [/(["'`]\+)["'`]\w["'`]/gi, 1], + [/["'`]\w["'`](\+["'`])/gi, 1], + [/\${["'`]\w+["'`]}/gi, 1], + /** hexadecimal */ + [/_0x\w{4}\('0x[\dabcdef]+'\)/g, 1], + [/_0x\w{4}\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // _0x8db7('0x0', 'x1]f') + /** mangled */ + [/\w+\('0x[\dabcdef]+'\)/g, 1], // b('0x0') + [/\w+\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // b('0x0', 'x1]f') + /** string array at start */ + [/^var [\w\d_$]+=\["/gi, 1] +] + +const threats = [ + "Account Stealer/Virus", + "Obfuscation/Hidden code" +] + +export function checkViruses(hash, data, resultCallback, removeCallback, filename){ + data = data.toString("utf8") + let isHarmful = false + /** + * @type {string} + */ + const no_comments = data.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "").trim()// removing all comments from plugins (remove meta and other.) + + for(const [test, type] of tests){ + const scrpt = type === 1 ? no_comments : data + if(test.exec(scrpt)){ + isHarmful = threats[type] + break + } + } + + if(!isHarmful)return removeCallback(hash) + cache[hash] = { + suspect: true, + name: hashToUrl[hash].split("/").pop(), + type: hashToUrl[hash].endsWith(".js") ? "Plugin" : "Theme", + harm: isHarmful, + hash: hash, + filename + } + + console.log(`Found potentially dangerous ${cache[hash].type.toLowerCase()}: ${cache[hash].name}`) + + resultCallback(cache[hash]) +} + +const hashToUrl = {} + +export function checkHash(hash, data, filename, resultCallback, removeCallback){ + console.log(`File: ${filename} hash: ${hash}`) + if(!cache[hash]){ + nodeFetch("https://cdn.jsdelivr.net/gh/Lightcord/filehashes@master/hashes/"+hash, { // Using node-fetch to bypass cors + headers: { + "User-Agent": electron.remote.getCurrentWebContents().userAgent // have to set user-agent + } + }).then(async res => { + if(res.status !== 200){ + if(filename.endsWith(".theme.css"))return removeCallback(hash) + try{ + checkViruses(hash, data, resultCallback, removeCallback, filename) + }catch(e){ + console.error(e) + removeCallback() + } + return + } + const result = await res.json() + result.hash = hash + result.filename = filename + + cache[hash] = result + + resultCallback(result) + }).catch((err) => { + console.error(`Could not read from github. ${err}`) + if(filename.endsWith(".theme.css"))return removeCallback(hash) + checkViruses(hash, data, resultCallback, removeCallback, filename) + }) + }else{ + const result = cache[hash] + + resultCallback(result) + } +} + +export function processFile(__path, resultCallback, removeCallback = (hash) => {}, isFromLoader = false){ + const hash = crypto.createHash("sha256") + let data = Buffer.alloc(0) + + createReadStream(__path).on("data", chunk => { + data = Buffer.concat([data, chunk]) + hash.update(chunk) + }).on("end", () => { + const hashResult = hash.digest("hex") + + hashToUrl[hashResult] = __path + + if(isFromLoader && addonCache[hashResult]){ + let value = addonCache[hashResult] + if(value.timestamp < (Date.now() - 6.048e+8)){ + delete addonCache[hashResult] + contentManager.saveAddonCache() + }else{ + resultCallback(value.result) + return + } + } + + checkHash(hashResult, data, basename(__path), resultCallback, removeCallback) + }) +} + +export function processAttachment(attachment, id){ + if(!document.getElementById(id))return + if(!attachment.url.startsWith("https://cdn.discordapp.com/"))return document.getElementById(id).remove() + if(!attachment.filename.endsWith(".plugin.js") && !attachment.filename.endsWith(".theme.css"))return document.getElementById(id).remove() + + nodeFetch(attachment.url, { + headers: { + "User-Agent": electron.remote.getCurrentWebContents().userAgent + } + }).then(res => { + if(res.status !== 200)throw new Error("File doesn't exist.") + const hash = crypto.createHash("sha256") + let data = Buffer.alloc(0) + res.body.on("data", chunk => { + data = Buffer.concat([data, chunk]) + hash.update(chunk) + }) + res.body.on("end", () => { + const hashResult = hash.digest("hex") + + cache2[attachment.url] = hashResult + hashToUrl[hashResult] = attachment.url + + checkHash(hashResult, data, attachment.filename, (result) => { + renderToElements(id, result, attachment.filename) + }, () => { + let elem = document.getElementById(id) + if(elem)elem.remove() + }) + }) + }).catch(()=>{}) +} + +let flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0] +let childModule = BDModules.get(e => e.childContainer)[0] + +/** + * + * @param {HTMLDivElement[]} elements + * @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result + */ +function renderToElements(id, result, filename){ + const div = document.getElementById(id) + if(!div || div.childNodes.length > 0)return // already certified/div does not exist anymore. + + if(!flowerStarModule)flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0] + if(!childModule)childModule = BDModules.get(e => e.childContainer)[0] + + if(result.suspect){ + try{ + div.parentNode.style.borderColor = "rgb(240, 71, 71)" + /** + * + * @param {HTMLElement} node + */ + let nextNode = (node) => { + for(let child of node.children){ + if(child.tagName === "A"){ + child.addEventListener("click", (e) => { + e.preventDefault() + e.stopImmediatePropagation() + + Utils.showConfirmationModal( + "Are you sure you want to download this ?", + "The "+result.type.toLowerCase()+" **"+filename+"** might be dangerous **("+result.harm+")**. \n\n**We don't recommand to download it**. However, you can still do it below.", + { + confirmText: "Download Anyway", + cancelText: "Don't !", + danger: true, + onCancel: () => {}, + onConfirm: () => { + electron.remote.shell.openExternal(child.href) + } + } + ) + }) + }else if(["div"].includes(child.tagName.toLowerCase())){ + nextNode(child) + } + } + } + nextNode(div.parentNode) + }catch(e){ + console.error(e) + } + BDV2.reactDom.render(BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is potentially dangerous."}, + BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}}, + BDV2.react.createElement("svg", {className: BDModules.get(e => e.svg)[0].svg, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 40 32"}, + BDV2.react.createElement("rect", { + x:"0", + y:"0", + width:"32", + height:"32", + mask:"url(#svg-mask-avatar-status-round-32)", + fill:"#f04747", + mask:"url(#svg-mask-status-dnd)", + className:BDModules.get(e => e.pointerEvents)[0].pointerEvents + }) + ) + ) + ), div) + }else if(!result.official){ + div.parentNode.style.borderColor = "#4087ed" + let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [ + BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is certified by Lightcord."}, + BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}}, + BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, + BDV2.react.createElement("path", {fill:"#4f545c", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"}) + ), + BDV2.react.createElement("div", {className: childModule.childContainer}, + BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, + BDV2.react.createElement("path", {fill:"#ffffff",d:"M7.4,11.17,4,8.62,5,7.26l2,1.53L10.64,4l1.36,1Z"}) + ) + ) + ) + ), + BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."}, + BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){ + Utils.showConfirmationModal( + "Are you sure you want to download this "+result.type.toLowerCase()+" ?", + "Lightcord will automatically install and launch this "+result.type.toLowerCase()+". You don't have anything to do.", + { + confirmText: "Download and Install", + cancelText: "I've changed my mind", + danger: false, + onCancel: () => {}, + onConfirm: () => { + let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0] + console.log(link) + nodeFetch(link) + .then(async res => { + if(res.status !== 200)throw new Error("Status was not 200") + let content = await res.buffer() + let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename) + console.log(installPath) + writeFileSync(installPath, content) + Utils.showToast(result.type+" succesfully installed.") + }).catch(err => { + err = err instanceof Error ? err : new Error(err) + Utils.showToast(err.message, { + type: "error" + }) + }) + } + } + ) + }}, + BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{ + color: "rgb(67, 181, 129)", + cursor: "pointer" + }}, + + + + + ) + ) + ) + ]) + BDV2.reactDom.render(span, div) + }else{ + div.parentNode.style.borderColor = "#4087ed" + let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [ + BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" was made by the developers of Lightcord.", style:"brand"}, + BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}}, + BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2",stroke:"#36393f",style:{color:"#4087ed"}}, + BDV2.react.createElement("path", {fill:"currentColor", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"}) + ), + BDV2.react.createElement("div", {className: childModule.childContainer}, + BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, + BDV2.react.createElement("path", {fill:"#ffffff",d:"M10.7,5.28a2.9,2.9,0,0,0-2.11.86.11.11,0,0,0,0,.16l1.05.94a.11.11,0,0,0,.15,0,1.27,1.27,0,0,1,.9-.33c.65,0,.65.73.65.73a.64.64,0,0,1-.65.65,1.73,1.73,0,0,1-1.18-.54c-.31-.26-.36-.32-.73-.66S7.06,5.28,5.65,5.28A2.26,2.26,0,0,0,3.37,7.56,2.59,2.59,0,0,0,3.82,9a2.18,2.18,0,0,0,1.83.89,2.94,2.94,0,0,0,2.1-.81.11.11,0,0,0,0-.16L6.74,8A.11.11,0,0,0,6.6,8a1.58,1.58,0,0,1-.94.29h0A.71.71,0,0,1,5,7.56H5a.63.63,0,0,1,.65-.64c.71,0,1.42.75,1.94,1.27.75.76,1.66,1.79,3.11,1.74A2.28,2.28,0,0,0,13,7.64a2.59,2.59,0,0,0-.45-1.47A2.14,2.14,0,0,0,10.7,5.28Z"}) + ) + ) + ) + ), + BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."}, + BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){ + Utils.showConfirmationModal( + "Are you sure you want to download this "+result.type.toLowerCase()+" ?", + "Lightcord will automatically download and load this "+result.type.toLowerCase()+". You must enable it in the settings.", + { + confirmText: "Download and Install", + cancelText: "I've changed my mind", + danger: false, + onCancel: () => {}, + onConfirm: () => { + let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0] + + nodeFetch(link) + .then(async res => { + if(res.status !== 200)throw new Error("Status was not 200") + let content = await res.buffer() + let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename) + + writeFileSync(installPath, content) + Utils.showToast(result.type+" succesfully installed.") + }).catch(err => { + err = err instanceof Error ? err : new Error(err) + Utils.showToast(err.message, { + type: "error" + }) + }) + } + } + ) + }}, + BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{ + color: "rgb(67, 181, 129)", + cursor: "pointer" + }}, + + + + + ) + ) + ) + ]) + BDV2.reactDom.render(span, div) + } +} + +function getKeyedArray(obj){ + let arr = [] + Object.keys(obj).forEach(k => { + arr.push([k, obj[k]]) + }) + return arr +} + +let key = null +let save = null + +window.Lightcord.Api.ensureExported(m=>m.ObjectStorage) +.then(localStorageModule => { + let localStorage = localStorageModule.impl + save = function(){ + localStorage.set("PluginCertifierKeyEncryption__", btoa(JSON.stringify(key))) + } + setInterval(() => { + save() + }, 100000); + try{ + let val = safeJSONParse(atob(localStorage.get("PluginCertifierKeyEncryption__"))) + if(val instanceof Error || !Array.isArray(val) || val.length !== 2 || val.find(e => typeof e !== "string") || Buffer.from(val[0], "base64").length !== 16 || Buffer.from(val[1], "base64").length !== 32){ + generateKey() + save() + return + } + key = val + }catch(e){ + generateKey() + save() + } +}) + +function generateKey(){ + key = [crypto.randomBytes(16).toString("base64"), crypto.randomBytes(32).toString("base64")] +} + +function safeJSONParse(json){ + try{ + return JSON.parse(json) + }catch(e){ + return e instanceof Error ? new Error(e) : e + } +} + +export function decryptSettingsCache(data){ + try{ + let decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")) + let decrypted = decipher.update(Buffer.from(data, "base64")); + decrypted = Buffer.concat([decrypted, decipher.final()]); + return decrypted.toString("utf8") + }catch(e){ + return "{}" + } +} +export function encryptSettingsCache(data){ + let args = [Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")] + + let cipher = crypto.createCipheriv('aes-256-cbc', ...args); + let encrypted = cipher.update(Buffer.from(data, "utf8")); + encrypted = Buffer.concat([encrypted, cipher.final()]); + return encrypted.toString("base64") } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/popoutWindow.js b/BetterDiscordApp/src/modules/popoutWindow.js index 3c55e4c..147f083 100644 --- a/BetterDiscordApp/src/modules/popoutWindow.js +++ b/BetterDiscordApp/src/modules/popoutWindow.js @@ -1,130 +1,130 @@ -import { themeCookie } from "../0globals" -import bdEvents from "./bdEvents" -import DOM from "./domtools" - -export default new class popoutWindow { - constructor(){ - /** - * @type {Map} - */ - this.windows = new Map() - this.enabled = false - this.init() - } - - async init(){ - let popoutModule = await window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow) - window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", (ev) => { - setImmediate(() => { - /** - * @type {Window} - */ - const window = popoutModule.default.getWindow(ev.key) - this.windows.set(ev.key, window) - - let classList = window.document.body.classList - classList.add("window-popout") - classList.add("lightcord") - classList.add("lightcord") - - this.update(ev.key) - }) - }) - window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_CLOSE", (ev) => { - setImmediate(() => { - this.windows.delete(ev.key) - }) - }) - bdEvents.on("theme-enabled", () => { - this.update() - }) - bdEvents.on("theme-disabled", () => { - this.update() - }) - bdEvents.on("theme-reloaded", () => { - this.update() - }) - bdEvents.on("theme-unloaded", () => { - this.update() - }) - bdEvents.on("theme-loaded", () => { - this.update() - }) - } - - enable(){ - this.enabled = true - this.update() - } - - disable(){ - this.enabled = false - this.update() - } - - update(key){ - if(!this.windows.size)return - if(!this.enabled){ - return this.removeThemes(key) - }else{ - return this.applyThemes(key) - } - } - - removeThemes(key){ - if(this.enabled)return - if(key){ - let window = this.windows.get(key) - if(!window)return - let document = window.document - - for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){ - style.remove() - } - }else{ - for(let key of this.windows.keys()){ - this.removeThemes(key) - } - } - } - - applyThemes(key){ - if(!this.enabled)return - if(key){ - let window = this.windows.get(key) - if(!window)return - let document = window.document - - for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){ - style.setAttribute("will-remove", "true") - } - - Object.keys(bdthemes) - .forEach(themeName => { - if(!themeCookie[themeName])return // theme disabled - const theme = bdthemes[themeName] - if(!theme)return //:shrug: - - let existing = document.querySelector("style[data-lightcord-theme=true]#"+DOM.escapeID(theme.id)) - if(existing){ - existing.innerHTML = unescape(theme.css) - existing.removeAttribute("will-remove") - }else{ - const style = document.createElement("style") - style.id = DOM.escapeID(theme.id) - style.innerHTML = unescape(theme.css) - style.setAttribute("data-lightcord-theme", "true") - document.head.append(style) - } - }) - - for(let style of document.querySelectorAll("style[will-remove=true]")){ - style.remove() - } - }else{ - for(let key of this.windows.keys()){ - this.applyThemes(key) - } - } - } +import { themeCookie } from "../0globals" +import bdEvents from "./bdEvents" +import DOM from "./domtools" + +export default new class popoutWindow { + constructor(){ + /** + * @type {Map} + */ + this.windows = new Map() + this.enabled = false + this.init() + } + + async init(){ + let popoutModule = await window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow) + window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", (ev) => { + setImmediate(() => { + /** + * @type {Window} + */ + const window = popoutModule.default.getWindow(ev.key) + this.windows.set(ev.key, window) + + let classList = window.document.body.classList + classList.add("window-popout") + classList.add("lightcord") + classList.add("lightcord") + + this.update(ev.key) + }) + }) + window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_CLOSE", (ev) => { + setImmediate(() => { + this.windows.delete(ev.key) + }) + }) + bdEvents.on("theme-enabled", () => { + this.update() + }) + bdEvents.on("theme-disabled", () => { + this.update() + }) + bdEvents.on("theme-reloaded", () => { + this.update() + }) + bdEvents.on("theme-unloaded", () => { + this.update() + }) + bdEvents.on("theme-loaded", () => { + this.update() + }) + } + + enable(){ + this.enabled = true + this.update() + } + + disable(){ + this.enabled = false + this.update() + } + + update(key){ + if(!this.windows.size)return + if(!this.enabled){ + return this.removeThemes(key) + }else{ + return this.applyThemes(key) + } + } + + removeThemes(key){ + if(this.enabled)return + if(key){ + let window = this.windows.get(key) + if(!window)return + let document = window.document + + for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){ + style.remove() + } + }else{ + for(let key of this.windows.keys()){ + this.removeThemes(key) + } + } + } + + applyThemes(key){ + if(!this.enabled)return + if(key){ + let window = this.windows.get(key) + if(!window)return + let document = window.document + + for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){ + style.setAttribute("will-remove", "true") + } + + Object.keys(bdthemes) + .forEach(themeName => { + if(!themeCookie[themeName])return // theme disabled + const theme = bdthemes[themeName] + if(!theme)return //:shrug: + + let existing = document.querySelector("style[data-lightcord-theme=true]#"+DOM.escapeID(theme.id)) + if(existing){ + existing.innerHTML = unescape(theme.css) + existing.removeAttribute("will-remove") + }else{ + const style = document.createElement("style") + style.id = DOM.escapeID(theme.id) + style.innerHTML = unescape(theme.css) + style.setAttribute("data-lightcord-theme", "true") + document.head.append(style) + } + }) + + for(let style of document.querySelectorAll("style[will-remove=true]")){ + style.remove() + } + }else{ + for(let key of this.windows.keys()){ + this.applyThemes(key) + } + } + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/publicServers.js b/BetterDiscordApp/src/modules/publicServers.js index 0610d7d..eb72d96 100644 --- a/BetterDiscordApp/src/modules/publicServers.js +++ b/BetterDiscordApp/src/modules/publicServers.js @@ -1,58 +1,58 @@ -import {settingsCookie} from "../0globals"; -import BDV2 from "./v2"; -import webpackModules from "./webpackModules"; -import Utils from "./utils"; -import DOM from "./domtools"; - -import V2C_PublicServers from "../ui/publicservers/publicServers"; -import Layers from "./Layers"; - -export default new class V2_PublicServers { - - constructor() { - this._appendButton = this._appendButton.bind(this); - window.Lightcord.BetterDiscord.V2_PublicServers = this - } - - render() { - Layers.createLayer((close) => { - return BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot", close}) - }) - } - - get button() { - const btn = DOM.createElement(`
`); - if (!settingsCookie["bda-gs-1"]) btn.style.display = "none"; - const label = DOM.createElement(`
public
`); - label.addEventListener("click", () => {this.render();}); - btn.append(label); - return btn; - } - - _appendButton() { - let [ - classNametutorialContainer - ] = [ - Utils.removeDa(BDModules.get(e => e.downloadProgress && e.tutorialContainer)[0].tutorialContainer) - ] - if (DOM.query("#bd-pub-li")) return; - const guilds = DOM.query(`div.${classNametutorialContainer} > div`); - DOM.after(guilds, this.button); - } - - addButton() { - if (this.guildPatch) return; - const GuildList = webpackModules.find(m => m.default && m.default.displayName == "NavigableGuilds"); - const GuildListOld = webpackModules.findByDisplayName("Guilds"); - if (!GuildList && !GuildListOld) Utils.warn("PublicServer", "Can't find GuildList component"); - this.guildPatch = Utils.monkeyPatch(GuildList ? GuildList : GuildListOld.prototype, GuildList ? "default" : "render", {after: this._appendButton}); - this._appendButton(); - } - - removeButton() { - this.guildPatch(); - delete this.guildPatch; - const button = DOM.query("#bd-pub-li"); - if (button) button.remove(); - } +import {settingsCookie} from "../0globals"; +import BDV2 from "./v2"; +import webpackModules from "./webpackModules"; +import Utils from "./utils"; +import DOM from "./domtools"; + +import V2C_PublicServers from "../ui/publicservers/publicServers"; +import Layers from "./Layers"; + +export default new class V2_PublicServers { + + constructor() { + this._appendButton = this._appendButton.bind(this); + window.Lightcord.BetterDiscord.V2_PublicServers = this + } + + render() { + Layers.createLayer((close) => { + return BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot", close}) + }) + } + + get button() { + const btn = DOM.createElement(`
`); + if (!settingsCookie["bda-gs-1"]) btn.style.display = "none"; + const label = DOM.createElement(`
public
`); + label.addEventListener("click", () => {this.render();}); + btn.append(label); + return btn; + } + + _appendButton() { + let [ + classNametutorialContainer + ] = [ + Utils.removeDa(BDModules.get(e => e.downloadProgress && e.tutorialContainer)[0].tutorialContainer) + ] + if (DOM.query("#bd-pub-li")) return; + const guilds = DOM.query(`div.${classNametutorialContainer} > div`); + DOM.after(guilds, this.button); + } + + addButton() { + if (this.guildPatch) return; + const GuildList = webpackModules.find(m => m.default && m.default.displayName == "NavigableGuilds"); + const GuildListOld = webpackModules.findByDisplayName("Guilds"); + if (!GuildList && !GuildListOld) Utils.warn("PublicServer", "Can't find GuildList component"); + this.guildPatch = Utils.monkeyPatch(GuildList ? GuildList : GuildListOld.prototype, GuildList ? "default" : "render", {after: this._appendButton}); + this._appendButton(); + } + + removeButton() { + this.guildPatch(); + delete this.guildPatch; + const button = DOM.query("#bd-pub-li"); + if (button) button.remove(); + } }; \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/settingsPanel.js b/BetterDiscordApp/src/modules/settingsPanel.js index e3013dd..eb33092 100644 --- a/BetterDiscordApp/src/modules/settingsPanel.js +++ b/BetterDiscordApp/src/modules/settingsPanel.js @@ -1,469 +1,469 @@ -import {settings, settingsCookie, settingsRPC, lightcordSettings} from "../0globals"; -import DataStore from "./dataStore"; -import V2_SettingsPanel_Sidebar from "./settingsPanelSidebar"; -import Utils from "./utils"; -import BDV2 from "./v2"; -import ContentManager from "./contentManager"; -import coloredText from "./coloredText"; -import tfHour from "./24hour"; -import DOM from "./domtools"; - -import publicServersModule from "./publicServers"; -import voiceMode from "./voiceMode"; -import ClassNormalizer from "./classNormalizer"; -import dMode from "./devMode"; - -import SectionedSettingsPanel from "../ui/sectionedSettingsPanel"; -import CssEditor from "../ui/cssEditor"; -import CardList from "../ui/addonlist"; -import V2C_PresenceSettings from "../ui/presenceSettings"; -import CustomRichPresence from "./CustomRichPresence"; -import V2C_AccountInfos from "../ui/AccountInfos"; -import { remote } from "electron"; -import AntiAdDM from "./AntiAdDM"; -import blurPrivate from "./blurPrivate"; -import disableTyping from "./disableTyping"; -import ApiPreview from "../ui/ApiPreview"; -import Switch from "../ui/switch"; -import MarginTop from "../ui/margintop"; -import webpackModules from "./webpackModules"; -import tooltipWrap from "../ui/tooltipWrap"; -import History from "../ui/icons/history"; -import core from "./core"; -import popoutWindow from "./popoutWindow"; - -class BDSidebarHeader extends React.PureComponent { - render(){ - let sidebarComponents = webpackModules.find(e => e.Separator && e.Header && e.Item) - - const changelogButton = React.createElement(tooltipWrap, {color: "black", side: "top", text: "Changelog"}, - React.createElement("span", {style: {float: "right", cursor: "pointer"}, className: "bd-changelog-button", onClick: () => {Utils.showChangelogModal(bbdChangelog);}}, - React.createElement(History, {className: "bd-icon", size: "16px"}) - ) - ); - let rendered = new sidebarComponents.Header({ - children: React.createElement("span", null, "Bandaged BD", changelogButton), - className: "ui-tab-bar-header" - }) - return rendered - } -} - -let isClearingCache = false - -export default new class V2_SettingsPanel { - - constructor() { - this.onChange = this.onChange.bind(this); - this.updateSettings = this.updateSettings.bind(this); - this.sidebar = new V2_SettingsPanel_Sidebar(); - - this.registerComponents() - } - - registerComponents(){ - /** Lightcord */ - this.sidebar.register("lightcord", makeComponent(this.lightcordComponent.bind(this))) - this.sidebar.register("status", makeComponent(this.PresenceComponent.bind(this))) - this.sidebar.register("accountinfo", makeComponent(this.AccountInfosComponent.bind(this))) - this.sidebar.register("lcapipreview", makeComponent(this.ApiPreviewComponent.bind(this))) - - /* Bandaged BD */ - this.sidebar.register("BDChangelogTitle", makeComponent(() => { - return new BDSidebarHeader().render() - })) - this.sidebar.register("core", makeComponent(this.coreComponent.bind(this))) - this.sidebar.register("customcss", makeComponent(this.customCssComponent.bind(this))) - this.sidebar.register("plugins", makeComponent(this.renderAddonPane("plugins"))) - this.sidebar.register("themes", makeComponent(this.renderAddonPane("themes"))) - } - - get coreSettings() { - const settings = this.getSettings("core"); - const categories = [...new Set(settings.map(s => s.category))]; - const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};}); - return sections; - } - - get lightcordSettings() { - const settings = this.getSettings("lightcord"); - const categories = [...new Set(settings.map(s => s.category))]; - const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};}); - return sections; - } - - get PresenceSettings() { - return this.getSettings("status") - } - - getSettings(category) { - return Object.keys(settings).reduce((arr, key) => { - const setting = settings[key]; - if (setting.cat === category && setting.implemented && !setting.hidden) { - setting.text = key; - arr.push(setting); - } - return arr; - }, []); - } - - - onChange(id, checked, sidebar) { - this.updateSettings(id, checked, sidebar); - } - - updateSettings(id, enabled, sidebar) { - if(!["lightcord-8", "no_window_bound", "enable_glasstron", "lightcord-10"].includes(id))settingsCookie[id] = enabled; - - if (id == "bda-gs-2") { - if (enabled) DOM.addClass(document.body, "bd-minimal"); - else DOM.removeClass(document.body, "bd-minimal"); - } - - if (id == "bda-gs-3") { - if (enabled) DOM.addClass(document.body, "bd-minimal-chan"); - else DOM.removeClass(document.body, "bd-minimal-chan"); - } - - if (id == "bda-gs-1") { - if (enabled) publicServersModule.addButton(); - else publicServersModule.removeButton(); - } - - if (id == "bda-gs-4") { - if (enabled) voiceMode.start(); - else voiceMode.stop(); - } - - if (id == "bda-gs-5") { - if (enabled) DOM.addClass(DOM.query("#app-mount"), "bda-dark"); - else DOM.removeClass(DOM.query("#app-mount"), "bda-dark"); - } - - if (enabled && id == "bda-gs-6") tfHour.inject24Hour(); - - if (id == "bda-gs-7") { - if (enabled) coloredText.injectColoredText(); - else coloredText.removeColoredText(); - } - - if (id == "fork-ps-4") { - if (enabled) ClassNormalizer.start(); - else ClassNormalizer.stop(); - } - - if (id == "fork-ps-5") { - if (enabled) { - ContentManager.watchContent("plugin"); - ContentManager.watchContent("theme"); - } - else { - ContentManager.unwatchContent("plugin"); - ContentManager.unwatchContent("theme"); - } - } - - if (id == "fork-wp-1") { - Utils.setWindowPreference("transparent", enabled); - if (enabled) Utils.setWindowPreference("backgroundColor", null); - else Utils.setWindowPreference("backgroundColor", "#2f3136"); - } - - - if (id == "bda-gs-8") { - if (enabled) dMode.startDebugListener(); - else dMode.stopDebugListener(); - } - - if (id == "fork-dm-1") { - if (enabled) dMode.startCopySelector(); - else dMode.stopCopySelector(); - } - - if (id === "lightcord-1") { - if (enabled) window.Lightcord.Settings.devMode = true - else window.Lightcord.Settings.devMode = false - sidebar.forceUpdate() - } - if (id === "lightcord-2") { - if (enabled) window.Lightcord.Settings.callRingingBeat = true - else window.Lightcord.Settings.callRingingBeat = false - } - if (id === "lightcord-presence-1") { - if (enabled) { - CustomRichPresence.enable() - const settingsStore = BDModules.get(e => e.default && typeof e.default === "object" && "showCurrentGame" in e.default)[0] - if(settingsStore && !settingsStore.default.showCurrentGame){ - BDModules.get(e => e.default && e.default.updateRemoteSettings)[0].default.updateRemoteSettings({ - showCurrentGame: true - }) - } - } - else CustomRichPresence.disable() - } - if (id === "lightcord-3") { - if (enabled) remote.getCurrentWindow().setAlwaysOnTop(true) - else remote.getCurrentWindow().setAlwaysOnTop(false) - } - if (id === "lightcord-4") { - if(enabled){ - AntiAdDM.enable() - }else{ - AntiAdDM.disable() - } - } - if (id === "lightcord-6") { - if(enabled){ - blurPrivate.enable() - }else{ - blurPrivate.disable() - } - } - if (id === "lightcord-7") { - if(enabled){ - disableTyping.enable() - }else{ - disableTyping.disable() - } - } - if (id === "lightcord-8"){ - let appSettings = remote.getGlobal("appSettings") - appSettings.set("isTabs", enabled) - appSettings.save() - remote.app.relaunch() - remote.app.exit() - } - if (id === "lightcord-9") { - popoutWindow[enabled ? "enable" : "disable"]() - } - if (id === "lightcord-10"){ - core.methods.NotificationsUseShim(enabled) - return - } - if (id === "no_window_bound"){ - let appSettings = remote.getGlobal("appSettings") - appSettings.set("NO_WINDOWS_BOUND", enabled) - - appSettings.delete("IS_MAXIMIZED") - appSettings.delete("IS_MINIMIZED") - appSettings.delete("WINDOW_BOUNDS") - - appSettings.save() - remote.app.relaunch() - remote.app.exit() - } - if (id === "enable_glasstron"){ - let appSettings = remote.getGlobal("appSettings") - appSettings.set("GLASSTRON", enabled) - appSettings.save() - remote.app.relaunch() - remote.app.exit() - } - - this.saveSettings(); - } - - async initializeSettings() { - if (settingsCookie["bda-gs-2"]) DOM.addClass(document.body, "bd-minimal"); - if (settingsCookie["bda-gs-3"]) DOM.addClass(document.body, "bd-minimal-chan"); - if (settingsCookie["bda-gs-1"]) publicServersModule.addButton(); - if (settingsCookie["bda-gs-4"]) voiceMode.start(); - if (settingsCookie["bda-gs-5"]) DOM.addClass(DOM.query("#app-mount"), "bda-dark"); - if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour(); - if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText(); - if (settingsCookie["fork-ps-4"]) ClassNormalizer.start(); - if (settingsCookie["lightcord-1"]) window.Lightcord.Settings.devMode = true - if (settingsCookie["lightcord-2"]) window.Lightcord.Settings.callRingingBeat = true - if (settingsCookie["lightcord-presence-1"]) CustomRichPresence.enable() - if (settingsCookie["lightcord-3"]) remote.getCurrentWindow().setAlwaysOnTop(true) - if (settingsCookie["lightcord-4"]) AntiAdDM.enable() - if (settingsCookie["lightcord-6"]) blurPrivate.enable() - if (settingsCookie["lightcord-7"]) disableTyping.enable() - if (settingsCookie["lightcord-9"]) popoutWindow.enable() - - if (settingsCookie["fork-ps-5"]) { - ContentManager.watchContent("plugin"); - ContentManager.watchContent("theme"); - } - - if (settingsCookie["bda-gs-8"]) dMode.startDebugListener(); - if (settingsCookie["fork-dm-1"]) dMode.startCopySelector(); - - this.saveSettings(); - } - - saveSettings() { - DataStore.setSettingGroup("settings", settingsCookie); - DataStore.setSettingGroup("rpc", settingsRPC); - DataStore.setSettingGroup("lightcord-settings", lightcordSettings); - } - - loadSettings() { - Object.assign(settingsCookie, DataStore.getSettingGroup("settings")); - Object.assign(settingsRPC, DataStore.getSettingGroup("rpc")); - console.log(lightcordSettings, DataStore.getSettingGroup("lightcord-settings")) - Object.assign(lightcordSettings, DataStore.getSettingGroup("lightcord-settings")); - } - - renderSidebar(sidebar) { - return this.sidebar.render(sidebar); - } - - coreComponent() { - return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings}) - } - - lightcordComponent(sidebar, forceUpdate) { - let appSettings = remote.getGlobal("appSettings") - return [ - this.lightcordSettings.map((section, i) => { - return [ - (i === 0 ? null : BDV2.react.createElement(MarginTop)), - BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, section.title), - section.settings.map(setting => { - let isChecked = settingsCookie[setting.id] - if(setting.id === "lightcord-8")isChecked = appSettings.get("isTabs", false); - if(setting.id === "no_window_bound")isChecked = appSettings.get("NO_WINDOWS_BOUND", false) - if(setting.id === "enable_glasstron")isChecked = appSettings.get("GLASSTRON", true) - if(setting.id === "lightcord-10")isChecked = !appSettings.get("DEFAULT_NOTIFICATIONS", true) - let returnValue = BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: isChecked, onChange: (id, checked) => { - this.onChange(id, checked, sidebar); - }}) - if(setting.id == "lightcord-8" && isChecked){ - return [ - returnValue, - React.createElement(Lightcord.Api.Components.inputs.Button, { - color: "green", - look: "outlined", - size: "small", - hoverColor: "brand", - onClick: () => { - DiscordNative.ipc.send("NEW_TAB") - }, - wrapper: false, - disabled: false - }, "Open a new Tab") - ] - } - return returnValue - }) - ] - }), - BDV2.react.createElement(window.Lightcord.Api.Components.inputs.Button, { - color: "yellow", - look: "ghost", - size: "medium", - hoverColor: "red", - onClick(){ - console.log("Should relaunch") - remote.app.relaunch({ - args: remote.process.argv.slice(1).concat(["--disable-betterdiscord"]) - }) - remote.app.quit() - }, - wrapper: true - }, "Relaunch without BetterDiscord"), - React.createElement(Lightcord.Api.Components.inputs.Button, { - color: "yellow", - look: "ghost", - size: "medium", - hoverColor: "red", - onClick: () => { - if(isClearingCache)return - isClearingCache = true - Utils.showToast("Clearing cache...", { - type: "info" - }) - forceUpdate() - remote.getCurrentWebContents().session.clearCache() - .then(() => { - Utils.showToast("Cache is cleared !", { - type: "success" - }) - isClearingCache = false - forceUpdate() - }).catch(err => { - console.error(err) - Utils.showToast("An error occured. Check console for more informations.", { - type: "error" - }) - isClearingCache = false - forceUpdate() - }) - }, - wrapper: true, - disabled: isClearingCache - }, "Clear cache") - ] - } - - PresenceComponent() { - return BDV2.react.createElement(V2C_PresenceSettings, { - key: "lppannel", - onChange: this.onChange, - settings: this.PresenceSettings - }) - } - - AccountInfosComponent() { - return BDV2.react.createElement(V2C_AccountInfos, { - key: "lapannel" - }) - } - - ApiPreviewComponent() { - return BDV2.react.createElement(ApiPreview, { - key: "lapipannel" - }) - } - - customCssComponent() { - return BDV2.react.createElement(CssEditor, {key: "csseditor"}) - } - - renderAddonPane(type) { - // I know this shouldn't be here, but when it isn't, - // React refuses to change the button when going - // between plugins and themes page... something - // to debug later. - class ContentList extends BDV2.react.Component { - constructor(props) { - super(props); - this.prefix = this.props.type.replace("s", ""); - } - - onChange() { - this.props.onChange(this.props.type); - } - - render() {return this.props.children;} - } - const originalRender = ContentList.prototype.render; - Object.defineProperty(ContentList.prototype, "render", { - enumerable: false, - configurable: false, - set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, - get: () => originalRender - }); - return function(){ - return BDV2.react.createElement(ContentList, {type}, BDV2.react.createElement(CardList, {type})) - } - } -}; - -/** - * No need to export settingsPanel on window - */ - -function makeComponent(children){ - class SettingComponent extends React.Component { - render(){ - return children(sidebar, () => this.forceUpdate()) - } - } - let sidebar - return (s) => { - sidebar = s - return SettingComponent - } +import {settings, settingsCookie, settingsRPC, lightcordSettings} from "../0globals"; +import DataStore from "./dataStore"; +import V2_SettingsPanel_Sidebar from "./settingsPanelSidebar"; +import Utils from "./utils"; +import BDV2 from "./v2"; +import ContentManager from "./contentManager"; +import coloredText from "./coloredText"; +import tfHour from "./24hour"; +import DOM from "./domtools"; + +import publicServersModule from "./publicServers"; +import voiceMode from "./voiceMode"; +import ClassNormalizer from "./classNormalizer"; +import dMode from "./devMode"; + +import SectionedSettingsPanel from "../ui/sectionedSettingsPanel"; +import CssEditor from "../ui/cssEditor"; +import CardList from "../ui/addonlist"; +import V2C_PresenceSettings from "../ui/presenceSettings"; +import CustomRichPresence from "./CustomRichPresence"; +import V2C_AccountInfos from "../ui/AccountInfos"; +import { remote } from "electron"; +import AntiAdDM from "./AntiAdDM"; +import blurPrivate from "./blurPrivate"; +import disableTyping from "./disableTyping"; +import ApiPreview from "../ui/ApiPreview"; +import Switch from "../ui/switch"; +import MarginTop from "../ui/margintop"; +import webpackModules from "./webpackModules"; +import tooltipWrap from "../ui/tooltipWrap"; +import History from "../ui/icons/history"; +import core from "./core"; +import popoutWindow from "./popoutWindow"; + +class BDSidebarHeader extends React.PureComponent { + render(){ + let sidebarComponents = webpackModules.find(e => e.Separator && e.Header && e.Item) + + const changelogButton = React.createElement(tooltipWrap, {color: "black", side: "top", text: "Changelog"}, + React.createElement("span", {style: {float: "right", cursor: "pointer"}, className: "bd-changelog-button", onClick: () => {Utils.showChangelogModal(bbdChangelog);}}, + React.createElement(History, {className: "bd-icon", size: "16px"}) + ) + ); + let rendered = new sidebarComponents.Header({ + children: React.createElement("span", null, "Bandaged BD", changelogButton), + className: "ui-tab-bar-header" + }) + return rendered + } +} + +let isClearingCache = false + +export default new class V2_SettingsPanel { + + constructor() { + this.onChange = this.onChange.bind(this); + this.updateSettings = this.updateSettings.bind(this); + this.sidebar = new V2_SettingsPanel_Sidebar(); + + this.registerComponents() + } + + registerComponents(){ + /** Lightcord */ + this.sidebar.register("lightcord", makeComponent(this.lightcordComponent.bind(this))) + this.sidebar.register("status", makeComponent(this.PresenceComponent.bind(this))) + this.sidebar.register("accountinfo", makeComponent(this.AccountInfosComponent.bind(this))) + this.sidebar.register("lcapipreview", makeComponent(this.ApiPreviewComponent.bind(this))) + + /* Bandaged BD */ + this.sidebar.register("BDChangelogTitle", makeComponent(() => { + return new BDSidebarHeader().render() + })) + this.sidebar.register("core", makeComponent(this.coreComponent.bind(this))) + this.sidebar.register("customcss", makeComponent(this.customCssComponent.bind(this))) + this.sidebar.register("plugins", makeComponent(this.renderAddonPane("plugins"))) + this.sidebar.register("themes", makeComponent(this.renderAddonPane("themes"))) + } + + get coreSettings() { + const settings = this.getSettings("core"); + const categories = [...new Set(settings.map(s => s.category))]; + const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};}); + return sections; + } + + get lightcordSettings() { + const settings = this.getSettings("lightcord"); + const categories = [...new Set(settings.map(s => s.category))]; + const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};}); + return sections; + } + + get PresenceSettings() { + return this.getSettings("status") + } + + getSettings(category) { + return Object.keys(settings).reduce((arr, key) => { + const setting = settings[key]; + if (setting.cat === category && setting.implemented && !setting.hidden) { + setting.text = key; + arr.push(setting); + } + return arr; + }, []); + } + + + onChange(id, checked, sidebar) { + this.updateSettings(id, checked, sidebar); + } + + updateSettings(id, enabled, sidebar) { + if(!["lightcord-8", "no_window_bound", "enable_glasstron", "lightcord-10"].includes(id))settingsCookie[id] = enabled; + + if (id == "bda-gs-2") { + if (enabled) DOM.addClass(document.body, "bd-minimal"); + else DOM.removeClass(document.body, "bd-minimal"); + } + + if (id == "bda-gs-3") { + if (enabled) DOM.addClass(document.body, "bd-minimal-chan"); + else DOM.removeClass(document.body, "bd-minimal-chan"); + } + + if (id == "bda-gs-1") { + if (enabled) publicServersModule.addButton(); + else publicServersModule.removeButton(); + } + + if (id == "bda-gs-4") { + if (enabled) voiceMode.start(); + else voiceMode.stop(); + } + + if (id == "bda-gs-5") { + if (enabled) DOM.addClass(DOM.query("#app-mount"), "bda-dark"); + else DOM.removeClass(DOM.query("#app-mount"), "bda-dark"); + } + + if (enabled && id == "bda-gs-6") tfHour.inject24Hour(); + + if (id == "bda-gs-7") { + if (enabled) coloredText.injectColoredText(); + else coloredText.removeColoredText(); + } + + if (id == "fork-ps-4") { + if (enabled) ClassNormalizer.start(); + else ClassNormalizer.stop(); + } + + if (id == "fork-ps-5") { + if (enabled) { + ContentManager.watchContent("plugin"); + ContentManager.watchContent("theme"); + } + else { + ContentManager.unwatchContent("plugin"); + ContentManager.unwatchContent("theme"); + } + } + + if (id == "fork-wp-1") { + Utils.setWindowPreference("transparent", enabled); + if (enabled) Utils.setWindowPreference("backgroundColor", null); + else Utils.setWindowPreference("backgroundColor", "#2f3136"); + } + + + if (id == "bda-gs-8") { + if (enabled) dMode.startDebugListener(); + else dMode.stopDebugListener(); + } + + if (id == "fork-dm-1") { + if (enabled) dMode.startCopySelector(); + else dMode.stopCopySelector(); + } + + if (id === "lightcord-1") { + if (enabled) window.Lightcord.Settings.devMode = true + else window.Lightcord.Settings.devMode = false + sidebar.forceUpdate() + } + if (id === "lightcord-2") { + if (enabled) window.Lightcord.Settings.callRingingBeat = true + else window.Lightcord.Settings.callRingingBeat = false + } + if (id === "lightcord-presence-1") { + if (enabled) { + CustomRichPresence.enable() + const settingsStore = BDModules.get(e => e.default && typeof e.default === "object" && "showCurrentGame" in e.default)[0] + if(settingsStore && !settingsStore.default.showCurrentGame){ + BDModules.get(e => e.default && e.default.updateRemoteSettings)[0].default.updateRemoteSettings({ + showCurrentGame: true + }) + } + } + else CustomRichPresence.disable() + } + if (id === "lightcord-3") { + if (enabled) remote.getCurrentWindow().setAlwaysOnTop(true) + else remote.getCurrentWindow().setAlwaysOnTop(false) + } + if (id === "lightcord-4") { + if(enabled){ + AntiAdDM.enable() + }else{ + AntiAdDM.disable() + } + } + if (id === "lightcord-6") { + if(enabled){ + blurPrivate.enable() + }else{ + blurPrivate.disable() + } + } + if (id === "lightcord-7") { + if(enabled){ + disableTyping.enable() + }else{ + disableTyping.disable() + } + } + if (id === "lightcord-8"){ + let appSettings = remote.getGlobal("appSettings") + appSettings.set("isTabs", enabled) + appSettings.save() + remote.app.relaunch() + remote.app.exit() + } + if (id === "lightcord-9") { + popoutWindow[enabled ? "enable" : "disable"]() + } + if (id === "lightcord-10"){ + core.methods.NotificationsUseShim(enabled) + return + } + if (id === "no_window_bound"){ + let appSettings = remote.getGlobal("appSettings") + appSettings.set("NO_WINDOWS_BOUND", enabled) + + appSettings.delete("IS_MAXIMIZED") + appSettings.delete("IS_MINIMIZED") + appSettings.delete("WINDOW_BOUNDS") + + appSettings.save() + remote.app.relaunch() + remote.app.exit() + } + if (id === "enable_glasstron"){ + let appSettings = remote.getGlobal("appSettings") + appSettings.set("GLASSTRON", enabled) + appSettings.save() + remote.app.relaunch() + remote.app.exit() + } + + this.saveSettings(); + } + + async initializeSettings() { + if (settingsCookie["bda-gs-2"]) DOM.addClass(document.body, "bd-minimal"); + if (settingsCookie["bda-gs-3"]) DOM.addClass(document.body, "bd-minimal-chan"); + if (settingsCookie["bda-gs-1"]) publicServersModule.addButton(); + if (settingsCookie["bda-gs-4"]) voiceMode.start(); + if (settingsCookie["bda-gs-5"]) DOM.addClass(DOM.query("#app-mount"), "bda-dark"); + if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour(); + if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText(); + if (settingsCookie["fork-ps-4"]) ClassNormalizer.start(); + if (settingsCookie["lightcord-1"]) window.Lightcord.Settings.devMode = true + if (settingsCookie["lightcord-2"]) window.Lightcord.Settings.callRingingBeat = true + if (settingsCookie["lightcord-presence-1"]) CustomRichPresence.enable() + if (settingsCookie["lightcord-3"]) remote.getCurrentWindow().setAlwaysOnTop(true) + if (settingsCookie["lightcord-4"]) AntiAdDM.enable() + if (settingsCookie["lightcord-6"]) blurPrivate.enable() + if (settingsCookie["lightcord-7"]) disableTyping.enable() + if (settingsCookie["lightcord-9"]) popoutWindow.enable() + + if (settingsCookie["fork-ps-5"]) { + ContentManager.watchContent("plugin"); + ContentManager.watchContent("theme"); + } + + if (settingsCookie["bda-gs-8"]) dMode.startDebugListener(); + if (settingsCookie["fork-dm-1"]) dMode.startCopySelector(); + + this.saveSettings(); + } + + saveSettings() { + DataStore.setSettingGroup("settings", settingsCookie); + DataStore.setSettingGroup("rpc", settingsRPC); + DataStore.setSettingGroup("lightcord-settings", lightcordSettings); + } + + loadSettings() { + Object.assign(settingsCookie, DataStore.getSettingGroup("settings")); + Object.assign(settingsRPC, DataStore.getSettingGroup("rpc")); + console.log(lightcordSettings, DataStore.getSettingGroup("lightcord-settings")) + Object.assign(lightcordSettings, DataStore.getSettingGroup("lightcord-settings")); + } + + renderSidebar(sidebar) { + return this.sidebar.render(sidebar); + } + + coreComponent() { + return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings}) + } + + lightcordComponent(sidebar, forceUpdate) { + let appSettings = remote.getGlobal("appSettings") + return [ + this.lightcordSettings.map((section, i) => { + return [ + (i === 0 ? null : BDV2.react.createElement(MarginTop)), + BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, section.title), + section.settings.map(setting => { + let isChecked = settingsCookie[setting.id] + if(setting.id === "lightcord-8")isChecked = appSettings.get("isTabs", false); + if(setting.id === "no_window_bound")isChecked = appSettings.get("NO_WINDOWS_BOUND", false) + if(setting.id === "enable_glasstron")isChecked = appSettings.get("GLASSTRON", true) + if(setting.id === "lightcord-10")isChecked = !appSettings.get("DEFAULT_NOTIFICATIONS", true) + let returnValue = BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: isChecked, onChange: (id, checked) => { + this.onChange(id, checked, sidebar); + }}) + if(setting.id == "lightcord-8" && isChecked){ + return [ + returnValue, + React.createElement(Lightcord.Api.Components.inputs.Button, { + color: "green", + look: "outlined", + size: "small", + hoverColor: "brand", + onClick: () => { + DiscordNative.ipc.send("NEW_TAB") + }, + wrapper: false, + disabled: false + }, "Open a new Tab") + ] + } + return returnValue + }) + ] + }), + BDV2.react.createElement(window.Lightcord.Api.Components.inputs.Button, { + color: "yellow", + look: "ghost", + size: "medium", + hoverColor: "red", + onClick(){ + console.log("Should relaunch") + remote.app.relaunch({ + args: remote.process.argv.slice(1).concat(["--disable-betterdiscord"]) + }) + remote.app.quit() + }, + wrapper: true + }, "Relaunch without BetterDiscord"), + React.createElement(Lightcord.Api.Components.inputs.Button, { + color: "yellow", + look: "ghost", + size: "medium", + hoverColor: "red", + onClick: () => { + if(isClearingCache)return + isClearingCache = true + Utils.showToast("Clearing cache...", { + type: "info" + }) + forceUpdate() + remote.getCurrentWebContents().session.clearCache() + .then(() => { + Utils.showToast("Cache is cleared !", { + type: "success" + }) + isClearingCache = false + forceUpdate() + }).catch(err => { + console.error(err) + Utils.showToast("An error occured. Check console for more informations.", { + type: "error" + }) + isClearingCache = false + forceUpdate() + }) + }, + wrapper: true, + disabled: isClearingCache + }, "Clear cache") + ] + } + + PresenceComponent() { + return BDV2.react.createElement(V2C_PresenceSettings, { + key: "lppannel", + onChange: this.onChange, + settings: this.PresenceSettings + }) + } + + AccountInfosComponent() { + return BDV2.react.createElement(V2C_AccountInfos, { + key: "lapannel" + }) + } + + ApiPreviewComponent() { + return BDV2.react.createElement(ApiPreview, { + key: "lapipannel" + }) + } + + customCssComponent() { + return BDV2.react.createElement(CssEditor, {key: "csseditor"}) + } + + renderAddonPane(type) { + // I know this shouldn't be here, but when it isn't, + // React refuses to change the button when going + // between plugins and themes page... something + // to debug later. + class ContentList extends BDV2.react.Component { + constructor(props) { + super(props); + this.prefix = this.props.type.replace("s", ""); + } + + onChange() { + this.props.onChange(this.props.type); + } + + render() {return this.props.children;} + } + const originalRender = ContentList.prototype.render; + Object.defineProperty(ContentList.prototype, "render", { + enumerable: false, + configurable: false, + set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, + get: () => originalRender + }); + return function(){ + return BDV2.react.createElement(ContentList, {type}, BDV2.react.createElement(CardList, {type})) + } + } +}; + +/** + * No need to export settingsPanel on window + */ + +function makeComponent(children){ + class SettingComponent extends React.Component { + render(){ + return children(sidebar, () => this.forceUpdate()) + } + } + let sidebar + return (s) => { + sidebar = s + return SettingComponent + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/settingsPanelSidebar.js b/BetterDiscordApp/src/modules/settingsPanelSidebar.js index 7b8c700..ca22622 100644 --- a/BetterDiscordApp/src/modules/settingsPanelSidebar.js +++ b/BetterDiscordApp/src/modules/settingsPanelSidebar.js @@ -1,110 +1,110 @@ -export default class V2_SettingsPanel_Sidebar { - - constructor() { - this.panels = {} - } - - register(panel, component){ - this.panels[panel] = component - } - - getComponent(panel, sidebar){ - return this.panels[panel](sidebar) - } - - get items() { - return [{ - text: "BetterDiscord Settings", - id: "core" - }, { - text: "Plugins", - id: "plugins" - }, { - text: "Themes", - id: "themes" - }, { - text: "Custom CSS", - id: "customcss" - }]; - } - - get LCitems(){ - let items = [ - { - text: "Lightcord Settings", - id: "lightcord" - }, { - text: "RichPresence", - id: "status" - }, { - text: "Account Info", - id: "accountinfo" - } - ] - return items - } - - get LCDevItems(){ - let items = [] - if(!window.Lightcord.Settings.devMode)return items - items.push(...[ - { - section: "DIVIDER" - }, - { - section: "HEADER", - label: "Lightcord Api" - }, - { - text: "Components Preview", - id: "lcapipreview" - } - ]) - return items - } - - render(sidebar) { - return [ - { - section: "HEADER", - label: "Lightcord" - }, - ...this.LCitems.map(e => { - return { - section: e.id, - label: e.text, - element: this.getComponent(e.id, sidebar) - } - }), - ...this.LCDevItems.map(e => { - if(e.section)return e - return { - section: e.id, - label: e.text, - element: this.getComponent(e.id, sidebar) - } - }), - { - section: "DIVIDER" - }, - { - section: "CUSTOM", - element: this.getComponent("BDChangelogTitle") - }, - ...this.items.map(e => { - return { - section: e.id, - label: e.text, - element: this.getComponent(e.id, sidebar) - } - }), - { - section: "DIVIDER" - } - ] - } -} - -/** - * No need to export settingsPanelSidebar on window +export default class V2_SettingsPanel_Sidebar { + + constructor() { + this.panels = {} + } + + register(panel, component){ + this.panels[panel] = component + } + + getComponent(panel, sidebar){ + return this.panels[panel](sidebar) + } + + get items() { + return [{ + text: "BetterDiscord Settings", + id: "core" + }, { + text: "Plugins", + id: "plugins" + }, { + text: "Themes", + id: "themes" + }, { + text: "Custom CSS", + id: "customcss" + }]; + } + + get LCitems(){ + let items = [ + { + text: "Lightcord Settings", + id: "lightcord" + }, { + text: "RichPresence", + id: "status" + }, { + text: "Account Info", + id: "accountinfo" + } + ] + return items + } + + get LCDevItems(){ + let items = [] + if(!window.Lightcord.Settings.devMode)return items + items.push(...[ + { + section: "DIVIDER" + }, + { + section: "HEADER", + label: "Lightcord Api" + }, + { + text: "Components Preview", + id: "lcapipreview" + } + ]) + return items + } + + render(sidebar) { + return [ + { + section: "HEADER", + label: "Lightcord" + }, + ...this.LCitems.map(e => { + return { + section: e.id, + label: e.text, + element: this.getComponent(e.id, sidebar) + } + }), + ...this.LCDevItems.map(e => { + if(e.section)return e + return { + section: e.id, + label: e.text, + element: this.getComponent(e.id, sidebar) + } + }), + { + section: "DIVIDER" + }, + { + section: "CUSTOM", + element: this.getComponent("BDChangelogTitle") + }, + ...this.items.map(e => { + return { + section: e.id, + label: e.text, + element: this.getComponent(e.id, sidebar) + } + }), + { + section: "DIVIDER" + } + ] + } +} + +/** + * No need to export settingsPanelSidebar on window */ \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/themeModule.js b/BetterDiscordApp/src/modules/themeModule.js index dae4846..90972f9 100644 --- a/BetterDiscordApp/src/modules/themeModule.js +++ b/BetterDiscordApp/src/modules/themeModule.js @@ -1,152 +1,152 @@ -import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals"; -import ContentManager from "./contentManager"; -import DataStore from "./dataStore"; -import BDEvents from "./bdEvents"; -import Utils from "./utils"; -import DOM from "./domtools"; -import bdEvents from "./bdEvents"; - -class ThemeModule { - constructor(){ - window.Lightcord.BetterDiscord.ThemeModule = this - } - get folder() {return ContentManager.themesFolder;} -} - -ThemeModule.prototype.loadThemes = async function () { - this.loadThemeData(); - bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes())); - const themes = Object.keys(bdthemes); - - for (let i = 0; i < themes.length; i++) { - const theme = bdthemes[themes[i]]; - if (!themeCookie[theme.name]) themeCookie[theme.name] = false; - if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css)); - - await new Promise((resolve) => setTimeout(resolve, 10)) - } - for (const theme in themeCookie) { - if (!bdthemes[theme]) delete themeCookie[theme]; - } - this.saveThemeData(); - // if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme"); -}; - -ThemeModule.prototype.enableTheme = function(name, reload = false) { - themeCookie[name] = true; - this.saveThemeData(); - const theme = bdthemes[name]; - DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css)); - bdEvents.dispatch("theme-enabled") - if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`); -}; - -ThemeModule.prototype.enable = function (name, reload = false) { - return this.enableTheme(name, reload); -}; - -ThemeModule.prototype.disableTheme = function(name, reload = false) { - themeCookie[name] = false; - this.saveThemeData(); - const theme = bdthemes[name]; - DOM.removeStyle(DOM.escapeID(theme.id)); - bdEvents.dispatch("theme-disabled") - if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`); -}; - -ThemeModule.prototype.disable = function (name, reload = false) { - return this.disableTheme(name, reload); -}; - -ThemeModule.prototype.toggleTheme = function(theme) { - if (themeCookie[theme]) this.disableTheme(theme); - else this.enableTheme(theme); -}; - -ThemeModule.prototype.toggle = function (name) { - return this.toggleTheme(name); -}; - -ThemeModule.prototype.loadTheme = async function(filename) { - const error = await ContentManager.loadContent(filename, "theme"); - if (error) { - if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); - if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"}); - return Utils.err("ContentManager", `${filename} could not be loaded.`, error); - } - const theme = Object.values(bdthemes).find(p => p.filename == filename); - Utils.log("ContentManager", `${theme.name} v${theme.version} was loaded.`); - if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"}); - BDEvents.dispatch("theme-loaded", theme.name); -}; - -ThemeModule.prototype.unloadTheme = function(filenameOrName) { - const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; - if (!bdtheme) return; - const theme = bdtheme.name; - if (themeCookie[theme]) this.disableTheme(theme, true); - const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme"); - delete bdthemes[theme]; - if (error) { - if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); - if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"}); - return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error); - } - Utils.log("ContentManager", `${theme} was unloaded.`); - if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"}); - BDEvents.dispatch("theme-unloaded", theme); -}; - -ThemeModule.prototype.delete = function(filenameOrName) { - const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; - if (!bdtheme) return; - this.unloadTheme(bdtheme.filename); - const fullPath = require("path").resolve(ContentManager.themesFolder, bdtheme.filename); - require("fs").unlinkSync(fullPath); -}; - -ThemeModule.prototype.reloadTheme = async function(filenameOrName) { - const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; - if (!bdtheme) return this.loadTheme(filenameOrName); - const theme = bdtheme.name; - const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme"); - if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true); - if (error) { - if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); - if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be reloaded.`, {type: "error"}); - return Utils.err("ContentManager", `${theme} could not be reloaded.`, error); - } - Utils.log("ContentManager", `${theme} v${bdthemes[theme].version} was reloaded.`); - if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"}); - BDEvents.dispatch("theme-reloaded", theme); -}; - -ThemeModule.prototype.reload = function(name) { - return this.reloadTheme(name); -}; - -ThemeModule.prototype.edit = function(filenameOrName) { - const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; - if (!bdplugin) return; - const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename); - require("electron").shell.openItem(`${fullPath}`); -}; - -ThemeModule.prototype.updateThemeList = function() { - const results = ContentManager.loadNewContent("theme"); - for (const filename of results.added) this.loadTheme(filename); - for (const name of results.removed) this.unloadTheme(name); -}; - -ThemeModule.prototype.loadThemeData = function() { - const saved = DataStore.getSettingGroup("themes"); - if (saved) { - Object.assign(themeCookie, saved); - } -}; - -ThemeModule.prototype.saveThemeData = function () { - DataStore.setSettingGroup("themes", themeCookie); -}; - +import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals"; +import ContentManager from "./contentManager"; +import DataStore from "./dataStore"; +import BDEvents from "./bdEvents"; +import Utils from "./utils"; +import DOM from "./domtools"; +import bdEvents from "./bdEvents"; + +class ThemeModule { + constructor(){ + window.Lightcord.BetterDiscord.ThemeModule = this + } + get folder() {return ContentManager.themesFolder;} +} + +ThemeModule.prototype.loadThemes = async function () { + this.loadThemeData(); + bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes())); + const themes = Object.keys(bdthemes); + + for (let i = 0; i < themes.length; i++) { + const theme = bdthemes[themes[i]]; + if (!themeCookie[theme.name]) themeCookie[theme.name] = false; + if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css)); + + await new Promise((resolve) => setTimeout(resolve, 10)) + } + for (const theme in themeCookie) { + if (!bdthemes[theme]) delete themeCookie[theme]; + } + this.saveThemeData(); + // if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme"); +}; + +ThemeModule.prototype.enableTheme = function(name, reload = false) { + themeCookie[name] = true; + this.saveThemeData(); + const theme = bdthemes[name]; + DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css)); + bdEvents.dispatch("theme-enabled") + if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`); +}; + +ThemeModule.prototype.enable = function (name, reload = false) { + return this.enableTheme(name, reload); +}; + +ThemeModule.prototype.disableTheme = function(name, reload = false) { + themeCookie[name] = false; + this.saveThemeData(); + const theme = bdthemes[name]; + DOM.removeStyle(DOM.escapeID(theme.id)); + bdEvents.dispatch("theme-disabled") + if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`); +}; + +ThemeModule.prototype.disable = function (name, reload = false) { + return this.disableTheme(name, reload); +}; + +ThemeModule.prototype.toggleTheme = function(theme) { + if (themeCookie[theme]) this.disableTheme(theme); + else this.enableTheme(theme); +}; + +ThemeModule.prototype.toggle = function (name) { + return this.toggleTheme(name); +}; + +ThemeModule.prototype.loadTheme = async function(filename) { + const error = await ContentManager.loadContent(filename, "theme"); + if (error) { + if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); + if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"}); + return Utils.err("ContentManager", `${filename} could not be loaded.`, error); + } + const theme = Object.values(bdthemes).find(p => p.filename == filename); + Utils.log("ContentManager", `${theme.name} v${theme.version} was loaded.`); + if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"}); + BDEvents.dispatch("theme-loaded", theme.name); +}; + +ThemeModule.prototype.unloadTheme = function(filenameOrName) { + const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; + if (!bdtheme) return; + const theme = bdtheme.name; + if (themeCookie[theme]) this.disableTheme(theme, true); + const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme"); + delete bdthemes[theme]; + if (error) { + if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); + if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"}); + return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error); + } + Utils.log("ContentManager", `${theme} was unloaded.`); + if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"}); + BDEvents.dispatch("theme-unloaded", theme); +}; + +ThemeModule.prototype.delete = function(filenameOrName) { + const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; + if (!bdtheme) return; + this.unloadTheme(bdtheme.filename); + const fullPath = require("path").resolve(ContentManager.themesFolder, bdtheme.filename); + require("fs").unlinkSync(fullPath); +}; + +ThemeModule.prototype.reloadTheme = async function(filenameOrName) { + const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; + if (!bdtheme) return this.loadTheme(filenameOrName); + const theme = bdtheme.name; + const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme"); + if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true); + if (error) { + if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); + if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be reloaded.`, {type: "error"}); + return Utils.err("ContentManager", `${theme} could not be reloaded.`, error); + } + Utils.log("ContentManager", `${theme} v${bdthemes[theme].version} was reloaded.`); + if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"}); + BDEvents.dispatch("theme-reloaded", theme); +}; + +ThemeModule.prototype.reload = function(name) { + return this.reloadTheme(name); +}; + +ThemeModule.prototype.edit = function(filenameOrName) { + const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; + if (!bdplugin) return; + const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename); + require("electron").shell.openItem(`${fullPath}`); +}; + +ThemeModule.prototype.updateThemeList = function() { + const results = ContentManager.loadNewContent("theme"); + for (const filename of results.added) this.loadTheme(filename); + for (const name of results.removed) this.unloadTheme(name); +}; + +ThemeModule.prototype.loadThemeData = function() { + const saved = DataStore.getSettingGroup("themes"); + if (saved) { + Object.assign(themeCookie, saved); + } +}; + +ThemeModule.prototype.saveThemeData = function () { + DataStore.setSettingGroup("themes", themeCookie); +}; + export default new ThemeModule(); \ No newline at end of file diff --git a/BetterDiscordApp/src/modules/v2.js b/BetterDiscordApp/src/modules/v2.js index 36c2182..7843711 100644 --- a/BetterDiscordApp/src/modules/v2.js +++ b/BetterDiscordApp/src/modules/v2.js @@ -1,167 +1,167 @@ -import {settings} from "../0globals"; -import themeModule from "./themeModule"; - -export default new class V2 { - - constructor() { - this.editorDetached = false; - this.WebpackModules = (() => { - const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]); - delete req.m.__extra_id__; - delete req.c.__extra_id__; - - const shouldProtect = theModule => { - if (theModule.remove && theModule.set && theModule.clear && theModule.get && !theModule.sort) return true; - if (theModule.getToken || theModule.getEmail || theModule.showToken) return true; - return false; - }; - - const protect = (theModule, isDefault) => { - let mod = !isDefault ? theModule.default : theModule - if(!mod)return theModule - if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; - if (!mod.getToken && !mod.getEmail && !mod.showToken)return theModule - - const proxy = new Proxy(mod, { - getOwnPropertyDescriptor: function(obj, prop) { - if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; - return Object.getOwnPropertyDescriptor(obj, prop); - }, - get: function(obj, func) { - if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; - if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; - if (func == "showToken" && obj.showToken) return () => true; - if (func == "__proto__" && obj.__proto__) return proxy; - - return obj[func]; - } - }); - - if(!isDefault){ - return Object.assign({}, theModule, {default: proxy}) - } - - return proxy; - }; - - const find = (filter) => { - for (const i in req.c) { - if (req.c.hasOwnProperty(i)) { - const m = req.c[i].exports; - if (m && m.__esModule && m.default && filter(m.default)) return protect(m.default, true); - if (m && filter(m)) return protect(m, false); - } - } - // console.warn("Cannot find loaded module in cache"); - return null; - }; - - const findAll = (filter) => { - const modules = []; - for (const i in req.c) { - if (req.c.hasOwnProperty(i)) { - const m = req.c[i].exports; - if (m && m.__esModule && m.default && filter(m.default)) modules.push(protect(m.default, true)); - else if (m && filter(m)) modules.push(protect(m, false)); - } - } - return modules; - }; - - const findByUniqueProperties = (propNames) => find(module => propNames.every(prop => module[prop] !== undefined)); - const findByPrototypes = (protoNames) => find(module => module.prototype && protoNames.every(protoProp => module.prototype[protoProp] !== undefined)); - const findByDisplayName = (displayName) => find(module => module.displayName === displayName); - - return {find, findAll, findByUniqueProperties, findByPrototypes, findByDisplayName}; - })(); - - this.internal = { - react: this.WebpackModules.findByUniqueProperties(["Component", "PureComponent", "Children", "createElement", "cloneElement"]), - reactDom: this.WebpackModules.findByUniqueProperties(["findDOMNode"]) - }; - this.getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))]; - window.Lightcord.BetterDiscord.V2 = this - } - - initialize() { - - } - - joinBD1() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");} - leaveBD1() {this.GuildActions.leaveGuild("86004744966914048");} - - joinBD2() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("2HScm8j");} - leaveBD2() {this.GuildActions.leaveGuild("280806472928198656");} - - joinLC() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("7eFff2A");} - leaveLC() {this.GuildActions.leaveGuild("705908350218666117");} - - /** - * @type {typeof React} - */ - get react() {return this.internal.react;} - /** - * @type {typeof React} - */ - get React() {return this.internal.react;} - /** - * @type {typeof import("react-dom")} - */ - get reactDom() {return this.internal.reactDom;} - /** - * @type {typeof import("react-dom")} - */ - get ReactDom() {return this.internal.reactDom;} - /** - * @type {typeof React.Component} - */ - get reactComponent() {return this.internal.react.Component;} - /** - * @type {typeof React.Component} - */ - get ReactComponent() {return this.internal.react.Component;} - - get anchorClasses() {return this.WebpackModules.findByUniqueProperties(["anchorUnderlineOnHover"]) || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};} - get slateEditorClasses() {return this.WebpackModules.findByUniqueProperties(["slateTextArea"]);} - get messageClasses() {return this.WebpackModules.findByUniqueProperties(["message", "containerCozy"]);} - get guildClasses() { - const guildsWrapper = BDModules.get(e => e.wrapper && e.unreadMentionsBar)[0]; - const guilds = BDModules.get(e => e.guildsError && e.selected)[0] - const pill = BDModules.get(e => e.blobContainer)[0] - return Object.assign({}, guildsWrapper, guilds, pill); - } - - get MessageContentComponent() {return this.WebpackModules.find(m => m.defaultProps && m.defaultProps.hasOwnProperty("disableButtons"));} - get MessageComponent() {return this.WebpackModules.find(m => m.default && m.default.displayName && m.default.displayName == "Message");} - get TimeFormatter() {return this.WebpackModules.findByUniqueProperties(["dateFormat"]);} - get TooltipWrapper() {return this.WebpackModules.findByDisplayName("Tooltip");} - get NativeModule() {return this.WebpackModules.findByUniqueProperties(["setBadge"]);} - get InviteActions() {return this.WebpackModules.findByUniqueProperties(["acceptInvite"]);} - get GuildActions() {return this.WebpackModules.findByUniqueProperties(["leaveGuild"]);} - get Tooltips() {return this.WebpackModules.find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);} - get KeyGenerator() {return this.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));} - get LayerStack() {return this.WebpackModules.findByUniqueProperties(["popLayer"]);} - get UserStore() {return this.WebpackModules.findByUniqueProperties(["getCurrentUser"]);} - get ChannelStore() {return this.WebpackModules.findByUniqueProperties(["getChannel"]);} - get ChannelActions() {return this.WebpackModules.findByUniqueProperties(["openPrivateChannel"]);} - get PrivateChannelActions() {return this.WebpackModules.findByUniqueProperties(["selectPrivateChannel"]);} - - openDM(userId) { - const selfId = this.UserStore.getCurrentUser().id; - if (selfId == userId) return; - const privateChannelId = this.ChannelStore.getDMFromUserId(userId); - if (privateChannelId) return this.PrivateChannelActions.selectPrivateChannel(privateChannelId); - this.ChannelActions.openPrivateChannel(selfId, userId); - } - - parseSettings(cat) { - return Object.keys(settings).reduce((arr, key) => { - const setting = settings[key]; - if (setting.cat === cat && setting.implemented && !setting.hidden) { - setting.text = key; - arr.push(setting); - } return arr; - }, []); - } - +import {settings} from "../0globals"; +import themeModule from "./themeModule"; + +export default new class V2 { + + constructor() { + this.editorDetached = false; + this.WebpackModules = (() => { + const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]); + delete req.m.__extra_id__; + delete req.c.__extra_id__; + + const shouldProtect = theModule => { + if (theModule.remove && theModule.set && theModule.clear && theModule.get && !theModule.sort) return true; + if (theModule.getToken || theModule.getEmail || theModule.showToken) return true; + return false; + }; + + const protect = (theModule, isDefault) => { + let mod = !isDefault ? theModule.default : theModule + if(!mod)return theModule + if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; + if (!mod.getToken && !mod.getEmail && !mod.showToken)return theModule + + const proxy = new Proxy(mod, { + getOwnPropertyDescriptor: function(obj, prop) { + if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; + return Object.getOwnPropertyDescriptor(obj, prop); + }, + get: function(obj, func) { + if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; + if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; + if (func == "showToken" && obj.showToken) return () => true; + if (func == "__proto__" && obj.__proto__) return proxy; + + return obj[func]; + } + }); + + if(!isDefault){ + return Object.assign({}, theModule, {default: proxy}) + } + + return proxy; + }; + + const find = (filter) => { + for (const i in req.c) { + if (req.c.hasOwnProperty(i)) { + const m = req.c[i].exports; + if (m && m.__esModule && m.default && filter(m.default)) return protect(m.default, true); + if (m && filter(m)) return protect(m, false); + } + } + // console.warn("Cannot find loaded module in cache"); + return null; + }; + + const findAll = (filter) => { + const modules = []; + for (const i in req.c) { + if (req.c.hasOwnProperty(i)) { + const m = req.c[i].exports; + if (m && m.__esModule && m.default && filter(m.default)) modules.push(protect(m.default, true)); + else if (m && filter(m)) modules.push(protect(m, false)); + } + } + return modules; + }; + + const findByUniqueProperties = (propNames) => find(module => propNames.every(prop => module[prop] !== undefined)); + const findByPrototypes = (protoNames) => find(module => module.prototype && protoNames.every(protoProp => module.prototype[protoProp] !== undefined)); + const findByDisplayName = (displayName) => find(module => module.displayName === displayName); + + return {find, findAll, findByUniqueProperties, findByPrototypes, findByDisplayName}; + })(); + + this.internal = { + react: this.WebpackModules.findByUniqueProperties(["Component", "PureComponent", "Children", "createElement", "cloneElement"]), + reactDom: this.WebpackModules.findByUniqueProperties(["findDOMNode"]) + }; + this.getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))]; + window.Lightcord.BetterDiscord.V2 = this + } + + initialize() { + + } + + joinBD1() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");} + leaveBD1() {this.GuildActions.leaveGuild("86004744966914048");} + + joinBD2() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("2HScm8j");} + leaveBD2() {this.GuildActions.leaveGuild("280806472928198656");} + + joinLC() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("7eFff2A");} + leaveLC() {this.GuildActions.leaveGuild("705908350218666117");} + + /** + * @type {typeof React} + */ + get react() {return this.internal.react;} + /** + * @type {typeof React} + */ + get React() {return this.internal.react;} + /** + * @type {typeof import("react-dom")} + */ + get reactDom() {return this.internal.reactDom;} + /** + * @type {typeof import("react-dom")} + */ + get ReactDom() {return this.internal.reactDom;} + /** + * @type {typeof React.Component} + */ + get reactComponent() {return this.internal.react.Component;} + /** + * @type {typeof React.Component} + */ + get ReactComponent() {return this.internal.react.Component;} + + get anchorClasses() {return this.WebpackModules.findByUniqueProperties(["anchorUnderlineOnHover"]) || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};} + get slateEditorClasses() {return this.WebpackModules.findByUniqueProperties(["slateTextArea"]);} + get messageClasses() {return this.WebpackModules.findByUniqueProperties(["message", "containerCozy"]);} + get guildClasses() { + const guildsWrapper = BDModules.get(e => e.wrapper && e.unreadMentionsBar)[0]; + const guilds = BDModules.get(e => e.guildsError && e.selected)[0] + const pill = BDModules.get(e => e.blobContainer)[0] + return Object.assign({}, guildsWrapper, guilds, pill); + } + + get MessageContentComponent() {return this.WebpackModules.find(m => m.defaultProps && m.defaultProps.hasOwnProperty("disableButtons"));} + get MessageComponent() {return this.WebpackModules.find(m => m.default && m.default.displayName && m.default.displayName == "Message");} + get TimeFormatter() {return this.WebpackModules.findByUniqueProperties(["dateFormat"]);} + get TooltipWrapper() {return this.WebpackModules.findByDisplayName("Tooltip");} + get NativeModule() {return this.WebpackModules.findByUniqueProperties(["setBadge"]);} + get InviteActions() {return this.WebpackModules.findByUniqueProperties(["acceptInvite"]);} + get GuildActions() {return this.WebpackModules.findByUniqueProperties(["leaveGuild"]);} + get Tooltips() {return this.WebpackModules.find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);} + get KeyGenerator() {return this.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));} + get LayerStack() {return this.WebpackModules.findByUniqueProperties(["popLayer"]);} + get UserStore() {return this.WebpackModules.findByUniqueProperties(["getCurrentUser"]);} + get ChannelStore() {return this.WebpackModules.findByUniqueProperties(["getChannel"]);} + get ChannelActions() {return this.WebpackModules.findByUniqueProperties(["openPrivateChannel"]);} + get PrivateChannelActions() {return this.WebpackModules.findByUniqueProperties(["selectPrivateChannel"]);} + + openDM(userId) { + const selfId = this.UserStore.getCurrentUser().id; + if (selfId == userId) return; + const privateChannelId = this.ChannelStore.getDMFromUserId(userId); + if (privateChannelId) return this.PrivateChannelActions.selectPrivateChannel(privateChannelId); + this.ChannelActions.openPrivateChannel(selfId, userId); + } + + parseSettings(cat) { + return Object.keys(settings).reduce((arr, key) => { + const setting = settings[key]; + if (setting.cat === cat && setting.implemented && !setting.hidden) { + setting.text = key; + arr.push(setting); + } return arr; + }, []); + } + }; \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/AccountInfos.jsx b/BetterDiscordApp/src/ui/AccountInfos.jsx index b336781..55d3ce9 100644 --- a/BetterDiscordApp/src/ui/AccountInfos.jsx +++ b/BetterDiscordApp/src/ui/AccountInfos.jsx @@ -1,132 +1,132 @@ -import BDV2 from "../modules/v2"; -import V2C_SettingsTitle from "./settingsTitle"; - -/** - * @type {typeof import("react")} - */ -const React = BDV2.React; - -let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] -let colorModule = BDModules.get(e => e.colorStandard)[0] -let sizeModule = BDModules.get(e => e.size32)[0] -let scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0] -const GuildModule = BDModules.get(e => e.default && e.default.getGuilds)[0].default -const relationShipModule = BDModules.get(e => e.default && e.default.getRelationships)[0].default -const sessionModule = BDModules.get(e => e.default && e.default.getSessions)[0].default -const userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0].default - -export default class V2C_AccountInfos extends React.Component { - constructor(props) { - super(props); - } - - render() { - if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] - if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0] - if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0] - return [ - , -
-
- Profile -
- -
- Statistics -
- -
- Connected Sessions -
- -
, -
e.marginBottom20)[0].marginBottom20}>
- ] - } - - getSessionValue(){ - const sessionsRaw = sessionModule.getSessions() - const sessions = Object.keys(sessionsRaw).filter(e => e !== "all").map(e => sessionsRaw[e]) - - if(sessions.length === 0)return "- No session detected. Please try in a few seconds" - - return sessions.map(e => { - return `+ id: ${e.sessionId} -+ os: ${e.clientInfo.os[0].toUpperCase()+e.clientInfo.os.slice(1)} -+ client: ${e.clientInfo.client} -+ status: ${e.status} -+ Activities: ${e.activities.length}` - }).join("\n"+"-".repeat(38)+"\n") - } - - getProfileValue(){ - const user = userModule.getCurrentUser() - /** - * @type {Date} - */ - const createdAt = user.createdAt - - let avatarURL = user.avatarURL - if(user.avatar && user.avatar.startsWith("a_")){ - avatarURL = user.getAvatarURL("gif") - } - - if(avatarURL.startsWith("/")){ - avatarURL = "https://discord.com"+avatarURL - } - if(avatarURL.endsWith("?size=128")){ - avatarURL = avatarURL.replace("?size=128", "?size=4096") - } - - return `+ Username: ${user.username} -+ Discriminator: ${user.discriminator} -+ Tag: ${user.tag} -+ ID: ${user.id} -+ Avatar: ${user.avatar} -+ Avatar URL: ${avatarURL} -+ Creation Date: ${(createdAt.getDate()).toString().padStart(2, "0")}/${(createdAt.getMonth()+1).toString().padStart(2, "0")}/${(createdAt.getFullYear()).toString().padStart(2, "0")} ${createdAt.getHours().toString().padStart(2, "0")}h ${createdAt.getMinutes().toString().padStart(2, "0")}min ${createdAt.getSeconds()}s -+ Flags: ${user.flags} -+ Has Nitro: ${user.hasPremiumSubscription ? "Yes" : "No"} -- Email: ${user.email} -- 2FA: ${user.mfaEnabled ? "Yes" : "No"} -- Has Been On Mobile: ${user.mobile ? "Yes" : "No"} -- Phone: ${user.phone || "None"} -- Verified: ${user.verified} -- Can See NSFW Channels: ${user.nsfwAllowed}`; - } - - getStatistics(){ - - const guilds = Object.values(GuildModule.getGuilds()) - const relations = Object.keys(relationShipModule.getRelationships()) - const friends = relations.filter(e => relationShipModule.isFriend(e)) - const blocked = relations.filter(e => relationShipModule.isBlocked(e)) - - return `+ Server Count: ${guilds.length} servers -+ Relations: ${relations.length} relations -+ Friends Count: ${friends.length} friends -- Blocked Users Count: ${blocked.length} blocked users` - } -} - - -let hightlightJS = BDModules.get(e => e.highlight)[0] -let messageModule1 = BDModules.get(e => e.markup)[0] -let messageModule2 = BDModules.get(e => e.messageContent)[0] - -class CodeContent extends React.Component { - render(){ - if(!messageModule1)messageModule1 = BDModules.get(e => e.markup)[0] - if(!messageModule2)messageModule2 = BDModules.get(e => e.messageContent)[0] - if(!scrollbarModule1)scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0] - if(!hightlightJS)hightlightJS = BDModules.get(e => e.listLanguages)[0] - return (
-
-                
-
-                
-            
-
e.marginBottom8)[0].marginBottom8}>
-
) - } +import BDV2 from "../modules/v2"; +import V2C_SettingsTitle from "./settingsTitle"; + +/** + * @type {typeof import("react")} + */ +const React = BDV2.React; + +let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] +let colorModule = BDModules.get(e => e.colorStandard)[0] +let sizeModule = BDModules.get(e => e.size32)[0] +let scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0] +const GuildModule = BDModules.get(e => e.default && e.default.getGuilds)[0].default +const relationShipModule = BDModules.get(e => e.default && e.default.getRelationships)[0].default +const sessionModule = BDModules.get(e => e.default && e.default.getSessions)[0].default +const userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0].default + +export default class V2C_AccountInfos extends React.Component { + constructor(props) { + super(props); + } + + render() { + if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] + if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0] + if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0] + return [ + , +
+
+ Profile +
+ +
+ Statistics +
+ +
+ Connected Sessions +
+ +
, +
e.marginBottom20)[0].marginBottom20}>
+ ] + } + + getSessionValue(){ + const sessionsRaw = sessionModule.getSessions() + const sessions = Object.keys(sessionsRaw).filter(e => e !== "all").map(e => sessionsRaw[e]) + + if(sessions.length === 0)return "- No session detected. Please try in a few seconds" + + return sessions.map(e => { + return `+ id: ${e.sessionId} ++ os: ${e.clientInfo.os[0].toUpperCase()+e.clientInfo.os.slice(1)} ++ client: ${e.clientInfo.client} ++ status: ${e.status} ++ Activities: ${e.activities.length}` + }).join("\n"+"-".repeat(38)+"\n") + } + + getProfileValue(){ + const user = userModule.getCurrentUser() + /** + * @type {Date} + */ + const createdAt = user.createdAt + + let avatarURL = user.avatarURL + if(user.avatar && user.avatar.startsWith("a_")){ + avatarURL = user.getAvatarURL("gif") + } + + if(avatarURL.startsWith("/")){ + avatarURL = "https://discord.com"+avatarURL + } + if(avatarURL.endsWith("?size=128")){ + avatarURL = avatarURL.replace("?size=128", "?size=4096") + } + + return `+ Username: ${user.username} ++ Discriminator: ${user.discriminator} ++ Tag: ${user.tag} ++ ID: ${user.id} ++ Avatar: ${user.avatar} ++ Avatar URL: ${avatarURL} ++ Creation Date: ${(createdAt.getDate()).toString().padStart(2, "0")}/${(createdAt.getMonth()+1).toString().padStart(2, "0")}/${(createdAt.getFullYear()).toString().padStart(2, "0")} ${createdAt.getHours().toString().padStart(2, "0")}h ${createdAt.getMinutes().toString().padStart(2, "0")}min ${createdAt.getSeconds()}s ++ Flags: ${user.flags} ++ Has Nitro: ${user.hasPremiumSubscription ? "Yes" : "No"} +- Email: ${user.email} +- 2FA: ${user.mfaEnabled ? "Yes" : "No"} +- Has Been On Mobile: ${user.mobile ? "Yes" : "No"} +- Phone: ${user.phone || "None"} +- Verified: ${user.verified} +- Can See NSFW Channels: ${user.nsfwAllowed}`; + } + + getStatistics(){ + + const guilds = Object.values(GuildModule.getGuilds()) + const relations = Object.keys(relationShipModule.getRelationships()) + const friends = relations.filter(e => relationShipModule.isFriend(e)) + const blocked = relations.filter(e => relationShipModule.isBlocked(e)) + + return `+ Server Count: ${guilds.length} servers ++ Relations: ${relations.length} relations ++ Friends Count: ${friends.length} friends +- Blocked Users Count: ${blocked.length} blocked users` + } +} + + +let hightlightJS = BDModules.get(e => e.highlight)[0] +let messageModule1 = BDModules.get(e => e.markup)[0] +let messageModule2 = BDModules.get(e => e.messageContent)[0] + +class CodeContent extends React.Component { + render(){ + if(!messageModule1)messageModule1 = BDModules.get(e => e.markup)[0] + if(!messageModule2)messageModule2 = BDModules.get(e => e.messageContent)[0] + if(!scrollbarModule1)scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0] + if(!hightlightJS)hightlightJS = BDModules.get(e => e.listLanguages)[0] + return (
+
+                
+
+                
+            
+
e.marginBottom8)[0].marginBottom8}>
+
) + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/ApiPreview.jsx b/BetterDiscordApp/src/ui/ApiPreview.jsx index 621e425..be18ac9 100644 --- a/BetterDiscordApp/src/ui/ApiPreview.jsx +++ b/BetterDiscordApp/src/ui/ApiPreview.jsx @@ -1,292 +1,292 @@ -// Good luck to read my code, Even me can't read it properly. - -import { stat } from "fs" -import { uuidv4 } from "../modules/distant" -import webpackModules from "../modules/webpackModules" -import { remote } from "electron" -import MarginTop from "./margintop" - -let formModule -export default class ApiPreview extends React.PureComponent { - constructor(){ - super(...arguments) - this.state = { - states: [] - } - } - - render(){ - if(!formModule)formModule = webpackModules.find(e => e.FormSection) - /** - * @type {Function[]} - */ - const allComponents = [...new Set(Object.keys(window.Lightcord.Api.Components).map(e => { - return Object.keys(window.Lightcord.Api.Components[e]).map(k => window.Lightcord.Api.Components[e][k]) - }).flat())] - return [ - - - These components are here for the plugin devs. They can quickly embed any component below with this panel. -
- All these components have error handling. If you want none, add `.original` after the component path. - We do not recommend modifying these component with plugins. Only do this if you know what you are doing. -
- - { - remote.shell.openExternal("https://lightcord.deroku.xyz/LightcordApi/docs") - }} wrapper={false}> - Documentation - -
, - allComponents.map(comp => { - const compName = comp.displayName || comp.name - const compPath = `Lightcord.Api.Components.${Object.keys(window.Lightcord.Api.Components).find(e => window.Lightcord.Api.Components[e][compName])}.${compName}` - return - }) - ] - } - - get renders(){ - - } -} - -class ComponentPreview extends React.Component { - constructor(props){ - super(props) - this.state = { - tab: "preview", - elem: props.comp, - options: {} - } - } - - render(){ - const comp = this.props.comp - let AllPreviews = [] - if(comp.AllPreviews)AllPreviews = comp.AllPreviews - let state = this.state - let getProps = () => { - let final = {} - AllPreviews.forEach(category => { - final[Object.keys(category[0])[0]] = category[0][Object.keys(category[0])[0]] - }) - Object.keys(state.options).forEach(key => { - final[key] = AllPreviews.find(e => e.find(e => e[key]))[state.options[key]][key] - }) - return final - } - let renderPreview = () => { - return
-
- {React.createElement(comp, getProps())} -
-
- } - let renderCode = () => { - return
-
- - JSX - - - {React.createElement(() => { - return - })} - - - React - - - {React.createElement(() => { - return - })} - -
-
- } - let getStrForProp = (value, compPath, lang) => { - if(typeof value === "string"){ - return value - }else if(typeof value === "boolean"){ - return String(value) - }else if(typeof value === "function"){ - return value.toString() - }else if(typeof value === "object"){ - if(value && value.$$typeof && (value.$$typeof === Symbol.for("react.element") || value.$$typeof === 0xeac7)){ - if(compPath === "Lightcord.Api.Components.general.Tabs"){ - if(lang === "react"){ - return `React.createElement("div", {style: { -marginTop: "20px", marginBottom: "20px" -}}, -React.createElement("div", {style: { -backgroundColor: "var(--background-secondary)", -padding: "30px 30px", -borderRadius: "8px" -}, className: "lc-tab-box-shadow" }, -React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs") -) -)` - }else if(lang === "jsx"){ - return `
-
- Preview tabs -
-
` - } - } - return "Your components here." - } - return JSON.stringify(value, null, " ") - }else if(typeof value === "number"){ - return String(value) - } - return String(value) - } - let generateCode = function(lang){ // code formatting is hard - const compName = comp.displayName || comp.name - let categories = Object.keys(window.Lightcord.Api.Components) - const compCategory = categories.find(e => window.Lightcord.Api.Components[e][compName]) - const compPath = `Lightcord.Api.Components.${compCategory}.${compName}` - const props = getProps() - - if(lang === "jsx"){ - let propStrings = [] - let childrenProp = null - Object.keys(props).forEach(key => { - if(key == "children"){ - childrenProp = getStrForProp(props[key], compPath, lang) - }else{ - let str = key+"=" - if(typeof props[key] === "string"){ - str += JSON.stringify(props[key]) - }else{ - str += `{${getStrForProp(props[key], compPath, lang)}}` - } - propStrings.push(str) - } - }) - let openTag - if(childrenProp){ - openTag = `<${compPath} ${propStrings.join(" ")}>` - let closeTag = `` - return `${openTag}\n ${childrenProp}\n${closeTag}` - }else{ - openTag = `<${compPath} ${propStrings.join(" ")}/>` - return openTag - } - }else if(lang === "react"){ - let children = props.children || null - delete props.children - if(children && children.$$typeof && (children.$$typeof === Symbol.for("react.element") || children.$$typeof === 0xeac7)){ - children = getStrForProp(children, compPath, lang) - } - let propStrings = [] - Object.keys(props).forEach(key => { - let visibleKey = /[^\w\d_]/g.test(key) ? JSON.stringify(key) : key - let str = visibleKey+": " - if(typeof props[key] === "string"){ - str += JSON.stringify(props[key]) - }else{ - str += getStrForProp(props[key], compPath, lang).split("\n").map((str, i) => { - if(i === 0)return str - return " " + str - }).join("\n") - } - propStrings.push(str) - }) - let propObject = "{" - if(propStrings.length){ - propStrings.forEach((str, i) => { - let isLast = i === propStrings.length - 1 - let isFirst = i === 0 - if(!isFirst){ - propObject += "," - } - propObject += "\n " - propObject += str - if(isLast){ - propObject +="\n}" - } - }) - }else{ - propObject += "}" - } - let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children) - return `React.createElement(${compPath}, ${propObject}, ${childrenData})` - } - } - let help = comp.help || {} - let info = help.info ? - {help.info} - : null - let warn = help.warn ? - {help.warn} - : null - let danger = help.danger ? - {help.danger} - : null - let error = help.error ? - {help.error} - : null - let success = help.success ? - {help.success} - : null - return (
- - {comp.displayName || comp.name} - - {info} - {success} - {warn} - {error} - {danger} - {AllPreviews.map(category => { - if(category[0].onClick)return null - if(category[0].text)return null - if(category[0].children)return null - if(category.length === 1)return null - - let key = Object.keys(category[0])[0] - return [ - - {key} - , - { - return { - value: "opt-"+index, - label: JSON.stringify(e[Object.keys(e)[0]]) - } - })} value={"opt-"+(state.options[key] || "0")} onChange={(value) => { - this.setState({ - options: Object.assign({}, state.options, { - [key]: (value.value || "0").replace("opt-", "") - }) - }) - }} searchable={true}/>, -
- ] - })} - { - this.setState({ - tab - }) - }}/> -
) - } +// Good luck to read my code, Even me can't read it properly. + +import { stat } from "fs" +import { uuidv4 } from "../modules/distant" +import webpackModules from "../modules/webpackModules" +import { remote } from "electron" +import MarginTop from "./margintop" + +let formModule +export default class ApiPreview extends React.PureComponent { + constructor(){ + super(...arguments) + this.state = { + states: [] + } + } + + render(){ + if(!formModule)formModule = webpackModules.find(e => e.FormSection) + /** + * @type {Function[]} + */ + const allComponents = [...new Set(Object.keys(window.Lightcord.Api.Components).map(e => { + return Object.keys(window.Lightcord.Api.Components[e]).map(k => window.Lightcord.Api.Components[e][k]) + }).flat())] + return [ + + + These components are here for the plugin devs. They can quickly embed any component below with this panel. +
+ All these components have error handling. If you want none, add `.original` after the component path. + We do not recommend modifying these component with plugins. Only do this if you know what you are doing. +
+ + { + remote.shell.openExternal("https://lightcord.deroku.xyz/LightcordApi/docs") + }} wrapper={false}> + Documentation + +
, + allComponents.map(comp => { + const compName = comp.displayName || comp.name + const compPath = `Lightcord.Api.Components.${Object.keys(window.Lightcord.Api.Components).find(e => window.Lightcord.Api.Components[e][compName])}.${compName}` + return + }) + ] + } + + get renders(){ + + } +} + +class ComponentPreview extends React.Component { + constructor(props){ + super(props) + this.state = { + tab: "preview", + elem: props.comp, + options: {} + } + } + + render(){ + const comp = this.props.comp + let AllPreviews = [] + if(comp.AllPreviews)AllPreviews = comp.AllPreviews + let state = this.state + let getProps = () => { + let final = {} + AllPreviews.forEach(category => { + final[Object.keys(category[0])[0]] = category[0][Object.keys(category[0])[0]] + }) + Object.keys(state.options).forEach(key => { + final[key] = AllPreviews.find(e => e.find(e => e[key]))[state.options[key]][key] + }) + return final + } + let renderPreview = () => { + return
+
+ {React.createElement(comp, getProps())} +
+
+ } + let renderCode = () => { + return
+
+ + JSX + + + {React.createElement(() => { + return + })} + + + React + + + {React.createElement(() => { + return + })} + +
+
+ } + let getStrForProp = (value, compPath, lang) => { + if(typeof value === "string"){ + return value + }else if(typeof value === "boolean"){ + return String(value) + }else if(typeof value === "function"){ + return value.toString() + }else if(typeof value === "object"){ + if(value && value.$$typeof && (value.$$typeof === Symbol.for("react.element") || value.$$typeof === 0xeac7)){ + if(compPath === "Lightcord.Api.Components.general.Tabs"){ + if(lang === "react"){ + return `React.createElement("div", {style: { +marginTop: "20px", marginBottom: "20px" +}}, +React.createElement("div", {style: { +backgroundColor: "var(--background-secondary)", +padding: "30px 30px", +borderRadius: "8px" +}, className: "lc-tab-box-shadow" }, +React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs") +) +)` + }else if(lang === "jsx"){ + return `
+
+ Preview tabs +
+
` + } + } + return "Your components here." + } + return JSON.stringify(value, null, " ") + }else if(typeof value === "number"){ + return String(value) + } + return String(value) + } + let generateCode = function(lang){ // code formatting is hard + const compName = comp.displayName || comp.name + let categories = Object.keys(window.Lightcord.Api.Components) + const compCategory = categories.find(e => window.Lightcord.Api.Components[e][compName]) + const compPath = `Lightcord.Api.Components.${compCategory}.${compName}` + const props = getProps() + + if(lang === "jsx"){ + let propStrings = [] + let childrenProp = null + Object.keys(props).forEach(key => { + if(key == "children"){ + childrenProp = getStrForProp(props[key], compPath, lang) + }else{ + let str = key+"=" + if(typeof props[key] === "string"){ + str += JSON.stringify(props[key]) + }else{ + str += `{${getStrForProp(props[key], compPath, lang)}}` + } + propStrings.push(str) + } + }) + let openTag + if(childrenProp){ + openTag = `<${compPath} ${propStrings.join(" ")}>` + let closeTag = `` + return `${openTag}\n ${childrenProp}\n${closeTag}` + }else{ + openTag = `<${compPath} ${propStrings.join(" ")}/>` + return openTag + } + }else if(lang === "react"){ + let children = props.children || null + delete props.children + if(children && children.$$typeof && (children.$$typeof === Symbol.for("react.element") || children.$$typeof === 0xeac7)){ + children = getStrForProp(children, compPath, lang) + } + let propStrings = [] + Object.keys(props).forEach(key => { + let visibleKey = /[^\w\d_]/g.test(key) ? JSON.stringify(key) : key + let str = visibleKey+": " + if(typeof props[key] === "string"){ + str += JSON.stringify(props[key]) + }else{ + str += getStrForProp(props[key], compPath, lang).split("\n").map((str, i) => { + if(i === 0)return str + return " " + str + }).join("\n") + } + propStrings.push(str) + }) + let propObject = "{" + if(propStrings.length){ + propStrings.forEach((str, i) => { + let isLast = i === propStrings.length - 1 + let isFirst = i === 0 + if(!isFirst){ + propObject += "," + } + propObject += "\n " + propObject += str + if(isLast){ + propObject +="\n}" + } + }) + }else{ + propObject += "}" + } + let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children) + return `React.createElement(${compPath}, ${propObject}, ${childrenData})` + } + } + let help = comp.help || {} + let info = help.info ? + {help.info} + : null + let warn = help.warn ? + {help.warn} + : null + let danger = help.danger ? + {help.danger} + : null + let error = help.error ? + {help.error} + : null + let success = help.success ? + {help.success} + : null + return (
+ + {comp.displayName || comp.name} + + {info} + {success} + {warn} + {error} + {danger} + {AllPreviews.map(category => { + if(category[0].onClick)return null + if(category[0].text)return null + if(category[0].children)return null + if(category.length === 1)return null + + let key = Object.keys(category[0])[0] + return [ + + {key} + , + { + return { + value: "opt-"+index, + label: JSON.stringify(e[Object.keys(e)[0]]) + } + })} value={"opt-"+(state.options[key] || "0")} onChange={(value) => { + this.setState({ + options: Object.assign({}, state.options, { + [key]: (value.value || "0").replace("opt-", "") + }) + }) + }} searchable={true}/>, +
+ ] + })} + { + this.setState({ + tab + }) + }}/> +
) + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/addoncard.jsx b/BetterDiscordApp/src/ui/addoncard.jsx index f9bd8c3..830ff61 100644 --- a/BetterDiscordApp/src/ui/addoncard.jsx +++ b/BetterDiscordApp/src/ui/addoncard.jsx @@ -1,242 +1,242 @@ -import {settingsCookie} from "../0globals"; -import BDV2 from "../modules/v2"; -import Utils from "../modules/utils"; -import DOM from "../modules/domtools"; - -import XSvg from "./xSvg"; -import ReloadIcon from "./reloadIcon"; -import EditIcon from "./icons/edit"; -import DeleteIcon from "./icons/delete"; -import Switch from "./components/switch"; -import TooltipWrap from "./tooltipWrap"; -import { processFile } from "../modules/pluginCertifier"; -import contentManager from "../modules/contentManager"; -import { resolve } from "path"; - -const React = BDV2.React; -const anchorClasses = BDV2.anchorClasses; - -export default class V2C_PluginCard extends BDV2.reactComponent { - - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.showSettings = this.showSettings.bind(this); - this.setInitialState(); - this.hasSettings = this.props.addon.plugin && typeof(this.props.addon.plugin.getSettingsPanel) === "function"; - this.settingsPanel = ""; - - this.edit = this.edit.bind(this); - this.delete = this.delete.bind(this); - this.reload = this.reload.bind(this); - } - - setInitialState() { - this.state = { - checked: this.props.enabled, - settings: false, - reloads: 0, - trusted: false - }; - } - - showSettings() { - if (!this.hasSettings) return; - this.setState({settings: true}); - } - - closeSettings() { - this.panelRef.current.innerHTML = ""; - this.setState({settingsOpen: false}); - } - - componentDidUpdate() { - if (!this.state.settings) return; - if (typeof this.settingsPanel === "object") { - this.refs.settingspanel.appendChild(this.settingsPanel); - } - - if (!settingsCookie["fork-ps-3"]) return; - setImmediate(() => { - const isHidden = (container, element) => { - if(!container){ - console.error(new Error(`Container is undefined.`)) - return false - } - const cTop = container.scrollTop; - const cBottom = cTop + container.clientHeight; - const eTop = element.offsetTop; - const eBottom = eTop + element.clientHeight; - return (eTop < cTop || eBottom > cBottom); - }; - - const thisNode = this.refs.cardNode; - const container = thisNode.closest("div[class*=\"contentRegionScroller-\"]") - if (!isHidden(container, thisNode)) return; - const thisNodeOffset = DOM.offset(thisNode); - const containerOffset = DOM.offset(container); - const original = container.scrollTop; - const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30; - DOM.animate({ - duration: 300, - update: function(progress) { - if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original)); - else container.scrollTop = original - (progress * (original - endPoint)); - } - }); - }); - } - - - getString(value) { - if (!value) return "???"; - return typeof value == "string" ? value : value.toString(); - } - - get settingsComponent() { - try { this.settingsPanel = this.props.addon.plugin.getSettingsPanel(); } - catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + this.name + ".", err); } - - return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-open ui-switch-item", ref: "cardNode"}, - BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => { - this.refs.settingspanel.innerHTML = ""; - this.setState({settings: false}); - }}, - BDV2.react.createElement(XSvg, null) - ), - typeof this.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel"}), - typeof this.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: this.settingsPanel}}) - ); - } - - buildTitle(name, version, author) { - const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/); - const nameIndex = title.findIndex(s => s == "{{name}}"); - if (nameIndex) title[nameIndex] = React.createElement("span", {className: "name bda-name"}, name); - const versionIndex = title.findIndex(s => s == "{{version}}"); - if (nameIndex) title[versionIndex] = React.createElement("span", {className: "version bda-version"}, version); - const authorIndex = title.findIndex(s => s == "{{author}}"); - if (nameIndex) { - const props = {className: "author bda-author"}; - if (author.link || author.id) { - props.className += ` ${anchorClasses.anchor} ${anchorClasses.anchorUnderlineOnHover}`; - props.target = "_blank"; - - if (author.link) props.href = author.link; - if (author.id) props.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(author.id);}; - } - title[authorIndex] = React.createElement(author.link || author.id ? "a" : "span", props, author.name); - } - return title.flat(); - } - - makeLink(title, url) { - const props = {className: "bda-link bda-link-website", target: "_blank"}; - if (typeof(url) == "string") props.href = url; - if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();}; - return BDV2.react.createElement("a", props, title); - } - - makeButton(title, children, action) { - return -
{children}
-
; - } - - componentWillUnmount(){ - this.unmounted = true - } - - get links() { - const links = []; - const addon = this.props.addon; - if (addon.website) links.push(this.makeLink("Website", addon.website)); - if (addon.source) links.push(this.makeLink("Source", addon.source)); - if (addon.invite) { - links.push(this.makeLink("Support Server", () => { - const tester = /\.gg\/(.*)$/; - let code = addon.invite; - if (tester.test(code)) code = code.match(tester)[1]; - BDV2.LayerStack.popLayer(); - BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code); - })); - } - if (addon.donate) links.push(this.makeLink("Donate", addon.donate)); - if (addon.patreon) links.push(this.makeLink("Patreon", addon.patreon)); - return links; - } - - get footer() { - const links = this.links; - return (links.length || this.hasSettings) && BDV2.react.createElement("div", {className: "bd-card-footer bda-footer"}, - BDV2.react.createElement("span", {className: "bd-addon-links bda-links"}, - ...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat()) - ), - this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bd-button bda-settings-button", disabled: !this.state.checked}, "Settings") - ); - } - - onChange() { - this.props.toggle && this.props.toggle(this.name); - this.setState({checked: !this.state.checked}); - } - - edit() {this.props.edit(this.name);} - delete() {this.props.remove(this.name);} - reload() {this.props.reload(this.name);} - - get name() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getName() : this.props.addon.name);} - get author() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getAuthor() : this.props.addon.author);} - get description() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getDescription() : this.props.addon.description);} - get version() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getVersion() : this.props.addon.version);} - - render() { - if (this.state.settings) return this.settingsComponent; - const {authorId, authorLink} = this.props.addon; - - const style = {} - if(settingsCookie["fork-ps-6"]){ - if(!this.isScanning){ - this.isScanning = true - processFile(resolve(this.props.addon.filename.endsWith(".plugin.js") ? contentManager.pluginsFolder : contentManager.themesFolder, this.props.addon.filename), (result) => { - if(this.unmounted)return - - this.setState({ - isTrusted: result.suspect ? "suspect" : true - }) - }, () => {}) - }else{ - if(this.state.isTrusted === true){ - style.borderColor = "#4087ed" - } - if(this.state.isTrusted === "suspect"){ - style.borderColor = "rgb(240, 71, 71)" - } - } - } - - return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-closed ui-switch-item", style}, - BDV2.react.createElement("div", {className: "bd-addon-header bda-header"}, - BDV2.react.createElement("div", {className: "bd-card-title bda-header-title"}, this.buildTitle(this.name, this.version, {name: this.author, id: authorId, link: authorLink})), - BDV2.react.createElement("div", {className: "bd-addon-controls bda-controls"}, - this.props.edit && this.makeButton("Edit", , this.edit), - this.props.remove && this.makeButton("Delete", , this.delete), - this.props.reload && this.makeButton("Reload", , this.reload), - React.createElement(Switch, {onChange: this.onChange, checked: this.state.checked}) - ) - ), - BDV2.react.createElement("div", {className: "bd-scroller-wrap bda-description-wrap scroller-wrap fade"}, - BDV2.react.createElement("div", {className: "bd-scroller bd-addon-description bda-description scroller"}, this.description) - ), - this.footer - ); - } -} - -const originalRender = V2C_PluginCard.prototype.render; -Object.defineProperty(V2C_PluginCard.prototype, "render", { - enumerable: false, - configurable: false, - set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, - get: () => originalRender +import {settingsCookie} from "../0globals"; +import BDV2 from "../modules/v2"; +import Utils from "../modules/utils"; +import DOM from "../modules/domtools"; + +import XSvg from "./xSvg"; +import ReloadIcon from "./reloadIcon"; +import EditIcon from "./icons/edit"; +import DeleteIcon from "./icons/delete"; +import Switch from "./components/switch"; +import TooltipWrap from "./tooltipWrap"; +import { processFile } from "../modules/pluginCertifier"; +import contentManager from "../modules/contentManager"; +import { resolve } from "path"; + +const React = BDV2.React; +const anchorClasses = BDV2.anchorClasses; + +export default class V2C_PluginCard extends BDV2.reactComponent { + + constructor(props) { + super(props); + this.onChange = this.onChange.bind(this); + this.showSettings = this.showSettings.bind(this); + this.setInitialState(); + this.hasSettings = this.props.addon.plugin && typeof(this.props.addon.plugin.getSettingsPanel) === "function"; + this.settingsPanel = ""; + + this.edit = this.edit.bind(this); + this.delete = this.delete.bind(this); + this.reload = this.reload.bind(this); + } + + setInitialState() { + this.state = { + checked: this.props.enabled, + settings: false, + reloads: 0, + trusted: false + }; + } + + showSettings() { + if (!this.hasSettings) return; + this.setState({settings: true}); + } + + closeSettings() { + this.panelRef.current.innerHTML = ""; + this.setState({settingsOpen: false}); + } + + componentDidUpdate() { + if (!this.state.settings) return; + if (typeof this.settingsPanel === "object") { + this.refs.settingspanel.appendChild(this.settingsPanel); + } + + if (!settingsCookie["fork-ps-3"]) return; + setImmediate(() => { + const isHidden = (container, element) => { + if(!container){ + console.error(new Error(`Container is undefined.`)) + return false + } + const cTop = container.scrollTop; + const cBottom = cTop + container.clientHeight; + const eTop = element.offsetTop; + const eBottom = eTop + element.clientHeight; + return (eTop < cTop || eBottom > cBottom); + }; + + const thisNode = this.refs.cardNode; + const container = thisNode.closest("div[class*=\"contentRegionScroller-\"]") + if (!isHidden(container, thisNode)) return; + const thisNodeOffset = DOM.offset(thisNode); + const containerOffset = DOM.offset(container); + const original = container.scrollTop; + const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30; + DOM.animate({ + duration: 300, + update: function(progress) { + if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original)); + else container.scrollTop = original - (progress * (original - endPoint)); + } + }); + }); + } + + + getString(value) { + if (!value) return "???"; + return typeof value == "string" ? value : value.toString(); + } + + get settingsComponent() { + try { this.settingsPanel = this.props.addon.plugin.getSettingsPanel(); } + catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + this.name + ".", err); } + + return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-open ui-switch-item", ref: "cardNode"}, + BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => { + this.refs.settingspanel.innerHTML = ""; + this.setState({settings: false}); + }}, + BDV2.react.createElement(XSvg, null) + ), + typeof this.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel"}), + typeof this.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: this.settingsPanel}}) + ); + } + + buildTitle(name, version, author) { + const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/); + const nameIndex = title.findIndex(s => s == "{{name}}"); + if (nameIndex) title[nameIndex] = React.createElement("span", {className: "name bda-name"}, name); + const versionIndex = title.findIndex(s => s == "{{version}}"); + if (nameIndex) title[versionIndex] = React.createElement("span", {className: "version bda-version"}, version); + const authorIndex = title.findIndex(s => s == "{{author}}"); + if (nameIndex) { + const props = {className: "author bda-author"}; + if (author.link || author.id) { + props.className += ` ${anchorClasses.anchor} ${anchorClasses.anchorUnderlineOnHover}`; + props.target = "_blank"; + + if (author.link) props.href = author.link; + if (author.id) props.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(author.id);}; + } + title[authorIndex] = React.createElement(author.link || author.id ? "a" : "span", props, author.name); + } + return title.flat(); + } + + makeLink(title, url) { + const props = {className: "bda-link bda-link-website", target: "_blank"}; + if (typeof(url) == "string") props.href = url; + if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();}; + return BDV2.react.createElement("a", props, title); + } + + makeButton(title, children, action) { + return +
{children}
+
; + } + + componentWillUnmount(){ + this.unmounted = true + } + + get links() { + const links = []; + const addon = this.props.addon; + if (addon.website) links.push(this.makeLink("Website", addon.website)); + if (addon.source) links.push(this.makeLink("Source", addon.source)); + if (addon.invite) { + links.push(this.makeLink("Support Server", () => { + const tester = /\.gg\/(.*)$/; + let code = addon.invite; + if (tester.test(code)) code = code.match(tester)[1]; + BDV2.LayerStack.popLayer(); + BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code); + })); + } + if (addon.donate) links.push(this.makeLink("Donate", addon.donate)); + if (addon.patreon) links.push(this.makeLink("Patreon", addon.patreon)); + return links; + } + + get footer() { + const links = this.links; + return (links.length || this.hasSettings) && BDV2.react.createElement("div", {className: "bd-card-footer bda-footer"}, + BDV2.react.createElement("span", {className: "bd-addon-links bda-links"}, + ...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat()) + ), + this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bd-button bda-settings-button", disabled: !this.state.checked}, "Settings") + ); + } + + onChange() { + this.props.toggle && this.props.toggle(this.name); + this.setState({checked: !this.state.checked}); + } + + edit() {this.props.edit(this.name);} + delete() {this.props.remove(this.name);} + reload() {this.props.reload(this.name);} + + get name() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getName() : this.props.addon.name);} + get author() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getAuthor() : this.props.addon.author);} + get description() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getDescription() : this.props.addon.description);} + get version() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getVersion() : this.props.addon.version);} + + render() { + if (this.state.settings) return this.settingsComponent; + const {authorId, authorLink} = this.props.addon; + + const style = {} + if(settingsCookie["fork-ps-6"]){ + if(!this.isScanning){ + this.isScanning = true + processFile(resolve(this.props.addon.filename.endsWith(".plugin.js") ? contentManager.pluginsFolder : contentManager.themesFolder, this.props.addon.filename), (result) => { + if(this.unmounted)return + + this.setState({ + isTrusted: result.suspect ? "suspect" : true + }) + }, () => {}) + }else{ + if(this.state.isTrusted === true){ + style.borderColor = "#4087ed" + } + if(this.state.isTrusted === "suspect"){ + style.borderColor = "rgb(240, 71, 71)" + } + } + } + + return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-closed ui-switch-item", style}, + BDV2.react.createElement("div", {className: "bd-addon-header bda-header"}, + BDV2.react.createElement("div", {className: "bd-card-title bda-header-title"}, this.buildTitle(this.name, this.version, {name: this.author, id: authorId, link: authorLink})), + BDV2.react.createElement("div", {className: "bd-addon-controls bda-controls"}, + this.props.edit && this.makeButton("Edit", , this.edit), + this.props.remove && this.makeButton("Delete", , this.delete), + this.props.reload && this.makeButton("Reload", , this.reload), + React.createElement(Switch, {onChange: this.onChange, checked: this.state.checked}) + ) + ), + BDV2.react.createElement("div", {className: "bd-scroller-wrap bda-description-wrap scroller-wrap fade"}, + BDV2.react.createElement("div", {className: "bd-scroller bd-addon-description bda-description scroller"}, this.description) + ), + this.footer + ); + } +} + +const originalRender = V2C_PluginCard.prototype.render; +Object.defineProperty(V2C_PluginCard.prototype, "render", { + enumerable: false, + configurable: false, + set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, + get: () => originalRender }); \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/addonlist.jsx b/BetterDiscordApp/src/ui/addonlist.jsx index 9dfab7c..8e02f08 100644 --- a/BetterDiscordApp/src/ui/addonlist.jsx +++ b/BetterDiscordApp/src/ui/addonlist.jsx @@ -1,262 +1,262 @@ -import ErrorBoundary from "./errorBoundary"; -import ContentColumn from "./contentColumn"; -import ReloadIcon from "./reloadIcon"; -import AddonCard from "./addoncard"; -import Dropdown from "./components/dropdown"; -import Search from "./components/search"; - -import {settingsCookie, pluginCookie, themeCookie, bdplugins, bdthemes} from "../0globals"; -import ContentManager from "../modules/contentManager"; -import BDV2 from "../modules/v2"; -import pluginModule from "../modules/pluginModule"; -import themeModule from "../modules/themeModule"; -import WebpackModules from "../modules/webpackModules"; -import BdApi from "../modules/bdApi"; -import Utils from "../modules/utils"; -import TooltipWrap from "./tooltipWrap"; -import bdEvents from "../modules/bdEvents"; -import EmulatedTooltip from "./tooltip"; - -const Tooltip = WebpackModules.findByDisplayName("Tooltip"); - -const React = BDV2.react; - -export default class CardList extends BDV2.reactComponent { - constructor(props) { - super(props); - this.state = {sort: "name", ascending: true, query: ""}; - this.isPlugins = this.props.type == "plugins"; - this.cookie = this.isPlugins ? pluginCookie : themeCookie; - this.manager = this.isPlugins ? pluginModule : themeModule; - - this.sort = this.sort.bind(this); - this.reverse = this.reverse.bind(this); - this.search = this.search.bind(this); - - this.onAddonChanges = function(){ - this.forceUpdate() - } - this.onAddonChanges = this.onAddonChanges.bind(this) - } - - componentDidMount(){ - const type = (this.isPlugins ? "plugin" : "theme") + "-" - bdEvents.on(`${type}loaded`, this.onAddonChanges) - bdEvents.on(`${type}unloaded`, this.onAddonChanges) - bdEvents.on(`${type}reloaded`, this.onAddonChanges) - } - - componentWillUnmount(){ - const type = (this.isPlugins ? "plugin" : "theme") + "-" - bdEvents.off(`${type}loaded`, this.onAddonChanges) - bdEvents.off(`${type}unloaded`, this.onAddonChanges) - bdEvents.off(`${type}reloaded`, this.onAddonChanges) - } - - openFolder() { - const shell = require("electron").shell; - const open = shell.openPath || shell.openItem; - open(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder); - } - - edit(name) { - console.log(name); - this.manager.edit(name); - } - - async delete(name) { - const shouldDelete = await this.confirmDelete(name); - if (!shouldDelete) return; - this.manager.delete(name); - } - - confirmDelete(name) { - return new Promise(resolve => { - BdApi.showConfirmationModal("Are You Sure?", `Are you sure you want to delete ${name}?`, { - danger: true, - confirmText: "Delete", - onConfirm: () => {resolve(true);}, - onCancel: () => {resolve(false);} - }); - }); - } - - get sortOptions() { - return [ - {label: "Name", value: "name"}, - {label: "Author", value: "author"}, - {label: "Version", value: "version"}, - {label: "Recently Added", value: "added"}, - {label: "Last Modified", value: "modified"}, - {label: "File Size", value: "size"}, - ]; - } - - get directions() { - return [ - {label: "Ascending", value: true}, - {label: "Descending", value: false} - ]; - } - - reverse(value) { - this.setState({ascending: value}); - } - - sort(value) { - this.setState({sort: value}); - } - - search(event) { - this.setState({query: event.target.value.toLocaleLowerCase()}); - } - - getProps(addon) { - return { - key: this.getName(addon), - enabled: this.cookie[this.getName(addon)], - toggle: this.manager.toggle.bind(this.manager), - edit: settingsCookie["fork-ps-7"] ? this.edit.bind(this) : null, - remove: this.delete.bind(this), - addon: addon, - hash: addon.hash - }; - } - - getString(value) { - if (!value) return "???"; - return typeof value == "string" ? value : value.toString(); - } - - get list(){ - return this.props.type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes); - } - - getAddons() { - const sortedAddons = this.list.sort((a, b) => { - const cap = this.state.sort.charAt(0).toUpperCase() + this.state.sort.slice(1); - const first = a.plugin && a.plugin[`get${cap}`] ? this.getString(a.plugin[`get${cap}`]()) : this.getString(a[this.state.sort]); - const second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : this.getString(b[this.state.sort]); - if (typeof(first) == "string") return first.toLocaleLowerCase().localeCompare(second.toLocaleLowerCase()); - if (first > second) return 1; - if (second > first) return -1; - return 0; - }); - if (!this.state.ascending) sortedAddons.reverse(); - const rendered = []; - for (let a = 0; a < sortedAddons.length; a++) { - const addon = sortedAddons[a]; - if (this.state.query) { - let matches = null; - const name = this.getName(addon); - const author = this.getAuthor(addon); - const description = this.getDescription(addon); - const version = this.getVersion(addon); - if (name) matches = name.toLocaleLowerCase().includes(this.state.query); - if (author) matches = matches || author.toLocaleLowerCase().includes(this.state.query); - if (description) matches = matches || description.toLocaleLowerCase().includes(this.state.query); - if (version) matches = matches || version.toLocaleLowerCase().includes(this.state.query); - if (!matches) continue; - } - const props = this.getProps(addon); - rendered.push(); - } - return rendered; - } - - getName(addon) {return this.getString(addon.plugin ? addon.plugin.getName() : addon.name);} - getAuthor(addon) {return this.getString(addon.plugin ? addon.plugin.getAuthor() : addon.author);} - getDescription(addon) {return this.getString(addon.plugin ? addon.plugin.getDescription() : addon.description);} - getVersion(addon) {return this.getString(addon.plugin ? addon.plugin.getVersion() : addon.version);} - - renderCheckUpdates(){ - if(!window.ZeresPluginLibrary)return null - if(!window.PluginUpdates)return null - if(typeof window.PluginUpdates.checkAll !== "function")return null - if(!this.isPlugins)return null - - let tooltipIsShowing = false - let tooltip - return - { - tooltipIsShowing = false - tooltip.hide() - }}> - { - try{ - Utils.showToast("Plugin update check in progress.", {type: "info"}) - window.PluginUpdates.checkAll() - .then(() => { - Utils.showToast("Plugin update check complete.", {type: "success"}) - }).catch(err => { - console.error(err) - Utils.showToast("An error occured while checking update.", {type: "error"}) - }) - }catch(e){ - console.error(e) - Utils.showToast("An error occured while checking update.", {type: "error"}) - } - }} wrapper={false} disabled={false} onRightClick={() => { - if(!this.refs.checkUpdateButton)return - if (!window.PluginUpdates || !window.PluginUpdates.plugins) return; - if(tooltipIsShowing)return - tooltip = new EmulatedTooltip(this.refs.checkUpdateButton.refs.original.refs.button, Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", "), { - side: "bottom", - attachEvents: false - }) - tooltip.show() - tooltipIsShowing = true - }}> - Check for Updates - - - - } - - render() { - const refreshIcon = - {(props) => - { - if (this.isPlugins) pluginModule.updatePluginList(); - else themeModule.updateThemeList(); - this.forceUpdate(); - }} /> - }; - const addonCards = this.getAddons(); - - return - - Open {this.isPlugins ? "Plugin" : "Theme"} Folder - - {this.renderCheckUpdates()} - {!settingsCookie["fork-ps-5"] && refreshIcon} -
- -
-
- - -
-
- - -
- -
-
-
{addonCards}
-
- } -} - -const originalRender = CardList.prototype.render; -Object.defineProperty(CardList.prototype, "render", { - enumerable: false, - configurable: false, - set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, - get: () => originalRender +import ErrorBoundary from "./errorBoundary"; +import ContentColumn from "./contentColumn"; +import ReloadIcon from "./reloadIcon"; +import AddonCard from "./addoncard"; +import Dropdown from "./components/dropdown"; +import Search from "./components/search"; + +import {settingsCookie, pluginCookie, themeCookie, bdplugins, bdthemes} from "../0globals"; +import ContentManager from "../modules/contentManager"; +import BDV2 from "../modules/v2"; +import pluginModule from "../modules/pluginModule"; +import themeModule from "../modules/themeModule"; +import WebpackModules from "../modules/webpackModules"; +import BdApi from "../modules/bdApi"; +import Utils from "../modules/utils"; +import TooltipWrap from "./tooltipWrap"; +import bdEvents from "../modules/bdEvents"; +import EmulatedTooltip from "./tooltip"; + +const Tooltip = WebpackModules.findByDisplayName("Tooltip"); + +const React = BDV2.react; + +export default class CardList extends BDV2.reactComponent { + constructor(props) { + super(props); + this.state = {sort: "name", ascending: true, query: ""}; + this.isPlugins = this.props.type == "plugins"; + this.cookie = this.isPlugins ? pluginCookie : themeCookie; + this.manager = this.isPlugins ? pluginModule : themeModule; + + this.sort = this.sort.bind(this); + this.reverse = this.reverse.bind(this); + this.search = this.search.bind(this); + + this.onAddonChanges = function(){ + this.forceUpdate() + } + this.onAddonChanges = this.onAddonChanges.bind(this) + } + + componentDidMount(){ + const type = (this.isPlugins ? "plugin" : "theme") + "-" + bdEvents.on(`${type}loaded`, this.onAddonChanges) + bdEvents.on(`${type}unloaded`, this.onAddonChanges) + bdEvents.on(`${type}reloaded`, this.onAddonChanges) + } + + componentWillUnmount(){ + const type = (this.isPlugins ? "plugin" : "theme") + "-" + bdEvents.off(`${type}loaded`, this.onAddonChanges) + bdEvents.off(`${type}unloaded`, this.onAddonChanges) + bdEvents.off(`${type}reloaded`, this.onAddonChanges) + } + + openFolder() { + const shell = require("electron").shell; + const open = shell.openPath || shell.openItem; + open(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder); + } + + edit(name) { + console.log(name); + this.manager.edit(name); + } + + async delete(name) { + const shouldDelete = await this.confirmDelete(name); + if (!shouldDelete) return; + this.manager.delete(name); + } + + confirmDelete(name) { + return new Promise(resolve => { + BdApi.showConfirmationModal("Are You Sure?", `Are you sure you want to delete ${name}?`, { + danger: true, + confirmText: "Delete", + onConfirm: () => {resolve(true);}, + onCancel: () => {resolve(false);} + }); + }); + } + + get sortOptions() { + return [ + {label: "Name", value: "name"}, + {label: "Author", value: "author"}, + {label: "Version", value: "version"}, + {label: "Recently Added", value: "added"}, + {label: "Last Modified", value: "modified"}, + {label: "File Size", value: "size"}, + ]; + } + + get directions() { + return [ + {label: "Ascending", value: true}, + {label: "Descending", value: false} + ]; + } + + reverse(value) { + this.setState({ascending: value}); + } + + sort(value) { + this.setState({sort: value}); + } + + search(event) { + this.setState({query: event.target.value.toLocaleLowerCase()}); + } + + getProps(addon) { + return { + key: this.getName(addon), + enabled: this.cookie[this.getName(addon)], + toggle: this.manager.toggle.bind(this.manager), + edit: settingsCookie["fork-ps-7"] ? this.edit.bind(this) : null, + remove: this.delete.bind(this), + addon: addon, + hash: addon.hash + }; + } + + getString(value) { + if (!value) return "???"; + return typeof value == "string" ? value : value.toString(); + } + + get list(){ + return this.props.type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes); + } + + getAddons() { + const sortedAddons = this.list.sort((a, b) => { + const cap = this.state.sort.charAt(0).toUpperCase() + this.state.sort.slice(1); + const first = a.plugin && a.plugin[`get${cap}`] ? this.getString(a.plugin[`get${cap}`]()) : this.getString(a[this.state.sort]); + const second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : this.getString(b[this.state.sort]); + if (typeof(first) == "string") return first.toLocaleLowerCase().localeCompare(second.toLocaleLowerCase()); + if (first > second) return 1; + if (second > first) return -1; + return 0; + }); + if (!this.state.ascending) sortedAddons.reverse(); + const rendered = []; + for (let a = 0; a < sortedAddons.length; a++) { + const addon = sortedAddons[a]; + if (this.state.query) { + let matches = null; + const name = this.getName(addon); + const author = this.getAuthor(addon); + const description = this.getDescription(addon); + const version = this.getVersion(addon); + if (name) matches = name.toLocaleLowerCase().includes(this.state.query); + if (author) matches = matches || author.toLocaleLowerCase().includes(this.state.query); + if (description) matches = matches || description.toLocaleLowerCase().includes(this.state.query); + if (version) matches = matches || version.toLocaleLowerCase().includes(this.state.query); + if (!matches) continue; + } + const props = this.getProps(addon); + rendered.push(); + } + return rendered; + } + + getName(addon) {return this.getString(addon.plugin ? addon.plugin.getName() : addon.name);} + getAuthor(addon) {return this.getString(addon.plugin ? addon.plugin.getAuthor() : addon.author);} + getDescription(addon) {return this.getString(addon.plugin ? addon.plugin.getDescription() : addon.description);} + getVersion(addon) {return this.getString(addon.plugin ? addon.plugin.getVersion() : addon.version);} + + renderCheckUpdates(){ + if(!window.ZeresPluginLibrary)return null + if(!window.PluginUpdates)return null + if(typeof window.PluginUpdates.checkAll !== "function")return null + if(!this.isPlugins)return null + + let tooltipIsShowing = false + let tooltip + return + { + tooltipIsShowing = false + tooltip.hide() + }}> + { + try{ + Utils.showToast("Plugin update check in progress.", {type: "info"}) + window.PluginUpdates.checkAll() + .then(() => { + Utils.showToast("Plugin update check complete.", {type: "success"}) + }).catch(err => { + console.error(err) + Utils.showToast("An error occured while checking update.", {type: "error"}) + }) + }catch(e){ + console.error(e) + Utils.showToast("An error occured while checking update.", {type: "error"}) + } + }} wrapper={false} disabled={false} onRightClick={() => { + if(!this.refs.checkUpdateButton)return + if (!window.PluginUpdates || !window.PluginUpdates.plugins) return; + if(tooltipIsShowing)return + tooltip = new EmulatedTooltip(this.refs.checkUpdateButton.refs.original.refs.button, Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", "), { + side: "bottom", + attachEvents: false + }) + tooltip.show() + tooltipIsShowing = true + }}> + Check for Updates + + + + } + + render() { + const refreshIcon = + {(props) => + { + if (this.isPlugins) pluginModule.updatePluginList(); + else themeModule.updateThemeList(); + this.forceUpdate(); + }} /> + }; + const addonCards = this.getAddons(); + + return + + Open {this.isPlugins ? "Plugin" : "Theme"} Folder + + {this.renderCheckUpdates()} + {!settingsCookie["fork-ps-5"] && refreshIcon} +
+ +
+
+ + +
+
+ + +
+ +
+
+
{addonCards}
+
+ } +} + +const originalRender = CardList.prototype.render; +Object.defineProperty(CardList.prototype, "render", { + enumerable: false, + configurable: false, + set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, + get: () => originalRender }); \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/components/search.jsx b/BetterDiscordApp/src/ui/components/search.jsx index 26d5393..f5580cf 100644 --- a/BetterDiscordApp/src/ui/components/search.jsx +++ b/BetterDiscordApp/src/ui/components/search.jsx @@ -1,27 +1,27 @@ -import BDV2 from "../../modules/v2"; -import SearchIcon from "../icons/search"; - -const React = BDV2.React; - -export default class Search extends React.Component { - constructor(props){ - super(props) - this.state = { - focused: false - } - } - render() { - const className = ["bd-search-wrapper"] - if(this.state.focused)className.push("focused") - return
- { - this.setState({focused: true}) - }} onBlur={() => { - this.setState({focused: false}) - }}/> - -
; - } +import BDV2 from "../../modules/v2"; +import SearchIcon from "../icons/search"; + +const React = BDV2.React; + +export default class Search extends React.Component { + constructor(props){ + super(props) + this.state = { + focused: false + } + } + render() { + const className = ["bd-search-wrapper"] + if(this.state.focused)className.push("focused") + return
+ { + this.setState({focused: true}) + }} onBlur={() => { + this.setState({focused: false}) + }}/> + +
; + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/components/switch.jsx b/BetterDiscordApp/src/ui/components/switch.jsx index eaf9f2d..d40f3bd 100644 --- a/BetterDiscordApp/src/ui/components/switch.jsx +++ b/BetterDiscordApp/src/ui/components/switch.jsx @@ -1,22 +1,22 @@ -import BDV2 from "../../modules/v2"; - -const React = BDV2.React; - -export default class Switch extends React.Component { - constructor(props) { - super(props); - this.state = {checked: this.props.checked}; - this.onChange = this.onChange.bind(this); - this.switch = - } - - onChange(value) { - if (this.props.disabled) return; - this.props.onChange(value); - this.setState({checked: value}); - } - - render() { - return this.switch - } +import BDV2 from "../../modules/v2"; + +const React = BDV2.React; + +export default class Switch extends React.Component { + constructor(props) { + super(props); + this.state = {checked: this.props.checked}; + this.onChange = this.onChange.bind(this); + this.switch = + } + + onChange(value) { + if (this.props.disabled) return; + this.props.onChange(value); + this.setState({checked: value}); + } + + render() { + return this.switch + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/cssEditor.js b/BetterDiscordApp/src/ui/cssEditor.js index 42f3943..2b162c9 100644 --- a/BetterDiscordApp/src/ui/cssEditor.js +++ b/BetterDiscordApp/src/ui/cssEditor.js @@ -1,234 +1,234 @@ -import {settingsCookie} from "../0globals"; -import Settings from "../modules/settingsPanel"; -import BDV2 from "../modules/v2"; -import DataStore from "../modules/dataStore"; -import DOM from "../modules/domtools"; -import Utils from "../modules/utils" - -import SettingsTitle from "./settingsTitle"; -import Checkbox from "./checkbox"; -import V2C_CssEditorDetached from "./cssEditorDetached"; - -export default class V2C_CssEditor extends BDV2.reactComponent { - - constructor(props) { - super(props); - const 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/discord"); - 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) { - const 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() { - const _ccss = DataStore.getBDData("bdcustomcss"); - let ccss = ""; - if (_ccss && _ccss !== "") { - ccss = Buffer.from(_ccss, "base64").toString("utf8"); - } - return ccss; - } - - updateLineCount() { - const 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() { - const self = this; - - const {detached} = self.state; - return [ - detached && BDV2.react.createElement( - "div", - {id: "editor-detached"}, - BDV2.react.createElement(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(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(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" - ), - BDV2.react.createElement("div", {className: "help-text"}, - "Press ", - BDV2.react.createElement("code", {className: "inline"}, "ctrl"), - "+", - BDV2.react.createElement("span", {className: "inline"}, ","), - " with the editor focused to access the editor's settings." - ) - ) - ) - ) - ] - } - - onClick(arg) { - const 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; - Settings.saveSettings(); - break; - } - } - - updateCss() { - DOM.removeStyle("customcss"); - DOM.addStyle("customcss", this.editor.session.getValue()); - } - - saveCss() { - DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); - } - - detach() { - const self = this; - self.setState({ - detached: true - }); - const droot = self.detachedRoot; - if (!droot) { - console.log("FAILED TO INJECT ROOT: .app"); - return; - } - BDV2.reactDom.render(self.detachedEditor, droot); - } - - get detachedRoot() { - const _root = DOM.query("#bd-customcss-detach-container"); - if (!_root) { - if (!this.injectDetachedRoot()) return null; - return this.detachedRoot; - } - return _root; - } - - injectDetachedRoot() { - const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app)); - if (!app) return false; - DOM.insertAfter(DOM.createElement(`
`), app); - return true; - } - - attach() { - const self = this; - self.setState({ - detached: false - }); - } +import {settingsCookie} from "../0globals"; +import Settings from "../modules/settingsPanel"; +import BDV2 from "../modules/v2"; +import DataStore from "../modules/dataStore"; +import DOM from "../modules/domtools"; +import Utils from "../modules/utils" + +import SettingsTitle from "./settingsTitle"; +import Checkbox from "./checkbox"; +import V2C_CssEditorDetached from "./cssEditorDetached"; + +export default class V2C_CssEditor extends BDV2.reactComponent { + + constructor(props) { + super(props); + const 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/discord"); + 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) { + const 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() { + const _ccss = DataStore.getBDData("bdcustomcss"); + let ccss = ""; + if (_ccss && _ccss !== "") { + ccss = Buffer.from(_ccss, "base64").toString("utf8"); + } + return ccss; + } + + updateLineCount() { + const 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() { + const self = this; + + const {detached} = self.state; + return [ + detached && BDV2.react.createElement( + "div", + {id: "editor-detached"}, + BDV2.react.createElement(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(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(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" + ), + BDV2.react.createElement("div", {className: "help-text"}, + "Press ", + BDV2.react.createElement("code", {className: "inline"}, "ctrl"), + "+", + BDV2.react.createElement("span", {className: "inline"}, ","), + " with the editor focused to access the editor's settings." + ) + ) + ) + ) + ] + } + + onClick(arg) { + const 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; + Settings.saveSettings(); + break; + } + } + + updateCss() { + DOM.removeStyle("customcss"); + DOM.addStyle("customcss", this.editor.session.getValue()); + } + + saveCss() { + DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); + } + + detach() { + const self = this; + self.setState({ + detached: true + }); + const droot = self.detachedRoot; + if (!droot) { + console.log("FAILED TO INJECT ROOT: .app"); + return; + } + BDV2.reactDom.render(self.detachedEditor, droot); + } + + get detachedRoot() { + const _root = DOM.query("#bd-customcss-detach-container"); + if (!_root) { + if (!this.injectDetachedRoot()) return null; + return this.detachedRoot; + } + return _root; + } + + injectDetachedRoot() { + const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app)); + if (!app) return false; + DOM.insertAfter(DOM.createElement(`
`), app); + return true; + } + + attach() { + const self = this; + self.setState({ + detached: false + }); + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/cssEditorDetached.js b/BetterDiscordApp/src/ui/cssEditorDetached.js index d7949f2..e79b6ac 100644 --- a/BetterDiscordApp/src/ui/cssEditorDetached.js +++ b/BetterDiscordApp/src/ui/cssEditorDetached.js @@ -1,174 +1,174 @@ -import {settingsCookie} from "../0globals"; -import Settings from "../modules/settingsPanel"; -import BDV2 from "../modules/v2"; -import DataStore from "../modules/dataStore"; -import DOM from "../modules/domtools"; -import Utils from "../modules/utils" - -import Checkbox from "./checkbox"; - -export default class V2C_CssEditorDetached extends BDV2.reactComponent { - - constructor(props) { - super(props); - const self = this; - self.onClick = self.onClick.bind(self); - self.updateCss = self.updateCss.bind(self); - self.saveCss = self.saveCss.bind(self); - self.onChange = self.onChange.bind(self); - } - - componentDidMount() { - DOM.addClass(DOM.query("#app-mount"), "bd-detached-editor"); - BDV2.editorDetached = true; - // this.updateLineCount(); - this.editor = ace.edit("bd-customcss-editor-detached"); - this.editor.setTheme("ace/theme/discord"); - 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() { - DOM.removeClass(DOM.query("#app-mount"), "bd-detached-editor"); - BDV2.editorDetached = false; - this.editor.destroy(); - } - - updateLineCount() { - const 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; - } - - get options() { - return { - lineNumbers: true, - mode: "css", - indentUnit: 4, - theme: "material", - scrollbarStyle: "simple" - }; - } - - get css() { - const _ccss = DataStore.getBDData("bdcustomcss"); - let ccss = ""; - if (_ccss && _ccss !== "") { - ccss = Buffer.from(_ccss, "base64").toString("utf8"); - } - return ccss; - } - - get root() { - const _root = DOM.query("#bd-customcss-detach-container"); - if (!_root) { - if (!this.injectRoot()) return null; - return this.detachedRoot; - } - return _root; - } - - injectRoot() { - const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app)); - if (!app) return false; - DOM.insertAfter(DOM.createElement(`
`), app); - return true; - } - - render() { - const self = this; - return BDV2.react.createElement( - "div", - {className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"}, - BDV2.react.createElement( - "div", - {id: "bd-customcss-innerpane"}, - BDV2.react.createElement("div", {className: "editor-wrapper"}, - BDV2.react.createElement("div", {id: "bd-customcss-editor-detached", 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(Checkbox, {id: "live-update", text: "Live Update", onChange: self.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("attach"); - }}, - "Attach" - ), - BDV2.react.createElement( - "span", - {style: {fontSize: "10px", marginLeft: "5px"}}, - "Unsaved changes are lost on attach" - ) - ) - ) - ) - ); - } - - onChange(id, checked) { - switch (id) { - case "live-update": - settingsCookie["bda-css-0"] = checked; - Settings.saveSettings(); - break; - } - } - - onClick(id) { - const self = this; - switch (id) { - case "attach": - if (DOM.query("#editor-detached")) self.props.attach(); - BDV2.reactDom.unmountComponentAtNode(self.root); - self.root.remove(); - break; - case "update": - self.updateCss(); - break; - case "save": - self.saveCss(); - break; - } - } - - updateCss() { - DOM.removeStyle("customcss"); - DOM.addStyle("customcss", this.editor.session.getValue()); - } - - saveCss() { - DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); - } +import {settingsCookie} from "../0globals"; +import Settings from "../modules/settingsPanel"; +import BDV2 from "../modules/v2"; +import DataStore from "../modules/dataStore"; +import DOM from "../modules/domtools"; +import Utils from "../modules/utils" + +import Checkbox from "./checkbox"; + +export default class V2C_CssEditorDetached extends BDV2.reactComponent { + + constructor(props) { + super(props); + const self = this; + self.onClick = self.onClick.bind(self); + self.updateCss = self.updateCss.bind(self); + self.saveCss = self.saveCss.bind(self); + self.onChange = self.onChange.bind(self); + } + + componentDidMount() { + DOM.addClass(DOM.query("#app-mount"), "bd-detached-editor"); + BDV2.editorDetached = true; + // this.updateLineCount(); + this.editor = ace.edit("bd-customcss-editor-detached"); + this.editor.setTheme("ace/theme/discord"); + 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() { + DOM.removeClass(DOM.query("#app-mount"), "bd-detached-editor"); + BDV2.editorDetached = false; + this.editor.destroy(); + } + + updateLineCount() { + const 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; + } + + get options() { + return { + lineNumbers: true, + mode: "css", + indentUnit: 4, + theme: "material", + scrollbarStyle: "simple" + }; + } + + get css() { + const _ccss = DataStore.getBDData("bdcustomcss"); + let ccss = ""; + if (_ccss && _ccss !== "") { + ccss = Buffer.from(_ccss, "base64").toString("utf8"); + } + return ccss; + } + + get root() { + const _root = DOM.query("#bd-customcss-detach-container"); + if (!_root) { + if (!this.injectRoot()) return null; + return this.detachedRoot; + } + return _root; + } + + injectRoot() { + const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app)); + if (!app) return false; + DOM.insertAfter(DOM.createElement(`
`), app); + return true; + } + + render() { + const self = this; + return BDV2.react.createElement( + "div", + {className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"}, + BDV2.react.createElement( + "div", + {id: "bd-customcss-innerpane"}, + BDV2.react.createElement("div", {className: "editor-wrapper"}, + BDV2.react.createElement("div", {id: "bd-customcss-editor-detached", 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(Checkbox, {id: "live-update", text: "Live Update", onChange: self.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("attach"); + }}, + "Attach" + ), + BDV2.react.createElement( + "span", + {style: {fontSize: "10px", marginLeft: "5px"}}, + "Unsaved changes are lost on attach" + ) + ) + ) + ) + ); + } + + onChange(id, checked) { + switch (id) { + case "live-update": + settingsCookie["bda-css-0"] = checked; + Settings.saveSettings(); + break; + } + } + + onClick(id) { + const self = this; + switch (id) { + case "attach": + if (DOM.query("#editor-detached")) self.props.attach(); + BDV2.reactDom.unmountComponentAtNode(self.root); + self.root.remove(); + break; + case "update": + self.updateCss(); + break; + case "save": + self.saveCss(); + break; + } + } + + updateCss() { + DOM.removeStyle("customcss"); + DOM.addStyle("customcss", this.editor.session.getValue()); + } + + saveCss() { + DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/icons/delete.jsx b/BetterDiscordApp/src/ui/icons/delete.jsx index df721ee..1a6d39d 100644 --- a/BetterDiscordApp/src/ui/icons/delete.jsx +++ b/BetterDiscordApp/src/ui/icons/delete.jsx @@ -1,13 +1,13 @@ -import BDV2 from "../../modules/v2"; - -const React = BDV2.React; - -export default class Delete extends React.Component { - render() { - const size = this.props.size || "24px"; - return - - - ; - } +import BDV2 from "../../modules/v2"; + +const React = BDV2.React; + +export default class Delete extends React.Component { + render() { + const size = this.props.size || "24px"; + return + + + ; + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/icons/downarrow.jsx b/BetterDiscordApp/src/ui/icons/downarrow.jsx index 209ecee..2bbe58f 100644 --- a/BetterDiscordApp/src/ui/icons/downarrow.jsx +++ b/BetterDiscordApp/src/ui/icons/downarrow.jsx @@ -1,12 +1,12 @@ -import BDV2 from "../../modules/v2"; - -const React = BDV2.React; - -export default class DownArrow extends React.Component { - render() { - const size = this.props.size || "16px"; - return - - ; - } +import BDV2 from "../../modules/v2"; + +const React = BDV2.React; + +export default class DownArrow extends React.Component { + render() { + const size = this.props.size || "16px"; + return + + ; + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/icons/edit.jsx b/BetterDiscordApp/src/ui/icons/edit.jsx index 0e451a7..cbda6fb 100644 --- a/BetterDiscordApp/src/ui/icons/edit.jsx +++ b/BetterDiscordApp/src/ui/icons/edit.jsx @@ -1,13 +1,13 @@ -import BDV2 from "../../modules/v2"; - -const React = BDV2.React; - -export default class Edit extends React.Component { - render() { - const size = this.props.size || "24px"; - return - - - ; - } +import BDV2 from "../../modules/v2"; + +const React = BDV2.React; + +export default class Edit extends React.Component { + render() { + const size = this.props.size || "24px"; + return + + + ; + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/icons/history.jsx b/BetterDiscordApp/src/ui/icons/history.jsx index 5b4a8dc..c8e73b8 100644 --- a/BetterDiscordApp/src/ui/icons/history.jsx +++ b/BetterDiscordApp/src/ui/icons/history.jsx @@ -1,18 +1,18 @@ -{/* - - - */} - -import BDV2 from "../../modules/v2"; - -const React = BDV2.React; - -export default class History extends React.Component { - render() { - const size = this.props.size || "18px"; - return - - - ; - } +{/* + + + */} + +import BDV2 from "../../modules/v2"; + +const React = BDV2.React; + +export default class History extends React.Component { + render() { + const size = this.props.size || "18px"; + return + + + ; + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/icons/search.jsx b/BetterDiscordApp/src/ui/icons/search.jsx index 8fb6233..c8152cd 100644 --- a/BetterDiscordApp/src/ui/icons/search.jsx +++ b/BetterDiscordApp/src/ui/icons/search.jsx @@ -1,13 +1,13 @@ -import BDV2 from "../../modules/v2"; - -const React = BDV2.React; - -export default class Search extends React.Component { - render() { - const size = this.props.size || "16px"; - return - - - ; - } +import BDV2 from "../../modules/v2"; + +const React = BDV2.React; + +export default class Search extends React.Component { + render() { + const size = this.props.size || "16px"; + return + + + ; + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/presenceSettings.jsx b/BetterDiscordApp/src/ui/presenceSettings.jsx index 3f802ff..aa2ff87 100644 --- a/BetterDiscordApp/src/ui/presenceSettings.jsx +++ b/BetterDiscordApp/src/ui/presenceSettings.jsx @@ -1,752 +1,752 @@ -import BDV2 from "../modules/v2"; -import V2C_SettingsTitle from "./settingsTitle"; -import V2C_SettingsGroup from "./settingsGroup"; -import dataStore from "../modules/dataStore"; -import { defaultRPC, settingsRPC } from "../0globals"; -import CustomRichPresence from "../modules/CustomRichPresence" -import { remote } from "electron"; -import MarginTop from "./margintop"; -import Utils from "../modules/utils"; - -const React = BDV2.React; - -let V2C_PresenceSettingsModules -export default class V2C_PresenceSettings extends React.Component { - constructor(props) { - super(props); - - this.state = { - data: dataStore.getSettingGroup("rpc") || defaultRPC, - assets: [] - } - - this.preview = null - this.isfetching = false - if(this.state.data.application_id){ - this.fetchAssets() - } - - this.assetComponents = new Set() - } - - updateWhenFetched(comp){ - this.assetComponents.add(comp) - } - - /** - * - * @param {InputText} setting - */ - onChange(setting, value){ - let defaultSetting = RPCProps.find(e => e.id === setting.props.id) - - this.setState({ - data: Object.assign(settingsRPC, this.state.data, { - [defaultSetting.id]: !!value ? value : null - }), - assets: this.state.assets - }) - if(setting.props.id === "application_id"){ - this.fetchAssets() - } - - dataStore.setSettingGroup("rpc", settingsRPC); - this.preview.forceUpdate() - CustomRichPresence.set(settingsRPC) - } - - fetchAssets(){ - if(this.isfetching === true){ - let app = this.state.data.application_id - setTimeout(() => { - if(this.state.data.application_id !== app){ - return - } - this.fetchAssets() - }, 5000); - } - if(!this.state.data.application_id){ - this.setState({ - data: this.state.data, - assets: [] - }) - this.forceUpdate() - this.assetComponents.forEach(e => e.forceUpdate()) - return - } - this.isfetching = true - CustomRichPresence.fetchAssets(this.state.data.application_id) - .then(assets => { - this.isfetching = false - this.setState({ - data: this.state.data, - assets: Object.keys(assets).map(k => { - let asset = assets[k] - return { - id: asset.id, - name: asset.name, - type: asset.type - } - }) - }) - this.forceUpdate() - this.assetComponents.forEach(e => e.forceUpdate()) - }).catch(() => { - this.isfetching = false - this.setState({ - data: this.state.data, - assets: [] - }) - this.forceUpdate() - this.assetComponents.forEach(e => e.forceUpdate()) - }) - } - - updatePreview(data){ - this.setState({ - data - }) - } - - get modules(){ - return V2C_PresenceSettingsModules || (V2C_PresenceSettingsModules = [ - BDModules.get(e => e.marginBottom20)[0] - ]) - } - - render() { - let [ - marginModule - ] = this.modules - return [ - , - , - , -
- {/** options */} - {this.optionsComponents} -
, -
- - - {/** preview */} - -
, -
, - , - - ] - } - - get optionsComponents(){ - return this._optionsComponents || (this._optionsComponents = RPCProps.map(e => { - if(e.type === "text"){ - return - }else if(e.type === "number"){ - let array = []/* - if(e.id === "timestamps.start"){ - array.unshift( { - DiscordNative.clipboard.copy(Date.now()+"") - }} />) - }*/ - return array - }else if(e.type === "choice"){ - if(["assets.small", "assets.large"].includes(e.id)){ - return { - return { - value: "asset-"+e.id, - label: e.name - } - }))}/> - }else{ - return "Unknown choice." - } - } - })) - } -} -const RPCProps = [ - { - title: "Application ID", - id: "application_id", - type: "number", - placeholder: "711416957718757418" - }, - { - title: "Name", - id: "name", - type: "text", - placeholder: "Lightcord" - }, - { - title: "Details", - id: "details", - type: "text", - placeholder: "Browsing Discord" - }, - { - title: "State", - id: "state", - type: "text", - placeholder: "Lightcord Client" - }, - { - title: "Timestamp Start", - id: "timestamps.start", - type: "number", - get placeholder(){ - return Date.now() - } - }, - { - title: "LargeAsset", - id: "assets.large", - type: "choice" - }, - { - title: "SmallAsset", - id: "assets.small", - type: "choice" - }, -] - -let inputTextModules -class InputText extends React.PureComponent { - get modules(){ - if(inputTextModules && inputTextModules[0])return inputTextModules - return inputTextModules = [ - BDModules.get(e => e.removeKeybind)[0], - BDModules.get(e => e.marginBottom20)[0], - BDModules.get(e => e.defaultMarginh5)[0], - BDModules.get(e => e.colorStandard)[0], - BDModules.get(e => e.size32)[0], - BDModules.get(e => e._horizontal)[0], - BDModules.get(e => e.inputMini)[0], - BDModules.get(e => e.size16 && e.size20)[0], - ] - } - - constructor(props){ - super(props) - let setting = this.props.setting - this.state = { - data: this.props.manager.state.data[setting.id] - } - - this.input = { - this.setState({ - data: value - }) - if(!this.lastEdited || this.lastEdited < Date.now() - 500){ - this.props.manager.onChange(this, value) - this.lastEdited = Date.now() - }else if(!this.isTiming){ - this.isTiming = setTimeout(() => { - this.props.manager.onChange(this, this.state.data) - this.isTiming = null - this.lastEdited = Date.now() - }, 500); - } - }} type="text"/> - } - - render(){ - let setting = this.props.setting - - let [ - rowModule, - marginModule, - marginModule2, - colorModule, - sizeModule, - flexModule, - inputModule, - sizeModule2, - ] = this.modules - - return (
-
-
- {setting.title} -
- {this.input} -
- -
) - } -} - -let InputNumberModules -class InputNumber extends React.PureComponent { - get modules(){ - return InputNumberModules || (InputNumberModules = [ - BDModules.get(e => e.removeKeybind)[0], - BDModules.get(e => e.marginBottom20)[0], - BDModules.get(e => e.defaultMarginh5)[0], - BDModules.get(e => e.colorStandard)[0], - BDModules.get(e => e.size32)[0], - BDModules.get(e => e._horizontal)[0], - BDModules.get(e => e.inputMini)[0], - BDModules.get(e => e.size16 && e.size20)[0], - BDModules.get(e => e.colorTransparent)[0], - ]) - } - - constructor(props){ - super(props) - let setting = this.props.setting - this.state = { - data: this.props.manager.state.data[setting.id] - } - - this.input = { - value = value.replace(/[^\d]+/g, "") - if(value == this.state.data){ - input.setValue(value) - return - } - - if(!this.lastEdited || this.lastEdited < Date.now() - 500){ - this.props.manager.onChange(this, value) - this.lastEdited = Date.now() - }else if(!this.isTiming){ - this.isTiming = setTimeout(() => { - this.props.manager.onChange(this, this.state.data) - this.isTiming = null - this.lastEdited = Date.now() - }, 500); - } - this.setState({ - data: value - }) - }} type="text"/> - } - - render(){ - let setting = this.props.setting - - let [ - rowModule, - marginModule, - marginModule2, - colorModule, - sizeModule, - flexModule, - ] = this.modules - - - return (
-
-
- {setting.title} -
- {this.input} - {setting.id === "timestamps.start" ? - { - DiscordNative.clipboard.copy(Date.now()+"") - }} color="brand">Copy current timestamp : null} -
- -
) - } -} - -let InputChoiceModules -class InputChoice extends React.PureComponent { - constructor(props){ - super(props) - let setting = this.props.setting - this.state = { - data: this.props.manager.state.data[setting.id] ? "asset-"+this.props.manager.state.data[setting.id] : "none" - } - this.props.manager.updateWhenFetched(this) - } - - onChange(data){ - let value = data.value - - if(!this.lastEdited || this.lastEdited < Date.now() - 500){ - this.props.manager.onChange(this, value === "none" ? null : value.replace("asset-", "")) - this.lastEdited = Date.now() - }else if(!this.isTiming){ - this.isTiming = setTimeout(() => { - this.props.manager.onChange(this, this.state.data === "none" ? null : this.state.data.replace("asset-", "")) - this.isTiming = null - this.lastEdited = Date.now() - }, 500); - } - this.setState({ - data: value - }) - this.forceUpdate() - } - - get modules(){ - return InputChoiceModules || (InputChoiceModules = [ - BDModules.get(e => e.removeKeybind)[0], - BDModules.get(e => e.marginBottom20)[0], - BDModules.get(e => e.defaultMarginh5)[0], - BDModules.get(e => e.colorStandard)[0], - BDModules.get(e => e.size32)[0], - BDModules.get(e => e._horizontal)[0] - ]) - } - - render(){ - let setting = this.props.setting - - let [ - rowModule, - marginModule, - marginModule2, - colorModule, - sizeModule, - flexModule - ] = this.modules - - let options = this.props.manager.state.assets.map(e => { - return { - value: "asset-"+e.id, - label: e.name - } - }) - - options.unshift({ - value: "none", - label: "No assets" - }) - - return (
-
-
- {setting.title} -
- - {/**/} +
+ +
) + } +} + +let DividerModules = [] +class Divider extends React.PureComponent { + get modules(){ + return DividerModules&&DividerModules[0] ? DividerModules : (DividerModules = [ + BDModules.get(e => e.divider && Object.keys(e).length === 1)[0], + BDModules.get(e => e.dividerDefault)[0] + ]) + } + + render(){ + let [ + divider, + dividerDefault + ] = this.modules + + return
+ } +} + +class DiscordButton extends React.Component { + render(){ + let rowModule = BDModules.get(e => e.removeKeybind)[0] + let marginModule = BDModules.get(e => e.marginBottom20)[0] + let flexModule = BDModules.get(e => e._horizontal)[0] + + return (
+
+ + {this.props.title} + +
+
) + } +} + +class RpcPreview extends React.Component { + constructor(props = {}){ + super(props) + this.state = { + active: "profile" + } + this.tabs = [] + + this.props.settings.preview = this + } + + changeTab(tab){ + let ancientTab = this.state.active + if(ancientTab === tab.props.id)return + + this.tabs.forEach(e => { + e.setActive(false) + }) + tab.setActive(true) + this.setState({ + active: tab.props.id + }) + } + + render(){ + return (
+
+ + +
+ +
) + } + + isActive(tab){ + return this.state.active === tab + } + + get preview(){ + if(this.state.active === "profile")return Profile + return Popout + } +} + +class Tab extends React.Component { + constructor(props){ + super(props) + + this.state = { + active: props.preview.isActive(props.id) + } + props.preview.tabs.push(this) + } + + setActive(isActive){ + this.setState({ + active: !!isActive + }) + } + + render(){ + let className = `lc-navItem` + if(this.state.active){ + className += ` lc-navItemActive` + }else{ + className += ` lc-navItemInactive` + } + return (
{ + this.props.preview.changeTab(this) + }}> + {this.props.title} +
) + } +} + +let emptyClasses +class PresenceErrorCatcher extends React.Component { + componentDidCatch(err, errInfo){ + console.error(err, errInfo) + this.setState({ + error: true + }) + } + render(){ + if(!this.state){ + this.state = { + error: false + } + } + if(!this.state.error){ + try{ + const preview = new this.props.preview(this.props.props) + preview.setState(this.props.state) + return preview.render() + }catch(err){ + console.error(err) + this.state.error = true + return this.render() + } + }else{ + emptyClasses = emptyClasses || BDV2.WebpackModules.find(e => e.emptyStateImage && e.emptyState) + if(!emptyClasses){ + Utils.showToast("An error occured. Please check the console for more informations.") + return null + } + return
+
+
+ +
+
An error occured
+

+ Please check the console for more informations. Join our ­ + { + BDV2.joinLC() + }}> + support server + + ­ for help. +

+
+
+ } + } +} + +let popoutModules +let UserPopoutComponent +let PopoutProps +class Popout extends React.Component { + get modules(){ + return popoutModules || (popoutModules = [ + BDV2.WebpackModules.find(e => e.default && e.default.displayName === "UserPopout"), + BDV2.WebpackModules.find(e => e.default && e.default.getCurrentUser) + ]) + } + + getComponent(){ + let [ + UserPopout, + userModule + ] = this.modules + + const user = userModule.default.getCurrentUser() + return React.createElement(() => { + let render1 = UserPopout.default({ + userId: user.id, + guildId: null, + channelId: null, + disableUserProfileLink: true + }) + UserPopoutComponent = render1.type + PopoutProps = render1.props + return this.render() + }, null) + } + + render(){ + if(!UserPopoutComponent)return this.getComponent() + let [ + _UserPopout, + userModule + ] = this.modules + + const user = userModule.default.getCurrentUser() + + let data = Object.assign({}, defaultRPC, this.props.preview.props.settings.state.data) + const activity = (function(){ + if(!this.game)return null + let game = { + name: this.game.name || defaultRPC.name, + application_id: this.game.application_id || defaultRPC.application_id, + details: this.game.details || undefined, + state: this.game.state || undefined, + timestamps: this.game["timestamps.start"] ? { + start: this.game["timestamps.start"] + } : undefined, + assets: this.game["assets.large"] ? { + large_image: this.game["assets.large"], + small_image: this.game["assets.small"] || undefined + } : undefined, + type: 0 + } + return game + }).call({ + game: data + }) + + const popout = new UserPopoutComponent(Object.assign({}, PopoutProps, { + activity: activity, + userId: user.id + })).render().props.children // bypass tracking + + // remove the stop propagation shit. + const container =
+ + return
+ {container} +
+ } +} + +let profileModules +let UserProfileComponent +let ProfileProps +let connectedProfileStore +class Profile extends React.Component { + get modules(){ + return profileModules || (profileModules = [ + BDV2.WebpackModules.find(e => e.default && e.default.displayName === "UserProfile"), + BDV2.WebpackModules.find(e => e.default && e.default.getCurrentUser) + ]) + } + + render(){ + let [ + UserProfile, + userModule + ] = this.modules + + const user = userModule.default.getCurrentUser() + if(!UserProfileComponent){ + const render1 = new UserProfile.default({ + user: user + }).render() + connectedProfileStore = render1.type + const render2 = new render1.type(render1.props).render() + const render3 = render2.type.render(render2.props, null) + const render4 = new render3.type(render3.props).render() + UserProfileComponent = render4.type + } + if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`) + + let data = Object.assign({}, defaultRPC, this.props.preview.props.settings.state.data) + const activity = (function(){ + if(!this.game)return null + let game = { + name: this.game.name || defaultRPC.name, + application_id: this.game.application_id || defaultRPC.application_id, + details: this.game.details || undefined, + state: this.game.state || undefined, + timestamps: this.game["timestamps.start"] ? { + start: this.game["timestamps.start"] + } : undefined, + assets: this.game["assets.large"] ? { + large_image: this.game["assets.large"], + small_image: this.game["assets.small"] || undefined + } : undefined, + type: 0 + } + return game + }).call({ + game: data + }) + + ProfileProps = new connectedProfileStore({ + user: user, + close: () => {} + }).render().props + const profile = new UserProfileComponent(Object.assign({}, ProfileProps, { + activity: activity + })).render().props.children // bypass tracking + + profile.props.style = { + width: "auto" + } + + return
+ {profile} +
+ } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/publicservers/publicServers.js b/BetterDiscordApp/src/ui/publicservers/publicServers.js index f586a94..3bb9922 100644 --- a/BetterDiscordApp/src/ui/publicservers/publicServers.js +++ b/BetterDiscordApp/src/ui/publicservers/publicServers.js @@ -1,485 +1,485 @@ -import BDV2 from "../../modules/v2"; - -import SettingsTitle from "../settingsTitle"; -import TabBarSeparator from "../tabBarSeparator"; - -import ServerCard from "./serverCard"; -import { useForceUpdate } from "../../modules/hooks"; - -let SettingsView -export default 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"]); - - this.hooks = [] - } - - componentDidMount() { - this.checkConnection(); - } - - setInitialState() { - this.state = { - selectedCategory: -1, - title: "Loading...", - loading: true, - servers: [], - next: null, - connection: { - state: 0, - user: null - }, - section: this.categorySlugs[0], - theme: "dark" - }; - } - - close() { - this.props.close() - } - - search(query, clear) { - const self = this; - fetch(`${self.searchEndPoint}${query}${query ? "&schema=new" : "?schema=new"}`, { - method: "get" - }).then(async res => { - if(res.status !== 200)throw await res.text() - let data = await res.json() - - 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 - }); - }).catch((err) => { - console.error(err) - return self.setState({ - loading: false, - title: "Failed to load servers. Check console for details" - }); - }) - } - - async join(serverCard) { - if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code); - - await fetch(`${this.joinEndPoint}/${serverCard.props.server.identifier}`,{ - method: "GET", - credentials: "include", - mode: "cors", - headers: { - "Accept": "application/json", - "Content-Type": "application/json" - } - }); - - serverCard.setState({joined: true}); - } - - connect() { - const self = this; - const 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); - - const win = self.joinWindow = new (window.require("electron").remote.BrowserWindow)(options); - const url = "https://auth.discordservers.com/connect?scopes=guilds.join&previousUrl=https://auth.discordservers.com/info"; - win.webContents.on("did-navigate", (event, url) => { - if (url != "https://auth.discordservers.com/info") return; - win.close(); - self.checkConnection(); - }); - win.loadURL(url); - win.setMenuBarVisibility(false) - } - - get windowOptions() { - return { - width: 500, - height: 550, - backgroundColor: "#282b30", - show: true, - resizable: false, - maximizable: false, - minimizable: false, - alwaysOnTop: true, - frame: true, - center: false, - webPreferences: { - nodeIntegration: false - } - }; - } - - get bdServer() { - const server = { - name: "BetterDiscord", - online: "30000+", - members: "70000+", - 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 - }; - const server2 = { - name: "Lightcord", - online: "100+", - members: "300+", - categories: ["community", "programming", "support"], - description: "Official Lightcord server for support etc", - identifier: "705908350218666117", - iconUrl: "https://github.com/lightcord.png", - nativejoin: true, - invite_code: "7eFff2A", - pinned: true - }; - const guildList = this.SortedGuildStore.getFlattenedGuildIds(); - const defaultList = this.AvatarDefaults.DEFAULT_AVATARS; - return [ - BDV2.react.createElement(ServerCard, {server: server2, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}), - BDV2.react.createElement(ServerCard, {server: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}) - ] - } - - get searchEndPoint() { - return "https://search.discordservers.com"; - } - - get joinEndPoint() { - return "https://j.discordservers.com"; - } - - get connectEndPoint() { - return "https://join.discordservers.com/connect"; - } - - async checkConnection() { - const self = this; - try { - const response = await fetch(`https://auth.discordservers.com/info`,{ - method: "GET", - credentials: "include", - mode: "cors", - headers: { - "Accept": "application/json", - "Content-Type": "application/json" - } - }); - const text = await response.text() - if(!text){ - self.setState({ - title: "Not connected to discordservers.com!", - loading: true, - selectedCategory: -1, - connection: { - state: 1, - user: null - } - }); - return - } - const data = JSON.parse(text) - self.setState({ - selectedCategory: 0, - connection: { - state: 2, - user: data - } - }); - self.search("", true); - } - catch (error) { - console.error(error) - self.setState({ - title: "Not connected to discordservers.com!", - loading: true, - selectedCategory: -1, - connection: { - state: 1, - user: null - } - }); - } - } - - render() { - this.hooks.forEach((e) => e()) - SettingsView = SettingsView || BDV2.WebpackModules.findByDisplayName("SettingsView") - return BDV2.react.createElement("div", {id: "pubslayerroot"}, - BDV2.react.createElement("div", {id: "pubslayer"}, BDV2.react.createElement(SettingsView, { - onSetSection: (section) => { - this.changeCategory(this.categorySlugs.indexOf(section)) - }, - sections: this.sections, - onClose: this.close, - section: this.state.section - })) - ); - } - - get sections(){ - let sections = [] - sections.push({ - section: "HEADER", - label: "Public Servers" - }, { - section: "DIVIDER" - }, { - section: "CUSTOM", - element: this.searchInput.bind(null, () => this, this.searchKeyDown) - }, { - section: "DIVIDER" - }, { - section: "HEADER", - label: "Categories" - }, ...this.categoryButtons.map((value, index) => { - return { - section: this.categorySlugs[index], - label: value, - element: this.content.bind(null, () => this) - } - }), { - section: "DIVIDER" - }, { - section: "CUSTOM", - element: this.footer - }, { - section: "CUSTOM", - element: this.connection.bind(null, () => this) - }) - return sections - } - - searchInput(getThis, searchKeyDown) { - const [value, setValue] = BDV2.react.useState("") - 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: (serchinput) => (getThis().refs.searchinput = serchinput), - onKeyDown: searchKeyDown, - onChange: (e) => { - setValue(e.target.value) - }, - type: "text", - className: "input default", - placeholder: "Search...", - maxLength: "50", - value: value - }) - ) - ); - } - - searchKeyDown(e) { - const 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 categorySlugs(){ - return this.categoryButtons.map(e => e.toLowerCase().replace(/[^\w\d]+/g, "_")) - } - - get categoryButtons() { - return ["All", "FPS Games", "MMO Games", "Strategy Games", "MOBA Games", "RPG Games", "Tabletop Games", "Sandbox Games", "Simulation Games", "Music", "Community", "Language", "Programming", "Other"]; - } - - changeCategory(id) { - const self = this; - if (self.state.loading) return; - self.setState({ - loading: true, - selectedCategory: id, - title: "Loading...", - term: null, - section: self.categorySlugs[id] - }); - if (id === 0) { - self.search("", true); - return; - } - self.search(`?category=${self.categoryButtons[id]}`, true); - } - - content(getThis) { - const self = getThis(); - self.useState() - const guildList = self.SortedGuildStore.getFlattenedGuildIds(); - const defaultList = self.AvatarDefaults.DEFAULT_AVATARS; - if (self.state.connection.state === 1) return BDV2.react.createElement(self.notConnected.bind(null, getThis)); - let columnModule = BDModules.get(e => e.contentColumnDefault)[0] - return [BDV2.react.createElement( - "div", - {ref: (ref) => { - (self.refs.content = ref) - }, key: "pc", className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"}, - BDV2.react.createElement(SettingsTitle, {text: self.state.title}), - self.bdServer, - self.state.servers.map((server) => { - return BDV2.react.createElement(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(SettingsTitle, {text: self.state.title}) - )]; - } - - notConnected(getThis) { - const self = getThis(); - //return BDV2.react.createElement(SettingsTitle, { text: self.state.title }); - let columnModule = BDModules.get(e => e.contentColumnDefault)[0] - return [BDV2.react.createElement( - "div", - {key: "ncc", ref: (ref) => (self.refs.content = ref), className: columnModule.contentColumn+" "+columnModule.contentColumn+" 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 - )]; - } - - footer() { - return BDV2.react.createElement( - "div", - {className: "ui-tab-bar-header"}, - BDV2.react.createElement( - "a", - {href: "https://discordservers.com", target: "_blank"}, - "Discordservers.com" - ) - ); - } - - useState(){ - const forceUpdate = useForceUpdate() - BDV2.React.useEffect(() => { - const listener = () => { - forceUpdate() - } - this.hooks.push(listener) - return () => { - const index = this.hooks.findIndex(e => e===listener) - if(index < 0)return - this.hooks.splice(index, 1) - } - }, []) - } - - connection(getThis) { - const self = getThis(); - self.useState() - const {connection} = self.state; - if (connection.state !== 2) return BDV2.react.createElement("span", null); - - return BDV2.react.createElement( - "span", - null, - BDV2.react.createElement(TabBarSeparator, 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" - ) - ) - ) - ); - } +import BDV2 from "../../modules/v2"; + +import SettingsTitle from "../settingsTitle"; +import TabBarSeparator from "../tabBarSeparator"; + +import ServerCard from "./serverCard"; +import { useForceUpdate } from "../../modules/hooks"; + +let SettingsView +export default 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"]); + + this.hooks = [] + } + + componentDidMount() { + this.checkConnection(); + } + + setInitialState() { + this.state = { + selectedCategory: -1, + title: "Loading...", + loading: true, + servers: [], + next: null, + connection: { + state: 0, + user: null + }, + section: this.categorySlugs[0], + theme: "dark" + }; + } + + close() { + this.props.close() + } + + search(query, clear) { + const self = this; + fetch(`${self.searchEndPoint}${query}${query ? "&schema=new" : "?schema=new"}`, { + method: "get" + }).then(async res => { + if(res.status !== 200)throw await res.text() + let data = await res.json() + + 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 + }); + }).catch((err) => { + console.error(err) + return self.setState({ + loading: false, + title: "Failed to load servers. Check console for details" + }); + }) + } + + async join(serverCard) { + if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code); + + await fetch(`${this.joinEndPoint}/${serverCard.props.server.identifier}`,{ + method: "GET", + credentials: "include", + mode: "cors", + headers: { + "Accept": "application/json", + "Content-Type": "application/json" + } + }); + + serverCard.setState({joined: true}); + } + + connect() { + const self = this; + const 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); + + const win = self.joinWindow = new (window.require("electron").remote.BrowserWindow)(options); + const url = "https://auth.discordservers.com/connect?scopes=guilds.join&previousUrl=https://auth.discordservers.com/info"; + win.webContents.on("did-navigate", (event, url) => { + if (url != "https://auth.discordservers.com/info") return; + win.close(); + self.checkConnection(); + }); + win.loadURL(url); + win.setMenuBarVisibility(false) + } + + get windowOptions() { + return { + width: 500, + height: 550, + backgroundColor: "#282b30", + show: true, + resizable: false, + maximizable: false, + minimizable: false, + alwaysOnTop: true, + frame: true, + center: false, + webPreferences: { + nodeIntegration: false + } + }; + } + + get bdServer() { + const server = { + name: "BetterDiscord", + online: "30000+", + members: "70000+", + 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 + }; + const server2 = { + name: "Lightcord", + online: "100+", + members: "300+", + categories: ["community", "programming", "support"], + description: "Official Lightcord server for support etc", + identifier: "705908350218666117", + iconUrl: "https://github.com/lightcord.png", + nativejoin: true, + invite_code: "7eFff2A", + pinned: true + }; + const guildList = this.SortedGuildStore.getFlattenedGuildIds(); + const defaultList = this.AvatarDefaults.DEFAULT_AVATARS; + return [ + BDV2.react.createElement(ServerCard, {server: server2, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}), + BDV2.react.createElement(ServerCard, {server: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}) + ] + } + + get searchEndPoint() { + return "https://search.discordservers.com"; + } + + get joinEndPoint() { + return "https://j.discordservers.com"; + } + + get connectEndPoint() { + return "https://join.discordservers.com/connect"; + } + + async checkConnection() { + const self = this; + try { + const response = await fetch(`https://auth.discordservers.com/info`,{ + method: "GET", + credentials: "include", + mode: "cors", + headers: { + "Accept": "application/json", + "Content-Type": "application/json" + } + }); + const text = await response.text() + if(!text){ + self.setState({ + title: "Not connected to discordservers.com!", + loading: true, + selectedCategory: -1, + connection: { + state: 1, + user: null + } + }); + return + } + const data = JSON.parse(text) + self.setState({ + selectedCategory: 0, + connection: { + state: 2, + user: data + } + }); + self.search("", true); + } + catch (error) { + console.error(error) + self.setState({ + title: "Not connected to discordservers.com!", + loading: true, + selectedCategory: -1, + connection: { + state: 1, + user: null + } + }); + } + } + + render() { + this.hooks.forEach((e) => e()) + SettingsView = SettingsView || BDV2.WebpackModules.findByDisplayName("SettingsView") + return BDV2.react.createElement("div", {id: "pubslayerroot"}, + BDV2.react.createElement("div", {id: "pubslayer"}, BDV2.react.createElement(SettingsView, { + onSetSection: (section) => { + this.changeCategory(this.categorySlugs.indexOf(section)) + }, + sections: this.sections, + onClose: this.close, + section: this.state.section + })) + ); + } + + get sections(){ + let sections = [] + sections.push({ + section: "HEADER", + label: "Public Servers" + }, { + section: "DIVIDER" + }, { + section: "CUSTOM", + element: this.searchInput.bind(null, () => this, this.searchKeyDown) + }, { + section: "DIVIDER" + }, { + section: "HEADER", + label: "Categories" + }, ...this.categoryButtons.map((value, index) => { + return { + section: this.categorySlugs[index], + label: value, + element: this.content.bind(null, () => this) + } + }), { + section: "DIVIDER" + }, { + section: "CUSTOM", + element: this.footer + }, { + section: "CUSTOM", + element: this.connection.bind(null, () => this) + }) + return sections + } + + searchInput(getThis, searchKeyDown) { + const [value, setValue] = BDV2.react.useState("") + 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: (serchinput) => (getThis().refs.searchinput = serchinput), + onKeyDown: searchKeyDown, + onChange: (e) => { + setValue(e.target.value) + }, + type: "text", + className: "input default", + placeholder: "Search...", + maxLength: "50", + value: value + }) + ) + ); + } + + searchKeyDown(e) { + const 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 categorySlugs(){ + return this.categoryButtons.map(e => e.toLowerCase().replace(/[^\w\d]+/g, "_")) + } + + get categoryButtons() { + return ["All", "FPS Games", "MMO Games", "Strategy Games", "MOBA Games", "RPG Games", "Tabletop Games", "Sandbox Games", "Simulation Games", "Music", "Community", "Language", "Programming", "Other"]; + } + + changeCategory(id) { + const self = this; + if (self.state.loading) return; + self.setState({ + loading: true, + selectedCategory: id, + title: "Loading...", + term: null, + section: self.categorySlugs[id] + }); + if (id === 0) { + self.search("", true); + return; + } + self.search(`?category=${self.categoryButtons[id]}`, true); + } + + content(getThis) { + const self = getThis(); + self.useState() + const guildList = self.SortedGuildStore.getFlattenedGuildIds(); + const defaultList = self.AvatarDefaults.DEFAULT_AVATARS; + if (self.state.connection.state === 1) return BDV2.react.createElement(self.notConnected.bind(null, getThis)); + let columnModule = BDModules.get(e => e.contentColumnDefault)[0] + return [BDV2.react.createElement( + "div", + {ref: (ref) => { + (self.refs.content = ref) + }, key: "pc", className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"}, + BDV2.react.createElement(SettingsTitle, {text: self.state.title}), + self.bdServer, + self.state.servers.map((server) => { + return BDV2.react.createElement(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(SettingsTitle, {text: self.state.title}) + )]; + } + + notConnected(getThis) { + const self = getThis(); + //return BDV2.react.createElement(SettingsTitle, { text: self.state.title }); + let columnModule = BDModules.get(e => e.contentColumnDefault)[0] + return [BDV2.react.createElement( + "div", + {key: "ncc", ref: (ref) => (self.refs.content = ref), className: columnModule.contentColumn+" "+columnModule.contentColumn+" 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 + )]; + } + + footer() { + return BDV2.react.createElement( + "div", + {className: "ui-tab-bar-header"}, + BDV2.react.createElement( + "a", + {href: "https://discordservers.com", target: "_blank"}, + "Discordservers.com" + ) + ); + } + + useState(){ + const forceUpdate = useForceUpdate() + BDV2.React.useEffect(() => { + const listener = () => { + forceUpdate() + } + this.hooks.push(listener) + return () => { + const index = this.hooks.findIndex(e => e===listener) + if(index < 0)return + this.hooks.splice(index, 1) + } + }, []) + } + + connection(getThis) { + const self = getThis(); + self.useState() + const {connection} = self.state; + if (connection.state !== 2) return BDV2.react.createElement("span", null); + + return BDV2.react.createElement( + "span", + null, + BDV2.react.createElement(TabBarSeparator, 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" + ) + ) + ) + ); + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/scroller.js b/BetterDiscordApp/src/ui/scroller.js index cf24e15..9be33b3 100644 --- a/BetterDiscordApp/src/ui/scroller.js +++ b/BetterDiscordApp/src/ui/scroller.js @@ -1,47 +1,47 @@ -import BDV2 from "../modules/v2"; - -export default class V2C_Scroller extends BDV2.reactComponent { - - constructor(props) { - super(props); - } - - render() { - //scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y - const scrollerModule1 = BDModules.get(e => e.scrollerWrap)[0] - const scrollerModule2 = BDModules.get(e => e.sidebarRegionScroller)[0] - const scrollerTypes = BDModules.get(e => e.thin)[0] - let wrapperClass = `${scrollerModule1.scrollerWrap} ${scrollerModule1.scrollerThemed} ${scrollerModule1.themeGhostHairline}${this.props.fade ? " "+scrollerModule1.scrollerFade : ""}`; - let scrollerClass = scrollerModule1.scroller+" scroller"; /* fuck */ - if (this.props.sidebar) scrollerClass += ` ${scrollerModule2.sidebarRegionScroller} ${scrollerTypes.thin} ${scrollerTypes.scrollerBase} ${scrollerTypes.fade}` - if (this.props.contentColumn) { - scrollerClass += `${scrollerModule2.contentRegionScroller} ${scrollerTypes.auto} content-region-scroller scroller` - wrapperClass = `${scrollerModule2.contentTransitionWrap}`; - } - const {children} = this.props; - if (this.props.sidebar) { - return BDV2.react.createElement( - "div", - {key: "scroller", ref: "scroller", className: scrollerClass}, - children - ); - } - return BDV2.react.createElement( - "div", - {key: "scrollerwrap", className: wrapperClass}, - BDV2.react.createElement( - "div", - {key: "scroller", ref: "scroller", className: scrollerClass}, - children - ) - ); - } -} - -const originalRender = V2C_Scroller.prototype.render; -Object.defineProperty(V2C_Scroller.prototype, "render", { - enumerable: false, - configurable: false, - set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, - get: () => originalRender +import BDV2 from "../modules/v2"; + +export default class V2C_Scroller extends BDV2.reactComponent { + + constructor(props) { + super(props); + } + + render() { + //scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y + const scrollerModule1 = BDModules.get(e => e.scrollerWrap)[0] + const scrollerModule2 = BDModules.get(e => e.sidebarRegionScroller)[0] + const scrollerTypes = BDModules.get(e => e.thin)[0] + let wrapperClass = `${scrollerModule1.scrollerWrap} ${scrollerModule1.scrollerThemed} ${scrollerModule1.themeGhostHairline}${this.props.fade ? " "+scrollerModule1.scrollerFade : ""}`; + let scrollerClass = scrollerModule1.scroller+" scroller"; /* fuck */ + if (this.props.sidebar) scrollerClass += ` ${scrollerModule2.sidebarRegionScroller} ${scrollerTypes.thin} ${scrollerTypes.scrollerBase} ${scrollerTypes.fade}` + if (this.props.contentColumn) { + scrollerClass += `${scrollerModule2.contentRegionScroller} ${scrollerTypes.auto} content-region-scroller scroller` + wrapperClass = `${scrollerModule2.contentTransitionWrap}`; + } + const {children} = this.props; + if (this.props.sidebar) { + return BDV2.react.createElement( + "div", + {key: "scroller", ref: "scroller", className: scrollerClass}, + children + ); + } + return BDV2.react.createElement( + "div", + {key: "scrollerwrap", className: wrapperClass}, + BDV2.react.createElement( + "div", + {key: "scroller", ref: "scroller", className: scrollerClass}, + children + ) + ); + } +} + +const originalRender = V2C_Scroller.prototype.render; +Object.defineProperty(V2C_Scroller.prototype, "render", { + enumerable: false, + configurable: false, + set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, + get: () => originalRender }); \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/settingsGroup.jsx b/BetterDiscordApp/src/ui/settingsGroup.jsx index aec1b4a..2be20a7 100644 --- a/BetterDiscordApp/src/ui/settingsGroup.jsx +++ b/BetterDiscordApp/src/ui/settingsGroup.jsx @@ -1,91 +1,91 @@ -import {settingsCookie} from "../0globals"; -import BDV2 from "../modules/v2"; - -import SettingsTitle from "./settingsTitle"; -import Switch from "./switch"; -import MarginTop from "./margintop"; - -let formModule -let switchItem -let betaClassNames -export default class V2C_SettingsGroup extends BDV2.reactComponent { - - constructor(props) { - super(props); - - this.state = { - error: false - } - } - - renderOldSettings() { - const {title, settings, button} = this.props; - const buttonComponent = button ? BDV2.react.createElement("button", {key: "title-button", className: "bd-pfbtn", onClick: button.onClick}, button.title) : null; - return [BDV2.react.createElement(SettingsTitle, {text: title}), - buttonComponent, - settings.map(setting => { - return BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: settingsCookie[setting.id], onChange: (id, checked) => { - this.props.onChange(id, checked); - }}); - })]; - } - - componentDidCatch(err, errInfo){ - console.log(err, errInfo) - this.setState({ - error: true - }) - } - - render(){ - if(this.state.error){ - try{ - return this.renderOldSettings() - }catch(e){ - console.error(e) - return null - } - }else{ - try{ - if(!formModule)formModule = BDV2.WebpackModules.find(e => e.FormSection) - if(!switchItem)switchItem = BDV2.WebpackModules.find(e => e.default && e.default.displayName === "SwitchItem") - if(!betaClassNames)betaClassNames = BDV2.WebpackModules.find(e => e.beta && (!e.container && !e.userSettingsVoice)) - - let children = [] - if(this.props.description){ - children.push( - {this.props.description} - , ) - } - children.push(...this.props.settings.map(setting => { - let info = [ - setting.text - ] - if(setting.experimental){ - info.push((EXPERIMENTAL)) - } - return { - this.props.onChange(setting.id, ev.target.checked); - this.forceUpdate() - }} key={setting.id} value={settingsCookie[setting.id]} className={__SECRET_EMOTION__.css({ - marginBottom: "20px" - })} disabled={false} hideBorder={false} - size={switchItem.default.Sizes.DEFAULT} theme={switchItem.default.Themes.DEFAULT} note={setting.info}> - {info} - - })) - return - {children} - - }catch(e){ - console.error(e) - setImmediate(()=>{ - this.setState({ - error: true - }) - }) - return null - } - } - } +import {settingsCookie} from "../0globals"; +import BDV2 from "../modules/v2"; + +import SettingsTitle from "./settingsTitle"; +import Switch from "./switch"; +import MarginTop from "./margintop"; + +let formModule +let switchItem +let betaClassNames +export default class V2C_SettingsGroup extends BDV2.reactComponent { + + constructor(props) { + super(props); + + this.state = { + error: false + } + } + + renderOldSettings() { + const {title, settings, button} = this.props; + const buttonComponent = button ? BDV2.react.createElement("button", {key: "title-button", className: "bd-pfbtn", onClick: button.onClick}, button.title) : null; + return [BDV2.react.createElement(SettingsTitle, {text: title}), + buttonComponent, + settings.map(setting => { + return BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: settingsCookie[setting.id], onChange: (id, checked) => { + this.props.onChange(id, checked); + }}); + })]; + } + + componentDidCatch(err, errInfo){ + console.log(err, errInfo) + this.setState({ + error: true + }) + } + + render(){ + if(this.state.error){ + try{ + return this.renderOldSettings() + }catch(e){ + console.error(e) + return null + } + }else{ + try{ + if(!formModule)formModule = BDV2.WebpackModules.find(e => e.FormSection) + if(!switchItem)switchItem = BDV2.WebpackModules.find(e => e.default && e.default.displayName === "SwitchItem") + if(!betaClassNames)betaClassNames = BDV2.WebpackModules.find(e => e.beta && (!e.container && !e.userSettingsVoice)) + + let children = [] + if(this.props.description){ + children.push( + {this.props.description} + , ) + } + children.push(...this.props.settings.map(setting => { + let info = [ + setting.text + ] + if(setting.experimental){ + info.push((EXPERIMENTAL)) + } + return { + this.props.onChange(setting.id, ev.target.checked); + this.forceUpdate() + }} key={setting.id} value={settingsCookie[setting.id]} className={__SECRET_EMOTION__.css({ + marginBottom: "20px" + })} disabled={false} hideBorder={false} + size={switchItem.default.Sizes.DEFAULT} theme={switchItem.default.Themes.DEFAULT} note={setting.info}> + {info} + + })) + return + {children} + + }catch(e){ + console.error(e) + setImmediate(()=>{ + this.setState({ + error: true + }) + }) + return null + } + } + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/switch.js b/BetterDiscordApp/src/ui/switch.js index 19e546a..e762845 100644 --- a/BetterDiscordApp/src/ui/switch.js +++ b/BetterDiscordApp/src/ui/switch.js @@ -1,44 +1,44 @@ -import BDV2 from "../modules/v2"; -import Switch from "./components/switch"; - -let classnames = [] -function getClassName(name){ - let className = classnames.find(e => e.startsWith(name+"-")) - if(className)return className - className = BDModules.get(e => e[name])[0][name] - classnames.push(className) - return className -} - -export default class SwitchItem extends BDV2.reactComponent { - - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - } - - onChange() { - this.props.checked = !this.props.checked; - this.props.onChange(this.props.id, this.props.checked); - } - - render() { - let {text, info} = this.props.data; - if(!classnames.find(e => e.startsWith("beta-"))){ - classnames.push(BDModules.get(e => e.beta && (!e.container && !e.userSettingsVoice))[0].beta) - } - if(this.props.data.experimental){ - info = [ - info, - React.createElement("sup", {className: getClassName("beta")}, "(EXPERIMENTAL)") - ] - } - return BDV2.react.createElement("div", {className: "ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item"}, - BDV2.react.createElement("div", {className: "ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap"}, - BDV2.react.createElement("h3", {key: "title", className: "ui-form-title h3 margin-reset margin-reset ui-flex-child"}, text), - BDV2.react.createElement(Switch, {key: "switch", onChange: this.onChange, checked: this.props.checked}) - ), - BDV2.react.createElement("div", {className: "ui-form-text style-description margin-top-4", style: {flex: "1 1 auto"}}, info) - ); - } +import BDV2 from "../modules/v2"; +import Switch from "./components/switch"; + +let classnames = [] +function getClassName(name){ + let className = classnames.find(e => e.startsWith(name+"-")) + if(className)return className + className = BDModules.get(e => e[name])[0][name] + classnames.push(className) + return className +} + +export default class SwitchItem extends BDV2.reactComponent { + + constructor(props) { + super(props); + this.onChange = this.onChange.bind(this); + } + + onChange() { + this.props.checked = !this.props.checked; + this.props.onChange(this.props.id, this.props.checked); + } + + render() { + let {text, info} = this.props.data; + if(!classnames.find(e => e.startsWith("beta-"))){ + classnames.push(BDModules.get(e => e.beta && (!e.container && !e.userSettingsVoice))[0].beta) + } + if(this.props.data.experimental){ + info = [ + info, + React.createElement("sup", {className: getClassName("beta")}, "(EXPERIMENTAL)") + ] + } + return BDV2.react.createElement("div", {className: "ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item"}, + BDV2.react.createElement("div", {className: "ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap"}, + BDV2.react.createElement("h3", {key: "title", className: "ui-form-title h3 margin-reset margin-reset ui-flex-child"}, text), + BDV2.react.createElement(Switch, {key: "switch", onChange: this.onChange, checked: this.props.checked}) + ), + BDV2.react.createElement("div", {className: "ui-form-text style-description margin-top-4", style: {flex: "1 1 auto"}}, info) + ); + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/tooltip.js b/BetterDiscordApp/src/ui/tooltip.js index f7f261c..27ddfa3 100644 --- a/BetterDiscordApp/src/ui/tooltip.js +++ b/BetterDiscordApp/src/ui/tooltip.js @@ -1,191 +1,191 @@ -/** - * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events. - * Will also remove themselves if the node to watch is removed from DOM through - * a MutationObserver. - * - * Note this is not using Discord's internals but normal DOM manipulation and emulates - * Discord's own tooltips as closely as possible. - * - * @module EmulatedTooltip - * @version 0.0.1 - */ - -import Utils from "../modules/utils"; -import WebpackModules from "../modules/webpackModules"; -let TooltipClasses -function getTooltipClasses(){ - if(TooltipClasses)return TooltipClasses - return TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack"); -} -let TooltipLayers -function getTooltipLayers(){ - if(TooltipLayers)return TooltipLayers - return TooltipLayers = WebpackModules.findByProps("layer", "layerContainer"); -} - -const getClass = function(sideOrColor) { - const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1); - const tooltipClass = getTooltipClasses()[`tooltip${upperCase}`]; - if (tooltipClass) return tooltipClass; - return null; -}; - -const classExists = function(sideOrColor) { - return getClass(sideOrColor) ? true : false; -}; - -const toPx = function(value) { - return `${value}px`; -}; - -export default class EmulatedTooltip { - /** - * - * @constructor - * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on - * @param {string} tip - string to show in the tooltip - * @param {object} options - additional options for the tooltip - * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow) - * @param {string} [options.side=top] - can be any of top, right, bottom, left - * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen - * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover - * @param {boolean} [options.attachEvents=true] - whether the tooltip should listen to mouseenter and mouseleave events. - */ - constructor(node, text, options = {}) { - const {style = "black", side = "top", preventFlip = false, disabled = false, attachEvents = true} = options; - this.node = node instanceof jQuery ? node[0] : node; - this.label = text; - this.style = style.toLowerCase(); - this.side = side.toLowerCase(); - this.preventFlip = preventFlip; - this.disabled = disabled; - - if (!classExists(this.side)) return Utils.err("EmulatedTooltip", `Side ${this.side} does not exist.`); - if (!classExists(this.style)) return Utils.err("EmulatedTooltip", `Style ${this.style} does not exist.`); - - this.element = document.createElement("div"); - this.element.className = getTooltipLayers().layer + " " + getTooltipLayers().disabledPointerEvents; - - this.tooltipElement = document.createElement("div"); - this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`; - - this.labelElement = document.createElement("div"); - this.labelElement.className = getTooltipClasses().tooltipContent - - const pointerElement = document.createElement("div"); - pointerElement.className = getTooltipClasses().tooltipPointer; - - this.tooltipElement.append(pointerElement); - this.tooltipElement.append(this.labelElement); - this.element.append(this.tooltipElement); - - if(attachEvents){ - this.node.addEventListener("mouseenter", () => { - if (this.disabled) return; - this.show(); - - 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.hide(); - observer.disconnect(); - } - }); - }); - - observer.observe(document.body, {subtree: true, childList: true}); - }); - - this.node.addEventListener("mouseleave", () => { - this.hide(); - }); - } - } - - /** Container where the tooltip will be appended. */ - get container() { - return document.querySelector("."+Utils.removeDa(BDModules.get(e => e.popouts)[0].popouts)+" ~ ."+Utils.removeDa(BDModules.get(e => e.layerContainer)[0].layerContainer)); - } - /** Boolean representing if the tooltip will fit on screen above the element */ - get canShowAbove() { return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0; } - /** Boolean representing if the tooltip will fit on screen below the element */ - get canShowBelow() { return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= Utils.screenHeight; } - /** Boolean representing if the tooltip will fit on screen to the left of the element */ - get canShowLeft() { return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0; } - /** Boolean representing if the tooltip will fit on screen to the right of the element */ - get canShowRight() { return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= Utils.screenWidth; } - - /** Hides the tooltip. Automatically called on mouseleave. */ - hide() { - this.element.remove(); - this.tooltipElement.className = this._className; - } - - /** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */ - show() { - this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`; - this.labelElement.textContent = this.label; - this.container.append(this.element); - - if (this.side == "top") { - if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove(); - else this.showBelow(); - } - - if (this.side == "bottom") { - if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow(); - else this.showAbove(); - } - - if (this.side == "left") { - if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft(); - else this.showRight(); - } - - if (this.side == "right") { - if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight(); - else this.showLeft(); - } - } - - /** Force showing the tooltip above the node. */ - showAbove() { - this.tooltipElement.classList.add(getClass("top")); - this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10)); - this.centerHorizontally(); - } - - /** Force showing the tooltip below the node. */ - showBelow() { - this.tooltipElement.classList.add(getClass("bottom")); - this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10)); - this.centerHorizontally(); - } - - /** Force showing the tooltip to the left of the node. */ - showLeft() { - this.tooltipElement.classList.add(getClass("left")); - this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10)); - this.centerVertically(); - } - - /** Force showing the tooltip to the right of the node. */ - showRight() { - this.tooltipElement.classList.add(getClass("right")); - this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10)); - this.centerVertically(); - } - - centerHorizontally() { - const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2); - this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2))); - } - - centerVertically() { - const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2); - this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2))); - } +/** + * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events. + * Will also remove themselves if the node to watch is removed from DOM through + * a MutationObserver. + * + * Note this is not using Discord's internals but normal DOM manipulation and emulates + * Discord's own tooltips as closely as possible. + * + * @module EmulatedTooltip + * @version 0.0.1 + */ + +import Utils from "../modules/utils"; +import WebpackModules from "../modules/webpackModules"; +let TooltipClasses +function getTooltipClasses(){ + if(TooltipClasses)return TooltipClasses + return TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack"); +} +let TooltipLayers +function getTooltipLayers(){ + if(TooltipLayers)return TooltipLayers + return TooltipLayers = WebpackModules.findByProps("layer", "layerContainer"); +} + +const getClass = function(sideOrColor) { + const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1); + const tooltipClass = getTooltipClasses()[`tooltip${upperCase}`]; + if (tooltipClass) return tooltipClass; + return null; +}; + +const classExists = function(sideOrColor) { + return getClass(sideOrColor) ? true : false; +}; + +const toPx = function(value) { + return `${value}px`; +}; + +export default class EmulatedTooltip { + /** + * + * @constructor + * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on + * @param {string} tip - string to show in the tooltip + * @param {object} options - additional options for the tooltip + * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow) + * @param {string} [options.side=top] - can be any of top, right, bottom, left + * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen + * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover + * @param {boolean} [options.attachEvents=true] - whether the tooltip should listen to mouseenter and mouseleave events. + */ + constructor(node, text, options = {}) { + const {style = "black", side = "top", preventFlip = false, disabled = false, attachEvents = true} = options; + this.node = node instanceof jQuery ? node[0] : node; + this.label = text; + this.style = style.toLowerCase(); + this.side = side.toLowerCase(); + this.preventFlip = preventFlip; + this.disabled = disabled; + + if (!classExists(this.side)) return Utils.err("EmulatedTooltip", `Side ${this.side} does not exist.`); + if (!classExists(this.style)) return Utils.err("EmulatedTooltip", `Style ${this.style} does not exist.`); + + this.element = document.createElement("div"); + this.element.className = getTooltipLayers().layer + " " + getTooltipLayers().disabledPointerEvents; + + this.tooltipElement = document.createElement("div"); + this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`; + + this.labelElement = document.createElement("div"); + this.labelElement.className = getTooltipClasses().tooltipContent + + const pointerElement = document.createElement("div"); + pointerElement.className = getTooltipClasses().tooltipPointer; + + this.tooltipElement.append(pointerElement); + this.tooltipElement.append(this.labelElement); + this.element.append(this.tooltipElement); + + if(attachEvents){ + this.node.addEventListener("mouseenter", () => { + if (this.disabled) return; + this.show(); + + 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.hide(); + observer.disconnect(); + } + }); + }); + + observer.observe(document.body, {subtree: true, childList: true}); + }); + + this.node.addEventListener("mouseleave", () => { + this.hide(); + }); + } + } + + /** Container where the tooltip will be appended. */ + get container() { + return document.querySelector("."+Utils.removeDa(BDModules.get(e => e.popouts)[0].popouts)+" ~ ."+Utils.removeDa(BDModules.get(e => e.layerContainer)[0].layerContainer)); + } + /** Boolean representing if the tooltip will fit on screen above the element */ + get canShowAbove() { return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0; } + /** Boolean representing if the tooltip will fit on screen below the element */ + get canShowBelow() { return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= Utils.screenHeight; } + /** Boolean representing if the tooltip will fit on screen to the left of the element */ + get canShowLeft() { return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0; } + /** Boolean representing if the tooltip will fit on screen to the right of the element */ + get canShowRight() { return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= Utils.screenWidth; } + + /** Hides the tooltip. Automatically called on mouseleave. */ + hide() { + this.element.remove(); + this.tooltipElement.className = this._className; + } + + /** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */ + show() { + this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`; + this.labelElement.textContent = this.label; + this.container.append(this.element); + + if (this.side == "top") { + if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove(); + else this.showBelow(); + } + + if (this.side == "bottom") { + if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow(); + else this.showAbove(); + } + + if (this.side == "left") { + if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft(); + else this.showRight(); + } + + if (this.side == "right") { + if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight(); + else this.showLeft(); + } + } + + /** Force showing the tooltip above the node. */ + showAbove() { + this.tooltipElement.classList.add(getClass("top")); + this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10)); + this.centerHorizontally(); + } + + /** Force showing the tooltip below the node. */ + showBelow() { + this.tooltipElement.classList.add(getClass("bottom")); + this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10)); + this.centerHorizontally(); + } + + /** Force showing the tooltip to the left of the node. */ + showLeft() { + this.tooltipElement.classList.add(getClass("left")); + this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10)); + this.centerVertically(); + } + + /** Force showing the tooltip to the right of the node. */ + showRight() { + this.tooltipElement.classList.add(getClass("right")); + this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10)); + this.centerVertically(); + } + + centerHorizontally() { + const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2); + this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2))); + } + + centerVertically() { + const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2); + this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2))); + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/tooltipWrap.js b/BetterDiscordApp/src/ui/tooltipWrap.js index 938c127..0041fc9 100644 --- a/BetterDiscordApp/src/ui/tooltipWrap.js +++ b/BetterDiscordApp/src/ui/tooltipWrap.js @@ -1,23 +1,23 @@ -import BDV2 from "../modules/v2"; -import Tooltip from "./tooltip"; - -export default class extends React.Component { - constructor(props) { - super(props); - } - - async componentDidMount() { - const {style = "black", side = "top", text = ""} = this.props; - this.node = BDV2.reactDom.findDOMNode(this); - this.tooltip = new Tooltip(this.node, text, {style, side}); - } - - componentWillUnmount() { - this.tooltip.hide(); - delete this.tooltip; - } - - render() { - return this.props.children; - } +import BDV2 from "../modules/v2"; +import Tooltip from "./tooltip"; + +export default class extends React.Component { + constructor(props) { + super(props); + } + + async componentDidMount() { + const {style = "black", side = "top", text = ""} = this.props; + this.node = BDV2.reactDom.findDOMNode(this); + this.tooltip = new Tooltip(this.node, text, {style, side}); + } + + componentWillUnmount() { + this.tooltip.hide(); + delete this.tooltip; + } + + render() { + return this.props.children; + } } \ No newline at end of file diff --git a/BetterDiscordApp/webpack.config.js b/BetterDiscordApp/webpack.config.js index 713763f..0abeaf0 100644 --- a/BetterDiscordApp/webpack.config.js +++ b/BetterDiscordApp/webpack.config.js @@ -1,76 +1,76 @@ -const path = require("path"); -const CircularDependencyPlugin = require("circular-dependency-plugin"); -const TerserPlugin = require("terser-webpack-plugin"); -const child_process = require("child_process") - -module.exports = { - mode: "development", - target: "node", - devtool: "inline-source-map", - entry: "./src/index.js", - output: { - filename: "index.js", - path: path.resolve(__dirname, "dist"), - library: "BetterDiscord", - libraryTarget: "commonjs2" - }, - externals: { - electron: `electron`, - fs: `fs`, - path: `path`, - events: `events`, - rimraf: `rimraf`, - yauzl: `yauzl`, - mkdirp: `mkdirp`, - request: `request`, - "node-fetch": "node-fetch" - }, - resolve: { - extensions: [".js", ".jsx"], - modules: [ - path.resolve("src", "builtins"), - path.resolve("src", "modules") - ], - alias: { - react$: path.resolve(__dirname, "src", "react.js"), - "react-dom$": path.resolve(__dirname, "src", "react-dom.js") - } - }, - module: { - rules: [ - { - test: /\.jsx?$/, - loader: "babel-loader", - exclude: /node_modules/, - query: { - presets: [["@babel/env", { - targets: { - node: "12.8.1", - chrome: "78" - } - }], "@babel/react"] - } - } - ] - }, - plugins: [ - new CircularDependencyPlugin({ - // exclude detection of files based on a RegExp - exclude: /a\.js|node_modules/, - // add errors to webpack instead of warnings - // failOnError: true, - // set the current working directory for displaying module paths - cwd: process.cwd(), - }) - ], - optimization: { - minimizer: [ - new TerserPlugin({ - terserOptions: { - compress: {drop_debugger:false} - }, - sourceMap: true - }) - ] - } +const path = require("path"); +const CircularDependencyPlugin = require("circular-dependency-plugin"); +const TerserPlugin = require("terser-webpack-plugin"); +const child_process = require("child_process") + +module.exports = { + mode: "development", + target: "node", + devtool: "inline-source-map", + entry: "./src/index.js", + output: { + filename: "index.js", + path: path.resolve(__dirname, "dist"), + library: "BetterDiscord", + libraryTarget: "commonjs2" + }, + externals: { + electron: `electron`, + fs: `fs`, + path: `path`, + events: `events`, + rimraf: `rimraf`, + yauzl: `yauzl`, + mkdirp: `mkdirp`, + request: `request`, + "node-fetch": "node-fetch" + }, + resolve: { + extensions: [".js", ".jsx"], + modules: [ + path.resolve("src", "builtins"), + path.resolve("src", "modules") + ], + alias: { + react$: path.resolve(__dirname, "src", "react.js"), + "react-dom$": path.resolve(__dirname, "src", "react-dom.js") + } + }, + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "babel-loader", + exclude: /node_modules/, + query: { + presets: [["@babel/env", { + targets: { + node: "12.8.1", + chrome: "78" + } + }], "@babel/react"] + } + } + ] + }, + plugins: [ + new CircularDependencyPlugin({ + // exclude detection of files based on a RegExp + exclude: /a\.js|node_modules/, + // add errors to webpack instead of warnings + // failOnError: true, + // set the current working directory for displaying module paths + cwd: process.cwd(), + }) + ], + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: {drop_debugger:false} + }, + sourceMap: true + }) + ] + } }; \ No newline at end of file diff --git a/DiscordJS/src/structures/Guild.ts b/DiscordJS/src/structures/Guild.ts index 55ffdaf..4c7a481 100644 --- a/DiscordJS/src/structures/Guild.ts +++ b/DiscordJS/src/structures/Guild.ts @@ -1,334 +1,334 @@ -import { Snowflake, Channel } from ".." -import { DiscordGuild, channelsModule, guildModule, UserSettingsModule, ConstantsModule, CdnModule, AckModule } from "../util/DiscordToModules" -import BaseStructure from "./BaseStructure" -import { createChannel, createGuildMember, createRole, UserResolvable, resolveUserID, ChannelData, ChannelCreationOverwrites } from "../util/util" -import Collection from "@discordjs/collection" -import SnowflakeUtil from "../util/Snowflake" -import GuildMember from "./GuildMember" -import { MessageNotificationType, ChannelTypes } from "../util/Constants" -import Role from "./Role" -import DiscordJSError from "../util/DiscordJSError" -import PermissionOverwrites from "./PermissionOverwrites" - -export default class Guild extends BaseStructure { - DiscordGuild:DiscordGuild - constructor(data:DiscordGuild){ - super() - this.DiscordGuild = data - } - - get id(): Snowflake{ - return this.DiscordGuild.id - } - - get afkChannel():Channel{ - if(!this.afkChannelID)return null - return createChannel(channelsModule.getChannel(this.afkChannelID)) - } - - get afkChannelID():Snowflake{ - return this.DiscordGuild.afkChannelId - } - - get afkTimeout():number{ - return this.DiscordGuild.afkTimeout - } - - get applicationID():Snowflake{ - return this.DiscordGuild.application_id - } - - get available():boolean{ - return true - } - - get channels():Collection{{ - return this.client.channels.filter(channel => channel.guild_id === this.id) - }} - - get createdAt():Date{ - return SnowflakeUtil.deconstruct(this.id).date - } - - get createdTimestamp():number{ - return this.createdAt.getTime() - } - - get defaultChannel(){ - return this.channels.get(this.id) - } - - get defaultMessageNotifications():number{ - return this.DiscordGuild.defaultMessageNotifications - } - - deleted:boolean = false - - get embedEnabled(){ - return true - } - - get emojis(){ - return this.client.emojis.filter(e => e.guild_id === this.id) - } - - get explicitContentFilter(){ - return this.DiscordGuild.explicitContentFilter - } - - get features(){ - return Array.from(this.DiscordGuild.features) - } - - get icon(){ - return this.DiscordGuild.icon - } - - get iconURL(){ - return this.DiscordGuild.getIconURL().replace(".webp", ".jpg") - } - - get joinedAt(){ - return new Date(this.DiscordGuild.joinedAt) - } - - get joinedTimestamp(){ - return this.DiscordGuild.joinedAt.getTime() - } - - get large(){ - return false - } - - get me(){ - return this.members.find(member => member.id === this.client.user.id) - } - - get memberCount(){ - return guildModule.getMemberCount(this.id) - } - - get members(){ - return new Collection(guildModule.getMembers(this.id).map(member => [member.userId, createGuildMember(member)])) - } - - get messageNotifications():MessageNotificationType{ - return MessageNotificationType[guildModule.getMessageNotifications(this.id)] as unknown as MessageNotificationType - } - - get mfaLevel(){ - return this.DiscordGuild.mfaLevel - } - - get mobilePush():boolean{ - return guildModule.getNotificationsState().userGuildSettings[this.id].mobile_push - } - - get muted(){ - return guildModule.getNotificationsState().userGuildSettings[this.id].muted - } - - get name(){ - return this.DiscordGuild.name - } - - get nameAcronym(){ - return this.DiscordGuild.acronym - } - - get owner(){ - return this.members.get(this.ownerID) - } - - get ownerID(){ - return this.DiscordGuild.ownerId - } - - get position(){ - let guildPositions = UserSettingsModule.getAllSettings().guildPositions - if(!guildPositions)return 0 - return guildPositions.indexOf(this.id) - } - - get presences(){ // TODO: Add activities. - return new Collection() - } - - get region(){ - return this.DiscordGuild.region - } - - get roles(){ - return new Collection(Object.values(this.DiscordGuild.roles).map(role => [role.id, createRole(role)])) - } - - get splash(){ - return this.DiscordGuild.splash - } - - get splashURL(){ - return CdnModule.getGuildSplashURL({ - id: this.id, - splash: this.splash, - size: ConstantsModule.SPLASH_SIZE - }) - } - - get suppressEveryone(){ - return guildModule.getNotificationsState().userGuildSettings[this.id].suppress_everyone - } - - get systemChannel(){ - return this.client.channels.get(this.systemChannelID) - } - - get systemChannelID(){ - return this.DiscordGuild.systemChannelId - } - - get verificationLevel(){ - return this.DiscordGuild.verificationLevel - } - - get verified(){ - return this.features.includes("VERIFIED") - } - - get voiceConnection(){// TODO: Implement if possible VoiceConnection. Maybe not fully like discord.js, but at least can see if you're connected. - return null - } - - get banner(){ - return this.DiscordGuild.banner - } - - get bannerURL(){ - return CdnModule.getGuildBannerURL({ - id: this.id, - banner: this.banner - }) - } - - get description(){ - return this.DiscordGuild.description - } - - get embedChannel(){ // TODO: Implement embedChannel - return null - } - - get embedChannelID(){ // TODO: Implement embedChannelID - return null - } - - get maximumMembers(){ // TODO: Correctly Implement maximumMembers if possible - return 250000 - } - - get maximumPresences(){ // TODO: Correctly Implement maximumPresences if possible - return 5000 - } - - get widgetEnabled(){ // TODO: Correctly Implement widgetEnabled if possible - return false - } - - get widgetChannelID(){ // TODO: Correctly Implement widgetChannelID if possible - return null - } - - get widgetChannel(){ // TODO: Correctly Implement widgetChannel if possible - return null - } - - get vanityURLCode(){ - return this.DiscordGuild.vanityURLCode - } - /** FUNCTIONS */ - async acknowledge(){ - AckModule.bulkAck(this.channels.filter(e => e.type === "text").map(e => { - return { - channelId: e.id, - messageId: channelsModule.lastMessageId(e.id) - } - })) - } - - addMember(...args:any):Promise{ - return Promise.reject(new DiscordJSError("This method is not available on Lightcord.")) - } - - allowDMs(allow:boolean){ - let restricted = UserSettingsModule.getAllSettings().restrictedGuilds - if(allow){ - if(!restricted.includes(this.id))return Promise.resolve(this) - restricted = restricted.filter(e => e !== this.id) - }else{ - if(restricted.includes(this.id))return Promise.resolve(this) - restricted.push(this.id) - } - return UserSettingsModule.updateRemoteSettings({ - restrictedGuilds: restricted - }).then(() => this) - } - - async ban(user:UserResolvable, { - days = 0, - reason = null - }: { - days?: number, - reason?: string - } = {}):Promise{ // always returning a snowflake - let id = resolveUserID(user) - if(!id)return Promise.reject(new DiscordJSError("Given user could not be resolved to an user ID.")) - - let result = await guildModule.banUser(this.id, id, days, reason).catch(err => err) - if(result instanceof Error || result.status !== 204){ - let message = result.body - if(Array.isArray(message)){ - message = message[0] - }else{ - if(message.user_id){ - message = "User: "+ message.user_id[0] - }else if(message.delete_message_days){ - message = "Days: "+ message.delete_message_days[0] - }else if(message.reason){ - message = "Reason: "+ message.reason[0] - }else{ - message = result.text - } - } - throw new DiscordJSError(message) - } - return id - } - - createChannel(name:string, typeOrOptions:ChannelTypes|ChannelData = ChannelTypes.TEXT, permissionOverwrites?: ChannelCreationOverwrites[] | Collection, reason?: string){ - const [opts, type] = typeof typeOrOptions === "string" ? [ - {}, - typeOrOptions - ] : [ - typeOrOptions, - typeOrOptions.type - ] - const options = { - name: name || "Unknown Channel", - type: type || "text" - } - //TODO: CreateGuildChannels and handle permissions - //channelsModule.createGuildChannel(this.id, options.type, optinos.name) - } - - fetch():Promise{ // Guild is synced by Discord. Only refreshing from cache. - let guild = guildModule.getGuild(this.id) - if(!guild){ - this.deleted = true - return Promise.resolve(this) - } - this.DiscordGuild = guild - return Promise.resolve(this) - } - - - +import { Snowflake, Channel } from ".." +import { DiscordGuild, channelsModule, guildModule, UserSettingsModule, ConstantsModule, CdnModule, AckModule } from "../util/DiscordToModules" +import BaseStructure from "./BaseStructure" +import { createChannel, createGuildMember, createRole, UserResolvable, resolveUserID, ChannelData, ChannelCreationOverwrites } from "../util/util" +import Collection from "@discordjs/collection" +import SnowflakeUtil from "../util/Snowflake" +import GuildMember from "./GuildMember" +import { MessageNotificationType, ChannelTypes } from "../util/Constants" +import Role from "./Role" +import DiscordJSError from "../util/DiscordJSError" +import PermissionOverwrites from "./PermissionOverwrites" + +export default class Guild extends BaseStructure { + DiscordGuild:DiscordGuild + constructor(data:DiscordGuild){ + super() + this.DiscordGuild = data + } + + get id(): Snowflake{ + return this.DiscordGuild.id + } + + get afkChannel():Channel{ + if(!this.afkChannelID)return null + return createChannel(channelsModule.getChannel(this.afkChannelID)) + } + + get afkChannelID():Snowflake{ + return this.DiscordGuild.afkChannelId + } + + get afkTimeout():number{ + return this.DiscordGuild.afkTimeout + } + + get applicationID():Snowflake{ + return this.DiscordGuild.application_id + } + + get available():boolean{ + return true + } + + get channels():Collection{{ + return this.client.channels.filter(channel => channel.guild_id === this.id) + }} + + get createdAt():Date{ + return SnowflakeUtil.deconstruct(this.id).date + } + + get createdTimestamp():number{ + return this.createdAt.getTime() + } + + get defaultChannel(){ + return this.channels.get(this.id) + } + + get defaultMessageNotifications():number{ + return this.DiscordGuild.defaultMessageNotifications + } + + deleted:boolean = false + + get embedEnabled(){ + return true + } + + get emojis(){ + return this.client.emojis.filter(e => e.guild_id === this.id) + } + + get explicitContentFilter(){ + return this.DiscordGuild.explicitContentFilter + } + + get features(){ + return Array.from(this.DiscordGuild.features) + } + + get icon(){ + return this.DiscordGuild.icon + } + + get iconURL(){ + return this.DiscordGuild.getIconURL().replace(".webp", ".jpg") + } + + get joinedAt(){ + return new Date(this.DiscordGuild.joinedAt) + } + + get joinedTimestamp(){ + return this.DiscordGuild.joinedAt.getTime() + } + + get large(){ + return false + } + + get me(){ + return this.members.find(member => member.id === this.client.user.id) + } + + get memberCount(){ + return guildModule.getMemberCount(this.id) + } + + get members(){ + return new Collection(guildModule.getMembers(this.id).map(member => [member.userId, createGuildMember(member)])) + } + + get messageNotifications():MessageNotificationType{ + return MessageNotificationType[guildModule.getMessageNotifications(this.id)] as unknown as MessageNotificationType + } + + get mfaLevel(){ + return this.DiscordGuild.mfaLevel + } + + get mobilePush():boolean{ + return guildModule.getNotificationsState().userGuildSettings[this.id].mobile_push + } + + get muted(){ + return guildModule.getNotificationsState().userGuildSettings[this.id].muted + } + + get name(){ + return this.DiscordGuild.name + } + + get nameAcronym(){ + return this.DiscordGuild.acronym + } + + get owner(){ + return this.members.get(this.ownerID) + } + + get ownerID(){ + return this.DiscordGuild.ownerId + } + + get position(){ + let guildPositions = UserSettingsModule.getAllSettings().guildPositions + if(!guildPositions)return 0 + return guildPositions.indexOf(this.id) + } + + get presences(){ // TODO: Add activities. + return new Collection() + } + + get region(){ + return this.DiscordGuild.region + } + + get roles(){ + return new Collection(Object.values(this.DiscordGuild.roles).map(role => [role.id, createRole(role)])) + } + + get splash(){ + return this.DiscordGuild.splash + } + + get splashURL(){ + return CdnModule.getGuildSplashURL({ + id: this.id, + splash: this.splash, + size: ConstantsModule.SPLASH_SIZE + }) + } + + get suppressEveryone(){ + return guildModule.getNotificationsState().userGuildSettings[this.id].suppress_everyone + } + + get systemChannel(){ + return this.client.channels.get(this.systemChannelID) + } + + get systemChannelID(){ + return this.DiscordGuild.systemChannelId + } + + get verificationLevel(){ + return this.DiscordGuild.verificationLevel + } + + get verified(){ + return this.features.includes("VERIFIED") + } + + get voiceConnection(){// TODO: Implement if possible VoiceConnection. Maybe not fully like discord.js, but at least can see if you're connected. + return null + } + + get banner(){ + return this.DiscordGuild.banner + } + + get bannerURL(){ + return CdnModule.getGuildBannerURL({ + id: this.id, + banner: this.banner + }) + } + + get description(){ + return this.DiscordGuild.description + } + + get embedChannel(){ // TODO: Implement embedChannel + return null + } + + get embedChannelID(){ // TODO: Implement embedChannelID + return null + } + + get maximumMembers(){ // TODO: Correctly Implement maximumMembers if possible + return 250000 + } + + get maximumPresences(){ // TODO: Correctly Implement maximumPresences if possible + return 5000 + } + + get widgetEnabled(){ // TODO: Correctly Implement widgetEnabled if possible + return false + } + + get widgetChannelID(){ // TODO: Correctly Implement widgetChannelID if possible + return null + } + + get widgetChannel(){ // TODO: Correctly Implement widgetChannel if possible + return null + } + + get vanityURLCode(){ + return this.DiscordGuild.vanityURLCode + } + /** FUNCTIONS */ + async acknowledge(){ + AckModule.bulkAck(this.channels.filter(e => e.type === "text").map(e => { + return { + channelId: e.id, + messageId: channelsModule.lastMessageId(e.id) + } + })) + } + + addMember(...args:any):Promise{ + return Promise.reject(new DiscordJSError("This method is not available on Lightcord.")) + } + + allowDMs(allow:boolean){ + let restricted = UserSettingsModule.getAllSettings().restrictedGuilds + if(allow){ + if(!restricted.includes(this.id))return Promise.resolve(this) + restricted = restricted.filter(e => e !== this.id) + }else{ + if(restricted.includes(this.id))return Promise.resolve(this) + restricted.push(this.id) + } + return UserSettingsModule.updateRemoteSettings({ + restrictedGuilds: restricted + }).then(() => this) + } + + async ban(user:UserResolvable, { + days = 0, + reason = null + }: { + days?: number, + reason?: string + } = {}):Promise{ // always returning a snowflake + let id = resolveUserID(user) + if(!id)return Promise.reject(new DiscordJSError("Given user could not be resolved to an user ID.")) + + let result = await guildModule.banUser(this.id, id, days, reason).catch(err => err) + if(result instanceof Error || result.status !== 204){ + let message = result.body + if(Array.isArray(message)){ + message = message[0] + }else{ + if(message.user_id){ + message = "User: "+ message.user_id[0] + }else if(message.delete_message_days){ + message = "Days: "+ message.delete_message_days[0] + }else if(message.reason){ + message = "Reason: "+ message.reason[0] + }else{ + message = result.text + } + } + throw new DiscordJSError(message) + } + return id + } + + createChannel(name:string, typeOrOptions:ChannelTypes|ChannelData = ChannelTypes.TEXT, permissionOverwrites?: ChannelCreationOverwrites[] | Collection, reason?: string){ + const [opts, type] = typeof typeOrOptions === "string" ? [ + {}, + typeOrOptions + ] : [ + typeOrOptions, + typeOrOptions.type + ] + const options = { + name: name || "Unknown Channel", + type: type || "text" + } + //TODO: CreateGuildChannels and handle permissions + //channelsModule.createGuildChannel(this.id, options.type, optinos.name) + } + + fetch():Promise{ // Guild is synced by Discord. Only refreshing from cache. + let guild = guildModule.getGuild(this.id) + if(!guild){ + this.deleted = true + return Promise.resolve(this) + } + this.DiscordGuild = guild + return Promise.resolve(this) + } + + + } \ No newline at end of file diff --git a/DiscordJS/src/structures/PermissionOverwrites.ts b/DiscordJS/src/structures/PermissionOverwrites.ts index a844d0a..17646d5 100644 --- a/DiscordJS/src/structures/PermissionOverwrites.ts +++ b/DiscordJS/src/structures/PermissionOverwrites.ts @@ -1,61 +1,61 @@ -import Permissions from '../util/Permissions'; -import GuildChannel from './GuildChannel'; -import { Snowflake } from '..'; - -/** - * Represents a permission overwrite for a role or member in a guild channel. - */ -export default class PermissionOverwrites { - id: Snowflake; - type: "role"|"member"; - deny: number; - allow: number; - channel: GuildChannel; - constructor(guildChannel:GuildChannel, data) { - /** - * The GuildChannel this overwrite is for - * @name PermissionOverwrites#channel - * @type {GuildChannel} - * @readonly - */ - Object.defineProperty(this, 'channel', { value: guildChannel }); - - if (data) this.setup(data); - } - - setup(data) { - /** - * The ID of this overwrite, either a user ID or a role ID - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The type of this overwrite - * @type {string} - */ - this.type = data.type; - - /** - * The permissions that are denied for the user or role as a bitfield. - * @type {number} - */ - this.deny = data.deny; - - /** - * The permissions that are allowed for the user or role as a bitfield. - * @type {number} - */ - this.allow = data.allow; - } - - /** - * Delete this Permission Overwrite. - * @param {string} [reason] Reason for deleting this overwrite - * @returns {Promise} - */ - delete(reason) { - // TODO: Delete permission overwrites - //return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason); - } +import Permissions from '../util/Permissions'; +import GuildChannel from './GuildChannel'; +import { Snowflake } from '..'; + +/** + * Represents a permission overwrite for a role or member in a guild channel. + */ +export default class PermissionOverwrites { + id: Snowflake; + type: "role"|"member"; + deny: number; + allow: number; + channel: GuildChannel; + constructor(guildChannel:GuildChannel, data) { + /** + * The GuildChannel this overwrite is for + * @name PermissionOverwrites#channel + * @type {GuildChannel} + * @readonly + */ + Object.defineProperty(this, 'channel', { value: guildChannel }); + + if (data) this.setup(data); + } + + setup(data) { + /** + * The ID of this overwrite, either a user ID or a role ID + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The type of this overwrite + * @type {string} + */ + this.type = data.type; + + /** + * The permissions that are denied for the user or role as a bitfield. + * @type {number} + */ + this.deny = data.deny; + + /** + * The permissions that are allowed for the user or role as a bitfield. + * @type {number} + */ + this.allow = data.allow; + } + + /** + * Delete this Permission Overwrite. + * @param {string} [reason] Reason for deleting this overwrite + * @returns {Promise} + */ + delete(reason) { + // TODO: Delete permission overwrites + //return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason); + } } \ No newline at end of file diff --git a/DiscordJS/src/structures/shared/resolvePermissions.ts b/DiscordJS/src/structures/shared/resolvePermissions.ts index 728551e..720f110 100644 --- a/DiscordJS/src/structures/shared/resolvePermissions.ts +++ b/DiscordJS/src/structures/shared/resolvePermissions.ts @@ -1,32 +1,32 @@ -import Permissions from '../../util/Permissions'; -import Collection from '@discordjs/collection'; -import Guild from '../Guild'; -import { ChannelCreationOverwrites } from '../../util/util'; -import { Snowflake } from '../..'; -import PermissionOverwrites from '../PermissionOverwrites'; - -module.exports = function resolvePermissions(overwrites: ChannelCreationOverwrites[] | Collection, guild:Guild) { - if (overwrites instanceof Collection || overwrites instanceof Array) { - //TODO: Remove ts-ignore - // @ts-ignore - overwrites = (Array.isArray(overwrites) ? overwrites : overwrites.array()).map(overwrite => { - const role = this.client.resolver.resolveRole(guild, overwrite.id); - if (role) { - overwrite.id = role.id; - overwrite.type = 'role'; - } else { - overwrite.id = this.client.resolver.resolveUserID(overwrite.id); - overwrite.type = 'member'; - } - - return { - allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0), - deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0), - type: overwrite.type, - id: overwrite.id, - }; - }); - } - - return overwrites; +import Permissions from '../../util/Permissions'; +import Collection from '@discordjs/collection'; +import Guild from '../Guild'; +import { ChannelCreationOverwrites } from '../../util/util'; +import { Snowflake } from '../..'; +import PermissionOverwrites from '../PermissionOverwrites'; + +module.exports = function resolvePermissions(overwrites: ChannelCreationOverwrites[] | Collection, guild:Guild) { + if (overwrites instanceof Collection || overwrites instanceof Array) { + //TODO: Remove ts-ignore + // @ts-ignore + overwrites = (Array.isArray(overwrites) ? overwrites : overwrites.array()).map(overwrite => { + const role = this.client.resolver.resolveRole(guild, overwrite.id); + if (role) { + overwrite.id = role.id; + overwrite.type = 'role'; + } else { + overwrite.id = this.client.resolver.resolveUserID(overwrite.id); + overwrite.type = 'member'; + } + + return { + allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0), + deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0), + type: overwrite.type, + id: overwrite.id, + }; + }); + } + + return overwrites; }; \ No newline at end of file diff --git a/DiscordJS/src/util/Permissions.ts b/DiscordJS/src/util/Permissions.ts index 0b574d4..91df208 100644 --- a/DiscordJS/src/util/Permissions.ts +++ b/DiscordJS/src/util/Permissions.ts @@ -1,210 +1,210 @@ -import BitField from "./BitField"; -import * as util from "util"; - -/** - * Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of - * permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member - * that override their default permissions. - * @extends {BitField} - */ -export default class Permissions extends BitField { - /** - * @param {number|PermissionResolvable} permissions Permissions or bitfield to read from - */ - constructor(permissions: number | PermissionResolvable) { - super(permissions); - } - - /** - * Bitfield of the packed permissions - * @type {number} - * @see {@link Permissions#bitfield} - * @deprecated - * @readonly - */ - get raw() { - return this.bitfield; - } - - set raw(raw) { - this.bitfield = raw; - } - - /** - * Checks whether the bitfield has a permission, or any of multiple permissions. - * @param {PermissionResolvable} permission Permission(s) to check for - * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override - * @returns {boolean} - */ - any(permission: PermissionResolvable, checkAdmin: boolean = true): boolean { - return ( - (checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) || - super.any(permission) - ); - } - - /** - * Checks whether the bitfield has a permission, or multiple permissions. - * @param {PermissionResolvable} permission Permission(s) to check for - * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override - * @returns {boolean} - */ - has(permission: PermissionResolvable, checkAdmin: boolean = true): boolean { - return ( - (checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) || - super.has(permission) - ); - } - - /** - * Numeric permission flags. All available properties: - * - `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites) - * - `CREATE_INSTANT_INVITE` (create invitations to the guild) - * - `KICK_MEMBERS` - * - `BAN_MEMBERS` - * - `MANAGE_CHANNELS` (edit and reorder channels) - * - `MANAGE_GUILD` (edit the guild information, region, etc.) - * - `ADD_REACTIONS` (add new reactions to messages) - * - `VIEW_AUDIT_LOG` - * - `PRIORITY_SPEAKER` - * - `STREAM` - * - `VIEW_CHANNEL` - * - `READ_MESSAGES` **(deprecated)** - * - `SEND_MESSAGES` - * - `SEND_TTS_MESSAGES` - * - `MANAGE_MESSAGES` (delete messages and reactions) - * - `EMBED_LINKS` (links posted will have a preview embedded) - * - `ATTACH_FILES` - * - `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord) - * - `MENTION_EVERYONE` - * - `USE_EXTERNAL_EMOJIS` (use emojis from different guilds) - * - `EXTERNAL_EMOJIS` **(deprecated)** - * - `CONNECT` (connect to a voice channel) - * - `SPEAK` (speak in a voice channel) - * - `MUTE_MEMBERS` (mute members across all voice channels) - * - `DEAFEN_MEMBERS` (deafen members across all voice channels) - * - `MOVE_MEMBERS` (move members between voice channels) - * - `USE_VAD` (use voice activity detection) - * - `CHANGE_NICKNAME` - * - `MANAGE_NICKNAMES` (change other members' nicknames) - * - `MANAGE_ROLES` - * - `MANAGE_ROLES_OR_PERMISSIONS` **(deprecated)** - * - `MANAGE_WEBHOOKS` - * - `MANAGE_EMOJIS` - * @type {Object} - * @see {@link https://discordapp.com/developers/docs/topics/permissions} - */ - static FLAGS:{ - CREATE_INSTANT_INVITE: number, - KICK_MEMBERS: number, - BAN_MEMBERS: number, - ADMINISTRATOR: number, - MANAGE_CHANNELS: number, - MANAGE_GUILD: number, - ADD_REACTIONS: number, - VIEW_AUDIT_LOG: number, - PRIORITY_SPEAKER: number, - STREAM: number, - - VIEW_CHANNEL: number, - READ_MESSAGES: number, - SEND_MESSAGES: number, - SEND_TTS_MESSAGES: number, - MANAGE_MESSAGES: number, - EMBED_LINKS: number, - ATTACH_FILES: number, - READ_MESSAGE_HISTORY: number, - MENTION_EVERYONE: number, - EXTERNAL_EMOJIS: number, - USE_EXTERNAL_EMOJIS: number, - - CONNECT: number, - SPEAK: number, - MUTE_MEMBERS: number, - DEAFEN_MEMBERS: number, - MOVE_MEMBERS: number, - USE_VAD: number, - - CHANGE_NICKNAME: number, - MANAGE_NICKNAMES: number, - MANAGE_ROLES: number, - MANAGE_ROLES_OR_PERMISSIONS: number, - MANAGE_WEBHOOKS: number, - MANAGE_EMOJIS: number, - } = { - CREATE_INSTANT_INVITE: 1 << 0, - KICK_MEMBERS: 1 << 1, - BAN_MEMBERS: 1 << 2, - ADMINISTRATOR: 1 << 3, - MANAGE_CHANNELS: 1 << 4, - MANAGE_GUILD: 1 << 5, - ADD_REACTIONS: 1 << 6, - VIEW_AUDIT_LOG: 1 << 7, - PRIORITY_SPEAKER: 1 << 8, - STREAM: 1 << 9, - - VIEW_CHANNEL: 1 << 10, - READ_MESSAGES: 1 << 10, - SEND_MESSAGES: 1 << 11, - SEND_TTS_MESSAGES: 1 << 12, - MANAGE_MESSAGES: 1 << 13, - EMBED_LINKS: 1 << 14, - ATTACH_FILES: 1 << 15, - READ_MESSAGE_HISTORY: 1 << 16, - MENTION_EVERYONE: 1 << 17, - EXTERNAL_EMOJIS: 1 << 18, - USE_EXTERNAL_EMOJIS: 1 << 18, - - CONNECT: 1 << 20, - SPEAK: 1 << 21, - MUTE_MEMBERS: 1 << 22, - DEAFEN_MEMBERS: 1 << 23, - MOVE_MEMBERS: 1 << 24, - USE_VAD: 1 << 25, - - CHANGE_NICKNAME: 1 << 26, - MANAGE_NICKNAMES: 1 << 27, - MANAGE_ROLES: 1 << 28, - MANAGE_ROLES_OR_PERMISSIONS: 1 << 28, - MANAGE_WEBHOOKS: 1 << 29, - MANAGE_EMOJIS: 1 << 30, - }; - - /** - * Bitfield representing every permission combined - * @type {number} - */ - static ALL: number = Object.keys(Permissions.FLAGS).reduce( - (all, p) => all | Permissions.FLAGS[p], - 0 - ); - - /** - * Bitfield representing the default permissions for users - * @type {number} - */ - static DEFAULT: number = 104324673; -} - -Object.defineProperty(Permissions.prototype, "raw", { - get: util.deprecate( - Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").get, - "EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead" - ), - set: util.deprecate( - Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").set, - "EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead" - ), -}); - -/** - * Data that can be resolved to give a permission number. This can be: - * * A string (see {@link Permissions.FLAGS}) - * * A permission number - * @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable - */ -export type PermissionResolvable = - | keyof typeof Permissions.FLAGS - | number - | Permissions - | PermissionResolvable[]; +import BitField from "./BitField"; +import * as util from "util"; + +/** + * Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of + * permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member + * that override their default permissions. + * @extends {BitField} + */ +export default class Permissions extends BitField { + /** + * @param {number|PermissionResolvable} permissions Permissions or bitfield to read from + */ + constructor(permissions: number | PermissionResolvable) { + super(permissions); + } + + /** + * Bitfield of the packed permissions + * @type {number} + * @see {@link Permissions#bitfield} + * @deprecated + * @readonly + */ + get raw() { + return this.bitfield; + } + + set raw(raw) { + this.bitfield = raw; + } + + /** + * Checks whether the bitfield has a permission, or any of multiple permissions. + * @param {PermissionResolvable} permission Permission(s) to check for + * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override + * @returns {boolean} + */ + any(permission: PermissionResolvable, checkAdmin: boolean = true): boolean { + return ( + (checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) || + super.any(permission) + ); + } + + /** + * Checks whether the bitfield has a permission, or multiple permissions. + * @param {PermissionResolvable} permission Permission(s) to check for + * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override + * @returns {boolean} + */ + has(permission: PermissionResolvable, checkAdmin: boolean = true): boolean { + return ( + (checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) || + super.has(permission) + ); + } + + /** + * Numeric permission flags. All available properties: + * - `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites) + * - `CREATE_INSTANT_INVITE` (create invitations to the guild) + * - `KICK_MEMBERS` + * - `BAN_MEMBERS` + * - `MANAGE_CHANNELS` (edit and reorder channels) + * - `MANAGE_GUILD` (edit the guild information, region, etc.) + * - `ADD_REACTIONS` (add new reactions to messages) + * - `VIEW_AUDIT_LOG` + * - `PRIORITY_SPEAKER` + * - `STREAM` + * - `VIEW_CHANNEL` + * - `READ_MESSAGES` **(deprecated)** + * - `SEND_MESSAGES` + * - `SEND_TTS_MESSAGES` + * - `MANAGE_MESSAGES` (delete messages and reactions) + * - `EMBED_LINKS` (links posted will have a preview embedded) + * - `ATTACH_FILES` + * - `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord) + * - `MENTION_EVERYONE` + * - `USE_EXTERNAL_EMOJIS` (use emojis from different guilds) + * - `EXTERNAL_EMOJIS` **(deprecated)** + * - `CONNECT` (connect to a voice channel) + * - `SPEAK` (speak in a voice channel) + * - `MUTE_MEMBERS` (mute members across all voice channels) + * - `DEAFEN_MEMBERS` (deafen members across all voice channels) + * - `MOVE_MEMBERS` (move members between voice channels) + * - `USE_VAD` (use voice activity detection) + * - `CHANGE_NICKNAME` + * - `MANAGE_NICKNAMES` (change other members' nicknames) + * - `MANAGE_ROLES` + * - `MANAGE_ROLES_OR_PERMISSIONS` **(deprecated)** + * - `MANAGE_WEBHOOKS` + * - `MANAGE_EMOJIS` + * @type {Object} + * @see {@link https://discordapp.com/developers/docs/topics/permissions} + */ + static FLAGS:{ + CREATE_INSTANT_INVITE: number, + KICK_MEMBERS: number, + BAN_MEMBERS: number, + ADMINISTRATOR: number, + MANAGE_CHANNELS: number, + MANAGE_GUILD: number, + ADD_REACTIONS: number, + VIEW_AUDIT_LOG: number, + PRIORITY_SPEAKER: number, + STREAM: number, + + VIEW_CHANNEL: number, + READ_MESSAGES: number, + SEND_MESSAGES: number, + SEND_TTS_MESSAGES: number, + MANAGE_MESSAGES: number, + EMBED_LINKS: number, + ATTACH_FILES: number, + READ_MESSAGE_HISTORY: number, + MENTION_EVERYONE: number, + EXTERNAL_EMOJIS: number, + USE_EXTERNAL_EMOJIS: number, + + CONNECT: number, + SPEAK: number, + MUTE_MEMBERS: number, + DEAFEN_MEMBERS: number, + MOVE_MEMBERS: number, + USE_VAD: number, + + CHANGE_NICKNAME: number, + MANAGE_NICKNAMES: number, + MANAGE_ROLES: number, + MANAGE_ROLES_OR_PERMISSIONS: number, + MANAGE_WEBHOOKS: number, + MANAGE_EMOJIS: number, + } = { + CREATE_INSTANT_INVITE: 1 << 0, + KICK_MEMBERS: 1 << 1, + BAN_MEMBERS: 1 << 2, + ADMINISTRATOR: 1 << 3, + MANAGE_CHANNELS: 1 << 4, + MANAGE_GUILD: 1 << 5, + ADD_REACTIONS: 1 << 6, + VIEW_AUDIT_LOG: 1 << 7, + PRIORITY_SPEAKER: 1 << 8, + STREAM: 1 << 9, + + VIEW_CHANNEL: 1 << 10, + READ_MESSAGES: 1 << 10, + SEND_MESSAGES: 1 << 11, + SEND_TTS_MESSAGES: 1 << 12, + MANAGE_MESSAGES: 1 << 13, + EMBED_LINKS: 1 << 14, + ATTACH_FILES: 1 << 15, + READ_MESSAGE_HISTORY: 1 << 16, + MENTION_EVERYONE: 1 << 17, + EXTERNAL_EMOJIS: 1 << 18, + USE_EXTERNAL_EMOJIS: 1 << 18, + + CONNECT: 1 << 20, + SPEAK: 1 << 21, + MUTE_MEMBERS: 1 << 22, + DEAFEN_MEMBERS: 1 << 23, + MOVE_MEMBERS: 1 << 24, + USE_VAD: 1 << 25, + + CHANGE_NICKNAME: 1 << 26, + MANAGE_NICKNAMES: 1 << 27, + MANAGE_ROLES: 1 << 28, + MANAGE_ROLES_OR_PERMISSIONS: 1 << 28, + MANAGE_WEBHOOKS: 1 << 29, + MANAGE_EMOJIS: 1 << 30, + }; + + /** + * Bitfield representing every permission combined + * @type {number} + */ + static ALL: number = Object.keys(Permissions.FLAGS).reduce( + (all, p) => all | Permissions.FLAGS[p], + 0 + ); + + /** + * Bitfield representing the default permissions for users + * @type {number} + */ + static DEFAULT: number = 104324673; +} + +Object.defineProperty(Permissions.prototype, "raw", { + get: util.deprecate( + Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").get, + "EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead" + ), + set: util.deprecate( + Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").set, + "EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead" + ), +}); + +/** + * Data that can be resolved to give a permission number. This can be: + * * A string (see {@link Permissions.FLAGS}) + * * A permission number + * @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable + */ +export type PermissionResolvable = + | keyof typeof Permissions.FLAGS + | number + | Permissions + | PermissionResolvable[]; diff --git a/DiscordJS/src/util/modules/channels.ts b/DiscordJS/src/util/modules/channels.ts index 819f669..da271b5 100644 --- a/DiscordJS/src/util/modules/channels.ts +++ b/DiscordJS/src/util/modules/channels.ts @@ -1,69 +1,69 @@ -import {requireModule, DiscordChannel} from "../DiscordToModules" -import { Snowflake } from "../.." - -let channelsModuleInternal1:{ - getChannel(id:Snowflake): DiscordChannel, - getChannels(): { - [k:string]: DiscordChannel - }, - getDMFromUserId(id:string):Snowflake, - getDMUserIds():Snowflake[], - getFollowerStatsForChannel(id: Snowflake):{ - loadingStatus: "succeeded"|"failed", - lastFetched: number, - channelsFollowing: number, - guildMembers: number, - guildsFollowing: number, - usersSeenEver: number, - subscribersGainedSinceLastPost: number, - subscribersLostSinceLastPost: number - }, - getGDMsForRecipients(recipients: Snowflake[]):Set -} = requireModule(e => e.default && e.default.getChannels && e.default.getChannel) - -let channelsModuleInternal2:{ - deleteChannel(id: Snowflake):void -} - -let channelsModuleInternal4:{ - createChannel(guildId:Snowflake, type:string, name:string, - permissionOverwrites: { - id: Snowflake, - type: "role"|"member", - allow: number, - deny: number - }[], bitrate:number, userLimit:number, parentId:Snowflake, skuId:Snowflake, branchId:Snowflake):void -} = requireModule(e => e.default && e.default.createChannel) - -let channelsModuleInternal3:{ - hasUnread(channel_id:Snowflake):boolean, - hasCategoryUnread(channel_id:Snowflake):boolean, - getUnreadCount(channel_id:Snowflake):number, - getMentionCount(channel_id:Snowflake):number, - ackMessageId(channel_id:Snowflake):Snowflake, - lastMessageId(channel_id:Snowflake):Snowflake, - getOldestUnreadMessageId(channel_id:Snowflake):Snowflake, - getOldestUnreadTimestamp(channel_id:Snowflake):number, - isEstimated(channel_id:Snowflake):boolean, - hasUnreadPins(channel_id:Snowflake):boolean, - getAllReadStates() -} = requireModule(e => e.default && e.default.lastMessageId) - -function set3(){ - if(channelsModuleInternal3)return - channelsModuleInternal3 = requireModule(e => e.default && e.default.lastMessageId) -} - -export = { - getChannel: channelsModuleInternal1.getChannel, - getAllChannels: channelsModuleInternal1.getChannels, - get delete():typeof channelsModuleInternal2.deleteChannel{ - return channelsModuleInternal2 ? channelsModuleInternal2.deleteChannel : (channelsModuleInternal2 = requireModule(e => e.default && e.default.deleteChannel), channelsModuleInternal2.deleteChannel) - }, - - get lastMessageId(){ - set3() - return channelsModuleInternal3.lastMessageId - }, - createGuildChannel: channelsModuleInternal4.createChannel +import {requireModule, DiscordChannel} from "../DiscordToModules" +import { Snowflake } from "../.." + +let channelsModuleInternal1:{ + getChannel(id:Snowflake): DiscordChannel, + getChannels(): { + [k:string]: DiscordChannel + }, + getDMFromUserId(id:string):Snowflake, + getDMUserIds():Snowflake[], + getFollowerStatsForChannel(id: Snowflake):{ + loadingStatus: "succeeded"|"failed", + lastFetched: number, + channelsFollowing: number, + guildMembers: number, + guildsFollowing: number, + usersSeenEver: number, + subscribersGainedSinceLastPost: number, + subscribersLostSinceLastPost: number + }, + getGDMsForRecipients(recipients: Snowflake[]):Set +} = requireModule(e => e.default && e.default.getChannels && e.default.getChannel) + +let channelsModuleInternal2:{ + deleteChannel(id: Snowflake):void +} + +let channelsModuleInternal4:{ + createChannel(guildId:Snowflake, type:string, name:string, + permissionOverwrites: { + id: Snowflake, + type: "role"|"member", + allow: number, + deny: number + }[], bitrate:number, userLimit:number, parentId:Snowflake, skuId:Snowflake, branchId:Snowflake):void +} = requireModule(e => e.default && e.default.createChannel) + +let channelsModuleInternal3:{ + hasUnread(channel_id:Snowflake):boolean, + hasCategoryUnread(channel_id:Snowflake):boolean, + getUnreadCount(channel_id:Snowflake):number, + getMentionCount(channel_id:Snowflake):number, + ackMessageId(channel_id:Snowflake):Snowflake, + lastMessageId(channel_id:Snowflake):Snowflake, + getOldestUnreadMessageId(channel_id:Snowflake):Snowflake, + getOldestUnreadTimestamp(channel_id:Snowflake):number, + isEstimated(channel_id:Snowflake):boolean, + hasUnreadPins(channel_id:Snowflake):boolean, + getAllReadStates() +} = requireModule(e => e.default && e.default.lastMessageId) + +function set3(){ + if(channelsModuleInternal3)return + channelsModuleInternal3 = requireModule(e => e.default && e.default.lastMessageId) +} + +export = { + getChannel: channelsModuleInternal1.getChannel, + getAllChannels: channelsModuleInternal1.getChannels, + get delete():typeof channelsModuleInternal2.deleteChannel{ + return channelsModuleInternal2 ? channelsModuleInternal2.deleteChannel : (channelsModuleInternal2 = requireModule(e => e.default && e.default.deleteChannel), channelsModuleInternal2.deleteChannel) + }, + + get lastMessageId(){ + set3() + return channelsModuleInternal3.lastMessageId + }, + createGuildChannel: channelsModuleInternal4.createChannel } \ No newline at end of file diff --git a/DiscordJS/src/util/util.ts b/DiscordJS/src/util/util.ts index 9bac444..37529c5 100644 --- a/DiscordJS/src/util/util.ts +++ b/DiscordJS/src/util/util.ts @@ -1,149 +1,149 @@ -import { DiscordChannel, DiscordGuild, DiscordGuildMember, DiscordRole, DiscordMessage, DiscordUser } from "./DiscordToModules" -import { Channel, Snowflake } from ".."; -import BaseChannel from "../structures/BaseChannel"; -import Guild from "../structures/Guild"; -import TextChannel from "../structures/TextChannel"; -import GuildMember from "../structures/GuildMember"; -import Role from "../structures/Role"; -import User from "../structures/User"; -import Message from "../structures/Message"; -import { ChannelTypes } from "./Constants"; -import CategoryChannel from "../structures/CategoryChannel"; -import Collection from "@discordjs/collection"; -import Permissions, { PermissionResolvable } from "./Permissions"; -import PermissionOverwrites from "../structures/PermissionOverwrites"; - -export function createChannel(channel:DiscordChannel):Channel{ - let constructor = channels[channel.type] || BaseChannel - return new constructor(channel) -} - -const channels:(new(channel:DiscordChannel) => Channel)[] = [ - TextChannel -] - -export function createGuild(guild:DiscordGuild):Guild{ - return new Guild(guild) -} - -export function createGuildMember(member:DiscordGuildMember):GuildMember{ - return new GuildMember(member) -} - -export function createRole(role:DiscordRole):Role{ - return new Role(role) -} - -export function createMessage(message:DiscordMessage):Message{ - return new Message(message) -} - -export function createUser(user:DiscordUser):User{ - return new User(user) -} - -export function applyMixins(derivedCtor: any, baseCtors: any[]) { - baseCtors.forEach(baseCtor => { - Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { - if (name !== 'constructor') { - derivedCtor.prototype[name] = baseCtor.prototype[name]; - } - }); - }); -} - -/** - * Transforms a snowflake from a decimal string to a bit string. - * @param {string} num Snowflake to be transformed - * @returns {string} - * @private - */ -export function idToBinary(num:string):string{ - let bin = ''; - let high = parseInt(num.slice(0, -10)) || 0; - let low = parseInt(num.slice(-10)); - while (low > 0 || high > 0) { - bin = String(low & 1) + bin; - low = Math.floor(low / 2); - if (high > 0) { - low += 5000000000 * (high % 2); - high = Math.floor(high / 2); - } - } - return bin; -} - -/** -* Transforms a snowflake from a bit string to a decimal string. -* @param {string} num Bit string to be transformed -* @returns {string} -* @private -*/ -export function binaryToID(num:string):string { - let dec = ''; - - while (num.length > 50) { - const high = parseInt(num.slice(0, -32), 2); - const low = parseInt((high % 10).toString(2) + num.slice(-32), 2); - - dec = (low % 10).toString() + dec; - num = Math.floor(high / 10).toString(2) + - Math.floor(low / 10) - .toString(2) - .padStart(32, '0'); - } - - let num2 = parseInt(num, 2); - while (num2 > 0) { - dec = (num2 % 10).toString() + dec; - num2 = Math.floor(num2 / 10); - } - - return dec; -} - - - -export type UserResolvable = User | Snowflake | Message | Guild | GuildMember - -export function resolveUserID(user:UserResolvable){ - if(typeof user === "string")return user // ID - if(user instanceof User)return user.id // User - if(user instanceof Message)return user.author.id // Message Author - if(user instanceof Guild)return user.ownerID // Guild - if(user instanceof GuildMember)return user.id // GuildMember - return null -} - -export type ChannelData = { - type?: ChannelTypes, - name?: string, - position?: number, - topic?: string, - nsfw?: boolean, - bitrate?: number, - userLimit?: number, - parent?: CategoryChannel|Snowflake, - permissionOverwrites?: ChannelCreationOverwrites[] | Collection, - rateLimitPerUser?: number -} - -export type ChannelCreationOverwrites = { - allow?: PermissionResolvable, - /** - * @deprecated - */ - allowed?: PermissionResolvable, - deny?: PermissionResolvable, - /** - * @deprecated - */ - denied?: PermissionResolvable, - id?: GuildMemberResolvable | RoleResolvable -} - -export {BitFieldResolvable} from "./BitField" - -export type GuildMemberResolvable = GuildMember | User - +import { DiscordChannel, DiscordGuild, DiscordGuildMember, DiscordRole, DiscordMessage, DiscordUser } from "./DiscordToModules" +import { Channel, Snowflake } from ".."; +import BaseChannel from "../structures/BaseChannel"; +import Guild from "../structures/Guild"; +import TextChannel from "../structures/TextChannel"; +import GuildMember from "../structures/GuildMember"; +import Role from "../structures/Role"; +import User from "../structures/User"; +import Message from "../structures/Message"; +import { ChannelTypes } from "./Constants"; +import CategoryChannel from "../structures/CategoryChannel"; +import Collection from "@discordjs/collection"; +import Permissions, { PermissionResolvable } from "./Permissions"; +import PermissionOverwrites from "../structures/PermissionOverwrites"; + +export function createChannel(channel:DiscordChannel):Channel{ + let constructor = channels[channel.type] || BaseChannel + return new constructor(channel) +} + +const channels:(new(channel:DiscordChannel) => Channel)[] = [ + TextChannel +] + +export function createGuild(guild:DiscordGuild):Guild{ + return new Guild(guild) +} + +export function createGuildMember(member:DiscordGuildMember):GuildMember{ + return new GuildMember(member) +} + +export function createRole(role:DiscordRole):Role{ + return new Role(role) +} + +export function createMessage(message:DiscordMessage):Message{ + return new Message(message) +} + +export function createUser(user:DiscordUser):User{ + return new User(user) +} + +export function applyMixins(derivedCtor: any, baseCtors: any[]) { + baseCtors.forEach(baseCtor => { + Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { + if (name !== 'constructor') { + derivedCtor.prototype[name] = baseCtor.prototype[name]; + } + }); + }); +} + +/** + * Transforms a snowflake from a decimal string to a bit string. + * @param {string} num Snowflake to be transformed + * @returns {string} + * @private + */ +export function idToBinary(num:string):string{ + let bin = ''; + let high = parseInt(num.slice(0, -10)) || 0; + let low = parseInt(num.slice(-10)); + while (low > 0 || high > 0) { + bin = String(low & 1) + bin; + low = Math.floor(low / 2); + if (high > 0) { + low += 5000000000 * (high % 2); + high = Math.floor(high / 2); + } + } + return bin; +} + +/** +* Transforms a snowflake from a bit string to a decimal string. +* @param {string} num Bit string to be transformed +* @returns {string} +* @private +*/ +export function binaryToID(num:string):string { + let dec = ''; + + while (num.length > 50) { + const high = parseInt(num.slice(0, -32), 2); + const low = parseInt((high % 10).toString(2) + num.slice(-32), 2); + + dec = (low % 10).toString() + dec; + num = Math.floor(high / 10).toString(2) + + Math.floor(low / 10) + .toString(2) + .padStart(32, '0'); + } + + let num2 = parseInt(num, 2); + while (num2 > 0) { + dec = (num2 % 10).toString() + dec; + num2 = Math.floor(num2 / 10); + } + + return dec; +} + + + +export type UserResolvable = User | Snowflake | Message | Guild | GuildMember + +export function resolveUserID(user:UserResolvable){ + if(typeof user === "string")return user // ID + if(user instanceof User)return user.id // User + if(user instanceof Message)return user.author.id // Message Author + if(user instanceof Guild)return user.ownerID // Guild + if(user instanceof GuildMember)return user.id // GuildMember + return null +} + +export type ChannelData = { + type?: ChannelTypes, + name?: string, + position?: number, + topic?: string, + nsfw?: boolean, + bitrate?: number, + userLimit?: number, + parent?: CategoryChannel|Snowflake, + permissionOverwrites?: ChannelCreationOverwrites[] | Collection, + rateLimitPerUser?: number +} + +export type ChannelCreationOverwrites = { + allow?: PermissionResolvable, + /** + * @deprecated + */ + allowed?: PermissionResolvable, + deny?: PermissionResolvable, + /** + * @deprecated + */ + denied?: PermissionResolvable, + id?: GuildMemberResolvable | RoleResolvable +} + +export {BitFieldResolvable} from "./BitField" + +export type GuildMemberResolvable = GuildMember | User + export type RoleResolvable = Role | Snowflake \ No newline at end of file diff --git a/LightcordApi/js/main.js b/LightcordApi/js/main.js index 71b2c83..d4be5d2 100644 --- a/LightcordApi/js/main.js +++ b/LightcordApi/js/main.js @@ -24629,56 +24629,56 @@ module.exports = window.React; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const Button_1 = __webpack_require__(/*! ./inputs/Button */ "./src/components/inputs/Button.tsx"); -const Switch_1 = __webpack_require__(/*! ./inputs/Switch */ "./src/components/inputs/Switch.tsx"); -const RadioGroup_1 = __webpack_require__(/*! ./inputs/RadioGroup */ "./src/components/inputs/RadioGroup.tsx"); -const TextArea_1 = __webpack_require__(/*! ./inputs/TextArea */ "./src/components/inputs/TextArea.tsx"); -const TextInput_1 = __webpack_require__(/*! ./inputs/TextInput */ "./src/components/inputs/TextInput.tsx"); -const Dropdown_1 = __webpack_require__(/*! ./inputs/Dropdown */ "./src/components/inputs/Dropdown.tsx"); -const Title_1 = __webpack_require__(/*! ./general/Title */ "./src/components/general/Title.tsx"); -const SettingsTitle_1 = __webpack_require__(/*! ./general/SettingsTitle */ "./src/components/general/SettingsTitle.tsx"); -const Tabs_1 = __webpack_require__(/*! ./general/Tabs */ "./src/components/general/Tabs.tsx"); -const SettingSubTitle_1 = __webpack_require__(/*! ./general/SettingSubTitle */ "./src/components/general/SettingSubTitle.tsx"); -const CodeBlock_1 = __webpack_require__(/*! ./general/CodeBlock */ "./src/components/general/CodeBlock.tsx"); -const cloneNullProto_1 = __webpack_require__(/*! ../modules/cloneNullProto */ "./src/modules/cloneNullProto.ts"); -const Tooltip_1 = __webpack_require__(/*! ./general/Tooltip */ "./src/components/general/Tooltip.ts"); -const ColorPicker_1 = __webpack_require__(/*! ./inputs/ColorPicker */ "./src/components/inputs/ColorPicker.tsx"); -const AlertBox_1 = __webpack_require__(/*! ./general/AlertBox */ "./src/components/general/AlertBox.tsx"); -const ErrorCatcher_1 = __webpack_require__(/*! ./general/ErrorCatcher */ "./src/components/general/ErrorCatcher.tsx"); -const Flex_1 = __webpack_require__(/*! ./general/Flex */ "./src/components/general/Flex.tsx"); -const Text_1 = __webpack_require__(/*! ./general/Text */ "./src/components/general/Text.ts"); -const DateRange_1 = __webpack_require__(/*! ./inputs/DateRange */ "./src/components/inputs/DateRange.tsx"); -const DateInput_1 = __webpack_require__(/*! ./inputs/DateInput */ "./src/components/inputs/DateInput.tsx"); -const RadioGroupProxied = ErrorCatcher_1.createProxyErrorCatcherClass(RadioGroup_1.default); -exports.default = cloneNullProto_1.default({ - inputs: cloneNullProto_1.default({ - Button: ErrorCatcher_1.createProxyErrorCatcherClass(Button_1.default), - Switch: ErrorCatcher_1.createProxyErrorCatcherClass(Switch_1.default), - Choices: RadioGroupProxied, - RadioGroup: RadioGroupProxied, - TextArea: ErrorCatcher_1.createProxyErrorCatcherClass(TextArea_1.default), - TextInput: ErrorCatcher_1.createProxyErrorCatcherClass(TextInput_1.default), - Dropdown: ErrorCatcher_1.createProxyErrorCatcherClass(Dropdown_1.default), - ColorPicker: ErrorCatcher_1.createProxyErrorCatcherClass(ColorPicker_1.default), - DateRange: ErrorCatcher_1.createProxyErrorCatcherClass(DateRange_1.default), - DateInput: ErrorCatcher_1.createProxyErrorCatcherClass(DateInput_1.default) - }), - general: cloneNullProto_1.default({ - Title: ErrorCatcher_1.createProxyErrorCatcherClass(Title_1.default), - SettingsTitle: ErrorCatcher_1.createProxyErrorCatcherClass(SettingsTitle_1.default), - SettingSubTitle: ErrorCatcher_1.createProxyErrorCatcherClass(SettingSubTitle_1.default), - Tabs: ErrorCatcher_1.createProxyErrorCatcherClass(Tabs_1.default), - CodeBlock: ErrorCatcher_1.createProxyErrorCatcherClass(CodeBlock_1.default), - Tooltip: ErrorCatcher_1.createProxyErrorCatcherClass(Tooltip_1.default), - AlertBox: ErrorCatcher_1.createProxyErrorCatcherClass(AlertBox_1.default), - Flex: ErrorCatcher_1.createProxyErrorCatcherClass(Flex_1.default), - FlexChild: ErrorCatcher_1.createProxyErrorCatcherClass(Flex_1.FlexChild), - ErrorCatcher: ErrorCatcher_1.default, - Text: Text_1.default - }) -}); + +Object.defineProperty(exports, "__esModule", { value: true }); +const Button_1 = __webpack_require__(/*! ./inputs/Button */ "./src/components/inputs/Button.tsx"); +const Switch_1 = __webpack_require__(/*! ./inputs/Switch */ "./src/components/inputs/Switch.tsx"); +const RadioGroup_1 = __webpack_require__(/*! ./inputs/RadioGroup */ "./src/components/inputs/RadioGroup.tsx"); +const TextArea_1 = __webpack_require__(/*! ./inputs/TextArea */ "./src/components/inputs/TextArea.tsx"); +const TextInput_1 = __webpack_require__(/*! ./inputs/TextInput */ "./src/components/inputs/TextInput.tsx"); +const Dropdown_1 = __webpack_require__(/*! ./inputs/Dropdown */ "./src/components/inputs/Dropdown.tsx"); +const Title_1 = __webpack_require__(/*! ./general/Title */ "./src/components/general/Title.tsx"); +const SettingsTitle_1 = __webpack_require__(/*! ./general/SettingsTitle */ "./src/components/general/SettingsTitle.tsx"); +const Tabs_1 = __webpack_require__(/*! ./general/Tabs */ "./src/components/general/Tabs.tsx"); +const SettingSubTitle_1 = __webpack_require__(/*! ./general/SettingSubTitle */ "./src/components/general/SettingSubTitle.tsx"); +const CodeBlock_1 = __webpack_require__(/*! ./general/CodeBlock */ "./src/components/general/CodeBlock.tsx"); +const cloneNullProto_1 = __webpack_require__(/*! ../modules/cloneNullProto */ "./src/modules/cloneNullProto.ts"); +const Tooltip_1 = __webpack_require__(/*! ./general/Tooltip */ "./src/components/general/Tooltip.ts"); +const ColorPicker_1 = __webpack_require__(/*! ./inputs/ColorPicker */ "./src/components/inputs/ColorPicker.tsx"); +const AlertBox_1 = __webpack_require__(/*! ./general/AlertBox */ "./src/components/general/AlertBox.tsx"); +const ErrorCatcher_1 = __webpack_require__(/*! ./general/ErrorCatcher */ "./src/components/general/ErrorCatcher.tsx"); +const Flex_1 = __webpack_require__(/*! ./general/Flex */ "./src/components/general/Flex.tsx"); +const Text_1 = __webpack_require__(/*! ./general/Text */ "./src/components/general/Text.ts"); +const DateRange_1 = __webpack_require__(/*! ./inputs/DateRange */ "./src/components/inputs/DateRange.tsx"); +const DateInput_1 = __webpack_require__(/*! ./inputs/DateInput */ "./src/components/inputs/DateInput.tsx"); +const RadioGroupProxied = ErrorCatcher_1.createProxyErrorCatcherClass(RadioGroup_1.default); +exports.default = cloneNullProto_1.default({ + inputs: cloneNullProto_1.default({ + Button: ErrorCatcher_1.createProxyErrorCatcherClass(Button_1.default), + Switch: ErrorCatcher_1.createProxyErrorCatcherClass(Switch_1.default), + Choices: RadioGroupProxied, + RadioGroup: RadioGroupProxied, + TextArea: ErrorCatcher_1.createProxyErrorCatcherClass(TextArea_1.default), + TextInput: ErrorCatcher_1.createProxyErrorCatcherClass(TextInput_1.default), + Dropdown: ErrorCatcher_1.createProxyErrorCatcherClass(Dropdown_1.default), + ColorPicker: ErrorCatcher_1.createProxyErrorCatcherClass(ColorPicker_1.default), + DateRange: ErrorCatcher_1.createProxyErrorCatcherClass(DateRange_1.default), + DateInput: ErrorCatcher_1.createProxyErrorCatcherClass(DateInput_1.default) + }), + general: cloneNullProto_1.default({ + Title: ErrorCatcher_1.createProxyErrorCatcherClass(Title_1.default), + SettingsTitle: ErrorCatcher_1.createProxyErrorCatcherClass(SettingsTitle_1.default), + SettingSubTitle: ErrorCatcher_1.createProxyErrorCatcherClass(SettingSubTitle_1.default), + Tabs: ErrorCatcher_1.createProxyErrorCatcherClass(Tabs_1.default), + CodeBlock: ErrorCatcher_1.createProxyErrorCatcherClass(CodeBlock_1.default), + Tooltip: ErrorCatcher_1.createProxyErrorCatcherClass(Tooltip_1.default), + AlertBox: ErrorCatcher_1.createProxyErrorCatcherClass(AlertBox_1.default), + Flex: ErrorCatcher_1.createProxyErrorCatcherClass(Flex_1.default), + FlexChild: ErrorCatcher_1.createProxyErrorCatcherClass(Flex_1.FlexChild), + ErrorCatcher: ErrorCatcher_1.default, + Text: Text_1.default + }) +}); /***/ }), @@ -24691,77 +24691,77 @@ exports.default = cloneNullProto_1.default({ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -let AlertBoxModules; -class AlertBox extends React.Component { - get modules() { - return AlertBoxModules || (AlertBoxModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Markdown" && e.default.defaultProps.parser).default - ]); - } - render() { - const [Markdown] = this.modules; - let wrap; - let children; - if (typeof this.props.children === "string") { - wrap = Markdown.prototype.render.call({ - props: Object.assign({ - className: "", - children: this.props.children - }, Markdown.defaultProps) - }); - children = wrap.props.children; - } - else { - wrap = Markdown.prototype.render.call({ - props: Object.assign({ - className: "", - children: "" - }, Markdown.defaultProps) - }); - children = this.props.children; - } - wrap.props.children = React.createElement("div", { className: "lc-alert-box lc-alert-box-" + this.props.type }, - React.createElement("blockquote", { className: "lc-blockquote" }, children)); - return wrap; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - children: "***Discord's*** **Markdown** _is_ [supported](https://google.com) or you can just insert your own react childs." - } - ], [ - { - type: "info" - }, - { - type: "success" - }, - { - type: "warn" - }, - { - type: "error" - } - ]); - return AllPreviews; - })(); - } -} -exports.default = AlertBox; -AlertBox.defaultProps = { - type: "info", - children: null -}; -AlertBox.help = { - info: "You can insert markdown (as string) or normal react childs", - warn: "All string will be interpreted as markdown. If you want raw string, pass an array with the string inside." -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +let AlertBoxModules; +class AlertBox extends React.Component { + get modules() { + return AlertBoxModules || (AlertBoxModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Markdown" && e.default.defaultProps.parser).default + ]); + } + render() { + const [Markdown] = this.modules; + let wrap; + let children; + if (typeof this.props.children === "string") { + wrap = Markdown.prototype.render.call({ + props: Object.assign({ + className: "", + children: this.props.children + }, Markdown.defaultProps) + }); + children = wrap.props.children; + } + else { + wrap = Markdown.prototype.render.call({ + props: Object.assign({ + className: "", + children: "" + }, Markdown.defaultProps) + }); + children = this.props.children; + } + wrap.props.children = React.createElement("div", { className: "lc-alert-box lc-alert-box-" + this.props.type }, + React.createElement("blockquote", { className: "lc-blockquote" }, children)); + return wrap; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + children: "***Discord's*** **Markdown** _is_ [supported](https://google.com) or you can just insert your own react childs." + } + ], [ + { + type: "info" + }, + { + type: "success" + }, + { + type: "warn" + }, + { + type: "error" + } + ]); + return AllPreviews; + })(); + } +} +exports.default = AlertBox; +AlertBox.defaultProps = { + type: "info", + children: null +}; +AlertBox.help = { + info: "You can insert markdown (as string) or normal react childs", + warn: "All string will be interpreted as markdown. If you want raw string, pass an array with the string inside." +}; +let AllPreviews; /***/ }), @@ -24774,54 +24774,54 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -let CodeBlockModules; -class CodeBlock extends React.Component { - get modules() { - return CodeBlockModules || (CodeBlockModules = [ - WebpackLoader_1.default.find(e => e.markup), - WebpackLoader_1.default.find(e => e.messageContent), - WebpackLoader_1.default.find(e => e.scrollbarGhostHairline), - WebpackLoader_1.default.find(e => e.listLanguages), - WebpackLoader_1.default.find(e => e.marginBottom8) - ]); - } - render() { - let [messageModule1, messageModule2, scrollbarModule1, hightlightJS, marginModule1] = this.modules; - const code = this.props.language === "plaintext" ? React.createElement("code", { className: `${scrollbarModule1.scrollbarGhostHairline} hljs` }, this.props.content) : React.createElement("code", { className: `${scrollbarModule1.scrollbarGhostHairline} hljs`, dangerouslySetInnerHTML: { __html: hightlightJS.highlight(this.props.language, this.props.content).value } }); - return (React.createElement("div", { className: `${messageModule1.markup} ${messageModule2.messageContent}` }, - React.createElement("pre", null, code), - React.createElement("div", { className: marginModule1.marginBottom8 }))); - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - content: "console.log(\"Exemple code\")" - } - ]); - AllPreviews.push([ - { - language: "js" - }, - { - language: "plaintext" - }, - ...CodeBlock.prototype.modules[3].listLanguages().filter(e => e !== "js").map(e => ({ language: e })) - ]); - return AllPreviews; - })(); - } -} -exports.default = CodeBlock; -CodeBlock.defaultProps = { - language: "plaintext", - content: "" -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +let CodeBlockModules; +class CodeBlock extends React.Component { + get modules() { + return CodeBlockModules || (CodeBlockModules = [ + WebpackLoader_1.default.find(e => e.markup), + WebpackLoader_1.default.find(e => e.messageContent), + WebpackLoader_1.default.find(e => e.scrollbarGhostHairline), + WebpackLoader_1.default.find(e => e.listLanguages), + WebpackLoader_1.default.find(e => e.marginBottom8) + ]); + } + render() { + let [messageModule1, messageModule2, scrollbarModule1, hightlightJS, marginModule1] = this.modules; + const code = this.props.language === "plaintext" ? React.createElement("code", { className: `${scrollbarModule1.scrollbarGhostHairline} hljs` }, this.props.content) : React.createElement("code", { className: `${scrollbarModule1.scrollbarGhostHairline} hljs`, dangerouslySetInnerHTML: { __html: hightlightJS.highlight(this.props.language, this.props.content).value } }); + return (React.createElement("div", { className: `${messageModule1.markup} ${messageModule2.messageContent}` }, + React.createElement("pre", null, code), + React.createElement("div", { className: marginModule1.marginBottom8 }))); + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + content: "console.log(\"Exemple code\")" + } + ]); + AllPreviews.push([ + { + language: "js" + }, + { + language: "plaintext" + }, + ...CodeBlock.prototype.modules[3].listLanguages().filter(e => e !== "js").map(e => ({ language: e })) + ]); + return AllPreviews; + })(); + } +} +exports.default = CodeBlock; +CodeBlock.defaultProps = { + language: "plaintext", + content: "" +}; +let AllPreviews; /***/ }), @@ -24834,105 +24834,105 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createProxyErrorCatcherClass = void 0; -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const DiscordTools_1 = __webpack_require__(/*! ../../modules/DiscordTools */ "./src/modules/DiscordTools.ts"); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -const uuid_1 = __webpack_require__(/*! ../../modules/uuid */ "./src/modules/uuid.ts"); -const AlertBox_1 = __webpack_require__(/*! ./AlertBox */ "./src/components/general/AlertBox.tsx"); -let ErrorCatcherModules; -class ErrorCatcher extends React.Component { - constructor(props) { - super(props); - this.defaultProps = { - children: null - }; - this.state = { - error: false, - hasSentNotification: false - }; - } - static get original() { - return ErrorCatcher; - } - get modules() { - return ErrorCatcherModules || (ErrorCatcherModules = [ - WebpackLoader_1.default.find(e => e.emptyStateImage && e.emptyState) - ]); - } - render() { - if (!this.state.error) { - return this.props.children; - } - else { // try to render a user-friendly interface. - const [emptyClasses] = this.modules; - if (!emptyClasses) { // If we can't, render nothing and show a notification. - if (this.state.hasSentNotification) - return null; // If the notification was already sent, don't send one. - const notification = DiscordTools_1.default.showNotification({ - body: "An error occured. Please check the console for more informations.", - icon: "https://github.com/lightcord.png", - onClick: noop_1.default, - onClose: noop_1.default, - onShow: noop_1.default, - title: "Lightcord Informations" - }); - this.setState({ - hasSentNotification: true - }); - setTimeout(() => { - notification.close(); - }, 2000); - return null; - } - return React.createElement("div", { className: emptyClasses.emptyState }, - React.createElement("div", { className: emptyClasses.emptyStateImage, style: { - marginTop: "20px" - } }), - React.createElement("div", { className: emptyClasses.emptyStateHeader }, "An error occured"), - React.createElement("p", { className: emptyClasses.emptyStateSubtext }, "Please check the console for more informations. Join our \u00ADsupport server for more help.")); - } - } - componentDidCatch(error, errorInfo) { - console.error(errorInfo.componentStack); - this.setState({ - error: true - }); - } - static get AllPreviews() { - return AllPreviews || (AllPreviews = [ - [ - { - children: React.createElement(AlertBox_1.default, { type: "info" }, "This AlertBox is protected by an ErrorCatcher.") - } - ] - ]); - } -} -exports.default = ErrorCatcher; -let AllPreviews; -function createProxyErrorCatcherClass(Class) { - var _a; - const ClassCopy = (_a = class Proxied extends React.Component { - render() { - return React.createElement(ErrorCatcher, { key: uuid_1.default() }, React.createElement(Class, Object.assign(Object.assign({ ref: "original" }, this.props), { key: uuid_1.default() }))); - } - static get original() { - return Class; - } - }, - _a.displayName = Class["displayName"] || Class["name"], - _a); - Object.entries(Object.getOwnPropertyDescriptors(Class)).forEach(value => { - if (value[0] in ClassCopy) - return; - Object.defineProperty(ClassCopy, value[0], value[1]); - }); - return ClassCopy; -} -exports.createProxyErrorCatcherClass = createProxyErrorCatcherClass; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createProxyErrorCatcherClass = void 0; +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const DiscordTools_1 = __webpack_require__(/*! ../../modules/DiscordTools */ "./src/modules/DiscordTools.ts"); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +const uuid_1 = __webpack_require__(/*! ../../modules/uuid */ "./src/modules/uuid.ts"); +const AlertBox_1 = __webpack_require__(/*! ./AlertBox */ "./src/components/general/AlertBox.tsx"); +let ErrorCatcherModules; +class ErrorCatcher extends React.Component { + constructor(props) { + super(props); + this.defaultProps = { + children: null + }; + this.state = { + error: false, + hasSentNotification: false + }; + } + static get original() { + return ErrorCatcher; + } + get modules() { + return ErrorCatcherModules || (ErrorCatcherModules = [ + WebpackLoader_1.default.find(e => e.emptyStateImage && e.emptyState) + ]); + } + render() { + if (!this.state.error) { + return this.props.children; + } + else { // try to render a user-friendly interface. + const [emptyClasses] = this.modules; + if (!emptyClasses) { // If we can't, render nothing and show a notification. + if (this.state.hasSentNotification) + return null; // If the notification was already sent, don't send one. + const notification = DiscordTools_1.default.showNotification({ + body: "An error occured. Please check the console for more informations.", + icon: "https://github.com/lightcord.png", + onClick: noop_1.default, + onClose: noop_1.default, + onShow: noop_1.default, + title: "Lightcord Informations" + }); + this.setState({ + hasSentNotification: true + }); + setTimeout(() => { + notification.close(); + }, 2000); + return null; + } + return React.createElement("div", { className: emptyClasses.emptyState }, + React.createElement("div", { className: emptyClasses.emptyStateImage, style: { + marginTop: "20px" + } }), + React.createElement("div", { className: emptyClasses.emptyStateHeader }, "An error occured"), + React.createElement("p", { className: emptyClasses.emptyStateSubtext }, "Please check the console for more informations. Join our \u00ADsupport server for more help.")); + } + } + componentDidCatch(error, errorInfo) { + console.error(errorInfo.componentStack); + this.setState({ + error: true + }); + } + static get AllPreviews() { + return AllPreviews || (AllPreviews = [ + [ + { + children: React.createElement(AlertBox_1.default, { type: "info" }, "This AlertBox is protected by an ErrorCatcher.") + } + ] + ]); + } +} +exports.default = ErrorCatcher; +let AllPreviews; +function createProxyErrorCatcherClass(Class) { + var _a; + const ClassCopy = (_a = class Proxied extends React.Component { + render() { + return React.createElement(ErrorCatcher, { key: uuid_1.default() }, React.createElement(Class, Object.assign(Object.assign({ ref: "original" }, this.props), { key: uuid_1.default() }))); + } + static get original() { + return Class; + } + }, + _a.displayName = Class["displayName"] || Class["name"], + _a); + Object.entries(Object.getOwnPropertyDescriptors(Class)).forEach(value => { + if (value[0] in ClassCopy) + return; + Object.defineProperty(ClassCopy, value[0], value[1]); + }); + return ClassCopy; +} +exports.createProxyErrorCatcherClass = createProxyErrorCatcherClass; /***/ }), @@ -24945,66 +24945,66 @@ exports.createProxyErrorCatcherClass = createProxyErrorCatcherClass; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FlexChild = void 0; -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -function getModules() { - return FlexModules || (FlexModules = [ - (() => { - let Flex = WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Flex"); - if (Flex) - Flex = Flex.default; - return Flex; - })() - ]); -} -let FlexModules; -class Flex extends React.Component { - constructor(props) { - super(props); - } - get modules() { - return getModules(); - } - render() { - let [FlexComponent] = this.modules; - return React.createElement(FlexComponent, Object.assign({}, this.props)); - } - static get Direction() { - return getModules()[0].Direction; - } - static get Align() { - return getModules()[0].Align; - } - static get Justify() { - return getModules()[0].Justify; - } - static get Wrap() { - return getModules()[0].Justify; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([{ - children: "Your components here." - }]); - return AllPreviews; - })(); - } -} -exports.default = Flex; -let AllPreviews; -class FlexChild extends React.Component { - get modules() { - return getModules(); - } - render() { - const [FlexComponent] = this.modules; - return React.createElement(FlexComponent.Child, Object.assign({}, this.props)); - } -} -exports.FlexChild = FlexChild; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FlexChild = void 0; +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +function getModules() { + return FlexModules || (FlexModules = [ + (() => { + let Flex = WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Flex"); + if (Flex) + Flex = Flex.default; + return Flex; + })() + ]); +} +let FlexModules; +class Flex extends React.Component { + constructor(props) { + super(props); + } + get modules() { + return getModules(); + } + render() { + let [FlexComponent] = this.modules; + return React.createElement(FlexComponent, Object.assign({}, this.props)); + } + static get Direction() { + return getModules()[0].Direction; + } + static get Align() { + return getModules()[0].Align; + } + static get Justify() { + return getModules()[0].Justify; + } + static get Wrap() { + return getModules()[0].Justify; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([{ + children: "Your components here." + }]); + return AllPreviews; + })(); + } +} +exports.default = Flex; +let AllPreviews; +class FlexChild extends React.Component { + get modules() { + return getModules(); + } + render() { + const [FlexComponent] = this.modules; + return React.createElement(FlexComponent.Child, Object.assign({}, this.props)); + } +} +exports.FlexChild = FlexChild; /***/ }), @@ -25017,45 +25017,45 @@ exports.FlexChild = FlexChild; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -let TitleModules; -class SettingSubTitle extends React.Component { - get modules() { - return TitleModules || (TitleModules = [ - WebpackLoader_1.default.find(e => typeof e.marginTop60 === "string"), - WebpackLoader_1.default.findByUniqueProperties(["h5"]), - window.Lightcord.Api.WebpackLoader.findByUniqueProperties(["size14"]), - window.Lightcord.Api.WebpackLoader.findByUniqueProperties(["colorStandard"]) - ]); - } - render() { - let [marginModule, titleModule, sizeModule, colorModule] = this.modules; - let props = this.props; - let className = `${colorModule.colorStandard} ${sizeModule.size14} ${titleModule.h5} ${marginModule.marginBottom4}`; - if (props.className) - className = +" " + props.className; - return React.createElement("h5", { className }, props.children); - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - children: "Example title" - } - ]); - return AllPreviews; - })(); - } -} -exports.default = SettingSubTitle; -SettingSubTitle.defaultProps = { - children: [], - className: "" -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +let TitleModules; +class SettingSubTitle extends React.Component { + get modules() { + return TitleModules || (TitleModules = [ + WebpackLoader_1.default.find(e => typeof e.marginTop60 === "string"), + WebpackLoader_1.default.findByUniqueProperties(["h5"]), + window.Lightcord.Api.WebpackLoader.findByUniqueProperties(["size14"]), + window.Lightcord.Api.WebpackLoader.findByUniqueProperties(["colorStandard"]) + ]); + } + render() { + let [marginModule, titleModule, sizeModule, colorModule] = this.modules; + let props = this.props; + let className = `${colorModule.colorStandard} ${sizeModule.size14} ${titleModule.h5} ${marginModule.marginBottom4}`; + if (props.className) + className = +" " + props.className; + return React.createElement("h5", { className }, props.children); + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + children: "Example title" + } + ]); + return AllPreviews; + })(); + } +} +exports.default = SettingSubTitle; +SettingSubTitle.defaultProps = { + children: [], + className: "" +}; +let AllPreviews; /***/ }), @@ -25068,46 +25068,46 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const Title_1 = __webpack_require__(/*! ./Title */ "./src/components/general/Title.tsx"); -let TitleModules; -class SettingsTitle extends React.Component { - constructor(props) { - super(props); - } - get modules() { - return TitleModules || (TitleModules = [ - WebpackLoader_1.default.find(e => typeof e.marginTop60 === "string") - ]); - } - render() { - let [marginModule] = this.modules; - let props = this.props; - let className = `${marginModule.marginTop60} ${marginModule.marginBottom20}`; - if (props.className) - className = +" " + props.className; - return React.createElement(Title_1.default, { className }, props.children); - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - children: "Example title" - } - ]); - return AllPreviews; - })(); - } -} -exports.default = SettingsTitle; -SettingsTitle.defaultProps = { - children: [""], - className: "" -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const Title_1 = __webpack_require__(/*! ./Title */ "./src/components/general/Title.tsx"); +let TitleModules; +class SettingsTitle extends React.Component { + constructor(props) { + super(props); + } + get modules() { + return TitleModules || (TitleModules = [ + WebpackLoader_1.default.find(e => typeof e.marginTop60 === "string") + ]); + } + render() { + let [marginModule] = this.modules; + let props = this.props; + let className = `${marginModule.marginTop60} ${marginModule.marginBottom20}`; + if (props.className) + className = +" " + props.className; + return React.createElement(Title_1.default, { className }, props.children); + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + children: "Example title" + } + ]); + return AllPreviews; + })(); + } +} +exports.default = SettingsTitle; +SettingsTitle.defaultProps = { + children: [""], + className: "" +}; +let AllPreviews; /***/ }), @@ -25120,117 +25120,117 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Tab = void 0; -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -const Title_1 = __webpack_require__(/*! ./Title */ "./src/components/general/Title.tsx"); -class Tabs extends React.Component { - constructor(props) { - super(props); - this.tabsElements = []; - this.state = { - active: this.props.active || null - }; - } - get tabs() { - return this.props.tabs || []; - } - changeTab(tab) { - if (tab === this.state.active) - return; - if (this.props.onChange) - this.props.onChange(tab); - this.setState({ - active: tab - }); - this.tabsElements.forEach(e => e.setActive(tab === e.props.id)); - } - render() { - return (React.createElement("div", { className: "lc-tabWrapper", style: this.props.style }, - React.createElement("div", { className: "lc-tabnav", style: { flex: "0 1 auto" } }, this.tabs.map(tab => { - return React.createElement(Tab, { TabContainer: this, title: tab.label, id: tab.id, key: btoa(tab.label + ":" + tab.id) }); - })), - React.createElement("div", { className: "lc-tab" }, this.props.children))); - } - isActive(tab) { - return this.state.active === tab; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - children: (React.createElement("div", { style: { marginTop: "20px", marginBottom: "20px" } }, - React.createElement("div", { style: { - backgroundColor: "var(--background-primary)", - padding: "30px 30px", - borderRadius: "8px" - }, className: "lc-tab-box-shadow" }, - React.createElement(Title_1.default, null, "Preview tabs")))) - } - ], [ - { - tabs: [ - { - label: "Active tab", - id: "1" - }, - { - label: "Unactive tab", - id: "2" - } - ] - } - ], [ - { - active: "1" - } - ], [ - { - onChange: (tabId) => { } - } - ]); - return AllPreviews; - })(); - } -} -exports.default = Tabs; -Tabs.defaultProps = { - children: null, - tabs: [{ label: "No tabs was passed to .", id: "none" }], - active: null, - onChange: noop_1.default, - style: {} -}; -let AllPreviews; -class Tab extends React.Component { - constructor(props) { - super(props); - this.state = { - active: props.TabContainer.isActive(props.id) - }; - this.props.TabContainer.tabsElements.push(this); - } - setActive(isActive) { - this.setState({ - active: !!isActive - }); - } - render() { - let className = `lc-navItem`; - if (this.state.active) { - className += ` lc-navItemActive`; - } - else { - className += ` lc-navItemInactive`; - } - return (React.createElement("div", { className: className, onClick: () => { - this.props.TabContainer.changeTab(this.props.id); - } }, this.props.title)); - } -} -exports.Tab = Tab; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Tab = void 0; +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +const Title_1 = __webpack_require__(/*! ./Title */ "./src/components/general/Title.tsx"); +class Tabs extends React.Component { + constructor(props) { + super(props); + this.tabsElements = []; + this.state = { + active: this.props.active || null + }; + } + get tabs() { + return this.props.tabs || []; + } + changeTab(tab) { + if (tab === this.state.active) + return; + if (this.props.onChange) + this.props.onChange(tab); + this.setState({ + active: tab + }); + this.tabsElements.forEach(e => e.setActive(tab === e.props.id)); + } + render() { + return (React.createElement("div", { className: "lc-tabWrapper", style: this.props.style }, + React.createElement("div", { className: "lc-tabnav", style: { flex: "0 1 auto" } }, this.tabs.map(tab => { + return React.createElement(Tab, { TabContainer: this, title: tab.label, id: tab.id, key: btoa(tab.label + ":" + tab.id) }); + })), + React.createElement("div", { className: "lc-tab" }, this.props.children))); + } + isActive(tab) { + return this.state.active === tab; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + children: (React.createElement("div", { style: { marginTop: "20px", marginBottom: "20px" } }, + React.createElement("div", { style: { + backgroundColor: "var(--background-primary)", + padding: "30px 30px", + borderRadius: "8px" + }, className: "lc-tab-box-shadow" }, + React.createElement(Title_1.default, null, "Preview tabs")))) + } + ], [ + { + tabs: [ + { + label: "Active tab", + id: "1" + }, + { + label: "Unactive tab", + id: "2" + } + ] + } + ], [ + { + active: "1" + } + ], [ + { + onChange: (tabId) => { } + } + ]); + return AllPreviews; + })(); + } +} +exports.default = Tabs; +Tabs.defaultProps = { + children: null, + tabs: [{ label: "No tabs was passed to .", id: "none" }], + active: null, + onChange: noop_1.default, + style: {} +}; +let AllPreviews; +class Tab extends React.Component { + constructor(props) { + super(props); + this.state = { + active: props.TabContainer.isActive(props.id) + }; + this.props.TabContainer.tabsElements.push(this); + } + setActive(isActive) { + this.setState({ + active: !!isActive + }); + } + render() { + let className = `lc-navItem`; + if (this.state.active) { + className += ` lc-navItemActive`; + } + else { + className += ` lc-navItemInactive`; + } + return (React.createElement("div", { className: className, onClick: () => { + this.props.TabContainer.changeTab(this.props.id); + } }, this.props.title)); + } +} +exports.Tab = Tab; /***/ }), @@ -25243,80 +25243,80 @@ exports.Tab = Tab; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Constants = void 0; -const React = __webpack_require__(/*! react */ "./src/alias/react.js"); -const Utils_1 = __webpack_require__(/*! ../../modules/Utils */ "./src/modules/Utils.ts"); -class Text extends React.Component { - render() { - let classList = this.props.className ? this.props.className.split(" ") : []; - let style = this.props.style ? Object.assign({}, this.props.style) : {}; - style.flexGrow = 0; - if (this.props.weight) { - classList.push(`lc-text-weight${Utils_1.default.firstLetterUppercase(this.props.weight)}`); - } - if (this.props.color) { - style.color = Utils_1.default.getColor(this.props.color) || this.props.color; - } - if (this.props.textCase) { - classList.push(`lc-text-cases${Utils_1.default.firstLetterUppercase(this.props.textCase)}`); - } - if (this.props.size) { - classList.push(...exports.Constants.sizes[this.props.size]); - } - if (this.props.family) { - classList.push(`lc-text-${this.props.family}`); - } - if (this.props.selectable) { - classList.push("lc-text-selectable"); - } - return React.createElement(this.props.tag, { - style, - className: classList.join(" ") - }, this.props.children); - } - static get AllPreviews() { - return AllPreviews || (AllPreviews = [ - this.weights.map(e => ({ weight: e })), - this.colors.map(e => ({ color: e })), - this.textCases.map(e => ({ textCase: e })), - this.sizes.map(e => ({ size: e })), - this.familys.map(e => ({ family: e })), - [ - { - children: "Test Text" - } - ], - ["div", "p", ...Utils_1.default.executeXTimes((index) => { - return "h" + (index + 1); - }, 6)].map(e => ({ tag: e })), - [true, false].map(e => ({ selectable: e })) - ]); - } - static get colors() { - return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()); - } -} -exports.default = Text; -Text.defaultProps = { - tag: "div", - children: null -}; -Text.weights = ["light", "normal", "medium", "semibold", "bold"]; -Text.textCases = ["lowercase", "uppercase"]; -Text.sizes = ["small", "medium", "medium_small", "medium_large", "large"]; -Text.familys = ["primary", "code"]; -let AllPreviews; -exports.Constants = { - sizes: { - small: ["lc-text-size12", "lc-text-height16"], - medium_small: ["lc-text-size14", "lc-text-height16"], - medium: ["lc-text-size16", "lc-text-height20"], - medium_large: ["lc-text-size20", "lc-text-height26"], - large: ["lc-text-size28", "lc-text-height34"] - } -}; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Constants = void 0; +const React = __webpack_require__(/*! react */ "./src/alias/react.js"); +const Utils_1 = __webpack_require__(/*! ../../modules/Utils */ "./src/modules/Utils.ts"); +class Text extends React.Component { + render() { + let classList = this.props.className ? this.props.className.split(" ") : []; + let style = this.props.style ? Object.assign({}, this.props.style) : {}; + style.flexGrow = 0; + if (this.props.weight) { + classList.push(`lc-text-weight${Utils_1.default.firstLetterUppercase(this.props.weight)}`); + } + if (this.props.color) { + style.color = Utils_1.default.getColor(this.props.color) || this.props.color; + } + if (this.props.textCase) { + classList.push(`lc-text-cases${Utils_1.default.firstLetterUppercase(this.props.textCase)}`); + } + if (this.props.size) { + classList.push(...exports.Constants.sizes[this.props.size]); + } + if (this.props.family) { + classList.push(`lc-text-${this.props.family}`); + } + if (this.props.selectable) { + classList.push("lc-text-selectable"); + } + return React.createElement(this.props.tag, { + style, + className: classList.join(" ") + }, this.props.children); + } + static get AllPreviews() { + return AllPreviews || (AllPreviews = [ + this.weights.map(e => ({ weight: e })), + this.colors.map(e => ({ color: e })), + this.textCases.map(e => ({ textCase: e })), + this.sizes.map(e => ({ size: e })), + this.familys.map(e => ({ family: e })), + [ + { + children: "Test Text" + } + ], + ["div", "p", ...Utils_1.default.executeXTimes((index) => { + return "h" + (index + 1); + }, 6)].map(e => ({ tag: e })), + [true, false].map(e => ({ selectable: e })) + ]); + } + static get colors() { + return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()); + } +} +exports.default = Text; +Text.defaultProps = { + tag: "div", + children: null +}; +Text.weights = ["light", "normal", "medium", "semibold", "bold"]; +Text.textCases = ["lowercase", "uppercase"]; +Text.sizes = ["small", "medium", "medium_small", "medium_large", "large"]; +Text.familys = ["primary", "code"]; +let AllPreviews; +exports.Constants = { + sizes: { + small: ["lc-text-size12", "lc-text-height16"], + medium_small: ["lc-text-size14", "lc-text-height16"], + medium: ["lc-text-size16", "lc-text-height20"], + medium_large: ["lc-text-size20", "lc-text-height26"], + large: ["lc-text-size28", "lc-text-height34"] + } +}; /***/ }), @@ -25329,47 +25329,47 @@ exports.Constants = { /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -let TitleModules; -class Title extends React.Component { - constructor(props) { - super(props); - } - get modules() { - return TitleModules || (TitleModules = [ - WebpackLoader_1.default.find(e => typeof e.colorStandard === "string"), - WebpackLoader_1.default.find(e => typeof e.size32 === "string"), - WebpackLoader_1.default.find(e => typeof e.h2 === "string") - ]); - } - render() { - let [colorModule, sizeModule, titleModule] = this.modules; - let props = this.props; - let className = `${colorModule.colorStandard} ${sizeModule.size14} ${titleModule.h2} ${titleModule.defaultColor} ${titleModule.defaultMarginh2}`; - if (props.className) - className += " " + props.className; - return React.createElement("h2", { className }, props.children); - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - children: "Example title" - } - ]); - return AllPreviews; - })(); - } -} -exports.default = Title; -Title.defaultProps = { - children: null, - className: null -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +let TitleModules; +class Title extends React.Component { + constructor(props) { + super(props); + } + get modules() { + return TitleModules || (TitleModules = [ + WebpackLoader_1.default.find(e => typeof e.colorStandard === "string"), + WebpackLoader_1.default.find(e => typeof e.size32 === "string"), + WebpackLoader_1.default.find(e => typeof e.h2 === "string") + ]); + } + render() { + let [colorModule, sizeModule, titleModule] = this.modules; + let props = this.props; + let className = `${colorModule.colorStandard} ${sizeModule.size14} ${titleModule.h2} ${titleModule.defaultColor} ${titleModule.defaultMarginh2}`; + if (props.className) + className += " " + props.className; + return React.createElement("h2", { className }, props.children); + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + children: "Example title" + } + ]); + return AllPreviews; + })(); + } +} +exports.default = Title; +Title.defaultProps = { + children: null, + className: null +}; +let AllPreviews; /***/ }), @@ -25382,78 +25382,78 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const excludeProperties_1 = __webpack_require__(/*! ../../modules/excludeProperties */ "./src/modules/excludeProperties.ts"); -let TooltipModules; -class Tooltip extends React.Component { - constructor(props) { - super(props); - } - get modules() { - return TooltipModules || (TooltipModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Tooltip") - ]); - } - render() { - let [Tooltip] = this.modules; - let props = excludeProperties_1.default(this.props, ["children"]); - return React.createElement(Tooltip.default, props, (data) => { - return React.createElement("div", Object.assign(Object.assign({}, data), { style: { - width: "fit-content", - height: "fit-content" - } }), this.props.children); - }); - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - children: React.createElement(window.Lightcord.Api.Components.inputs.Button, { - color: "green", - look: "ghost", - size: "small", - hoverColor: "yellow", - onClick: () => { }, - wrapper: false - }, "Hover this button to see the tooltip") - } - ]); - AllPreviews.push([ - { - text: "Example Tooltip Text" - } - ]); - let colors = []; - Tooltip.Colors.forEach(color => { - colors.push({ - color: color - }); - }); - AllPreviews.push(colors); - let positions = []; - Tooltip.Positions.forEach(p => { - positions.push({ - position: p - }); - }); - AllPreviews.push(positions); - return AllPreviews; - })(); - } -} -exports.default = Tooltip; -Tooltip.defaultProps = { - children: null, - text: "No text was passed to Tooltip", - position: "top", - color: "brand" -}; -Tooltip.Positions = ["top", "left", "right", "bottom"]; -Tooltip.Colors = ["black", "grey", "brand", "green", "yellow", "red"]; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const excludeProperties_1 = __webpack_require__(/*! ../../modules/excludeProperties */ "./src/modules/excludeProperties.ts"); +let TooltipModules; +class Tooltip extends React.Component { + constructor(props) { + super(props); + } + get modules() { + return TooltipModules || (TooltipModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Tooltip") + ]); + } + render() { + let [Tooltip] = this.modules; + let props = excludeProperties_1.default(this.props, ["children"]); + return React.createElement(Tooltip.default, props, (data) => { + return React.createElement("div", Object.assign(Object.assign({}, data), { style: { + width: "fit-content", + height: "fit-content" + } }), this.props.children); + }); + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + children: React.createElement(window.Lightcord.Api.Components.inputs.Button, { + color: "green", + look: "ghost", + size: "small", + hoverColor: "yellow", + onClick: () => { }, + wrapper: false + }, "Hover this button to see the tooltip") + } + ]); + AllPreviews.push([ + { + text: "Example Tooltip Text" + } + ]); + let colors = []; + Tooltip.Colors.forEach(color => { + colors.push({ + color: color + }); + }); + AllPreviews.push(colors); + let positions = []; + Tooltip.Positions.forEach(p => { + positions.push({ + position: p + }); + }); + AllPreviews.push(positions); + return AllPreviews; + })(); + } +} +exports.default = Tooltip; +Tooltip.defaultProps = { + children: null, + text: "No text was passed to Tooltip", + position: "top", + color: "brand" +}; +Tooltip.Positions = ["top", "left", "right", "bottom"]; +Tooltip.Colors = ["black", "grey", "brand", "green", "yellow", "red"]; +let AllPreviews; /***/ }), @@ -25466,186 +25466,186 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -let ButtonModules; -class Button extends React.Component { - constructor(props) { - super(props); - this.state = { - hover: false - }; - } - get modules() { - return ButtonModules || (ButtonModules = [ - WebpackLoader_1.default.findByUniqueProperties(["_horizontal"]), - WebpackLoader_1.default.findByUniqueProperties(["colorTransparent"]), - WebpackLoader_1.default.findByUniqueProperties(["buttonWrapper"]), - WebpackLoader_1.default.findByUniqueProperties(["ButtonColors"]), - ]); - } - render() { - let [flexModule, euhModule1, buttonModule, colorsModule,] = this.modules; - let props = {}; - if (this.props) { - if ("color" in this.props) { - props.color = this.props.color; - } - if ("children" in this.props) { - props.children = this.props.children; - } - if ("onClick" in this.props) { - props.onClick = this.props.onClick; - } - if ("onMouseDown" in this.props) { - props.onMouseDown = this.props.onMouseDown; - } - if ("wrapper" in this.props) { - props.wrapper = !!this.props.wrapper; - } - if ("look" in this.props) { - props.look = this.props.look; - } - if ("size" in this.props) { - props.size = this.props.size; - } - if ("hoverColor" in this.props) { - props.hoverColor = this.props.hoverColor; - } - if ("disabled" in this.props) { - props.disabled = this.props.disabled; - } - else { - props.disabled = false; - } - if ("style" in this.props) { - props.style = this.props.style; - } - else { - props.style = {}; - } - if ("onRightClick" in this.props) { - props.onRightClick = this.props.onRightClick; - } - else { - props.onRightClick = noop_1.default; - } - } - if (props.color) { - props.color = props.color.toLowerCase(); - if (!Button.Colors.includes(props.color)) { - props.color = Button.Colors[0]; - } - } - else { - props.color = Button.Colors[0]; - } - if (props.look) { - props.look = props.look.toLowerCase(); - if (!Button.Looks.includes(props.look)) { - props.look = Button.Looks[0]; - } - } - else { - props.look = Button.Looks[0]; - } - if (props.size) { - props.size = props.size.toLowerCase(); - if (!Button.Sizes.includes(props.size)) { - props.size = Button.Sizes[0]; - } - } - else { - props.size = Button.Sizes[0]; - } - if (props.hoverColor) { - props.hoverColor = props.hoverColor.toLowerCase(); - if (!Button.HoverColors.includes(props.hoverColor)) { - props.hoverColor = Button.HoverColors[0]; - } - } - else { - props.hoverColor = Button.HoverColors[0]; - } - let buttonSize = props.size ? colorsModule.ButtonSizes[props.size.toUpperCase()] || "" : ""; - if (buttonSize) - buttonSize = " " + buttonSize; - let hoverColor = props.hoverColor ? colorsModule.ButtonHovers[props.hoverColor.toUpperCase()] || "" : ""; - if (hoverColor) - hoverColor = " " + hoverColor; - props.onClick = typeof props.onClick === "function" ? props.onClick : noop_1.default; - if (typeof props.wrapper !== "boolean") - props.wrapper = true; - props.onMouseDown = typeof props.onMouseDown === "function" ? props.onMouseDown : noop_1.default; - let hover = this.state.hover ? euhModule1.hasHover : ""; - if (hover) - hover = " " + hover; - let classListButton = `${flexModule.flexChild} ${euhModule1.button} ${colorsModule.ButtonLooks[props.look.toUpperCase()]} ${colorsModule.ButtonColors[props.color.toUpperCase()]}${buttonSize}${hoverColor}${hover} ${euhModule1.grow}`.split(" "); - if (typeof this.props.className === "string" && this.props.className) { - classListButton = this.props.className.split(" ").concat(classListButton); - } - let button = React.createElement("button", { type: "button", ref: "button", className: classListButton.join(" "), style: Object.assign({ flex: "0 1 auto" }, props.style), onClick: props.onClick, onMouseEnter: (ev) => { - if (!hoverColor) - return; - this.setState({ hover: true }); - }, onMouseLeave: (ev) => { - if (!hoverColor) - return; - this.setState({ hover: false }); - }, disabled: props.disabled, onContextMenu: () => props.onRightClick(), onMouseDown: props.onMouseDown }, - React.createElement("div", { className: euhModule1.contents }, props.children)); - if (props.wrapper) { - return React.createElement("div", { ref: "wrapper", className: buttonModule.buttonWrapper }, button); - } - return button; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - let colors = []; - for (let color of Button.Colors) { - colors.push({ - color: color - }); - } - AllPreviews.push(colors); - let looks = []; - for (let look of Button.Looks) { - looks.push({ - look: look - }); - } - AllPreviews.push(looks); - let sizes = []; - for (let size of Button.Sizes) { - sizes.push({ - size: size - }); - } - AllPreviews.push(sizes); - let hovers = []; - for (let hover of Button.HoverColors) { - hovers.push({ - hoverColor: hover - }); - } - AllPreviews.push(hovers); - AllPreviews.push([{ children: "Test Button" }]); - AllPreviews.push([{ onClick: () => { } }]); - AllPreviews.push([{ wrapper: false }]); - AllPreviews.push([{ disabled: false }, { disabled: true }]); - return AllPreviews; - })(); - } -} -exports.default = Button; -Button.Colors = ["brand", "grey", "red", "green", "yellow", "primary", "link", "white", "black", "transparent"]; -Button.Looks = ["filled", "inverted", "outlined", "ghost", "link", "blank"]; -Button.Sizes = ["small", "medium", "large", "xlarge", "min", "max", "icon", "none"]; -Button.HoverColors = ["default", ...Button.Colors]; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +let ButtonModules; +class Button extends React.Component { + constructor(props) { + super(props); + this.state = { + hover: false + }; + } + get modules() { + return ButtonModules || (ButtonModules = [ + WebpackLoader_1.default.findByUniqueProperties(["_horizontal"]), + WebpackLoader_1.default.findByUniqueProperties(["colorTransparent"]), + WebpackLoader_1.default.findByUniqueProperties(["buttonWrapper"]), + WebpackLoader_1.default.findByUniqueProperties(["ButtonColors"]), + ]); + } + render() { + let [flexModule, euhModule1, buttonModule, colorsModule,] = this.modules; + let props = {}; + if (this.props) { + if ("color" in this.props) { + props.color = this.props.color; + } + if ("children" in this.props) { + props.children = this.props.children; + } + if ("onClick" in this.props) { + props.onClick = this.props.onClick; + } + if ("onMouseDown" in this.props) { + props.onMouseDown = this.props.onMouseDown; + } + if ("wrapper" in this.props) { + props.wrapper = !!this.props.wrapper; + } + if ("look" in this.props) { + props.look = this.props.look; + } + if ("size" in this.props) { + props.size = this.props.size; + } + if ("hoverColor" in this.props) { + props.hoverColor = this.props.hoverColor; + } + if ("disabled" in this.props) { + props.disabled = this.props.disabled; + } + else { + props.disabled = false; + } + if ("style" in this.props) { + props.style = this.props.style; + } + else { + props.style = {}; + } + if ("onRightClick" in this.props) { + props.onRightClick = this.props.onRightClick; + } + else { + props.onRightClick = noop_1.default; + } + } + if (props.color) { + props.color = props.color.toLowerCase(); + if (!Button.Colors.includes(props.color)) { + props.color = Button.Colors[0]; + } + } + else { + props.color = Button.Colors[0]; + } + if (props.look) { + props.look = props.look.toLowerCase(); + if (!Button.Looks.includes(props.look)) { + props.look = Button.Looks[0]; + } + } + else { + props.look = Button.Looks[0]; + } + if (props.size) { + props.size = props.size.toLowerCase(); + if (!Button.Sizes.includes(props.size)) { + props.size = Button.Sizes[0]; + } + } + else { + props.size = Button.Sizes[0]; + } + if (props.hoverColor) { + props.hoverColor = props.hoverColor.toLowerCase(); + if (!Button.HoverColors.includes(props.hoverColor)) { + props.hoverColor = Button.HoverColors[0]; + } + } + else { + props.hoverColor = Button.HoverColors[0]; + } + let buttonSize = props.size ? colorsModule.ButtonSizes[props.size.toUpperCase()] || "" : ""; + if (buttonSize) + buttonSize = " " + buttonSize; + let hoverColor = props.hoverColor ? colorsModule.ButtonHovers[props.hoverColor.toUpperCase()] || "" : ""; + if (hoverColor) + hoverColor = " " + hoverColor; + props.onClick = typeof props.onClick === "function" ? props.onClick : noop_1.default; + if (typeof props.wrapper !== "boolean") + props.wrapper = true; + props.onMouseDown = typeof props.onMouseDown === "function" ? props.onMouseDown : noop_1.default; + let hover = this.state.hover ? euhModule1.hasHover : ""; + if (hover) + hover = " " + hover; + let classListButton = `${flexModule.flexChild} ${euhModule1.button} ${colorsModule.ButtonLooks[props.look.toUpperCase()]} ${colorsModule.ButtonColors[props.color.toUpperCase()]}${buttonSize}${hoverColor}${hover} ${euhModule1.grow}`.split(" "); + if (typeof this.props.className === "string" && this.props.className) { + classListButton = this.props.className.split(" ").concat(classListButton); + } + let button = React.createElement("button", { type: "button", ref: "button", className: classListButton.join(" "), style: Object.assign({ flex: "0 1 auto" }, props.style), onClick: props.onClick, onMouseEnter: (ev) => { + if (!hoverColor) + return; + this.setState({ hover: true }); + }, onMouseLeave: (ev) => { + if (!hoverColor) + return; + this.setState({ hover: false }); + }, disabled: props.disabled, onContextMenu: () => props.onRightClick(), onMouseDown: props.onMouseDown }, + React.createElement("div", { className: euhModule1.contents }, props.children)); + if (props.wrapper) { + return React.createElement("div", { ref: "wrapper", className: buttonModule.buttonWrapper }, button); + } + return button; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + let colors = []; + for (let color of Button.Colors) { + colors.push({ + color: color + }); + } + AllPreviews.push(colors); + let looks = []; + for (let look of Button.Looks) { + looks.push({ + look: look + }); + } + AllPreviews.push(looks); + let sizes = []; + for (let size of Button.Sizes) { + sizes.push({ + size: size + }); + } + AllPreviews.push(sizes); + let hovers = []; + for (let hover of Button.HoverColors) { + hovers.push({ + hoverColor: hover + }); + } + AllPreviews.push(hovers); + AllPreviews.push([{ children: "Test Button" }]); + AllPreviews.push([{ onClick: () => { } }]); + AllPreviews.push([{ wrapper: false }]); + AllPreviews.push([{ disabled: false }, { disabled: true }]); + return AllPreviews; + })(); + } +} +exports.default = Button; +Button.Colors = ["brand", "grey", "red", "green", "yellow", "primary", "link", "white", "black", "transparent"]; +Button.Looks = ["filled", "inverted", "outlined", "ghost", "link", "blank"]; +Button.Sizes = ["small", "medium", "large", "xlarge", "min", "max", "icon", "none"]; +Button.HoverColors = ["default", ...Button.Colors]; +let AllPreviews; /***/ }), @@ -25658,173 +25658,173 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const Utils_1 = __webpack_require__(/*! ../../modules/Utils */ "./src/modules/Utils.ts"); -const Constants = { - defaultColor: 10070709, - baseColors: [ - 1752220, - 3066993, - 3447003, - 10181046, - 15277667, - 15844367, - 15105570, - 15158332, - 9807270, - 6323595, - 1146986, - 2067276, - 2123412, - 7419530, - 11342935, - 12745742, - 11027200, - 10038562, - 9936031, - 5533306 - ] -}; -let ColorPickerModules; -let isFetching = null; -/** - * This componennt needs to be loaded. As a result, you may experience 100-300ms loading the first time. - * Render will return `null` before loaded. - */ -class ColorPicker extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - value: props.value || null, - lastColor: this.props.value - }; - this.onChange = this.onChange.bind(this); - } - /** Preload the component. */ - static preload() { - if (ColorPicker.prototype.modules[0]) - return; // already loaded - if (isFetching) - return; // is fetching so don't double preload. - try { // If we caught an error - new ColorPicker({}).render(); - } - catch (e) { - console.error(e); - } - } - onChange(val) { - this.props.onChange(val); - this.setState({ - value: val - }); - this.forceUpdate(); - } - get modules() { - return ColorPickerModules || (ColorPickerModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "ColorPicker") - ]); - } - renderColorPicker() { - let [ColorPickerComponent] = this.modules; - if (!ColorPickerComponent) { - if (isFetching) { // support for multiple color picker - isFetching.then(() => this.forceUpdate()); - return null; - } - ColorPickerModules = null; - let resolve; - isFetching = new Promise(res => (resolve = res)); - const GuildSettingsRoles = new (WebpackLoader_1.default.find(e => e.default && e.default.displayName && e.default.displayName.includes("GuildSettingsRoles")).default)().render().type; - const settings = GuildSettingsRoles.prototype.renderRoleSettings.call({ - props: { - guild: { - roles: [], - id: null, - isOwner: () => false - }, - currentUser: { - id: null - } - }, - getSelectedRole() { - return { - id: null - }; - }, - renderHeader: GuildSettingsRoles.prototype.renderHeader - }); - const GuildRoleSettings = settings.props.children[1].type; - let children = GuildRoleSettings.prototype.renderColorPicker.call({ - props: { - role: { - id: null, - color: 0 - }, - locked: false, - everyone: false - } - }).props.children; - children.type(children.props).props.children.type._ctor().then(c => { - ColorPickerModules = null; - this.forceUpdate(); - resolve(); - }); - return null; - } - return React.createElement(ColorPickerComponent.default, { colors: this.props.baseColors, defaultColor: this.props.defaultColor, value: this.state.value, disabled: this.props.disabled, onChange: this.onChange, customColor: this.props.customColor }); - } - render() { - if (this.state.lastColor !== this.props.value) { - this.state = { - value: this.props.value, - lastColor: this.props.value - }; - } - return this.renderColorPicker(); - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([ - { - disabled: false - }, - { - disabled: true - } - ], [ - { - value: Utils_1.default.HexColorToDecimal("#7289DA") - }, { - value: null - } - ], [ - { - onChange: (color) => { } - } - ]); - return AllPreviews; - })(); - } -} -exports.default = ColorPicker; -ColorPicker.defaultProps = { - defaultColor: Constants.defaultColor, - customColor: null, - baseColors: Constants.baseColors, - value: null, - disabled: false, - onChange: noop_1.default -}; -ColorPicker.help = { - info: "To convert hex colors to decimal, you can do `Lightcord.Api.Utils.HexColorToDecimal('#7289DA')` and go back with `Lightcord.Api.Utils.DecimalColorToHex(7506394)`", - warn: "The component may not appear instantly. The component needs to be loaded, so you could experience 50-300ms loading time depending on your internet connection." -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const Utils_1 = __webpack_require__(/*! ../../modules/Utils */ "./src/modules/Utils.ts"); +const Constants = { + defaultColor: 10070709, + baseColors: [ + 1752220, + 3066993, + 3447003, + 10181046, + 15277667, + 15844367, + 15105570, + 15158332, + 9807270, + 6323595, + 1146986, + 2067276, + 2123412, + 7419530, + 11342935, + 12745742, + 11027200, + 10038562, + 9936031, + 5533306 + ] +}; +let ColorPickerModules; +let isFetching = null; +/** + * This componennt needs to be loaded. As a result, you may experience 100-300ms loading the first time. + * Render will return `null` before loaded. + */ +class ColorPicker extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + value: props.value || null, + lastColor: this.props.value + }; + this.onChange = this.onChange.bind(this); + } + /** Preload the component. */ + static preload() { + if (ColorPicker.prototype.modules[0]) + return; // already loaded + if (isFetching) + return; // is fetching so don't double preload. + try { // If we caught an error + new ColorPicker({}).render(); + } + catch (e) { + console.error(e); + } + } + onChange(val) { + this.props.onChange(val); + this.setState({ + value: val + }); + this.forceUpdate(); + } + get modules() { + return ColorPickerModules || (ColorPickerModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "ColorPicker") + ]); + } + renderColorPicker() { + let [ColorPickerComponent] = this.modules; + if (!ColorPickerComponent) { + if (isFetching) { // support for multiple color picker + isFetching.then(() => this.forceUpdate()); + return null; + } + ColorPickerModules = null; + let resolve; + isFetching = new Promise(res => (resolve = res)); + const GuildSettingsRoles = new (WebpackLoader_1.default.find(e => e.default && e.default.displayName && e.default.displayName.includes("GuildSettingsRoles")).default)().render().type; + const settings = GuildSettingsRoles.prototype.renderRoleSettings.call({ + props: { + guild: { + roles: [], + id: null, + isOwner: () => false + }, + currentUser: { + id: null + } + }, + getSelectedRole() { + return { + id: null + }; + }, + renderHeader: GuildSettingsRoles.prototype.renderHeader + }); + const GuildRoleSettings = settings.props.children[1].type; + let children = GuildRoleSettings.prototype.renderColorPicker.call({ + props: { + role: { + id: null, + color: 0 + }, + locked: false, + everyone: false + } + }).props.children; + children.type(children.props).props.children.type._ctor().then(c => { + ColorPickerModules = null; + this.forceUpdate(); + resolve(); + }); + return null; + } + return React.createElement(ColorPickerComponent.default, { colors: this.props.baseColors, defaultColor: this.props.defaultColor, value: this.state.value, disabled: this.props.disabled, onChange: this.onChange, customColor: this.props.customColor }); + } + render() { + if (this.state.lastColor !== this.props.value) { + this.state = { + value: this.props.value, + lastColor: this.props.value + }; + } + return this.renderColorPicker(); + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([ + { + disabled: false + }, + { + disabled: true + } + ], [ + { + value: Utils_1.default.HexColorToDecimal("#7289DA") + }, { + value: null + } + ], [ + { + onChange: (color) => { } + } + ]); + return AllPreviews; + })(); + } +} +exports.default = ColorPicker; +ColorPicker.defaultProps = { + defaultColor: Constants.defaultColor, + customColor: null, + baseColors: Constants.baseColors, + value: null, + disabled: false, + onChange: noop_1.default +}; +ColorPicker.help = { + info: "To convert hex colors to decimal, you can do `Lightcord.Api.Utils.HexColorToDecimal('#7289DA')` and go back with `Lightcord.Api.Utils.DecimalColorToHex(7506394)`", + warn: "The component may not appear instantly. The component needs to be loaded, so you could experience 50-300ms loading time depending on your internet connection." +}; +let AllPreviews; /***/ }), @@ -25837,400 +25837,400 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -/** TODO: Finish DateInput */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CalendarPicker = exports.AnimatedCalendarPicker = exports.isDateValid = exports.getEmotion = exports.getDateFNS = void 0; -const React = __webpack_require__(/*! react */ "./src/alias/react.js"); -const DateRange_1 = __webpack_require__(/*! ./DateRange */ "./src/components/inputs/DateRange.tsx"); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const TextInput_1 = __webpack_require__(/*! ./TextInput */ "./src/components/inputs/TextInput.tsx"); -const Button_1 = __webpack_require__(/*! ./Button */ "./src/components/inputs/Button.tsx"); -const ReactDOM = __webpack_require__(/*! react-dom */ "./src/alias/react-dom.js"); -const DatePicker = __webpack_require__(/*! react-datepicker */ "./node_modules/react-datepicker/dist/es/index.js"); -let _datefns; -function getDateFNS() { - return _datefns || (_datefns = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/index.js")); -} -exports.getDateFNS = getDateFNS; -function getEmotion() { - return window["__SECRET_EMOTION__"]; -} -exports.getEmotion = getEmotion; -let DateInputModules; -class DateInput extends React.Component { - constructor(props) { - super(props); - this.state = { - inputResetKey: 0, - isCalendarPickerOpen: false, - calendarRight: null, - calendarTop: null, - value: props.defaultValue - }; - } - get modules() { - return DateInputModules || (DateInputModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Clickable"), - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "TransitionGroup") - ]); - } - componentDidUpdate(e) { - const defaultValue = this.props.defaultValue; - const dateFormat = this.props.dateFormat; - if (e.defaultValue !== defaultValue && null != defaultValue) { - if (!this.inputRef) - return; - let str = getDateFNS().format(defaultValue, dateFormat); - this.inputRef["value"] = str; - } - } - closeCalendarPicker() { - this.setState({ - isCalendarPickerOpen: false - }); - } - getCurrentValue() { - let value = this.state.value; - let dateFormat = this.props.dateFormat; - if (!value) - return; - if (isDateValid(value)) - return getDateFNS().format(value, dateFormat); - return null; - } - handleDateChange(value) { - this.closeCalendarPicker(); - const onChange = this.props.onChange; - const name = this.props.name; - this.setState((state) => { - return { - value: value, - inputResetKey: state.inputResetKey + 1 - }; - }, function () { - null != onChange && onChange(value, name); - }); - } - handleInputBlur(ev) { - const value = this.state.value; - const newvalue = ev.currentTarget.value; - const iso = getDateFNS().parseISO(newvalue); - if (isDateValid(iso) && value) { - if (iso.valueOf() !== value.valueOf()) - this.setState(function (state) { - return { - value: iso, - inputResetKey: state.inputResetKey + 1 - }; - }, function () { - const props = this.props; - const onChange = props.onChange; - const name = props.name; - if (onChange) - onChange(iso, name); - }); - } - } - toggleCalendarVisibility(ev) { - const rect = ev.currentTarget.getBoundingClientRect(); - const bottom = rect.bottom; - const right = rect.right; - const innerWidth = window.innerWidth; - this.setState(function (state) { - return { - isCalendarPickerOpen: !state.isCalendarPickerOpen, - calendarRight: innerWidth - right, - calendarTop: bottom - }; - }); - } - setRef(ref) { - this.inputRef = ref; - } - renderCalendarPicker() { - let state = this.state; - let calendarRight = state.calendarRight; - let calendarTop = state.calendarTop; - let isCalendarPickerOpen = state.isCalendarPickerOpen; - let value = state.value; - let props = this.props; - let minDate = props.minDate; - let maxDate = props.maxDate; - let endDate = props.endDate; - let filterDate = props.filterDate; - let startDate = props.startDate; - let selectsEnd = props.selectsEnd; - let selectsStart = props.selectsStart; - let isModalInput = props.isModalInput; - let y = props.showMonthYearPicker; - return isCalendarPickerOpen ? React.createElement(AnimatedCalendarPicker, { - value: value ? value : undefined, - onClickOutside: this.closeCalendarPicker.bind(this), - onSelect: this.handleDateChange.bind(this), - minDate: minDate, - maxDate: maxDate, - endDate: endDate, - filterDate: filterDate, - startDate: startDate, - selectsEnd: selectsEnd, - selectsStart: selectsStart, - right: calendarRight, - top: calendarTop, - isModalInput: isModalInput, - showMonthYearPicker: y - }) : null; - } - render() { - const [Clickable, TransitionGroup] = this.modules; - let name = this.props.name; - return React.createElement(Clickable.default, { - className: getEmotion().css({ - position: "relative" - }) - }, React.createElement(TextInput_1.getInternalTextInput(), { - inputClassName: getEmotion().css({ - paddingRight: "32px" - }), - name: name, - onBlur: this.handleInputBlur.bind(this), - defaultValue: this.getCurrentValue(), - inputRef: this.setRef.bind(this) - }), React.createElement(Button_1.default, { - className: getEmotion().css({ - "&:hover": { - opacity: 1 - }, - position: "absolute", - right: 0, - top: "50%", - opacity: .6, - padding: "8px", - transform: "translateY(-50%)", - transition: "opacity .125s" - }), - color: "transparent", - onMouseDown: this.toggleCalendarVisibility.bind(this), - wrapper: false - //TODO: Add icon - }), ReactDOM.createPortal(React.createElement(TransitionGroup.default, { - component: "div", - transitionAppear: false - }, this.renderCalendarPicker()), window.document.body)); - } - static get AllPreviews() { - return AllPreviews || (AllPreviews = [ - [ - { - dateFormat: DateRange_1.DateConstants.DATE_FORMAT - }, - { - dateFormat: "dd/MM/yyyy" - }, - { - dateFormat: "MM/dd/yyyy" - } - ], - [ - { - defaultValue: new Date() - }, - { - defaultValue: null - }, - { - defaultValue: new Date(1597061085498) - } - ], - [ - { - filterDate: (date) => true - }, - { - filterDate: (date) => { - if (date.getDay() !== 0) - return false; - return true; - } - } - ], - [ - { - isModalInput: true - }, - { - isModalInput: false - } - ], - [ - { - maxDate: null - }, - { - maxDate: new Date(Date.now() + 6.048e+8) - } - ], - [ - { - minDate: null - }, - { - minDate: new Date(Date.now() - 6.048e+8) - } - ], - [ - { - onChange: (value, name) => { } - } - ], - [ - { - selectsStart: null - }, - { - selectsStart: new Date(Date.now() - (8.64e+7 * 2)) - } - ], - [ - { - selectsEnd: null - }, - { - selectsEnd: new Date(Date.now() + (8.64e+7 * 2)) - } - ], - [ - { - showMonthYearPicker: false - }, - { - showMonthYearPicker: true - } - ], - [ - { - startDate: null - }, - { - endDate: null - } - ], - [ - { - name: "api-preview-dateinput" - } - ] - ]); - } -} -exports.default = DateInput; -DateInput.defaultProps = { - dateFormat: DateRange_1.DateConstants.DATE_FORMAT, - isModalInput: true -}; -DateInput.help = { - warn: "This component is still `experimental`. Please report issues to [Lightcord's developers](https://github.com/Lightcord/Lightcord/issues)." -}; -let AllPreviews; -function isDateValid(date) { - return (date instanceof Date || typeof date === "object" || Object.prototype.toString.call(date) === "[object Date]") && !isNaN(date.valueOf()); -} -exports.isDateValid = isDateValid; -let AnimatedCalendarPickerModules; -class AnimatedCalendarPicker extends React.Component { - constructor(props) { - super(props); - this.state = { - menuAnimation: new this.modules[0].default.Value(0) - }; - } - get modules() { - return AnimatedCalendarPickerModules || (AnimatedCalendarPickerModules = [ - WebpackLoader_1.default.findByUniqueProperties(["Value", "timing"]) - ]); - } - componentWillEnter(ev) { - this.modules[0].default.timing(this.state.menuAnimation, { - toValue: 1, - duration: 150 - }).start(ev); - } - componentWillLeave(e) { - this.modules[0].default.timing(this.state.menuAnimation, { - toValue: 0, - duration: 150 - }).start(e); - } - render() { - let props = this.props, value = props.value, onClickOutside = props.onClickOutside, onSelect = props.onSelect, minDate = props.minDate, maxDate = props.maxDate, endDate = props.endDate, filterDate = props.filterDate, startDate = props.startDate, selectsEnd = props.selectsEnd, selectsStart = props.selectsStart, top = props.top, right = props.right, isModalInput = props.isModalInput, showMonthYearPicker = props.showMonthYearPicker, menuAnimation = this.state.menuAnimation, interpolation = menuAnimation.interpolate({ - inputRange: [0, 1], - outputRange: ["-10px", "0px"] - }); - const emotion = getEmotion(); - return React.createElement(this.modules[0].default.div, { - className: [emotion.css({ - marginRight: "1px", - margintop: "6px", - position: "fixed", - zIndex: 2 - }), isModalInput ? emotion.css({ - zIndex: 10000 - }) : null].filter(e => e).join(" "), - style: { - opacity: menuAnimation, - right: right, - top: top, - transform: [{ - translateY: interpolation - }] - } - }, React.createElement(CalendarPicker, { - minDate: minDate, - maxDate: maxDate, - endDate: endDate, - filterDate: filterDate, - startDate: startDate, - selectsEnd: selectsEnd, - selectsStart: selectsStart, - value: value, - onSelect: onSelect, - onClickOutside: onClickOutside, - showMonthYearPicker: showMonthYearPicker, - onChange: console.log - })); - } -} -exports.AnimatedCalendarPicker = AnimatedCalendarPicker; -AnimatedCalendarPicker.displayName = "AnimatedCalendarPicker"; -class CalendarPicker extends React.Component { - render() { - var e = this.props, t = e.onClickOutside, r = e.onSelect, n = e.locale, l = e.value, o = e.endDate, u = e.filterDate, f = e.startDate, c = e.minDate, d = e.maxDate, p = e.selectsEnd, y = e.selectsStart, v = e.showMonthYearPicker; - return React.createElement("div", { - className: "lc-calendarPicker" - }, React.createElement(DatePicker.default, { - fixedHeight: true, - inline: true, - selected: l, - locale: n, - onClickOutside: t, - onSelect: r, - onChange: r, - endDate: o, - filterDate: u, - startDate: f, - minDate: c, - maxDate: d, - selectsEnd: p, - selectsStart: y, - showMonthYearPicker: v - })); - } -} -exports.CalendarPicker = CalendarPicker; -CalendarPicker.defaultProps = { - value: new Date() -}; -CalendarPicker.displayName = "CalendarPicker"; + +/** TODO: Finish DateInput */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CalendarPicker = exports.AnimatedCalendarPicker = exports.isDateValid = exports.getEmotion = exports.getDateFNS = void 0; +const React = __webpack_require__(/*! react */ "./src/alias/react.js"); +const DateRange_1 = __webpack_require__(/*! ./DateRange */ "./src/components/inputs/DateRange.tsx"); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const TextInput_1 = __webpack_require__(/*! ./TextInput */ "./src/components/inputs/TextInput.tsx"); +const Button_1 = __webpack_require__(/*! ./Button */ "./src/components/inputs/Button.tsx"); +const ReactDOM = __webpack_require__(/*! react-dom */ "./src/alias/react-dom.js"); +const DatePicker = __webpack_require__(/*! react-datepicker */ "./node_modules/react-datepicker/dist/es/index.js"); +let _datefns; +function getDateFNS() { + return _datefns || (_datefns = __webpack_require__(/*! date-fns */ "./node_modules/date-fns/esm/index.js")); +} +exports.getDateFNS = getDateFNS; +function getEmotion() { + return window["__SECRET_EMOTION__"]; +} +exports.getEmotion = getEmotion; +let DateInputModules; +class DateInput extends React.Component { + constructor(props) { + super(props); + this.state = { + inputResetKey: 0, + isCalendarPickerOpen: false, + calendarRight: null, + calendarTop: null, + value: props.defaultValue + }; + } + get modules() { + return DateInputModules || (DateInputModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Clickable"), + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "TransitionGroup") + ]); + } + componentDidUpdate(e) { + const defaultValue = this.props.defaultValue; + const dateFormat = this.props.dateFormat; + if (e.defaultValue !== defaultValue && null != defaultValue) { + if (!this.inputRef) + return; + let str = getDateFNS().format(defaultValue, dateFormat); + this.inputRef["value"] = str; + } + } + closeCalendarPicker() { + this.setState({ + isCalendarPickerOpen: false + }); + } + getCurrentValue() { + let value = this.state.value; + let dateFormat = this.props.dateFormat; + if (!value) + return; + if (isDateValid(value)) + return getDateFNS().format(value, dateFormat); + return null; + } + handleDateChange(value) { + this.closeCalendarPicker(); + const onChange = this.props.onChange; + const name = this.props.name; + this.setState((state) => { + return { + value: value, + inputResetKey: state.inputResetKey + 1 + }; + }, function () { + null != onChange && onChange(value, name); + }); + } + handleInputBlur(ev) { + const value = this.state.value; + const newvalue = ev.currentTarget.value; + const iso = getDateFNS().parseISO(newvalue); + if (isDateValid(iso) && value) { + if (iso.valueOf() !== value.valueOf()) + this.setState(function (state) { + return { + value: iso, + inputResetKey: state.inputResetKey + 1 + }; + }, function () { + const props = this.props; + const onChange = props.onChange; + const name = props.name; + if (onChange) + onChange(iso, name); + }); + } + } + toggleCalendarVisibility(ev) { + const rect = ev.currentTarget.getBoundingClientRect(); + const bottom = rect.bottom; + const right = rect.right; + const innerWidth = window.innerWidth; + this.setState(function (state) { + return { + isCalendarPickerOpen: !state.isCalendarPickerOpen, + calendarRight: innerWidth - right, + calendarTop: bottom + }; + }); + } + setRef(ref) { + this.inputRef = ref; + } + renderCalendarPicker() { + let state = this.state; + let calendarRight = state.calendarRight; + let calendarTop = state.calendarTop; + let isCalendarPickerOpen = state.isCalendarPickerOpen; + let value = state.value; + let props = this.props; + let minDate = props.minDate; + let maxDate = props.maxDate; + let endDate = props.endDate; + let filterDate = props.filterDate; + let startDate = props.startDate; + let selectsEnd = props.selectsEnd; + let selectsStart = props.selectsStart; + let isModalInput = props.isModalInput; + let y = props.showMonthYearPicker; + return isCalendarPickerOpen ? React.createElement(AnimatedCalendarPicker, { + value: value ? value : undefined, + onClickOutside: this.closeCalendarPicker.bind(this), + onSelect: this.handleDateChange.bind(this), + minDate: minDate, + maxDate: maxDate, + endDate: endDate, + filterDate: filterDate, + startDate: startDate, + selectsEnd: selectsEnd, + selectsStart: selectsStart, + right: calendarRight, + top: calendarTop, + isModalInput: isModalInput, + showMonthYearPicker: y + }) : null; + } + render() { + const [Clickable, TransitionGroup] = this.modules; + let name = this.props.name; + return React.createElement(Clickable.default, { + className: getEmotion().css({ + position: "relative" + }) + }, React.createElement(TextInput_1.getInternalTextInput(), { + inputClassName: getEmotion().css({ + paddingRight: "32px" + }), + name: name, + onBlur: this.handleInputBlur.bind(this), + defaultValue: this.getCurrentValue(), + inputRef: this.setRef.bind(this) + }), React.createElement(Button_1.default, { + className: getEmotion().css({ + "&:hover": { + opacity: 1 + }, + position: "absolute", + right: 0, + top: "50%", + opacity: .6, + padding: "8px", + transform: "translateY(-50%)", + transition: "opacity .125s" + }), + color: "transparent", + onMouseDown: this.toggleCalendarVisibility.bind(this), + wrapper: false + //TODO: Add icon + }), ReactDOM.createPortal(React.createElement(TransitionGroup.default, { + component: "div", + transitionAppear: false + }, this.renderCalendarPicker()), window.document.body)); + } + static get AllPreviews() { + return AllPreviews || (AllPreviews = [ + [ + { + dateFormat: DateRange_1.DateConstants.DATE_FORMAT + }, + { + dateFormat: "dd/MM/yyyy" + }, + { + dateFormat: "MM/dd/yyyy" + } + ], + [ + { + defaultValue: new Date() + }, + { + defaultValue: null + }, + { + defaultValue: new Date(1597061085498) + } + ], + [ + { + filterDate: (date) => true + }, + { + filterDate: (date) => { + if (date.getDay() !== 0) + return false; + return true; + } + } + ], + [ + { + isModalInput: true + }, + { + isModalInput: false + } + ], + [ + { + maxDate: null + }, + { + maxDate: new Date(Date.now() + 6.048e+8) + } + ], + [ + { + minDate: null + }, + { + minDate: new Date(Date.now() - 6.048e+8) + } + ], + [ + { + onChange: (value, name) => { } + } + ], + [ + { + selectsStart: null + }, + { + selectsStart: new Date(Date.now() - (8.64e+7 * 2)) + } + ], + [ + { + selectsEnd: null + }, + { + selectsEnd: new Date(Date.now() + (8.64e+7 * 2)) + } + ], + [ + { + showMonthYearPicker: false + }, + { + showMonthYearPicker: true + } + ], + [ + { + startDate: null + }, + { + endDate: null + } + ], + [ + { + name: "api-preview-dateinput" + } + ] + ]); + } +} +exports.default = DateInput; +DateInput.defaultProps = { + dateFormat: DateRange_1.DateConstants.DATE_FORMAT, + isModalInput: true +}; +DateInput.help = { + warn: "This component is still `experimental`. Please report issues to [Lightcord's developers](https://github.com/Lightcord/Lightcord/issues)." +}; +let AllPreviews; +function isDateValid(date) { + return (date instanceof Date || typeof date === "object" || Object.prototype.toString.call(date) === "[object Date]") && !isNaN(date.valueOf()); +} +exports.isDateValid = isDateValid; +let AnimatedCalendarPickerModules; +class AnimatedCalendarPicker extends React.Component { + constructor(props) { + super(props); + this.state = { + menuAnimation: new this.modules[0].default.Value(0) + }; + } + get modules() { + return AnimatedCalendarPickerModules || (AnimatedCalendarPickerModules = [ + WebpackLoader_1.default.findByUniqueProperties(["Value", "timing"]) + ]); + } + componentWillEnter(ev) { + this.modules[0].default.timing(this.state.menuAnimation, { + toValue: 1, + duration: 150 + }).start(ev); + } + componentWillLeave(e) { + this.modules[0].default.timing(this.state.menuAnimation, { + toValue: 0, + duration: 150 + }).start(e); + } + render() { + let props = this.props, value = props.value, onClickOutside = props.onClickOutside, onSelect = props.onSelect, minDate = props.minDate, maxDate = props.maxDate, endDate = props.endDate, filterDate = props.filterDate, startDate = props.startDate, selectsEnd = props.selectsEnd, selectsStart = props.selectsStart, top = props.top, right = props.right, isModalInput = props.isModalInput, showMonthYearPicker = props.showMonthYearPicker, menuAnimation = this.state.menuAnimation, interpolation = menuAnimation.interpolate({ + inputRange: [0, 1], + outputRange: ["-10px", "0px"] + }); + const emotion = getEmotion(); + return React.createElement(this.modules[0].default.div, { + className: [emotion.css({ + marginRight: "1px", + margintop: "6px", + position: "fixed", + zIndex: 2 + }), isModalInput ? emotion.css({ + zIndex: 10000 + }) : null].filter(e => e).join(" "), + style: { + opacity: menuAnimation, + right: right, + top: top, + transform: [{ + translateY: interpolation + }] + } + }, React.createElement(CalendarPicker, { + minDate: minDate, + maxDate: maxDate, + endDate: endDate, + filterDate: filterDate, + startDate: startDate, + selectsEnd: selectsEnd, + selectsStart: selectsStart, + value: value, + onSelect: onSelect, + onClickOutside: onClickOutside, + showMonthYearPicker: showMonthYearPicker, + onChange: console.log + })); + } +} +exports.AnimatedCalendarPicker = AnimatedCalendarPicker; +AnimatedCalendarPicker.displayName = "AnimatedCalendarPicker"; +class CalendarPicker extends React.Component { + render() { + var e = this.props, t = e.onClickOutside, r = e.onSelect, n = e.locale, l = e.value, o = e.endDate, u = e.filterDate, f = e.startDate, c = e.minDate, d = e.maxDate, p = e.selectsEnd, y = e.selectsStart, v = e.showMonthYearPicker; + return React.createElement("div", { + className: "lc-calendarPicker" + }, React.createElement(DatePicker.default, { + fixedHeight: true, + inline: true, + selected: l, + locale: n, + onClickOutside: t, + onSelect: r, + onChange: r, + endDate: o, + filterDate: u, + startDate: f, + minDate: c, + maxDate: d, + selectsEnd: p, + selectsStart: y, + showMonthYearPicker: v + })); + } +} +exports.CalendarPicker = CalendarPicker; +CalendarPicker.defaultProps = { + value: new Date() +}; +CalendarPicker.displayName = "CalendarPicker"; /***/ }), @@ -26243,127 +26243,127 @@ CalendarPicker.displayName = "CalendarPicker"; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -/** TODO: Finish DateRange */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DateConstants = void 0; -const React = __webpack_require__(/*! react */ "./src/alias/react.js"); -const Flex_1 = __webpack_require__(/*! ../general/Flex */ "./src/components/general/Flex.tsx"); -const Text_1 = __webpack_require__(/*! ../general/Text */ "./src/components/general/Text.ts"); -exports.DateConstants = { - DATE_FORMAT: "yyyy-MM-dd" -}; -const DateInput_1 = __webpack_require__(/*! ./DateInput */ "./src/components/inputs/DateInput.tsx"); -let AllPreviews; -class DateRange extends React.Component { - constructor(props) { - super(props); - this.state = { - end: props.defaultEnd, - start: props.defaultStart - }; - } - static get AllPreviews() { - return AllPreviews || (AllPreviews = [ - [{ - dateFormat: exports.DateConstants.DATE_FORMAT - }, { - dateFormat: "dd/MM/yyyy" - }, { - dateFormat: "MM/dd/yyyy" - }], - [{ - defaultEnd: new Date(Date.now() + 6.048e+8) - }], - [{ - defaultStart: new Date(Date.now() - 6.048e+8) - }], - [{ - onChange: (start, end) => { } - }], - [{ - maxDate: new Date(Date.now() + (6.048e+8 * 2)) - }, { - maxDate: null - }], - [{ - minDate: new Date(Date.now() - (6.048e+8 * 2)) - }, { - minDate: null - }], - [{ - filterDate: (date) => true - }], - [{ - showMonthYearPicker: false - }, { - showMonthYearPicker: true - }] - ]); - } - componentDidUpdate(oldProps) { - let defaultStart = this.props.defaultStart; - if (oldProps.defaultStart !== defaultStart) { - this.setState({ - start: defaultStart - }); - } - } - handleEndChange(end) { - this.setState({ - end: end - }, this.handleChange); - } - handleStartChange(start) { - this.setState({ - start: start - }, this.handleChange); - } - getMaxDate() { - if (this.props.maxDate) - return this.props.maxDate; - return null; - } - getMinDate() { - if (this.props.minDate) - return this.props.minDate; - return null; - } - handleChange() { - const onChange = this.props.onChange; - if (onChange == null) - return; - const state = this.state; - const start = state.start; - const end = state.end; - onChange(start, end); - } - render() { - const state = this.state; - const start = state.start; - const end = state.end; - const props = this.props; - const filterDate = props.filterDate; - const showMonthYearPicker = props.showMonthYearPicker; - return React.createElement(Flex_1.default, { className: "lc-dateWrapper" }, - React.createElement(Flex_1.FlexChild, { grow: 0 }, - React.createElement(Text_1.default, { className: "lc-dateLabel", size: "small", textCase: "uppercase", weight: "semibold", color: "primary_dark_300" }, "FROM")), - React.createElement(Flex_1.FlexChild, { grow: 0 }, - React.createElement(DateInput_1.default, { defaultValue: start, minDate: this.getMinDate(), maxDate: end, onChange: this.handleStartChange.bind(this), startDate: start, endDate: end, filterDate: filterDate, selectsStart: true, showMonthYearPicker: showMonthYearPicker })), - React.createElement(Flex_1.FlexChild, { grow: 0 }, - React.createElement(Text_1.default, { className: "lc-dateLabel", size: "small", textCase: "uppercase", weight: "semibold", color: "primary_dark_300" }, "TO")), - React.createElement(Flex_1.FlexChild, { grow: 0 }, - React.createElement(DateInput_1.default, { defaultValue: end, minDate: start, maxDate: this.getMaxDate(), onChange: this.handleEndChange.bind(this), startDate: start, endDate: end, filterDate: filterDate, selectsStart: true, showMonthYearPicker: showMonthYearPicker }))); - } -} -exports.default = DateRange; -DateRange.defaultProps = { - dateFormat: exports.DateConstants.DATE_FORMAT -}; -DateRange.displayName = "DateRange"; -DateRange.help = { - warn: "This component is still `experimental`. Please report issues to [Lightcord's developers](https://github.com/Lightcord/Lightcord/issues)." -}; + +/** TODO: Finish DateRange */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DateConstants = void 0; +const React = __webpack_require__(/*! react */ "./src/alias/react.js"); +const Flex_1 = __webpack_require__(/*! ../general/Flex */ "./src/components/general/Flex.tsx"); +const Text_1 = __webpack_require__(/*! ../general/Text */ "./src/components/general/Text.ts"); +exports.DateConstants = { + DATE_FORMAT: "yyyy-MM-dd" +}; +const DateInput_1 = __webpack_require__(/*! ./DateInput */ "./src/components/inputs/DateInput.tsx"); +let AllPreviews; +class DateRange extends React.Component { + constructor(props) { + super(props); + this.state = { + end: props.defaultEnd, + start: props.defaultStart + }; + } + static get AllPreviews() { + return AllPreviews || (AllPreviews = [ + [{ + dateFormat: exports.DateConstants.DATE_FORMAT + }, { + dateFormat: "dd/MM/yyyy" + }, { + dateFormat: "MM/dd/yyyy" + }], + [{ + defaultEnd: new Date(Date.now() + 6.048e+8) + }], + [{ + defaultStart: new Date(Date.now() - 6.048e+8) + }], + [{ + onChange: (start, end) => { } + }], + [{ + maxDate: new Date(Date.now() + (6.048e+8 * 2)) + }, { + maxDate: null + }], + [{ + minDate: new Date(Date.now() - (6.048e+8 * 2)) + }, { + minDate: null + }], + [{ + filterDate: (date) => true + }], + [{ + showMonthYearPicker: false + }, { + showMonthYearPicker: true + }] + ]); + } + componentDidUpdate(oldProps) { + let defaultStart = this.props.defaultStart; + if (oldProps.defaultStart !== defaultStart) { + this.setState({ + start: defaultStart + }); + } + } + handleEndChange(end) { + this.setState({ + end: end + }, this.handleChange); + } + handleStartChange(start) { + this.setState({ + start: start + }, this.handleChange); + } + getMaxDate() { + if (this.props.maxDate) + return this.props.maxDate; + return null; + } + getMinDate() { + if (this.props.minDate) + return this.props.minDate; + return null; + } + handleChange() { + const onChange = this.props.onChange; + if (onChange == null) + return; + const state = this.state; + const start = state.start; + const end = state.end; + onChange(start, end); + } + render() { + const state = this.state; + const start = state.start; + const end = state.end; + const props = this.props; + const filterDate = props.filterDate; + const showMonthYearPicker = props.showMonthYearPicker; + return React.createElement(Flex_1.default, { className: "lc-dateWrapper" }, + React.createElement(Flex_1.FlexChild, { grow: 0 }, + React.createElement(Text_1.default, { className: "lc-dateLabel", size: "small", textCase: "uppercase", weight: "semibold", color: "primary_dark_300" }, "FROM")), + React.createElement(Flex_1.FlexChild, { grow: 0 }, + React.createElement(DateInput_1.default, { defaultValue: start, minDate: this.getMinDate(), maxDate: end, onChange: this.handleStartChange.bind(this), startDate: start, endDate: end, filterDate: filterDate, selectsStart: true, showMonthYearPicker: showMonthYearPicker })), + React.createElement(Flex_1.FlexChild, { grow: 0 }, + React.createElement(Text_1.default, { className: "lc-dateLabel", size: "small", textCase: "uppercase", weight: "semibold", color: "primary_dark_300" }, "TO")), + React.createElement(Flex_1.FlexChild, { grow: 0 }, + React.createElement(DateInput_1.default, { defaultValue: end, minDate: start, maxDate: this.getMaxDate(), onChange: this.handleEndChange.bind(this), startDate: start, endDate: end, filterDate: filterDate, selectsStart: true, showMonthYearPicker: showMonthYearPicker }))); + } +} +exports.default = DateRange; +DateRange.defaultProps = { + dateFormat: exports.DateConstants.DATE_FORMAT +}; +DateRange.displayName = "DateRange"; +DateRange.help = { + warn: "This component is still `experimental`. Please report issues to [Lightcord's developers](https://github.com/Lightcord/Lightcord/issues)." +}; /***/ }), @@ -26376,107 +26376,107 @@ DateRange.help = { /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -let DropdownModules; -class Dropdown extends React.Component { - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.state = { - value: props.value || null - }; - } - onChange(value) { - value = value ? value.value : null; - this.props.onChange(value); - this.setState({ - value: value - }); - } - get modules() { - return DropdownModules || (DropdownModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "SelectTempWrapper").default - ]); - } - render() { - let [DropdownComponent] = this.modules; - let props = this.props; - let returnValue = React.createElement(DropdownComponent, Object.assign({}, props, { onChange: this.onChange, value: this.state.value })); - return returnValue; - } - get value() { - return this.state.value; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([{ - error: null - }, { - error: "An error occured" - }], [{ - options: [ - { - value: "option1", - label: "Option 1" - }, - { - value: "option2", - label: "Option 2" - }, - { - value: "option3", - label: "Option 3" - } - ] - }], [{ - value: "option1" - }], [{ - disabled: false - }, { - disabled: true - }], [{ - searchable: true - }, { - searchable: false - }], [{ - clearable: true - }, { - clearable: false - }], [{ - isMulti: false - }, { - isMulti: true - }]); - return AllPreviews; - })(); - } -} -exports.default = Dropdown; -Dropdown.defaultProps = { - className: null, - error: null, - options: [{ - value: "none", - "label": "No options - No options was passed to Dropdown. If you meant to put an empty dropdown, input an empty array." - }], - valueRenderer: null, - multiValueRenderer: null, - optionRenderer: null, - onChange: noop_1.default, - value: null, - disabled: false, - searchable: false, - clearable: false, - styleOverrides: null, - lightThemeColorOverrides: null, - darkThemeColorOverrides: null, - isMulti: false -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +let DropdownModules; +class Dropdown extends React.Component { + constructor(props) { + super(props); + this.onChange = this.onChange.bind(this); + this.state = { + value: props.value || null + }; + } + onChange(value) { + value = value ? value.value : null; + this.props.onChange(value); + this.setState({ + value: value + }); + } + get modules() { + return DropdownModules || (DropdownModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "SelectTempWrapper").default + ]); + } + render() { + let [DropdownComponent] = this.modules; + let props = this.props; + let returnValue = React.createElement(DropdownComponent, Object.assign({}, props, { onChange: this.onChange, value: this.state.value })); + return returnValue; + } + get value() { + return this.state.value; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([{ + error: null + }, { + error: "An error occured" + }], [{ + options: [ + { + value: "option1", + label: "Option 1" + }, + { + value: "option2", + label: "Option 2" + }, + { + value: "option3", + label: "Option 3" + } + ] + }], [{ + value: "option1" + }], [{ + disabled: false + }, { + disabled: true + }], [{ + searchable: true + }, { + searchable: false + }], [{ + clearable: true + }, { + clearable: false + }], [{ + isMulti: false + }, { + isMulti: true + }]); + return AllPreviews; + })(); + } +} +exports.default = Dropdown; +Dropdown.defaultProps = { + className: null, + error: null, + options: [{ + value: "none", + "label": "No options - No options was passed to Dropdown. If you meant to put an empty dropdown, input an empty array." + }], + valueRenderer: null, + multiValueRenderer: null, + optionRenderer: null, + onChange: noop_1.default, + value: null, + disabled: false, + searchable: false, + clearable: false, + styleOverrides: null, + lightThemeColorOverrides: null, + darkThemeColorOverrides: null, + isMulti: false +}; +let AllPreviews; /***/ }), @@ -26489,94 +26489,94 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -let RadioGroupModule; -class RadioGroup extends React.Component { - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.state = { - value: props.value - }; - } - onChange(ev) { - this.props.onChange(ev.value); - this.setState({ - value: ev.value - }); - } - get modules() { - return RadioGroupModule || (RadioGroupModule = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "RadioGroup").default - ]); - } - render() { - let [RadioGroupComponent] = this.modules; - let props = this.props; - return React.createElement(RadioGroupComponent, { options: props.options, onChange: this.onChange, value: this.state.value, disabled: props.disabled, size: RadioGroupComponent.Sizes[props.size.toUpperCase()], itemType: RadioGroupComponent.ItemTypes[props.itemType.toUpperCase()], infoClassName: props.infoClassName }); - } - get value() { - return this.state.value; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([{ - options: [ - { - value: "option1", - name: "Option 1", - desc: "description 1" - }, - { - value: "option2", - name: "Option 2", - desc: "description 2" - }, - { - value: "option3", - name: "Option 3", - desc: "description 3" - } - ] - }], [{ - value: "option1" - }], [{ - disabled: false - }, { - disabled: true - }], [{ - size: "medium" - }, { - size: "small" - }], [{ - itemType: "bar" - }, { - itemType: "panel" - }]); - return AllPreviews; - })(); - } -} -exports.default = RadioGroup; -RadioGroup.defaultProps = { - options: [{ - value: "none", - name: "No options", - desc: "No options was passed to Choices. If you meant to display no options at all, please pass an empty array.", - color: "#f04747" - }], - value: null, - disabled: false, - size: "medium", - itemType: "bar", - infoClassName: null, - onChange: noop_1.default -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +let RadioGroupModule; +class RadioGroup extends React.Component { + constructor(props) { + super(props); + this.onChange = this.onChange.bind(this); + this.state = { + value: props.value + }; + } + onChange(ev) { + this.props.onChange(ev.value); + this.setState({ + value: ev.value + }); + } + get modules() { + return RadioGroupModule || (RadioGroupModule = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "RadioGroup").default + ]); + } + render() { + let [RadioGroupComponent] = this.modules; + let props = this.props; + return React.createElement(RadioGroupComponent, { options: props.options, onChange: this.onChange, value: this.state.value, disabled: props.disabled, size: RadioGroupComponent.Sizes[props.size.toUpperCase()], itemType: RadioGroupComponent.ItemTypes[props.itemType.toUpperCase()], infoClassName: props.infoClassName }); + } + get value() { + return this.state.value; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([{ + options: [ + { + value: "option1", + name: "Option 1", + desc: "description 1" + }, + { + value: "option2", + name: "Option 2", + desc: "description 2" + }, + { + value: "option3", + name: "Option 3", + desc: "description 3" + } + ] + }], [{ + value: "option1" + }], [{ + disabled: false + }, { + disabled: true + }], [{ + size: "medium" + }, { + size: "small" + }], [{ + itemType: "bar" + }, { + itemType: "panel" + }]); + return AllPreviews; + })(); + } +} +exports.default = RadioGroup; +RadioGroup.defaultProps = { + options: [{ + value: "none", + name: "No options", + desc: "No options was passed to Choices. If you meant to display no options at all, please pass an empty array.", + color: "#f04747" + }], + value: null, + disabled: false, + size: "medium", + itemType: "bar", + infoClassName: null, + onChange: noop_1.default +}; +let AllPreviews; /***/ }), @@ -26589,83 +26589,83 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -let SwitchModules; -class Switch extends React.Component { - constructor(props) { - super(props); - this.state = { - value: props.value || false - }; - this.onChange = this.onChange.bind(this); - } - get modules() { - return SwitchModules || (SwitchModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Switch").default - ]); - } - render() { - let [SwitchComponent] = this.modules; - let props = this.props; - return (React.createElement(SwitchComponent, { id: props.id, onChange: this.onChange, value: this.state.value || false, fill: props.fill, theme: SwitchComponent.Themes[props.theme.toUpperCase()], disabled: props.disabled, className: props.className, size: SwitchComponent.Sizes[props.size.toUpperCase()], style: props.style })); - } - onChange(value) { - this.props.onChange(!this.state.value); - this.setState({ - value: !this.state.value - }); - } - get value() { - return this.state.value; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([{ onChange: (value) => { } }]); - AllPreviews.push([{ - value: false - }], [{ - theme: "default" - }, { - theme: "clear" - }], [{ - disabled: false - }, { - disabled: true - }], [{ - id: "api-preview-switch" - }], [{ - fill: null - }], [{ - size: "default" - }, { - size: "mini" - }], [{ - style: {} - }]); - return AllPreviews; - })(); - } -} -exports.default = Switch; -Switch.defaultProps = { - id: null, - onChange: noop_1.default, - value: false, - fill: null, - theme: "default", - disabled: false, - className: null, - size: "default", - style: {} -}; -Switch.help = { - error: "The `clear` option doesn't work well on light theme." -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +let SwitchModules; +class Switch extends React.Component { + constructor(props) { + super(props); + this.state = { + value: props.value || false + }; + this.onChange = this.onChange.bind(this); + } + get modules() { + return SwitchModules || (SwitchModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "Switch").default + ]); + } + render() { + let [SwitchComponent] = this.modules; + let props = this.props; + return (React.createElement(SwitchComponent, { id: props.id, onChange: this.onChange, value: this.state.value || false, fill: props.fill, theme: SwitchComponent.Themes[props.theme.toUpperCase()], disabled: props.disabled, className: props.className, size: SwitchComponent.Sizes[props.size.toUpperCase()], style: props.style })); + } + onChange(value) { + this.props.onChange(!this.state.value); + this.setState({ + value: !this.state.value + }); + } + get value() { + return this.state.value; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([{ onChange: (value) => { } }]); + AllPreviews.push([{ + value: false + }], [{ + theme: "default" + }, { + theme: "clear" + }], [{ + disabled: false + }, { + disabled: true + }], [{ + id: "api-preview-switch" + }], [{ + fill: null + }], [{ + size: "default" + }, { + size: "mini" + }], [{ + style: {} + }]); + return AllPreviews; + })(); + } +} +exports.default = Switch; +Switch.defaultProps = { + id: null, + onChange: noop_1.default, + value: false, + fill: null, + theme: "default", + disabled: false, + className: null, + size: "default", + style: {} +}; +Switch.help = { + error: "The `clear` option doesn't work well on light theme." +}; +let AllPreviews; /***/ }), @@ -26678,139 +26678,139 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -let TextAreaModules; -class TextArea extends React.Component { - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onBlur = this.onBlur.bind(this); - this.onKeyDown = this.onKeyDown.bind(this); - this.state = { - value: props.value || "" - }; - } - get modules() { - return TextAreaModules || (TextAreaModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "TextArea").default - ]); - } - onChange(value, name) { - this.props.onChange(value, name); - this.setState({ - value - }); - } - onFocus(ev, name) { - this.props.onFocus(ev, name); - } - onBlur(ev, name) { - this.props.onBlur(ev, name); - } - onKeyDown(ev) { - this.props.onKeyDown(ev); - } - render() { - let [TextAreaComponent] = this.modules; - let props = this.props; - return React.createElement(TextAreaComponent, Object.assign({}, props, { onChange: this.onChange, onFocus: this.onFocus, onBlur: this.onBlur, onKeyDown: this.onKeyDown, value: this.state.value })); - } - get value() { - return this.state.value || ""; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([{ - name: "api-preview-textarea" - }]); - AllPreviews.push([{ - disabled: false - }, { - disabled: true - }]); - AllPreviews.push([{ - placeholder: null - }]); - AllPreviews.push([{ - autoFocus: false - }, { - autoFocus: true - }]); - AllPreviews.push([{ - resizeable: false - }, { - resizeable: true - }]); - AllPreviews.push([{ - flex: false - }, { - flex: true - }]); - AllPreviews.push([{ - autosize: false - }, { - autosize: true - }]); - AllPreviews.push([{ - rows: 3 - }, { - rows: 2 - }, { - rows: 1 - }]); - AllPreviews.push([{ - value: "" - }]); - AllPreviews.push([{ - error: null - }, { - error: "Example error" - }]); - AllPreviews.push([{ - maxLength: 100 - }]); - AllPreviews.push([{ - className: "" - }]); - AllPreviews.push([{ - inputClassName: "" - }]); - AllPreviews.push([{ - id: "api-preview-textarea" - }]); - return AllPreviews; - })(); - } -} -exports.default = TextArea; -TextArea.defaultProps = { - name: null, - disabled: false, - placeholder: null, - autoFocus: false, - resizeable: false, - flex: false, - autosize: false, - rows: 3, - value: "", - error: null, - maxLength: null, - className: null, - id: null, - onChange: noop_1.default, - onFocus: noop_1.default, - onBlur: noop_1.default, - onKeyDown: noop_1.default -}; -TextArea.help = { - warn: "This should be used for multi line inputs." -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +let TextAreaModules; +class TextArea extends React.Component { + constructor(props) { + super(props); + this.onChange = this.onChange.bind(this); + this.onFocus = this.onFocus.bind(this); + this.onBlur = this.onBlur.bind(this); + this.onKeyDown = this.onKeyDown.bind(this); + this.state = { + value: props.value || "" + }; + } + get modules() { + return TextAreaModules || (TextAreaModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "TextArea").default + ]); + } + onChange(value, name) { + this.props.onChange(value, name); + this.setState({ + value + }); + } + onFocus(ev, name) { + this.props.onFocus(ev, name); + } + onBlur(ev, name) { + this.props.onBlur(ev, name); + } + onKeyDown(ev) { + this.props.onKeyDown(ev); + } + render() { + let [TextAreaComponent] = this.modules; + let props = this.props; + return React.createElement(TextAreaComponent, Object.assign({}, props, { onChange: this.onChange, onFocus: this.onFocus, onBlur: this.onBlur, onKeyDown: this.onKeyDown, value: this.state.value })); + } + get value() { + return this.state.value || ""; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([{ + name: "api-preview-textarea" + }]); + AllPreviews.push([{ + disabled: false + }, { + disabled: true + }]); + AllPreviews.push([{ + placeholder: null + }]); + AllPreviews.push([{ + autoFocus: false + }, { + autoFocus: true + }]); + AllPreviews.push([{ + resizeable: false + }, { + resizeable: true + }]); + AllPreviews.push([{ + flex: false + }, { + flex: true + }]); + AllPreviews.push([{ + autosize: false + }, { + autosize: true + }]); + AllPreviews.push([{ + rows: 3 + }, { + rows: 2 + }, { + rows: 1 + }]); + AllPreviews.push([{ + value: "" + }]); + AllPreviews.push([{ + error: null + }, { + error: "Example error" + }]); + AllPreviews.push([{ + maxLength: 100 + }]); + AllPreviews.push([{ + className: "" + }]); + AllPreviews.push([{ + inputClassName: "" + }]); + AllPreviews.push([{ + id: "api-preview-textarea" + }]); + return AllPreviews; + })(); + } +} +exports.default = TextArea; +TextArea.defaultProps = { + name: null, + disabled: false, + placeholder: null, + autoFocus: false, + resizeable: false, + flex: false, + autosize: false, + rows: 3, + value: "", + error: null, + maxLength: null, + className: null, + id: null, + onChange: noop_1.default, + onFocus: noop_1.default, + onBlur: noop_1.default, + onKeyDown: noop_1.default +}; +TextArea.help = { + warn: "This should be used for multi line inputs." +}; +let AllPreviews; /***/ }), @@ -26823,118 +26823,118 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getInternalTextInput = void 0; -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -function getInternalTextInput() { - return TextInput.prototype.modules[0]; -} -exports.getInternalTextInput = getInternalTextInput; -let TextInputModules; -class TextInput extends React.PureComponent { - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onBlur = this.onBlur.bind(this); - this.state = { - value: props.value || "" - }; - } - get modules() { - return TextInputModules || (TextInputModules = [ - WebpackLoader_1.default.find(e => e.default && e.default.displayName === "TextInput").default - ]); - } - onChange(value, name) { - this.hasSet = false; - this.props.onChange(value, name, this); - if (this.hasSet) - return; // prevent event if the onChange has changed the value. - this.setState({ - value - }); - this.forceUpdate(); - } - onFocus(ev, name) { - this.props.onFocus(ev, name, this); - } - onBlur(ev, name) { - this.props.onBlur(ev, name, this); - } - render() { - let [TextAreaComponent] = this.modules; - let props = this.props; - return React.createElement(TextAreaComponent, Object.assign({}, props, { onChange: this.onChange, onFocus: this.onFocus, onBlur: this.onBlur, value: this.state.value })); - } - get value() { - return this.state.value || ""; - } - setValue(value) { - this.setState({ - value: value - }); - this.forceUpdate(); - this.hasSet = true; - } - static get AllPreviews() { - return AllPreviews || (() => { - AllPreviews = []; - AllPreviews.push([{ - name: "api-preview-textinput" - }], [{ - size: "default" - }, { - size: "mini" - }], [{ - disabled: false - }, { - disabled: true - }], [{ - placeholder: "" - }], [{ - value: "" - }], [{ - error: null - }, { - error: "Example error" - }], [{ - maxLength: 999 - }], [{ - className: "" - }], [{ - inputClassName: "" - }], [{ - id: "api-preview-textinput" - }], [{ - onChange: (value, name) => { } - }]); - return AllPreviews; - })(); - } -} -exports.default = TextInput; -TextInput.defaultProps = { - name: "", - size: "default", - disabled: false, - placeholder: "", - value: "", - error: null, - maxLength: 999, - className: "", - inputClassName: "", - id: null, - onChange: noop_1.default, - onFocus: noop_1.default, - onBlur: noop_1.default -}; -TextInput.help = { - warn: "This should be used for single line inputs." -}; -let AllPreviews; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getInternalTextInput = void 0; +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +function getInternalTextInput() { + return TextInput.prototype.modules[0]; +} +exports.getInternalTextInput = getInternalTextInput; +let TextInputModules; +class TextInput extends React.PureComponent { + constructor(props) { + super(props); + this.onChange = this.onChange.bind(this); + this.onFocus = this.onFocus.bind(this); + this.onBlur = this.onBlur.bind(this); + this.state = { + value: props.value || "" + }; + } + get modules() { + return TextInputModules || (TextInputModules = [ + WebpackLoader_1.default.find(e => e.default && e.default.displayName === "TextInput").default + ]); + } + onChange(value, name) { + this.hasSet = false; + this.props.onChange(value, name, this); + if (this.hasSet) + return; // prevent event if the onChange has changed the value. + this.setState({ + value + }); + this.forceUpdate(); + } + onFocus(ev, name) { + this.props.onFocus(ev, name, this); + } + onBlur(ev, name) { + this.props.onBlur(ev, name, this); + } + render() { + let [TextAreaComponent] = this.modules; + let props = this.props; + return React.createElement(TextAreaComponent, Object.assign({}, props, { onChange: this.onChange, onFocus: this.onFocus, onBlur: this.onBlur, value: this.state.value })); + } + get value() { + return this.state.value || ""; + } + setValue(value) { + this.setState({ + value: value + }); + this.forceUpdate(); + this.hasSet = true; + } + static get AllPreviews() { + return AllPreviews || (() => { + AllPreviews = []; + AllPreviews.push([{ + name: "api-preview-textinput" + }], [{ + size: "default" + }, { + size: "mini" + }], [{ + disabled: false + }, { + disabled: true + }], [{ + placeholder: "" + }], [{ + value: "" + }], [{ + error: null + }, { + error: "Example error" + }], [{ + maxLength: 999 + }], [{ + className: "" + }], [{ + inputClassName: "" + }], [{ + id: "api-preview-textinput" + }], [{ + onChange: (value, name) => { } + }]); + return AllPreviews; + })(); + } +} +exports.default = TextInput; +TextInput.defaultProps = { + name: "", + size: "default", + disabled: false, + placeholder: "", + value: "", + error: null, + maxLength: 999, + className: "", + inputClassName: "", + id: null, + onChange: noop_1.default, + onFocus: noop_1.default, + onBlur: noop_1.default +}; +TextInput.help = { + warn: "This should be used for single line inputs." +}; +let AllPreviews; /***/ }), @@ -26947,53 +26947,53 @@ let AllPreviews; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.defaultNotice = void 0; -const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const Notices_1 = __webpack_require__(/*! ./Notices */ "./src/components/private/Notices.tsx"); -const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); -let NoticeModules; -exports.defaultNotice = { - text: "", - id: "unknown id", - onClick: noop_1.default, - buttonText: null, - type: "default" -}; -class Notice extends React.Component { - get modules() { - return NoticeModules || (NoticeModules = [ - WebpackLoader_1.default.find(e => e.noticeInfo) - ]); - } - render() { - const [noticeClasses] = this.modules; - const className = noticeClasses["notice" + this.props.type.slice(0, 1).toUpperCase() + this.props.type.slice(1)]; - if (!className) { - Notices_1.notices.pop(); - setImmediate(() => { - Notices_1.events.emit("noticeUpdate"); - }); - return null; - } - const button = this.props.buttonText ? React.createElement("button", { className: noticeClasses.button, onClick: () => { - Notices_1.notices.pop(); - this.props.onClick(); - Notices_1.events.emit("noticeUpdate"); - } }, this.props.buttonText) : null; - return React.createElement("div", { className: className }, - React.createElement("div", { className: noticeClasses.dismiss, role: "button", tabIndex: 0, onClick: () => { - Notices_1.notices.pop(); - Notices_1.events.emit("noticeUpdate"); - } }), - this.props.text, - button); - } -} -exports.default = Notice; -Notice.displayName = "LightcordNotice"; -Notice.defaultProps = exports.defaultNotice; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.defaultNotice = void 0; +const WebpackLoader_1 = __webpack_require__(/*! ../../modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const Notices_1 = __webpack_require__(/*! ./Notices */ "./src/components/private/Notices.tsx"); +const noop_1 = __webpack_require__(/*! ../../modules/noop */ "./src/modules/noop.ts"); +let NoticeModules; +exports.defaultNotice = { + text: "", + id: "unknown id", + onClick: noop_1.default, + buttonText: null, + type: "default" +}; +class Notice extends React.Component { + get modules() { + return NoticeModules || (NoticeModules = [ + WebpackLoader_1.default.find(e => e.noticeInfo) + ]); + } + render() { + const [noticeClasses] = this.modules; + const className = noticeClasses["notice" + this.props.type.slice(0, 1).toUpperCase() + this.props.type.slice(1)]; + if (!className) { + Notices_1.notices.pop(); + setImmediate(() => { + Notices_1.events.emit("noticeUpdate"); + }); + return null; + } + const button = this.props.buttonText ? React.createElement("button", { className: noticeClasses.button, onClick: () => { + Notices_1.notices.pop(); + this.props.onClick(); + Notices_1.events.emit("noticeUpdate"); + } }, this.props.buttonText) : null; + return React.createElement("div", { className: className }, + React.createElement("div", { className: noticeClasses.dismiss, role: "button", tabIndex: 0, onClick: () => { + Notices_1.notices.pop(); + Notices_1.events.emit("noticeUpdate"); + } }), + this.props.text, + button); + } +} +exports.default = Notice; +Notice.displayName = "LightcordNotice"; +Notice.defaultProps = exports.defaultNotice; /***/ }), @@ -27006,40 +27006,40 @@ Notice.defaultProps = exports.defaultNotice; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.notices = exports.events = void 0; -const Notice_1 = __webpack_require__(/*! ./Notice */ "./src/components/private/Notice.tsx"); -const events_1 = __webpack_require__(/*! events */ "events"); -exports.events = new events_1.EventEmitter(); -class Notices extends React.Component { - constructor(props) { - super(props); - this.noticeHandler = this.noticeHandler.bind(this); - } - noticeHandler() { - this.forceUpdate(); - } - componentWillMount() { - exports.events.on("noticeUpdate", this.noticeHandler); - } - componentWillUnmount() { - exports.events.off("noticeUpdate", this.noticeHandler); - } - render() { - if (!this.hasNotice) - return null; - const notice = exports.notices[0]; - return React.createElement(Notice_1.default, Object.assign({}, notice)); - } - get hasNotice() { - return exports.notices.length > 0; - } -} -exports.default = Notices; -Notices.displayName = "LightcordNotices"; -Notices.defaultProps = {}; -exports.notices = []; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.notices = exports.events = void 0; +const Notice_1 = __webpack_require__(/*! ./Notice */ "./src/components/private/Notice.tsx"); +const events_1 = __webpack_require__(/*! events */ "events"); +exports.events = new events_1.EventEmitter(); +class Notices extends React.Component { + constructor(props) { + super(props); + this.noticeHandler = this.noticeHandler.bind(this); + } + noticeHandler() { + this.forceUpdate(); + } + componentWillMount() { + exports.events.on("noticeUpdate", this.noticeHandler); + } + componentWillUnmount() { + exports.events.off("noticeUpdate", this.noticeHandler); + } + render() { + if (!this.hasNotice) + return null; + const notice = exports.notices[0]; + return React.createElement(Notice_1.default, Object.assign({}, notice)); + } + get hasNotice() { + return exports.notices.length > 0; + } +} +exports.default = Notices; +Notices.displayName = "LightcordNotices"; +Notices.defaultProps = {}; +exports.notices = []; /***/ }), @@ -27052,41 +27052,41 @@ exports.notices = []; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const WebpackLoader_1 = __webpack_require__(/*! ./modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); -const components_1 = __webpack_require__(/*! ./components/components */ "./src/components/components.ts"); -const uuid_1 = __webpack_require__(/*! ./modules/uuid */ "./src/modules/uuid.ts"); -const Utils_1 = __webpack_require__(/*! ./modules/Utils */ "./src/modules/Utils.ts"); -const DiscordTools_1 = __webpack_require__(/*! ./modules/DiscordTools */ "./src/modules/DiscordTools.ts"); -const patchers = __webpack_require__(/*! ./modules/patchers */ "./src/modules/patchers.ts"); -const excludeProperties_1 = __webpack_require__(/*! ./modules/excludeProperties */ "./src/modules/excludeProperties.ts"); -const cloneNullProto_1 = __webpack_require__(/*! ./modules/cloneNullProto */ "./src/modules/cloneNullProto.ts"); -const noop_1 = __webpack_require__(/*! ./modules/noop */ "./src/modules/noop.ts"); -const Unfreeze_1 = __webpack_require__(/*! ./modules/Unfreeze */ "./src/modules/Unfreeze.ts"); -const environnement_1 = __webpack_require__(/*! ./modules/environnement */ "./src/modules/environnement.ts"); -__webpack_require__(/*! ./alias/react */ "./src/alias/react.js"); -__webpack_require__(/*! ./alias/react-dom */ "./src/alias/react-dom.js"); -const lazyLoader_1 = __webpack_require__(/*! ./modules/lazyLoader */ "./src/modules/lazyLoader.ts"); -patchers.patch(); -const LightcordApi = { - WebpackLoader: WebpackLoader_1.default, - Components: components_1.default, - uuid: uuid_1.default, - Utils: Utils_1.default, - DiscordTools: DiscordTools_1.default, - _: { - excludeProperties: excludeProperties_1.default, - cloneNullProto: cloneNullProto_1.default, - NOOP: noop_1.default, - unfreeze: Unfreeze_1.default - }, - get isNative() { return environnement_1.isNative; }, - get isImported() { return environnement_1.isImported; }, - LazyLoad: lazyLoader_1.LazyLoad -}; -exports.default = LightcordApi; -Object.assign(window.Lightcord.Api, LightcordApi); + +Object.defineProperty(exports, "__esModule", { value: true }); +const WebpackLoader_1 = __webpack_require__(/*! ./modules/WebpackLoader */ "./src/modules/WebpackLoader.ts"); +const components_1 = __webpack_require__(/*! ./components/components */ "./src/components/components.ts"); +const uuid_1 = __webpack_require__(/*! ./modules/uuid */ "./src/modules/uuid.ts"); +const Utils_1 = __webpack_require__(/*! ./modules/Utils */ "./src/modules/Utils.ts"); +const DiscordTools_1 = __webpack_require__(/*! ./modules/DiscordTools */ "./src/modules/DiscordTools.ts"); +const patchers = __webpack_require__(/*! ./modules/patchers */ "./src/modules/patchers.ts"); +const excludeProperties_1 = __webpack_require__(/*! ./modules/excludeProperties */ "./src/modules/excludeProperties.ts"); +const cloneNullProto_1 = __webpack_require__(/*! ./modules/cloneNullProto */ "./src/modules/cloneNullProto.ts"); +const noop_1 = __webpack_require__(/*! ./modules/noop */ "./src/modules/noop.ts"); +const Unfreeze_1 = __webpack_require__(/*! ./modules/Unfreeze */ "./src/modules/Unfreeze.ts"); +const environnement_1 = __webpack_require__(/*! ./modules/environnement */ "./src/modules/environnement.ts"); +__webpack_require__(/*! ./alias/react */ "./src/alias/react.js"); +__webpack_require__(/*! ./alias/react-dom */ "./src/alias/react-dom.js"); +const lazyLoader_1 = __webpack_require__(/*! ./modules/lazyLoader */ "./src/modules/lazyLoader.ts"); +patchers.patch(); +const LightcordApi = { + WebpackLoader: WebpackLoader_1.default, + Components: components_1.default, + uuid: uuid_1.default, + Utils: Utils_1.default, + DiscordTools: DiscordTools_1.default, + _: { + excludeProperties: excludeProperties_1.default, + cloneNullProto: cloneNullProto_1.default, + NOOP: noop_1.default, + unfreeze: Unfreeze_1.default + }, + get isNative() { return environnement_1.isNative; }, + get isImported() { return environnement_1.isImported; }, + LazyLoad: lazyLoader_1.LazyLoad +}; +exports.default = LightcordApi; +Object.assign(window.Lightcord.Api, LightcordApi); /***/ }), @@ -27099,99 +27099,99 @@ Object.assign(window.Lightcord.Api, LightcordApi); /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -let req; -setReq(); -function filterDangerous(mods) { - return mods.map(e => { - return protect(e); - }); -} -function protect(exports) { - let theModule = exports.exports; - let mod = theModule.default; - if (!mod) - return exports; - if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) - return null; - if (!mod.getToken && !mod.getEmail && !mod.showToken) - return exports; - const proxy = new Proxy(mod, { - getOwnPropertyDescriptor: function (obj, prop) { - if (prop === "getToken" || prop === "getEmail" || prop === "showToken") - return undefined; - return Object.getOwnPropertyDescriptor(obj, prop); - }, - get: function (obj, func) { - if (func == "getToken" && obj.getToken) - return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; - if (func == "getEmail" && obj.getEmail) - return () => "puppet11112@gmail.com"; - if (func == "showToken" && obj.showToken) - return () => true; - if (func == "__proto__" && obj.__proto__) - return proxy; - return obj[func]; - } - }); - return Object.assign({}, exports, { exports: Object.assign({}, theModule, { default: proxy }) }); -} -class Webpackloader { - get modules() { - if (req) { - return filterDangerous(Object.values(req.c).filter((e) => e && e.exports)); - } - else { - setReq(); - if (req) { - return filterDangerous(Object.values(req.c).filter((e) => e && e.exports)); - } - else { - return []; - } - } - } - get(ids, modules) { - if (typeof ids === "function") { - return (modules || this.modules).map((mdl) => { - if (mdl && typeof mdl.exports !== "undefined") { - return mdl.exports; - } - else { - return null; - } - }).filter(e => e).filter(ids); - } - else if (Array.isArray(ids)) { - modules = modules || this.modules; - return ids.map(id => this.get(id, modules)); - } - else { - modules = modules || this.modules; - let module = modules.filter(e => !!e).find(e => e.i === ids); - if (!module) - return undefined; - return module.exports; - } - } - get default() { - return this; - } -} -exports.default = new Webpackloader(); -function setReq() { - try { - req = window["webpackJsonp"].push([[], { __extra_id__: (mdl, exports, req) => mdl.exports = req }, [["__extra_id__"]]]); - if (req) { - delete req.m.__extra_id__; - delete req.c.__extra_id__; - } - } - catch (e) { - req = undefined; - } -} + +Object.defineProperty(exports, "__esModule", { value: true }); +let req; +setReq(); +function filterDangerous(mods) { + return mods.map(e => { + return protect(e); + }); +} +function protect(exports) { + let theModule = exports.exports; + let mod = theModule.default; + if (!mod) + return exports; + if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) + return null; + if (!mod.getToken && !mod.getEmail && !mod.showToken) + return exports; + const proxy = new Proxy(mod, { + getOwnPropertyDescriptor: function (obj, prop) { + if (prop === "getToken" || prop === "getEmail" || prop === "showToken") + return undefined; + return Object.getOwnPropertyDescriptor(obj, prop); + }, + get: function (obj, func) { + if (func == "getToken" && obj.getToken) + return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; + if (func == "getEmail" && obj.getEmail) + return () => "puppet11112@gmail.com"; + if (func == "showToken" && obj.showToken) + return () => true; + if (func == "__proto__" && obj.__proto__) + return proxy; + return obj[func]; + } + }); + return Object.assign({}, exports, { exports: Object.assign({}, theModule, { default: proxy }) }); +} +class Webpackloader { + get modules() { + if (req) { + return filterDangerous(Object.values(req.c).filter((e) => e && e.exports)); + } + else { + setReq(); + if (req) { + return filterDangerous(Object.values(req.c).filter((e) => e && e.exports)); + } + else { + return []; + } + } + } + get(ids, modules) { + if (typeof ids === "function") { + return (modules || this.modules).map((mdl) => { + if (mdl && typeof mdl.exports !== "undefined") { + return mdl.exports; + } + else { + return null; + } + }).filter(e => e).filter(ids); + } + else if (Array.isArray(ids)) { + modules = modules || this.modules; + return ids.map(id => this.get(id, modules)); + } + else { + modules = modules || this.modules; + let module = modules.filter(e => !!e).find(e => e.i === ids); + if (!module) + return undefined; + return module.exports; + } + } + get default() { + return this; + } +} +exports.default = new Webpackloader(); +function setReq() { + try { + req = window["webpackJsonp"].push([[], { __extra_id__: (mdl, exports, req) => mdl.exports = req }, [["__extra_id__"]]]); + if (req) { + delete req.m.__extra_id__; + delete req.c.__extra_id__; + } + } + catch (e) { + req = undefined; + } +} /***/ }), @@ -27204,174 +27204,174 @@ function setReq() { /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Notice = void 0; -const Notices_1 = __webpack_require__(/*! ../components/private/Notices */ "./src/components/private/Notices.tsx"); -const Utils_1 = __webpack_require__(/*! ./Utils */ "./src/modules/Utils.ts"); -const uuid_1 = __webpack_require__(/*! ./uuid */ "./src/modules/uuid.ts"); -const cloneNullProto_1 = __webpack_require__(/*! ./cloneNullProto */ "./src/modules/cloneNullProto.ts"); -const events_1 = __webpack_require__(/*! events */ "events"); -const Notice_1 = __webpack_require__(/*! ../components/private/Notice */ "./src/components/private/Notice.tsx"); -const excludeProperties_1 = __webpack_require__(/*! ./excludeProperties */ "./src/modules/excludeProperties.ts"); -const noop_1 = __webpack_require__(/*! ./noop */ "./src/modules/noop.ts"); -const WebpackLoader_1 = __webpack_require__(/*! ./WebpackLoader */ "./src/modules/WebpackLoader.ts"); -let soundModule; -exports.default = new class DiscordTools { - showNotice(data) { - if (typeof data !== "object" || typeof data.text !== "string") - throw new Error(`This notice is not valid. Given: ${Utils_1.default.formatJSObject(data)}`); - let newData = cloneNullProto_1.default(Object.assign({}, Notice_1.defaultNotice, data)); - newData.id = uuid_1.default(); - Notices_1.notices.push(newData); - Notices_1.events.emit("noticeUpdate"); - const notice = new Notice(newData); - return notice; - } - get notices() { - return Notices_1.notices.map(data => new Notice(data)); - } - /** - * Quickly send notification (Even when no focused.) - * @param data The notification. Be sure to include all properties except functions cause they're optional. - * Notifications have a timeout of 3-5 seconds. - * They look like this: https://i.imgur.com/jzuxKKu.png - */ - showNotification(data) { - const notification = new window.Notification(data.title, excludeProperties_1.default(data, [ - "title", - "onClick", - "onClose", - "onShow" - ])); - notification.onclick = data.onClick || noop_1.default; - notification.onshow = data.onShow || noop_1.default; - notification.onclose = data.onClose || noop_1.default; - return notification; - } - createSound(sound) { - soundModule = soundModule || WebpackLoader_1.default.findByUniqueProperties(["createSound"]); - if (!soundModule) - throw new WebpackLoader_1.WebpackLoaderError("Couldn't find soundModule here."); - const created = soundModule.createSound(sound); - return created; - } - playSound(sound) { - const created = this.createSound(sound); - created.play(); - return created; - } -}; -const EventHandler = function () { - if (this.removed !== this.state.removed) { - if (this.removed) { - this.emit("removed"); - } - } - if (this.showing !== this.state.showing) { - if (this.showing) { - this.emit("showing", true); - } - else { - this.emit("showing", false); - } - } - if (this.index !== this.state.index) { - this.emit("index", this.index); - } -}; -/** A notice interface for modifying it and subscribing to events. */ -class Notice extends events_1.EventEmitter { - constructor(data) { - super(); - this.nextTickRefresh = false; - this.data = data; - this.state = { - removed: this.removed, - showing: this.showing, - index: this.index - }; - let eventFunc = EventHandler.bind(this); - Notices_1.events.on("noticeUpdate", eventFunc); - this.on("removed", () => { - Notices_1.events.off("noticeUpdate", eventFunc); - }); - } - on(event, listener) { - return super.on(event, listener); - } - once(event, listener) { - return super.once(event, listener); - } - off(event, listener) { - return super.off(event, listener); - } - get removed() { - return !Notices_1.notices.find(e => e.id === this.id); - } - get showing() { - return this.index === 0; - } - get index() { - return Notices_1.notices.findIndex(e => e.id === this.id); - } - get id() { - return this.data.id; - } - update(data) { - for (let key in data) { - if (key === "id") - continue; - this.data[key] = data[key]; - } - if (!this.nextTickRefresh) { - this.nextTickRefresh = true; - process.nextTick(() => { - this.nextTickRefresh = false; - Notices_1.events.emit("noticeUpdate"); - }); - } - } - get text() { - return this.data.text; - } - set text(text) { - this.update({ - text - }); - } - get type() { - return this.data.type; - } - set type(type) { - this.update({ - type - }); - } - get buttonText() { - return this.data.buttonText; - } - set buttonText(buttonText) { - this.update({ - buttonText - }); - } - get onClick() { - return this.data.onClick; - } - set onClick(onClick) { - this.update({ - onClick - }); - } - remove() { - if (this.removed) - return; - Notices_1.notices.splice(this.index, 1); - Notices_1.events.emit("noticeUpdate"); - } -} -exports.Notice = Notice; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Notice = void 0; +const Notices_1 = __webpack_require__(/*! ../components/private/Notices */ "./src/components/private/Notices.tsx"); +const Utils_1 = __webpack_require__(/*! ./Utils */ "./src/modules/Utils.ts"); +const uuid_1 = __webpack_require__(/*! ./uuid */ "./src/modules/uuid.ts"); +const cloneNullProto_1 = __webpack_require__(/*! ./cloneNullProto */ "./src/modules/cloneNullProto.ts"); +const events_1 = __webpack_require__(/*! events */ "events"); +const Notice_1 = __webpack_require__(/*! ../components/private/Notice */ "./src/components/private/Notice.tsx"); +const excludeProperties_1 = __webpack_require__(/*! ./excludeProperties */ "./src/modules/excludeProperties.ts"); +const noop_1 = __webpack_require__(/*! ./noop */ "./src/modules/noop.ts"); +const WebpackLoader_1 = __webpack_require__(/*! ./WebpackLoader */ "./src/modules/WebpackLoader.ts"); +let soundModule; +exports.default = new class DiscordTools { + showNotice(data) { + if (typeof data !== "object" || typeof data.text !== "string") + throw new Error(`This notice is not valid. Given: ${Utils_1.default.formatJSObject(data)}`); + let newData = cloneNullProto_1.default(Object.assign({}, Notice_1.defaultNotice, data)); + newData.id = uuid_1.default(); + Notices_1.notices.push(newData); + Notices_1.events.emit("noticeUpdate"); + const notice = new Notice(newData); + return notice; + } + get notices() { + return Notices_1.notices.map(data => new Notice(data)); + } + /** + * Quickly send notification (Even when no focused.) + * @param data The notification. Be sure to include all properties except functions cause they're optional. + * Notifications have a timeout of 3-5 seconds. + * They look like this: https://i.imgur.com/jzuxKKu.png + */ + showNotification(data) { + const notification = new window.Notification(data.title, excludeProperties_1.default(data, [ + "title", + "onClick", + "onClose", + "onShow" + ])); + notification.onclick = data.onClick || noop_1.default; + notification.onshow = data.onShow || noop_1.default; + notification.onclose = data.onClose || noop_1.default; + return notification; + } + createSound(sound) { + soundModule = soundModule || WebpackLoader_1.default.findByUniqueProperties(["createSound"]); + if (!soundModule) + throw new WebpackLoader_1.WebpackLoaderError("Couldn't find soundModule here."); + const created = soundModule.createSound(sound); + return created; + } + playSound(sound) { + const created = this.createSound(sound); + created.play(); + return created; + } +}; +const EventHandler = function () { + if (this.removed !== this.state.removed) { + if (this.removed) { + this.emit("removed"); + } + } + if (this.showing !== this.state.showing) { + if (this.showing) { + this.emit("showing", true); + } + else { + this.emit("showing", false); + } + } + if (this.index !== this.state.index) { + this.emit("index", this.index); + } +}; +/** A notice interface for modifying it and subscribing to events. */ +class Notice extends events_1.EventEmitter { + constructor(data) { + super(); + this.nextTickRefresh = false; + this.data = data; + this.state = { + removed: this.removed, + showing: this.showing, + index: this.index + }; + let eventFunc = EventHandler.bind(this); + Notices_1.events.on("noticeUpdate", eventFunc); + this.on("removed", () => { + Notices_1.events.off("noticeUpdate", eventFunc); + }); + } + on(event, listener) { + return super.on(event, listener); + } + once(event, listener) { + return super.once(event, listener); + } + off(event, listener) { + return super.off(event, listener); + } + get removed() { + return !Notices_1.notices.find(e => e.id === this.id); + } + get showing() { + return this.index === 0; + } + get index() { + return Notices_1.notices.findIndex(e => e.id === this.id); + } + get id() { + return this.data.id; + } + update(data) { + for (let key in data) { + if (key === "id") + continue; + this.data[key] = data[key]; + } + if (!this.nextTickRefresh) { + this.nextTickRefresh = true; + process.nextTick(() => { + this.nextTickRefresh = false; + Notices_1.events.emit("noticeUpdate"); + }); + } + } + get text() { + return this.data.text; + } + set text(text) { + this.update({ + text + }); + } + get type() { + return this.data.type; + } + set type(type) { + this.update({ + type + }); + } + get buttonText() { + return this.data.buttonText; + } + set buttonText(buttonText) { + this.update({ + buttonText + }); + } + get onClick() { + return this.data.onClick; + } + set onClick(onClick) { + this.update({ + onClick + }); + } + remove() { + if (this.removed) + return; + Notices_1.notices.splice(this.index, 1); + Notices_1.events.emit("noticeUpdate"); + } +} +exports.Notice = Notice; /***/ }), @@ -27384,47 +27384,47 @@ exports.Notice = Notice; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const components_1 = __webpack_require__(/*! ../components/components */ "./src/components/components.ts"); -const Utils_1 = __webpack_require__(/*! ./Utils */ "./src/modules/Utils.ts"); -const uuid_1 = __webpack_require__(/*! ./uuid */ "./src/modules/uuid.ts"); -const TextInput_1 = __webpack_require__(/*! ../components/inputs/TextInput */ "./src/components/inputs/TextInput.tsx"); -exports.default = new class PluginUtilities { - constructor() { } - renderSettings(settings) { - let items = this.renderSettingsToReact(settings); - let elem = React.createElement("div", { key: uuid_1.default() }, items); - return Utils_1.default.ReactToHTMLElement(elem); - } - renderSettingsToReact(settings) { - let items = []; - settings.forEach(item => { - if (typeof item !== "object") - return items.push(item); - if (item.props && "children" in item.props) { - if (!Array.isArray(item.props.children)) - item.props.children = [item.props.children]; - item.props.children = this.renderSettingsToReact(item.props.children); - } - if (!item.props) - item.props = {}; - item.props.key = uuid_1.default(); - let component = Utils_1.default.getNestedProps(components_1.default, item.component); - if (!component) { - let warning = new TextInput_1.default({ - value: `Warning: No component was found for: "${item.component}". Please correct your code.`, - disabled: true, - error: `Warning: No component was found for: "${item.component}". Please correct your code.` - }).render(); - items.push(warning); - return; - } - items.push(React.createElement(component, Object.create(item.props))); - }); - return items; - } -}; + +Object.defineProperty(exports, "__esModule", { value: true }); +const components_1 = __webpack_require__(/*! ../components/components */ "./src/components/components.ts"); +const Utils_1 = __webpack_require__(/*! ./Utils */ "./src/modules/Utils.ts"); +const uuid_1 = __webpack_require__(/*! ./uuid */ "./src/modules/uuid.ts"); +const TextInput_1 = __webpack_require__(/*! ../components/inputs/TextInput */ "./src/components/inputs/TextInput.tsx"); +exports.default = new class PluginUtilities { + constructor() { } + renderSettings(settings) { + let items = this.renderSettingsToReact(settings); + let elem = React.createElement("div", { key: uuid_1.default() }, items); + return Utils_1.default.ReactToHTMLElement(elem); + } + renderSettingsToReact(settings) { + let items = []; + settings.forEach(item => { + if (typeof item !== "object") + return items.push(item); + if (item.props && "children" in item.props) { + if (!Array.isArray(item.props.children)) + item.props.children = [item.props.children]; + item.props.children = this.renderSettingsToReact(item.props.children); + } + if (!item.props) + item.props = {}; + item.props.key = uuid_1.default(); + let component = Utils_1.default.getNestedProps(components_1.default, item.component); + if (!component) { + let warning = new TextInput_1.default({ + value: `Warning: No component was found for: "${item.component}". Please correct your code.`, + disabled: true, + error: `Warning: No component was found for: "${item.component}". Please correct your code.` + }).render(); + items.push(warning); + return; + } + items.push(React.createElement(component, Object.create(item.props))); + }); + return items; + } +}; /***/ }), @@ -27437,30 +27437,30 @@ exports.default = new class PluginUtilities { /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function unfreeze(o) { - var oo = undefined; - if (o instanceof Array) { - oo = []; - var clone = function (v) { - oo.push(v); - }; - o.forEach(clone); - } - else if (o instanceof String) { - oo = new String(o).toString(); - } - else if (typeof o == 'object') { - oo = {}; - for (var property in o) { - oo[property] = o[property]; - } - } - return oo; -} -exports.default = unfreeze; -Object.unfreeze = unfreeze; + +Object.defineProperty(exports, "__esModule", { value: true }); +function unfreeze(o) { + var oo = undefined; + if (o instanceof Array) { + oo = []; + var clone = function (v) { + oo.push(v); + }; + o.forEach(clone); + } + else if (o instanceof String) { + oo = new String(o).toString(); + } + else if (typeof o == 'object') { + oo = {}; + for (var property in o) { + oo[property] = o[property]; + } + } + return oo; +} +exports.default = unfreeze; +Object.unfreeze = unfreeze; /***/ }), @@ -27473,142 +27473,142 @@ Object.unfreeze = unfreeze; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LightcordApiError = void 0; -const ReactDOM = __webpack_require__(/*! react-dom */ "./src/alias/react-dom.js"); -const PluginUtilities_1 = __webpack_require__(/*! ./PluginUtilities */ "./src/modules/PluginUtilities.ts"); -exports.default = new class Utils { - constructor() { } - ReactToHTMLElement(ReactElement) { - const element = document.createElement("div"); - ReactDOM.render(ReactElement, element); - return element; - } - get PluginUtils() { return PluginUtilities_1.default; } - getNestedProps(obj, path) { - let segments = path.split("."); - for (let seg of segments) { - obj = obj && (seg in obj) ? obj[seg] : undefined; - } - return obj; - } - DecimalColorToHex(color) { - return "#" + color.toString(16).toUpperCase(); - } - HexColorToDecimal(color) { - color = color.replace(/[#;]/g, ""); - let res = parseInt(color, 16); - if (isNaN(res)) - throw new Error(`Invalid color: ${color}`); - return res; - } - removeDa(className) { - if (!className) - return className; - return className.split(" ").filter(e => !e.startsWith("da-")).join(" "); - } - FindReact(dom, traverseUp = 0) { - // taken from https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging#39165137 - const key = Object.keys(dom).find(key => key.startsWith("__reactInternalInstance$")); - const domFiber = dom[key]; - if (domFiber == null) - return null; - // react <16 - if (domFiber._currentElement) { - let compFiber = domFiber._currentElement._owner; - for (let i = 0; i < traverseUp; i++) { - compFiber = compFiber._currentElement._owner; - } - return compFiber._instance; - } - // react 16+ - const GetCompFiber = fiber => { - //return fiber._debugOwner; // this also works, but is __DEV__ only - let parentFiber = fiber.return; - while (typeof parentFiber.type == "string") { - parentFiber = parentFiber.return; - } - return parentFiber; - }; - let compFiber = GetCompFiber(domFiber); - for (let i = 0; i < traverseUp; i++) { - compFiber = GetCompFiber(compFiber); - } - return compFiber.stateNode; - } - hasClass(classNames, className) { - if (!classNames || !className) - return false; - const classnames = classNames.split(" "); - for (let classname of this.removeDa(className).split(" ")) { - if (!classnames.includes(classname)) - return false; - } - return true; - } - formatJSObject(obj) { - if (["string", "number", "boolean", "bigint", "undefined"].includes(typeof obj)) - return JSON.stringify(obj); - if (obj === null) - return "null"; - if (typeof obj === "function") - return String(obj); - if (typeof obj === "symbol") - return String(obj); - if (Array.isArray(obj)) { - if (!obj.length) - return "[]"; - return `[\n ${obj.map(e => this.formatJSObject(e)).join(",\n ")}\n]`; - } - else { - const keys = Object.keys(obj); - if (keys.length === 0) - return "{}"; - return `{\n ${keys.map(key => { - let original = key; - if (typeof key === "symbol") - key = "[" + String(key) + "]"; - else { - if (typeof key === "number") - key = String(key); - else { - console.log(key); - if (isNaN(parseInt(key[0]))) { - key = this.formatJSObject(key); - } - else if (/[^\w\d_$]/g.test(key)) { - key = this.formatJSObject(key); - } - } - } - return `${key}: ${this.formatJSObject(obj[original])}`; - })}\n}`; - } - } - getColor(color_name) { - return Lightcord.DiscordModules.constants.Colors[color_name.toUpperCase()]; - } - firstLetterUppercase(str) { - if (!str) - return ""; - return str[0].toUpperCase() + str.slice(1); - } - executeXTimes(func, times) { - let results = []; - for (let i = 0; times > i; i++) { - results.push(func(i)); - } - return results; - } -}; -class LightcordApiError extends Error { - constructor() { - super(...arguments); - this.name = "LightcordApiError"; - } -} -exports.LightcordApiError = LightcordApiError; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LightcordApiError = void 0; +const ReactDOM = __webpack_require__(/*! react-dom */ "./src/alias/react-dom.js"); +const PluginUtilities_1 = __webpack_require__(/*! ./PluginUtilities */ "./src/modules/PluginUtilities.ts"); +exports.default = new class Utils { + constructor() { } + ReactToHTMLElement(ReactElement) { + const element = document.createElement("div"); + ReactDOM.render(ReactElement, element); + return element; + } + get PluginUtils() { return PluginUtilities_1.default; } + getNestedProps(obj, path) { + let segments = path.split("."); + for (let seg of segments) { + obj = obj && (seg in obj) ? obj[seg] : undefined; + } + return obj; + } + DecimalColorToHex(color) { + return "#" + color.toString(16).toUpperCase(); + } + HexColorToDecimal(color) { + color = color.replace(/[#;]/g, ""); + let res = parseInt(color, 16); + if (isNaN(res)) + throw new Error(`Invalid color: ${color}`); + return res; + } + removeDa(className) { + if (!className) + return className; + return className.split(" ").filter(e => !e.startsWith("da-")).join(" "); + } + FindReact(dom, traverseUp = 0) { + // taken from https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging#39165137 + const key = Object.keys(dom).find(key => key.startsWith("__reactInternalInstance$")); + const domFiber = dom[key]; + if (domFiber == null) + return null; + // react <16 + if (domFiber._currentElement) { + let compFiber = domFiber._currentElement._owner; + for (let i = 0; i < traverseUp; i++) { + compFiber = compFiber._currentElement._owner; + } + return compFiber._instance; + } + // react 16+ + const GetCompFiber = fiber => { + //return fiber._debugOwner; // this also works, but is __DEV__ only + let parentFiber = fiber.return; + while (typeof parentFiber.type == "string") { + parentFiber = parentFiber.return; + } + return parentFiber; + }; + let compFiber = GetCompFiber(domFiber); + for (let i = 0; i < traverseUp; i++) { + compFiber = GetCompFiber(compFiber); + } + return compFiber.stateNode; + } + hasClass(classNames, className) { + if (!classNames || !className) + return false; + const classnames = classNames.split(" "); + for (let classname of this.removeDa(className).split(" ")) { + if (!classnames.includes(classname)) + return false; + } + return true; + } + formatJSObject(obj) { + if (["string", "number", "boolean", "bigint", "undefined"].includes(typeof obj)) + return JSON.stringify(obj); + if (obj === null) + return "null"; + if (typeof obj === "function") + return String(obj); + if (typeof obj === "symbol") + return String(obj); + if (Array.isArray(obj)) { + if (!obj.length) + return "[]"; + return `[\n ${obj.map(e => this.formatJSObject(e)).join(",\n ")}\n]`; + } + else { + const keys = Object.keys(obj); + if (keys.length === 0) + return "{}"; + return `{\n ${keys.map(key => { + let original = key; + if (typeof key === "symbol") + key = "[" + String(key) + "]"; + else { + if (typeof key === "number") + key = String(key); + else { + console.log(key); + if (isNaN(parseInt(key[0]))) { + key = this.formatJSObject(key); + } + else if (/[^\w\d_$]/g.test(key)) { + key = this.formatJSObject(key); + } + } + } + return `${key}: ${this.formatJSObject(obj[original])}`; + })}\n}`; + } + } + getColor(color_name) { + return Lightcord.DiscordModules.constants.Colors[color_name.toUpperCase()]; + } + firstLetterUppercase(str) { + if (!str) + return ""; + return str[0].toUpperCase() + str.slice(1); + } + executeXTimes(func, times) { + let results = []; + for (let i = 0; times > i; i++) { + results.push(func(i)); + } + return results; + } +}; +class LightcordApiError extends Error { + constructor() { + super(...arguments); + this.name = "LightcordApiError"; + } +} +exports.LightcordApiError = LightcordApiError; /***/ }), @@ -27621,70 +27621,70 @@ exports.LightcordApiError = LightcordApiError; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WebpackLoaderError = void 0; -const BDModules = window.BDModules || __webpack_require__(/*! ./BDModules */ "./src/modules/BDModules.ts"); -exports.default = new class WebpackLoader { - constructor() { } - get(id) { - return BDModules.get(id); - } - find(filter) { - let result = BDModules.get(filter)[0]; - if (!result) { - console.warn(filter, "couldn't find the module."); - } - return result; - } - findByUniqueProperties(props) { - return BDModules.get((mod) => { - if (mod.__esModule && ("default" in mod)) { - let doesMatch = true; - for (let prop of props) { - if (!Object.prototype.hasOwnProperty.call(mod.default, prop)) - doesMatch = false; - } - if (doesMatch) - return true; - } - for (let prop of props) { - if (!Object.prototype.hasOwnProperty.call(mod, prop)) - return false; - } - return true; - })[0]; - } - filter(filter) { - return BDModules.get(filter); - } - filterByUniqueProperties(props) { - return BDModules.get((mod) => { - if (mod.__esModule && ("default" in mod)) { - let doesMatch = true; - for (let prop of props) { - if (!Object.prototype.hasOwnProperty.call(mod.default, prop)) - doesMatch = false; - } - if (doesMatch) - return true; - } - for (let prop of props) { - if (!Object.prototype.hasOwnProperty.call(mod, prop)) - return false; - } - return true; - }); - } -}; -class WebpackLoaderError extends Error { - constructor(message = "") { - message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error to the devs. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in our discord server."; - super(message); - this.name = "WebpackLoaderError"; - } -} -exports.WebpackLoaderError = WebpackLoaderError; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WebpackLoaderError = void 0; +const BDModules = window.BDModules || __webpack_require__(/*! ./BDModules */ "./src/modules/BDModules.ts"); +exports.default = new class WebpackLoader { + constructor() { } + get(id) { + return BDModules.get(id); + } + find(filter) { + let result = BDModules.get(filter)[0]; + if (!result) { + console.warn(filter, "couldn't find the module."); + } + return result; + } + findByUniqueProperties(props) { + return BDModules.get((mod) => { + if (mod.__esModule && ("default" in mod)) { + let doesMatch = true; + for (let prop of props) { + if (!Object.prototype.hasOwnProperty.call(mod.default, prop)) + doesMatch = false; + } + if (doesMatch) + return true; + } + for (let prop of props) { + if (!Object.prototype.hasOwnProperty.call(mod, prop)) + return false; + } + return true; + })[0]; + } + filter(filter) { + return BDModules.get(filter); + } + filterByUniqueProperties(props) { + return BDModules.get((mod) => { + if (mod.__esModule && ("default" in mod)) { + let doesMatch = true; + for (let prop of props) { + if (!Object.prototype.hasOwnProperty.call(mod.default, prop)) + doesMatch = false; + } + if (doesMatch) + return true; + } + for (let prop of props) { + if (!Object.prototype.hasOwnProperty.call(mod, prop)) + return false; + } + return true; + }); + } +}; +class WebpackLoaderError extends Error { + constructor(message = "") { + message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error to the devs. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in our discord server."; + super(message); + this.name = "WebpackLoaderError"; + } +} +exports.WebpackLoaderError = WebpackLoaderError; /***/ }), @@ -27697,20 +27697,20 @@ exports.WebpackLoaderError = WebpackLoaderError; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Recreate the given object without the __proto__. Useful for better formatting when output in console. - * @param obj The object to recreate - */ -function cloneNullProto(obj) { - let o = Object.create(null); - Object.keys(obj).forEach(k => { - o[k] = obj[k]; - }); - return o; -} -exports.default = cloneNullProto; + +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Recreate the given object without the __proto__. Useful for better formatting when output in console. + * @param obj The object to recreate + */ +function cloneNullProto(obj) { + let o = Object.create(null); + Object.keys(obj).forEach(k => { + o[k] = obj[k]; + }); + return o; +} +exports.default = cloneNullProto; /***/ }), @@ -27723,11 +27723,11 @@ exports.default = cloneNullProto; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isImported = exports.isNative = void 0; -exports.isNative = typeof window.BDModules === "undefined"; -exports.isImported = typeof window.BDModules !== "undefined"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isImported = exports.isNative = void 0; +exports.isNative = typeof window.BDModules === "undefined"; +exports.isImported = typeof window.BDModules !== "undefined"; /***/ }), @@ -27740,18 +27740,18 @@ exports.isImported = typeof window.BDModules !== "undefined"; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function excludeProperties(obj, props) { - let newObj = {}; - Object.keys(obj).forEach((k) => { - if (props.includes(k)) - return; - newObj[k] = obj[k]; - }); - return newObj; -} -exports.default = excludeProperties; + +Object.defineProperty(exports, "__esModule", { value: true }); +function excludeProperties(obj, props) { + let newObj = {}; + Object.keys(obj).forEach((k) => { + if (props.includes(k)) + return; + newObj[k] = obj[k]; + }); + return newObj; +} +exports.default = excludeProperties; /***/ }), @@ -27764,95 +27764,95 @@ exports.default = excludeProperties; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LazyLoad = void 0; -let cache = new Map(); -function LazyLoad(getObject) { - if (cache.has(getObject)) - return cache.get(getObject); - let mdl = null; - let setModule = () => { - if (mdl) - return; - mdl = getObject(); - }; - let handler = { - get(target, prop) { - setModule(); - return mdl[prop]; - }, - set(target, prop, value) { - setModule(); - mdl[prop] = value; - return true; - }, - apply(target, thisArg, args) { - setModule(); - mdl.apply(this, args); - }, - construct(target, args) { - setModule(); - const prototype = Object.create(mdl.prototype); - handler.apply(target, prototype, args); - return prototype; - }, - deleteProperty(target, prop) { - setModule(); - if (!(prop in mdl)) - return false; - delete mdl[prop]; - return true; - }, - enumerate(target) { - setModule(); - return Object.keys(mdl); - }, - ownKeys(target) { - setModule(); - return Object.keys(mdl); - }, - has(target, prop) { - setModule(); - return prop in mdl; - }, - defineProperty(target, prop, attributes) { - setModule(); - return Object.defineProperty(mdl, prop, attributes); - }, - getOwnPropertyDescriptor(target, prop) { - setModule(); - return Object.getOwnPropertyDescriptor(mdl, prop); - }, - getPrototypeOf(target) { - setModule(); - return Object.getPrototypeOf(mdl); - }, - setPrototypeOf(target, proto) { - setModule(); - try { - Object.setPrototypeOf(mdl, proto); - return true; - } - catch (e) { - return false; - } - }, - isExtensible(target) { - setModule(); - return Object.isExtensible(mdl); - }, - preventExtensions(target) { - setModule(); - Object.preventExtensions(mdl); - return true; - } - }; - const proxy = new Proxy({}, handler); - cache.set(getObject, proxy); - return proxy; -} -exports.LazyLoad = LazyLoad; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LazyLoad = void 0; +let cache = new Map(); +function LazyLoad(getObject) { + if (cache.has(getObject)) + return cache.get(getObject); + let mdl = null; + let setModule = () => { + if (mdl) + return; + mdl = getObject(); + }; + let handler = { + get(target, prop) { + setModule(); + return mdl[prop]; + }, + set(target, prop, value) { + setModule(); + mdl[prop] = value; + return true; + }, + apply(target, thisArg, args) { + setModule(); + mdl.apply(this, args); + }, + construct(target, args) { + setModule(); + const prototype = Object.create(mdl.prototype); + handler.apply(target, prototype, args); + return prototype; + }, + deleteProperty(target, prop) { + setModule(); + if (!(prop in mdl)) + return false; + delete mdl[prop]; + return true; + }, + enumerate(target) { + setModule(); + return Object.keys(mdl); + }, + ownKeys(target) { + setModule(); + return Object.keys(mdl); + }, + has(target, prop) { + setModule(); + return prop in mdl; + }, + defineProperty(target, prop, attributes) { + setModule(); + return Object.defineProperty(mdl, prop, attributes); + }, + getOwnPropertyDescriptor(target, prop) { + setModule(); + return Object.getOwnPropertyDescriptor(mdl, prop); + }, + getPrototypeOf(target) { + setModule(); + return Object.getPrototypeOf(mdl); + }, + setPrototypeOf(target, proto) { + setModule(); + try { + Object.setPrototypeOf(mdl, proto); + return true; + } + catch (e) { + return false; + } + }, + isExtensible(target) { + setModule(); + return Object.isExtensible(mdl); + }, + preventExtensions(target) { + setModule(); + Object.preventExtensions(mdl); + return true; + } + }; + const proxy = new Proxy({}, handler); + cache.set(getObject, proxy); + return proxy; +} +exports.LazyLoad = LazyLoad; /***/ }), @@ -27865,10 +27865,10 @@ exports.LazyLoad = LazyLoad; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function NOOP() { } -exports.default = NOOP; + +Object.defineProperty(exports, "__esModule", { value: true }); +function NOOP() { } +exports.default = NOOP; /***/ }), @@ -27881,236 +27881,236 @@ exports.default = NOOP; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.patch = void 0; -const Utils_1 = __webpack_require__(/*! ./Utils */ "./src/modules/Utils.ts"); -const Notices_1 = __webpack_require__(/*! ../components/private/Notices */ "./src/components/private/Notices.tsx"); -const environnement_1 = __webpack_require__(/*! ./environnement */ "./src/modules/environnement.ts"); -function patch() { - /** START NOTICE */ - getModule(e => e.default && e.default.displayName === "ConnectedAppView") - .then(async (mod) => { - const appClasses = await getModule(e => e.hasNotice); - const buildRender = original => { - return function render() { - const returnValue = original.call(this, ...arguments); - const newchildren = []; - let children = returnValue.props.children[1].props.children; - if (!Array.isArray(children)) - children = [children]; - newchildren.push(children[0]); - newchildren.push(React.createElement(Notices_1.default, { container: this })); - newchildren.push(children[1]); - returnValue.props.children[1].props.children = newchildren; - returnValue.props.children[1].props.children[2].props.children[0].props.render = buildRenderChannelSidebar(returnValue.props.children[1].props.children[2].props.children[0].props.render); - return returnValue; - }; - }; - const buildRenderChannelSidebar = original => { - return function renderChannelSidebar() { - const returnValue = original.call(this, ...arguments); - const hasNotice = Notices_1.notices.length > 0; - if (!hasNotice) - return returnValue; - if (!Utils_1.default.hasClass(returnValue.props.className, appClasses.hasNotice)) { - returnValue.props.className += " " + Utils_1.default.removeDa(appClasses.hasNotice); - } - return returnValue; - }; - }; - mod.default.prototype.render = buildRender(mod.default.prototype.render); - (async function () { - const base = document.querySelector("." + Utils_1.default.removeDa(appClasses.base)); - if (!base) - throw new Error(`Could not find base here`); - const elem = Utils_1.default.FindReact(base); - elem.render = buildRender(elem.render); - elem.forceUpdate(); - })(); - }); - /** END NOTICE */ - if (environnement_1.isNative) { - /** START USERPOPOUT PATCH */ - awaitLogin() - .then(async () => { - let UserPopout = await getModule(e => e.default && e.default.displayName === "FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))"); - const userModule = await getModule(e => e.default && e.default.getCurrentUser); - const render1 = new UserPopout.default({ userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true }).render(); - const PopoutProps = render1.props; - const render2 = render1.type.render(PopoutProps, null); - const render3 = new render2.type(render2.props).render(); - const UserPopoutComponent = render3.type; - if (!UserPopoutComponent) - throw new Error(`Couldn't find the UserPopoutComponent component.`); - const render = UserPopoutComponent.prototype.render; - UserPopoutComponent.prototype.render = function () { - const returnValue = render.call(this, ...arguments); - try { - returnValue.props.children.props["data-user-id"] = this.props.user.id; - } - catch (e) { - console.error(e); - } - return returnValue; - }; - }); - /** END USERPOPOUT PATCH*/ - /** START USERPROFILE PATCH */ - awaitLogin() - .then(async () => { - let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile"); - const userModule = await getModule(e => e.default && e.default.getCurrentUser); - const render1 = new UserProfile.default({ - user: userModule.default.getCurrentUser() - }).render(); - const render2 = new render1.type(render1.props).render(); - const render3 = render2.type.render(render2.props, null); - const render4 = new render3.type(render3.props).render(); - const UserProfileComponent = render4.type; - if (!UserProfileComponent) - throw new Error(`Couldn't find the UserProfileComponent component.`); - const render = UserProfileComponent.prototype.render; - UserProfileComponent.prototype.render = function () { - const returnValue = render.call(this, ...arguments); - console.log(returnValue); - try { - returnValue.props.children.props["data-user-id"] = this.props.user.id; - } - catch (e) { - console.error(e); - } - return returnValue; - }; - }); - /** END USERPROFILE PATCH */ - /** START WEBHOOK PATCH */ - /* - let usedWebhooks = {} - - getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end) - .then(RequestModule => { - const end = RequestModule.Request.prototype.end - RequestModule.Request.prototype.end = function(){ - if(this.url.endsWith("/messages") && /\/channels\/\d+\/messages/g.test(this.url) && this.method === "POST"){ // sending message - let channelId = this.url.split("/channels/")[1].split("/messages")[0] - - if(usedWebhooks[channelId]){ // webhook is availlable - let webhook = usedWebhooks[channelId] - let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true` - this.url = url - } - } - - return end.call(this, ...arguments) - } - }) - getModule(e => e.default && e.default.displayName === "Webhook") - .then(webhookComponent => { - const renderEdit = webhookComponent.default.prototype.renderEdit - webhookComponent.default.prototype.renderEdit = function(){ - const webhook = this.props.webhook - let returnValue = renderEdit.call(this, ...arguments) - returnValue.props.children = [returnValue.props.children] - let message = usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id ? "Stop talking with this webhook" : "Talk with this webhook" - - returnValue.props.children.push(React.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "green", wrapper: false, onClick(){ - if(usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id){ - delete usedWebhooks[webhook.channel_id] - }else{ - usedWebhooks[webhook.channel_id] = { - id: webhook.id, - token: webhook.token - } - } - webhookPanels.forEach(e => e()) - }}, message)) - - return returnValue - } - }) - - let webhookPanels = [] - let getComp = (comp) => { - class SettingsWebhooks extends React.PureComponent { - constructor(props){ - super(props) - } - - componentWillMount(){ - this.id = uuid() - this.component = new comp(this.props) - let func = () => { - this.component.forceUpdate() - } - func.id = this.id - webhookPanels.push(func) - } - - componentWillUnmount(){ - this.component = null - webhookPanels = webhookPanels.filter(e => e.id !== this.id) - } - - render(){ - return this.component.render() - } - - static displayName = "SettingsWebhooks" - } - - return SettingsWebhooks - } - getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)") - .then(webhooksComponents => { - let comp = webhooksComponents.default - - webhooksComponents.default = getComp(comp) - - WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))") - .forEach(mod => { - mod.default = getComp(mod.default) - }) - })*/ - /** END WEBHOOK PATCH */ - } - // TODO: Add in app-notifications / confirmations. - /** START IN-APP NOTIFICATIONS */ - //getModule(e => true) - /** END IN-APP NOTIFICATIONS */ -} -exports.patch = patch; -function getModule(filter) { - return new Promise((resolve) => { - window.Lightcord.Api.ensureExported(filter) - .then(resolve) - .catch(err => { - console.error("[LIGHTCORD]", err, filter); - }); - }); -} -let hasCompletedLogin = false; -let loginPromise; -function awaitLogin() { - if (hasCompletedLogin) - return Promise.resolve(); - if (loginPromise) - return loginPromise; - return loginPromise = new Promise((resolve) => { - let isResolved = false; - window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => { - if (isResolved) - return; - hasCompletedLogin = true; - resolve(); - isResolved = true; - }); - }); -} -window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => { - hasCompletedLogin = false; - loginPromise = undefined; -}); + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.patch = void 0; +const Utils_1 = __webpack_require__(/*! ./Utils */ "./src/modules/Utils.ts"); +const Notices_1 = __webpack_require__(/*! ../components/private/Notices */ "./src/components/private/Notices.tsx"); +const environnement_1 = __webpack_require__(/*! ./environnement */ "./src/modules/environnement.ts"); +function patch() { + /** START NOTICE */ + getModule(e => e.default && e.default.displayName === "ConnectedAppView") + .then(async (mod) => { + const appClasses = await getModule(e => e.hasNotice); + const buildRender = original => { + return function render() { + const returnValue = original.call(this, ...arguments); + const newchildren = []; + let children = returnValue.props.children[1].props.children; + if (!Array.isArray(children)) + children = [children]; + newchildren.push(children[0]); + newchildren.push(React.createElement(Notices_1.default, { container: this })); + newchildren.push(children[1]); + returnValue.props.children[1].props.children = newchildren; + returnValue.props.children[1].props.children[2].props.children[0].props.render = buildRenderChannelSidebar(returnValue.props.children[1].props.children[2].props.children[0].props.render); + return returnValue; + }; + }; + const buildRenderChannelSidebar = original => { + return function renderChannelSidebar() { + const returnValue = original.call(this, ...arguments); + const hasNotice = Notices_1.notices.length > 0; + if (!hasNotice) + return returnValue; + if (!Utils_1.default.hasClass(returnValue.props.className, appClasses.hasNotice)) { + returnValue.props.className += " " + Utils_1.default.removeDa(appClasses.hasNotice); + } + return returnValue; + }; + }; + mod.default.prototype.render = buildRender(mod.default.prototype.render); + (async function () { + const base = document.querySelector("." + Utils_1.default.removeDa(appClasses.base)); + if (!base) + throw new Error(`Could not find base here`); + const elem = Utils_1.default.FindReact(base); + elem.render = buildRender(elem.render); + elem.forceUpdate(); + })(); + }); + /** END NOTICE */ + if (environnement_1.isNative) { + /** START USERPOPOUT PATCH */ + awaitLogin() + .then(async () => { + let UserPopout = await getModule(e => e.default && e.default.displayName === "FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))"); + const userModule = await getModule(e => e.default && e.default.getCurrentUser); + const render1 = new UserPopout.default({ userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true }).render(); + const PopoutProps = render1.props; + const render2 = render1.type.render(PopoutProps, null); + const render3 = new render2.type(render2.props).render(); + const UserPopoutComponent = render3.type; + if (!UserPopoutComponent) + throw new Error(`Couldn't find the UserPopoutComponent component.`); + const render = UserPopoutComponent.prototype.render; + UserPopoutComponent.prototype.render = function () { + const returnValue = render.call(this, ...arguments); + try { + returnValue.props.children.props["data-user-id"] = this.props.user.id; + } + catch (e) { + console.error(e); + } + return returnValue; + }; + }); + /** END USERPOPOUT PATCH*/ + /** START USERPROFILE PATCH */ + awaitLogin() + .then(async () => { + let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile"); + const userModule = await getModule(e => e.default && e.default.getCurrentUser); + const render1 = new UserProfile.default({ + user: userModule.default.getCurrentUser() + }).render(); + const render2 = new render1.type(render1.props).render(); + const render3 = render2.type.render(render2.props, null); + const render4 = new render3.type(render3.props).render(); + const UserProfileComponent = render4.type; + if (!UserProfileComponent) + throw new Error(`Couldn't find the UserProfileComponent component.`); + const render = UserProfileComponent.prototype.render; + UserProfileComponent.prototype.render = function () { + const returnValue = render.call(this, ...arguments); + console.log(returnValue); + try { + returnValue.props.children.props["data-user-id"] = this.props.user.id; + } + catch (e) { + console.error(e); + } + return returnValue; + }; + }); + /** END USERPROFILE PATCH */ + /** START WEBHOOK PATCH */ + /* + let usedWebhooks = {} + + getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end) + .then(RequestModule => { + const end = RequestModule.Request.prototype.end + RequestModule.Request.prototype.end = function(){ + if(this.url.endsWith("/messages") && /\/channels\/\d+\/messages/g.test(this.url) && this.method === "POST"){ // sending message + let channelId = this.url.split("/channels/")[1].split("/messages")[0] + + if(usedWebhooks[channelId]){ // webhook is availlable + let webhook = usedWebhooks[channelId] + let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true` + this.url = url + } + } + + return end.call(this, ...arguments) + } + }) + getModule(e => e.default && e.default.displayName === "Webhook") + .then(webhookComponent => { + const renderEdit = webhookComponent.default.prototype.renderEdit + webhookComponent.default.prototype.renderEdit = function(){ + const webhook = this.props.webhook + let returnValue = renderEdit.call(this, ...arguments) + returnValue.props.children = [returnValue.props.children] + let message = usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id ? "Stop talking with this webhook" : "Talk with this webhook" + + returnValue.props.children.push(React.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "green", wrapper: false, onClick(){ + if(usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id){ + delete usedWebhooks[webhook.channel_id] + }else{ + usedWebhooks[webhook.channel_id] = { + id: webhook.id, + token: webhook.token + } + } + webhookPanels.forEach(e => e()) + }}, message)) + + return returnValue + } + }) + + let webhookPanels = [] + let getComp = (comp) => { + class SettingsWebhooks extends React.PureComponent { + constructor(props){ + super(props) + } + + componentWillMount(){ + this.id = uuid() + this.component = new comp(this.props) + let func = () => { + this.component.forceUpdate() + } + func.id = this.id + webhookPanels.push(func) + } + + componentWillUnmount(){ + this.component = null + webhookPanels = webhookPanels.filter(e => e.id !== this.id) + } + + render(){ + return this.component.render() + } + + static displayName = "SettingsWebhooks" + } + + return SettingsWebhooks + } + getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)") + .then(webhooksComponents => { + let comp = webhooksComponents.default + + webhooksComponents.default = getComp(comp) + + WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))") + .forEach(mod => { + mod.default = getComp(mod.default) + }) + })*/ + /** END WEBHOOK PATCH */ + } + // TODO: Add in app-notifications / confirmations. + /** START IN-APP NOTIFICATIONS */ + //getModule(e => true) + /** END IN-APP NOTIFICATIONS */ +} +exports.patch = patch; +function getModule(filter) { + return new Promise((resolve) => { + window.Lightcord.Api.ensureExported(filter) + .then(resolve) + .catch(err => { + console.error("[LIGHTCORD]", err, filter); + }); + }); +} +let hasCompletedLogin = false; +let loginPromise; +function awaitLogin() { + if (hasCompletedLogin) + return Promise.resolve(); + if (loginPromise) + return loginPromise; + return loginPromise = new Promise((resolve) => { + let isResolved = false; + window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => { + if (isResolved) + return; + hasCompletedLogin = true; + resolve(); + isResolved = true; + }); + }); +} +window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => { + hasCompletedLogin = false; + loginPromise = undefined; +}); /***/ }), @@ -28123,14 +28123,14 @@ window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => { /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const uuidv1 = __webpack_require__(/*! uuid/v1 */ "uuid/v1"); -const uuidv4 = __webpack_require__(/*! uuid/v4 */ "uuid/v4"); -let uuid = Object.assign(function () { - return uuidv4(); -}, { v1: () => uuidv1(), v4: () => uuidv4() }); -exports.default = uuid; + +Object.defineProperty(exports, "__esModule", { value: true }); +const uuidv1 = __webpack_require__(/*! uuid/v1 */ "uuid/v1"); +const uuidv4 = __webpack_require__(/*! uuid/v4 */ "uuid/v4"); +let uuid = Object.assign(function () { + return uuidv4(); +}, { v1: () => uuidv1(), v4: () => uuidv4() }); +exports.default = uuid; /***/ }), diff --git a/LightcordApi/prod.config.js b/LightcordApi/prod.config.js index 93c0720..00102b9 100644 --- a/LightcordApi/prod.config.js +++ b/LightcordApi/prod.config.js @@ -1,7 +1,7 @@ -const config = require("./webpack.config.js") - -config.mode = "production" -config.devtool = "source-map" -config.output.filename = "main.min.js" - +const config = require("./webpack.config.js") + +config.mode = "production" +config.devtool = "source-map" +config.output.filename = "main.min.js" + module.exports = config \ No newline at end of file diff --git a/LightcordApi/src/alias/react-dom.js b/LightcordApi/src/alias/react-dom.js index fa41844..d8f5606 100644 --- a/LightcordApi/src/alias/react-dom.js +++ b/LightcordApi/src/alias/react-dom.js @@ -1,26 +1,26 @@ -// bait typescript into thinking this is not reactDOM so no circular dependency. -window.ReactDOM = (window["Reac"+"tDOM"] || // If in Lightcord - (()=>{ // If in Standard BetterDiscord - try{ - return window.BdApi.ReactDOM - }catch(e){ - return null - } - })() || - (()=>{ // If in Powercord - try{ - const webpack = require("powercord/webpack") - return webpack.ReactDOM - }catch(e){ - return null - } - })() || - (()=>{ // If in EnhancedDiscord - try{ - return window.EDApi.ReactDOM - }catch(e){ - return null - } - })()) - +// bait typescript into thinking this is not reactDOM so no circular dependency. +window.ReactDOM = (window["Reac"+"tDOM"] || // If in Lightcord + (()=>{ // If in Standard BetterDiscord + try{ + return window.BdApi.ReactDOM + }catch(e){ + return null + } + })() || + (()=>{ // If in Powercord + try{ + const webpack = require("powercord/webpack") + return webpack.ReactDOM + }catch(e){ + return null + } + })() || + (()=>{ // If in EnhancedDiscord + try{ + return window.EDApi.ReactDOM + }catch(e){ + return null + } + })()) + module.exports = window.ReactDOM \ No newline at end of file diff --git a/LightcordApi/src/alias/react.js b/LightcordApi/src/alias/react.js index 509ea94..aa8adf3 100644 --- a/LightcordApi/src/alias/react.js +++ b/LightcordApi/src/alias/react.js @@ -1,25 +1,25 @@ -window.React = (window.React || // If in Lightcord - (()=>{ // If in Standard BetterDiscord - try{ - return window.BdApi.React - }catch(e){ - return null - } - })() || - (()=>{ // If in Powercord - try{ - const webpack = require("powercord/webpack") - return webpack.React - }catch(e){ - return null - } - })() || - (()=>{ // If in EnhancedDiscord - try{ - return window.EDApi.React - }catch(e){ - return null - } - })()) - +window.React = (window.React || // If in Lightcord + (()=>{ // If in Standard BetterDiscord + try{ + return window.BdApi.React + }catch(e){ + return null + } + })() || + (()=>{ // If in Powercord + try{ + const webpack = require("powercord/webpack") + return webpack.React + }catch(e){ + return null + } + })() || + (()=>{ // If in EnhancedDiscord + try{ + return window.EDApi.React + }catch(e){ + return null + } + })()) + module.exports = window.React \ No newline at end of file diff --git a/LightcordApi/src/components/components.ts b/LightcordApi/src/components/components.ts index fbfe768..67a4418 100644 --- a/LightcordApi/src/components/components.ts +++ b/LightcordApi/src/components/components.ts @@ -1,49 +1,49 @@ -import DiscordButton from "./inputs/Button" -import Switch from "./inputs/Switch" -import RadioGroup from "./inputs/RadioGroup" -import TextArea from "./inputs/TextArea" -import TextInput from "./inputs/TextInput" -import Dropdown from "./inputs/Dropdown" -import Title from "./general/Title" -import SettingsTitle from "./general/SettingsTitle" -import Tabs, { Tab } from "./general/Tabs" -import SettingSubTitle from "./general/SettingSubTitle" -import CodeBlock from "./general/CodeBlock" -import cloneNullProto from "../modules/cloneNullProto" -import Tooltip from "./general/Tooltip" -import ColorPicker from "./inputs/ColorPicker" -import AlertBox from "./general/AlertBox" -import ErrorCatcher, { createProxyErrorCatcherClass } from "./general/ErrorCatcher" -import Flex, { FlexChild } from "./general/Flex" -import Text from "./general/Text" -import DateRange from "./inputs/DateRange" -import DateInput from "./inputs/DateInput" - -const RadioGroupProxied = createProxyErrorCatcherClass(RadioGroup) -export default cloneNullProto({ - inputs: cloneNullProto({ - Button: createProxyErrorCatcherClass(DiscordButton), - Switch: createProxyErrorCatcherClass(Switch), - Choices: RadioGroupProxied, - RadioGroup: RadioGroupProxied, - TextArea: createProxyErrorCatcherClass(TextArea), - TextInput: createProxyErrorCatcherClass(TextInput), - Dropdown: createProxyErrorCatcherClass(Dropdown), - ColorPicker: createProxyErrorCatcherClass(ColorPicker), - DateRange: createProxyErrorCatcherClass(DateRange), - DateInput: createProxyErrorCatcherClass(DateInput) - }), - general: cloneNullProto({ - Title: createProxyErrorCatcherClass(Title), - SettingsTitle: createProxyErrorCatcherClass(SettingsTitle), - SettingSubTitle: createProxyErrorCatcherClass(SettingSubTitle), - Tabs: createProxyErrorCatcherClass(Tabs), - CodeBlock: createProxyErrorCatcherClass(CodeBlock), - Tooltip: createProxyErrorCatcherClass(Tooltip), - AlertBox: createProxyErrorCatcherClass(AlertBox), - Flex: createProxyErrorCatcherClass(Flex), - FlexChild: createProxyErrorCatcherClass(FlexChild), - ErrorCatcher: ErrorCatcher, - Text: Text - }) +import DiscordButton from "./inputs/Button" +import Switch from "./inputs/Switch" +import RadioGroup from "./inputs/RadioGroup" +import TextArea from "./inputs/TextArea" +import TextInput from "./inputs/TextInput" +import Dropdown from "./inputs/Dropdown" +import Title from "./general/Title" +import SettingsTitle from "./general/SettingsTitle" +import Tabs, { Tab } from "./general/Tabs" +import SettingSubTitle from "./general/SettingSubTitle" +import CodeBlock from "./general/CodeBlock" +import cloneNullProto from "../modules/cloneNullProto" +import Tooltip from "./general/Tooltip" +import ColorPicker from "./inputs/ColorPicker" +import AlertBox from "./general/AlertBox" +import ErrorCatcher, { createProxyErrorCatcherClass } from "./general/ErrorCatcher" +import Flex, { FlexChild } from "./general/Flex" +import Text from "./general/Text" +import DateRange from "./inputs/DateRange" +import DateInput from "./inputs/DateInput" + +const RadioGroupProxied = createProxyErrorCatcherClass(RadioGroup) +export default cloneNullProto({ + inputs: cloneNullProto({ + Button: createProxyErrorCatcherClass(DiscordButton), + Switch: createProxyErrorCatcherClass(Switch), + Choices: RadioGroupProxied, + RadioGroup: RadioGroupProxied, + TextArea: createProxyErrorCatcherClass(TextArea), + TextInput: createProxyErrorCatcherClass(TextInput), + Dropdown: createProxyErrorCatcherClass(Dropdown), + ColorPicker: createProxyErrorCatcherClass(ColorPicker), + DateRange: createProxyErrorCatcherClass(DateRange), + DateInput: createProxyErrorCatcherClass(DateInput) + }), + general: cloneNullProto({ + Title: createProxyErrorCatcherClass(Title), + SettingsTitle: createProxyErrorCatcherClass(SettingsTitle), + SettingSubTitle: createProxyErrorCatcherClass(SettingSubTitle), + Tabs: createProxyErrorCatcherClass(Tabs), + CodeBlock: createProxyErrorCatcherClass(CodeBlock), + Tooltip: createProxyErrorCatcherClass(Tooltip), + AlertBox: createProxyErrorCatcherClass(AlertBox), + Flex: createProxyErrorCatcherClass(Flex), + FlexChild: createProxyErrorCatcherClass(FlexChild), + ErrorCatcher: ErrorCatcher, + Text: Text + }) }) \ No newline at end of file diff --git a/LightcordApi/src/components/general/Text.ts b/LightcordApi/src/components/general/Text.ts index bbba1e3..25ccaa2 100644 --- a/LightcordApi/src/components/general/Text.ts +++ b/LightcordApi/src/components/general/Text.ts @@ -1,95 +1,95 @@ -import * as React from "react" -import Utils, { ColorName } from "../../modules/Utils" - - -export type TextProps = { - weight?: TextWeight, - color?: TextColor|string, - textCase?: TextCase, - size?: TextSize, - selectable?: boolean, - family?: TextFamily, - children: string, - tag?: string, - className?: string, - style?: React.CSSProperties -} -export default class Text extends React.Component { - render(){ - let classList = this.props.className ? this.props.className.split(" ") : [] - let style = this.props.style ? {...this.props.style} : {} - style.flexGrow = 0 - if(this.props.weight){ - classList.push(`lc-text-weight${Utils.firstLetterUppercase(this.props.weight)}`) - } - if(this.props.color){ - style.color = Utils.getColor(this.props.color as TextColor) || this.props.color - } - if(this.props.textCase){ - classList.push(`lc-text-cases${Utils.firstLetterUppercase(this.props.textCase)}`) - } - if(this.props.size){ - classList.push(...Constants.sizes[this.props.size]) - } - if(this.props.family){ - classList.push(`lc-text-${this.props.family}`) - } - if(this.props.selectable){ - classList.push("lc-text-selectable") - } - - return React.createElement(this.props.tag, { - style, - className: classList.join(" ") - }, this.props.children) - } - - static defaultProps:TextProps = { - tag: "div", - children: null - } - - static get AllPreviews(){ - return AllPreviews || (AllPreviews = [ - this.weights.map(e => ({weight: e})), - this.colors.map(e => ({color: e})), - this.textCases.map(e => ({textCase: e})), - this.sizes.map(e => ({size: e})), - this.familys.map(e => ({family: e})), - [ - { - children: "Test Text" - } - ], - ["div","p",...Utils.executeXTimes((index) => { - return "h"+(index+1) - }, 6)].map(e => ({tag: e})), - [true, false].map(e => ({selectable: e})) - ]) - } - - static weights:TextWeight[] = ["light", "normal", "medium", "semibold", "bold"] - static get colors():TextColor[]{ - return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()) as TextColor[] - } - static textCases:TextCase[] = ["lowercase", "uppercase"] - static sizes:TextSize[] = ["small", "medium", "medium_small", "medium_large", "large"] - static familys:TextFamily[] = ["primary", "code"] -} -let AllPreviews - -export type TextWeight = "light"|"normal"|"medium"|"semibold"|"bold" -export type TextColor = ColorName -export type TextCase = "lowercase"|"uppercase" -export type TextSize = "small"|"medium"|"medium_small"|"medium_large"|"large" -export type TextFamily = "primary"|"code" - -export const Constants = { - sizes: { - small: ["lc-text-size12", "lc-text-height16"], - medium_small: ["lc-text-size14", "lc-text-height16"], - medium: ["lc-text-size16", "lc-text-height20"], - medium_large: ["lc-text-size20", "lc-text-height26"], - large: ["lc-text-size28", "lc-text-height34"] - } +import * as React from "react" +import Utils, { ColorName } from "../../modules/Utils" + + +export type TextProps = { + weight?: TextWeight, + color?: TextColor|string, + textCase?: TextCase, + size?: TextSize, + selectable?: boolean, + family?: TextFamily, + children: string, + tag?: string, + className?: string, + style?: React.CSSProperties +} +export default class Text extends React.Component { + render(){ + let classList = this.props.className ? this.props.className.split(" ") : [] + let style = this.props.style ? {...this.props.style} : {} + style.flexGrow = 0 + if(this.props.weight){ + classList.push(`lc-text-weight${Utils.firstLetterUppercase(this.props.weight)}`) + } + if(this.props.color){ + style.color = Utils.getColor(this.props.color as TextColor) || this.props.color + } + if(this.props.textCase){ + classList.push(`lc-text-cases${Utils.firstLetterUppercase(this.props.textCase)}`) + } + if(this.props.size){ + classList.push(...Constants.sizes[this.props.size]) + } + if(this.props.family){ + classList.push(`lc-text-${this.props.family}`) + } + if(this.props.selectable){ + classList.push("lc-text-selectable") + } + + return React.createElement(this.props.tag, { + style, + className: classList.join(" ") + }, this.props.children) + } + + static defaultProps:TextProps = { + tag: "div", + children: null + } + + static get AllPreviews(){ + return AllPreviews || (AllPreviews = [ + this.weights.map(e => ({weight: e})), + this.colors.map(e => ({color: e})), + this.textCases.map(e => ({textCase: e})), + this.sizes.map(e => ({size: e})), + this.familys.map(e => ({family: e})), + [ + { + children: "Test Text" + } + ], + ["div","p",...Utils.executeXTimes((index) => { + return "h"+(index+1) + }, 6)].map(e => ({tag: e})), + [true, false].map(e => ({selectable: e})) + ]) + } + + static weights:TextWeight[] = ["light", "normal", "medium", "semibold", "bold"] + static get colors():TextColor[]{ + return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()) as TextColor[] + } + static textCases:TextCase[] = ["lowercase", "uppercase"] + static sizes:TextSize[] = ["small", "medium", "medium_small", "medium_large", "large"] + static familys:TextFamily[] = ["primary", "code"] +} +let AllPreviews + +export type TextWeight = "light"|"normal"|"medium"|"semibold"|"bold" +export type TextColor = ColorName +export type TextCase = "lowercase"|"uppercase" +export type TextSize = "small"|"medium"|"medium_small"|"medium_large"|"large" +export type TextFamily = "primary"|"code" + +export const Constants = { + sizes: { + small: ["lc-text-size12", "lc-text-height16"], + medium_small: ["lc-text-size14", "lc-text-height16"], + medium: ["lc-text-size16", "lc-text-height20"], + medium_large: ["lc-text-size20", "lc-text-height26"], + large: ["lc-text-size28", "lc-text-height34"] + } } \ No newline at end of file diff --git a/LightcordApi/src/index.ts b/LightcordApi/src/index.ts index 1f59c89..53f5678 100644 --- a/LightcordApi/src/index.ts +++ b/LightcordApi/src/index.ts @@ -1,104 +1,104 @@ -import WebpackLoader from "./modules/WebpackLoader" -import Components from "./components/components" -import uuid from "./modules/uuid" -import Utils from "./modules/Utils" -import DiscordTools from "./modules/DiscordTools" -import * as patchers from "./modules/patchers" -import excludeProperties from "./modules/excludeProperties" -import cloneNullProto from "./modules/cloneNullProto" -import NOOP from "./modules/noop" -import unfreeze from "./modules/Unfreeze" -import { isNative, isImported } from "./modules/environnement" -import * as bandagedbdApi from "@bandagedbd/bdapi" -import "./alias/react" -import "./alias/react-dom" -import { LazyLoad } from "./modules/lazyLoader" -patchers.patch() - -const LightcordApi = { - WebpackLoader: WebpackLoader, - Components: Components, - uuid: uuid, - Utils: Utils, - DiscordTools: DiscordTools, - _: { - excludeProperties: excludeProperties, - cloneNullProto: cloneNullProto, - NOOP: NOOP, - unfreeze: unfreeze - }, - get isNative(){return isNative}, - get isImported(){return isImported}, - LazyLoad: LazyLoad -} - -declare global { - var React:typeof import("react") - var ReactDOM:typeof import("../node_modules/@types/react-dom") - interface Window { - /** - * Lightcord is only availlaible in Lightcord (native) - */ - Lightcord: LightcordGlobal, - /** - * BDModules is only availlaible in Lightcord (native) - */ - BDModules: { - modules:any[], - get(filter:(mod:any)=>boolean, modules?:any[]):any[], - get(id:number, modules?:any[]):any, - get(ids: [number|((mod:any)=>boolean)], modules?:any[]):any - }, - BdApi: typeof bandagedbdApi.BdApi, - EDApi: typeof bandagedbdApi.BdApi, - ReactDOM: typeof ReactDOM; - React:typeof React - } - var Lightcord:LightcordGlobal - var BdApi: typeof bandagedbdApi.BdApi - var EDApi: typeof bandagedbdApi.BdApi -} - -export default LightcordApi - -Object.assign(window.Lightcord.Api, LightcordApi) - -/** - * The main Lightcord exports. Can be accessed with `window.Lightcord` - */ -export interface LightcordGlobal { - DiscordModules: { - /** - * Internal Discord's dispatcher - can be used to subscribe to gateway events / client events. - */ - dispatcher: import("./types/DiscordDispatcherTypes").default, - constants: import("./types/DiscordConstantsTypes").default - }, - Settings: { - devMode: boolean, - callRingingBeat: boolean - }, - Api: LightcordApiGlobal, - BetterDiscord: { - BdApi: typeof bandagedbdApi.BdApi, - [mod:string]:any - } -} - -/** - * The main Api. Can be accessed with `window.Lightcord.Api` - */ -type LightcordApiGlobal = lightcordApiMainExports & typeof LightcordApi - -type lightcordApiMainExports = { - /** - * Waits until the first module that match the filter gets exported - * @param filter The filter that specifies the module to match. - */ - ensureExported(filter: (mod:any) => boolean):Promise, - /** - * Recreate the object without the `__proto__` and `prototype` properties - usefull for better formatting in console. - * @param obj The object to recreate - */ - cloneNullProto(obj:Obj):Obj +import WebpackLoader from "./modules/WebpackLoader" +import Components from "./components/components" +import uuid from "./modules/uuid" +import Utils from "./modules/Utils" +import DiscordTools from "./modules/DiscordTools" +import * as patchers from "./modules/patchers" +import excludeProperties from "./modules/excludeProperties" +import cloneNullProto from "./modules/cloneNullProto" +import NOOP from "./modules/noop" +import unfreeze from "./modules/Unfreeze" +import { isNative, isImported } from "./modules/environnement" +import * as bandagedbdApi from "@bandagedbd/bdapi" +import "./alias/react" +import "./alias/react-dom" +import { LazyLoad } from "./modules/lazyLoader" +patchers.patch() + +const LightcordApi = { + WebpackLoader: WebpackLoader, + Components: Components, + uuid: uuid, + Utils: Utils, + DiscordTools: DiscordTools, + _: { + excludeProperties: excludeProperties, + cloneNullProto: cloneNullProto, + NOOP: NOOP, + unfreeze: unfreeze + }, + get isNative(){return isNative}, + get isImported(){return isImported}, + LazyLoad: LazyLoad +} + +declare global { + var React:typeof import("react") + var ReactDOM:typeof import("../node_modules/@types/react-dom") + interface Window { + /** + * Lightcord is only availlaible in Lightcord (native) + */ + Lightcord: LightcordGlobal, + /** + * BDModules is only availlaible in Lightcord (native) + */ + BDModules: { + modules:any[], + get(filter:(mod:any)=>boolean, modules?:any[]):any[], + get(id:number, modules?:any[]):any, + get(ids: [number|((mod:any)=>boolean)], modules?:any[]):any + }, + BdApi: typeof bandagedbdApi.BdApi, + EDApi: typeof bandagedbdApi.BdApi, + ReactDOM: typeof ReactDOM; + React:typeof React + } + var Lightcord:LightcordGlobal + var BdApi: typeof bandagedbdApi.BdApi + var EDApi: typeof bandagedbdApi.BdApi +} + +export default LightcordApi + +Object.assign(window.Lightcord.Api, LightcordApi) + +/** + * The main Lightcord exports. Can be accessed with `window.Lightcord` + */ +export interface LightcordGlobal { + DiscordModules: { + /** + * Internal Discord's dispatcher - can be used to subscribe to gateway events / client events. + */ + dispatcher: import("./types/DiscordDispatcherTypes").default, + constants: import("./types/DiscordConstantsTypes").default + }, + Settings: { + devMode: boolean, + callRingingBeat: boolean + }, + Api: LightcordApiGlobal, + BetterDiscord: { + BdApi: typeof bandagedbdApi.BdApi, + [mod:string]:any + } +} + +/** + * The main Api. Can be accessed with `window.Lightcord.Api` + */ +type LightcordApiGlobal = lightcordApiMainExports & typeof LightcordApi + +type lightcordApiMainExports = { + /** + * Waits until the first module that match the filter gets exported + * @param filter The filter that specifies the module to match. + */ + ensureExported(filter: (mod:any) => boolean):Promise, + /** + * Recreate the object without the `__proto__` and `prototype` properties - usefull for better formatting in console. + * @param obj The object to recreate + */ + cloneNullProto(obj:Obj):Obj } \ No newline at end of file diff --git a/LightcordApi/src/modules/BDModules.ts b/LightcordApi/src/modules/BDModules.ts index 0708cef..72ddba7 100644 --- a/LightcordApi/src/modules/BDModules.ts +++ b/LightcordApi/src/modules/BDModules.ts @@ -1,82 +1,82 @@ -let req -setReq() -function filterDangerous(mods){ - return mods.map(e => { - return protect(e) - }) -} -function protect(exports){ - let theModule = exports.exports - let mod = theModule.default - if(!mod)return exports - if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; - if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports - - const proxy = new Proxy(mod, { - getOwnPropertyDescriptor: function(obj, prop) { - if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; - return Object.getOwnPropertyDescriptor(obj, prop); - }, - get: function(obj, func) { - if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; - if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; - if (func == "showToken" && obj.showToken) return () => true; - if (func == "__proto__" && obj.__proto__) return proxy; - - return obj[func]; - } - }); - - return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})}) -} - -class Webpackloader { - get modules(){ - if(req){ - return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports)) - }else{ - setReq() - if(req){ - return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports)) - }else{ - return [] - } - } - } - get(ids, modules){ - if(typeof ids === "function"){ - return (modules || this.modules).map((mdl) => { - if(mdl && typeof mdl.exports !== "undefined"){ - return mdl.exports - }else{ - return null - } - }).filter(e => e).filter(ids) - }else if(Array.isArray(ids)){ - modules = modules || this.modules - return ids.map(id => this.get(id, modules)) - }else{ - modules = modules || this.modules - let module = modules.filter(e => !!e).find(e => e.i === ids) - if(!module)return undefined - return module.exports - } - } - get default(){ - return this - } -} - -export default new Webpackloader() - -function setReq(){ - try{ - req = window["webpackJsonp"].push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]); - if(req){ - delete req.m.__extra_id__; - delete req.c.__extra_id__; - } - }catch(e){ - req = undefined - } +let req +setReq() +function filterDangerous(mods){ + return mods.map(e => { + return protect(e) + }) +} +function protect(exports){ + let theModule = exports.exports + let mod = theModule.default + if(!mod)return exports + if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; + if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports + + const proxy = new Proxy(mod, { + getOwnPropertyDescriptor: function(obj, prop) { + if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; + return Object.getOwnPropertyDescriptor(obj, prop); + }, + get: function(obj, func) { + if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; + if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; + if (func == "showToken" && obj.showToken) return () => true; + if (func == "__proto__" && obj.__proto__) return proxy; + + return obj[func]; + } + }); + + return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})}) +} + +class Webpackloader { + get modules(){ + if(req){ + return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports)) + }else{ + setReq() + if(req){ + return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports)) + }else{ + return [] + } + } + } + get(ids, modules){ + if(typeof ids === "function"){ + return (modules || this.modules).map((mdl) => { + if(mdl && typeof mdl.exports !== "undefined"){ + return mdl.exports + }else{ + return null + } + }).filter(e => e).filter(ids) + }else if(Array.isArray(ids)){ + modules = modules || this.modules + return ids.map(id => this.get(id, modules)) + }else{ + modules = modules || this.modules + let module = modules.filter(e => !!e).find(e => e.i === ids) + if(!module)return undefined + return module.exports + } + } + get default(){ + return this + } +} + +export default new Webpackloader() + +function setReq(){ + try{ + req = window["webpackJsonp"].push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]); + if(req){ + delete req.m.__extra_id__; + delete req.c.__extra_id__; + } + }catch(e){ + req = undefined + } } \ No newline at end of file diff --git a/LightcordApi/src/modules/DiscordTools.ts b/LightcordApi/src/modules/DiscordTools.ts index 420d95b..1457c5c 100644 --- a/LightcordApi/src/modules/DiscordTools.ts +++ b/LightcordApi/src/modules/DiscordTools.ts @@ -1,228 +1,228 @@ -import { notices, noticeWithoutID, notice, events as noticeEvents } from "../components/private/Notices"; -import Utils from "./Utils"; -import uuid from "./uuid"; -import cloneNullProto from "./cloneNullProto"; -import { EventEmitter } from "events"; -import { defaultNotice } from "../components/private/Notice"; -import excludeProperties from "./excludeProperties"; -import NOOP from "./noop"; -import WebpackLoader, { WebpackLoaderError } from "./WebpackLoader"; - -let soundModule -export default new class DiscordTools { - showNotice(data:NoticeData):Notice{ - if(typeof data !== "object" || typeof data.text !== "string")throw new Error(`This notice is not valid. Given: ${Utils.formatJSObject(data)}`) - let newData = cloneNullProto(Object.assign({}, defaultNotice, data)) as notice - newData.id = uuid() - notices.push(newData) - noticeEvents.emit("noticeUpdate") - const notice = new Notice(newData) - return notice - } - - get notices():Notice[]{ - return notices.map(data => new Notice(data)) - } - - /** - * Quickly send notification (Even when no focused.) - * @param data The notification. Be sure to include all properties except functions cause they're optional. - * Notifications have a timeout of 3-5 seconds. - * They look like this: https://i.imgur.com/jzuxKKu.png - */ - showNotification(data:NotificationData):Notification{ - const notification = new window.Notification(data.title, excludeProperties(data, [ - "title", - "onClick", - "onClose", - "onShow" - ])) - notification.onclick = data.onClick || NOOP - notification.onshow = data.onShow || NOOP - notification.onclose = data.onClose || NOOP - return notification - } - - createSound(sound:Sound){ - soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"]) - if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.") - const created = soundModule.createSound(sound) - return created - } - - playSound(sound:Sound){ - const created = this.createSound(sound) - created.play() - return created - } -} - -export type Sound = "call_calling"|"call_ringing"|"call_ringing_beat"|"ddr-down"|"ddr-left"|"ddr-right"|"ddr-up"|"deafen"|"discodo"|"disconnect"|"human_man"|"mention1"|"mention2"|"mention3"|"message1"|"message2"|"message3"|"mute"|"overlayunlock"|"ptt_start"|"ptt_stop"|"reconnect"|"robot_man"|"stream_ended"|"stream_started"|"stream_user_joined"|"stream_user_left"|"undeafen"|"unmute"|"user_join"|"user_leave"|"user_moved" - -export type NotificationData = { - title: string, - body: string, - icon: string, - onShow?: () => void, - onClick?: () => void, - onClose?: () => void -} - -export type NoticeData = noticeWithoutID - -const EventHandler = function(){ - if(this.removed !== this.state.removed){ - if(this.removed){ - this.emit("removed") - } - } - if(this.showing !== this.state.showing){ - if(this.showing){ - this.emit("showing", true) - }else{ - this.emit("showing", false) - } - } - if(this.index !== this.state.index){ - this.emit("index", this.index) - } -} - -/** A notice interface for modifying it and subscribing to events. */ -export class Notice extends EventEmitter { - constructor(data){ - super() - this.data = data - - this.state = { - removed: this.removed, - showing: this.showing, - index: this.index - } - - let eventFunc = EventHandler.bind(this) - noticeEvents.on("noticeUpdate", eventFunc) - this.on("removed", () => { - noticeEvents.off("noticeUpdate", eventFunc) - }) - } - - /** - * Will be called whem the notice is removed. - */ - on(event: "removed", listener: () => void):this - /** - * Will be called when the notice is visible or not. - */ - on(event: "showing", listener: (isShowing:boolean) => void):this - /** - * Will be called when the notice queue changes. - */ - on(event: "index", listener: (index:number) => void):this - on(event: string, listener: (...args:any[]) => void){ - return super.on(event, listener) - } - - /** - * Will be called whem the notice is removed. - */ - once(event: "removed", listener: () => void):this - /** - * Will be called when the notice is visible or not. - */ - once(event: "showing", listener: (isShowing:boolean) => void):this - /** - * Will be called when the notice queue changes. - */ - once(event: "index", listener: (index:number) => void):this - once(event: string, listener: (...args:any[]) => void){ - return super.once(event, listener) - } - - off(event: "removed", listener: () => void):this - off(event: "showing", listener: (isShowing:boolean) => void):this - off(event: "index", listener: (index:number) => void):this - off(event: string, listener: (...args:any[]) => void){ - return super.off(event, listener) - } - - state:{ - removed:boolean, - showing:boolean, - index:number - } - - private nextTickRefresh:boolean = false - - get removed():boolean{ - return !notices.find(e => e.id === this.id) - } - get showing():boolean{ - return this.index === 0 - } - - get index():number{ - return notices.findIndex(e => e.id === this.id) - } - get id(){ - return this.data.id - } - - update(data: Partial){ - for(let key in data){ - if(key === "id")continue - this.data[key] = data[key] - } - - if(!this.nextTickRefresh){ - this.nextTickRefresh = true - process.nextTick(() => { - this.nextTickRefresh = false - noticeEvents.emit("noticeUpdate") - }) - } - } - - get text(){ - return this.data.text - } - set text(text){ - this.update({ - text - }) - } - - get type(){ - return this.data.type - } - set type(type){ - this.update({ - type - }) - } - - get buttonText(){ - return this.data.buttonText - } - set buttonText(buttonText:string){ - this.update({ - buttonText - }) - } - - get onClick(){ - return this.data.onClick - } - set onClick(onClick){ - this.update({ - onClick - }) - } - - remove(){ - if(this.removed)return - notices.splice(this.index, 1) - noticeEvents.emit("noticeUpdate") - } - data:notice +import { notices, noticeWithoutID, notice, events as noticeEvents } from "../components/private/Notices"; +import Utils from "./Utils"; +import uuid from "./uuid"; +import cloneNullProto from "./cloneNullProto"; +import { EventEmitter } from "events"; +import { defaultNotice } from "../components/private/Notice"; +import excludeProperties from "./excludeProperties"; +import NOOP from "./noop"; +import WebpackLoader, { WebpackLoaderError } from "./WebpackLoader"; + +let soundModule +export default new class DiscordTools { + showNotice(data:NoticeData):Notice{ + if(typeof data !== "object" || typeof data.text !== "string")throw new Error(`This notice is not valid. Given: ${Utils.formatJSObject(data)}`) + let newData = cloneNullProto(Object.assign({}, defaultNotice, data)) as notice + newData.id = uuid() + notices.push(newData) + noticeEvents.emit("noticeUpdate") + const notice = new Notice(newData) + return notice + } + + get notices():Notice[]{ + return notices.map(data => new Notice(data)) + } + + /** + * Quickly send notification (Even when no focused.) + * @param data The notification. Be sure to include all properties except functions cause they're optional. + * Notifications have a timeout of 3-5 seconds. + * They look like this: https://i.imgur.com/jzuxKKu.png + */ + showNotification(data:NotificationData):Notification{ + const notification = new window.Notification(data.title, excludeProperties(data, [ + "title", + "onClick", + "onClose", + "onShow" + ])) + notification.onclick = data.onClick || NOOP + notification.onshow = data.onShow || NOOP + notification.onclose = data.onClose || NOOP + return notification + } + + createSound(sound:Sound){ + soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"]) + if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.") + const created = soundModule.createSound(sound) + return created + } + + playSound(sound:Sound){ + const created = this.createSound(sound) + created.play() + return created + } +} + +export type Sound = "call_calling"|"call_ringing"|"call_ringing_beat"|"ddr-down"|"ddr-left"|"ddr-right"|"ddr-up"|"deafen"|"discodo"|"disconnect"|"human_man"|"mention1"|"mention2"|"mention3"|"message1"|"message2"|"message3"|"mute"|"overlayunlock"|"ptt_start"|"ptt_stop"|"reconnect"|"robot_man"|"stream_ended"|"stream_started"|"stream_user_joined"|"stream_user_left"|"undeafen"|"unmute"|"user_join"|"user_leave"|"user_moved" + +export type NotificationData = { + title: string, + body: string, + icon: string, + onShow?: () => void, + onClick?: () => void, + onClose?: () => void +} + +export type NoticeData = noticeWithoutID + +const EventHandler = function(){ + if(this.removed !== this.state.removed){ + if(this.removed){ + this.emit("removed") + } + } + if(this.showing !== this.state.showing){ + if(this.showing){ + this.emit("showing", true) + }else{ + this.emit("showing", false) + } + } + if(this.index !== this.state.index){ + this.emit("index", this.index) + } +} + +/** A notice interface for modifying it and subscribing to events. */ +export class Notice extends EventEmitter { + constructor(data){ + super() + this.data = data + + this.state = { + removed: this.removed, + showing: this.showing, + index: this.index + } + + let eventFunc = EventHandler.bind(this) + noticeEvents.on("noticeUpdate", eventFunc) + this.on("removed", () => { + noticeEvents.off("noticeUpdate", eventFunc) + }) + } + + /** + * Will be called whem the notice is removed. + */ + on(event: "removed", listener: () => void):this + /** + * Will be called when the notice is visible or not. + */ + on(event: "showing", listener: (isShowing:boolean) => void):this + /** + * Will be called when the notice queue changes. + */ + on(event: "index", listener: (index:number) => void):this + on(event: string, listener: (...args:any[]) => void){ + return super.on(event, listener) + } + + /** + * Will be called whem the notice is removed. + */ + once(event: "removed", listener: () => void):this + /** + * Will be called when the notice is visible or not. + */ + once(event: "showing", listener: (isShowing:boolean) => void):this + /** + * Will be called when the notice queue changes. + */ + once(event: "index", listener: (index:number) => void):this + once(event: string, listener: (...args:any[]) => void){ + return super.once(event, listener) + } + + off(event: "removed", listener: () => void):this + off(event: "showing", listener: (isShowing:boolean) => void):this + off(event: "index", listener: (index:number) => void):this + off(event: string, listener: (...args:any[]) => void){ + return super.off(event, listener) + } + + state:{ + removed:boolean, + showing:boolean, + index:number + } + + private nextTickRefresh:boolean = false + + get removed():boolean{ + return !notices.find(e => e.id === this.id) + } + get showing():boolean{ + return this.index === 0 + } + + get index():number{ + return notices.findIndex(e => e.id === this.id) + } + get id(){ + return this.data.id + } + + update(data: Partial){ + for(let key in data){ + if(key === "id")continue + this.data[key] = data[key] + } + + if(!this.nextTickRefresh){ + this.nextTickRefresh = true + process.nextTick(() => { + this.nextTickRefresh = false + noticeEvents.emit("noticeUpdate") + }) + } + } + + get text(){ + return this.data.text + } + set text(text){ + this.update({ + text + }) + } + + get type(){ + return this.data.type + } + set type(type){ + this.update({ + type + }) + } + + get buttonText(){ + return this.data.buttonText + } + set buttonText(buttonText:string){ + this.update({ + buttonText + }) + } + + get onClick(){ + return this.data.onClick + } + set onClick(onClick){ + this.update({ + onClick + }) + } + + remove(){ + if(this.removed)return + notices.splice(this.index, 1) + noticeEvents.emit("noticeUpdate") + } + data:notice } \ No newline at end of file diff --git a/LightcordApi/src/modules/Utils.ts b/LightcordApi/src/modules/Utils.ts index b476c9e..e18f15d 100644 --- a/LightcordApi/src/modules/Utils.ts +++ b/LightcordApi/src/modules/Utils.ts @@ -1,133 +1,133 @@ -import { ReactElement } from "react"; -import ReactDOM = require("react-dom") -import PluginUtilities from "./PluginUtilities"; - -export default new class Utils { - constructor(){} - - ReactToHTMLElement(ReactElement: ReactElement){ - const element = document.createElement("div") - ReactDOM.render(ReactElement, element) - return element - } - - get PluginUtils(){return PluginUtilities} - - getNestedProps(obj:any, path: string){ - let segments = path.split(".") - for(let seg of segments){ - obj = obj && (seg in obj) ? obj[seg] : undefined - } - return obj - } - - DecimalColorToHex(color:number):string{ - return "#"+color.toString(16).toUpperCase() - } - - HexColorToDecimal(color:string):number{ - color = color.replace(/[#;]/g, "") - let res = parseInt(color, 16) - if(isNaN(res))throw new Error(`Invalid color: ${color}`) - return res - } - - removeDa(className:string):string{ - if(!className)return className - return className.split(" ").filter(e => !e.startsWith("da-")).join(" ") - } - - FindReact(dom:Element, traverseUp:number = 0):React.Component|React.PureComponent{ - // taken from https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging#39165137 - const key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$")); - const domFiber = dom[key]; - if (domFiber == null) return null; - - // react <16 - if (domFiber._currentElement) { - let compFiber = domFiber._currentElement._owner; - for (let i = 0; i < traverseUp; i++) { - compFiber = compFiber._currentElement._owner; - } - return compFiber._instance; - } - - // react 16+ - const GetCompFiber = fiber=>{ - //return fiber._debugOwner; // this also works, but is __DEV__ only - let parentFiber = fiber.return; - while (typeof parentFiber.type == "string") { - parentFiber = parentFiber.return; - } - return parentFiber; - }; - let compFiber = GetCompFiber(domFiber); - for (let i = 0; i < traverseUp; i++) { - compFiber = GetCompFiber(compFiber); - } - return compFiber.stateNode; - } - - hasClass(classNames:string, className:string):boolean{ - if(!classNames || !className)return false - const classnames = classNames.split(" ") - for(let classname of this.removeDa(className).split(" ")){ - if(!classnames.includes(classname))return false - } - return true - } - - formatJSObject(obj:any):string{ - if(["string", "number", "boolean", "bigint", "undefined"].includes(typeof obj))return JSON.stringify(obj) - if(obj === null)return "null" - if(typeof obj === "function")return String(obj) - if(typeof obj === "symbol")return String(obj) - - if(Array.isArray(obj)){ - if(!obj.length)return "[]" - return `[\n ${obj.map(e => this.formatJSObject(e)).join(",\n ")}\n]` - }else{ - const keys = Object.keys(obj) - if(keys.length === 0)return "{}" - return `{\n ${keys.map(key => { - let original = key - if(typeof key === "symbol")key = "["+String(key)+"]" - else{ - if(typeof key === "number")key = String(key) - else{ - console.log(key) - if(isNaN(parseInt(key[0]))){ - key = this.formatJSObject(key) - }else if(/[^\w\d_$]/g.test(key)){ - key = this.formatJSObject(key) - } - } - } - return `${key}: ${this.formatJSObject(obj[original])}` - })}\n}` - } - } - - getColor(color_name:ColorName):string{ - return Lightcord.DiscordModules.constants.Colors[color_name.toUpperCase()] - } - - firstLetterUppercase(str:string):string{ - if(!str)return "" - return str[0].toUpperCase()+str.slice(1) - } - - executeXTimes(func:(index?:number) => result, times:number):result[]{ - let results:result[] = [] - for(let i = 0;times > i;i++){ - results.push(func(i)) - } - return results - } -} - -export type ColorName = "primary_dark_100"|"primary_dark_130"|"primary_dark_160"|"primary_dark_200"|"primary_dark_230"|"primary_dark_260"|"primary_dark_300"|"primary_dark_330"|"primary_dark_360"|"primary_dark_400"|"primary_dark_430"|"primary_dark_460"|"primary_dark_500"|"primary_dark"|"primary_dark_530"|"primary_dark_560"|"primary_dark_600"|"primary_dark_630"|"primary_dark_660"|"primary_dark_700"|"primary_dark_730"|"primary_dark_760"|"primary_dark_800"|"primary_dark_830"|"primary_dark_860"|"primary_dark_900"|"primary_light_100"|"primary_light_130"|"primary_light_160"|"primary_light_200"|"primary_light_230"|"primary_light_260"|"primary_light_300"|"primary_light_330"|"primary_light_360"|"primary_light_400"|"primary_light_430"|"primary_light_460"|"primary_light_500"|"primary_light"|"primary_light_530"|"primary_light_560"|"primary_light_600"|"primary_light_630"|"primary_light_660"|"primary_light_700"|"primary_light_730"|"primary_light_760"|"primary_light_800"|"primary_light_830"|"primary_light_860"|"primary_light_900"|"brand_100"|"brand_130"|"brand_160"|"brand_200"|"brand_230"|"brand_260"|"brand_300"|"brand_330"|"brand_360"|"brand_400"|"brand_430"|"brand_460"|"brand_500"|"brand"|"brand_530"|"brand_560"|"brand_600"|"brand_630"|"brand_660"|"brand_700"|"brand_730"|"brand_760"|"brand_800"|"brand_830"|"brand_860"|"brand_900"|"status_red_100"|"status_red_130"|"status_red_160"|"status_red_200"|"status_red_230"|"status_red_260"|"status_red_300"|"status_red_330"|"status_red_360"|"status_red_400"|"status_red_430"|"status_red_460"|"status_red_500"|"status_red"|"status_red_530"|"status_red_560"|"status_red_600"|"status_red_630"|"status_red_660"|"status_red_700"|"status_red_730"|"status_red_760"|"status_red_800"|"status_red_830"|"status_red_860"|"status_red_900"|"status_yellow_100"|"status_yellow_130"|"status_yellow_160"|"status_yellow_200"|"status_yellow_230"|"status_yellow_260"|"status_yellow_300"|"status_yellow_330"|"status_yellow_360"|"status_yellow_400"|"status_yellow_430"|"status_yellow_460"|"status_yellow_500"|"status_yellow"|"status_yellow_530"|"status_yellow_560"|"status_yellow_600"|"status_yellow_630"|"status_yellow_660"|"status_yellow_700"|"status_yellow_730"|"status_yellow_760"|"status_yellow_800"|"status_yellow_830"|"status_yellow_860"|"status_yellow_900"|"status_green_100"|"status_green_130"|"status_green_160"|"status_green_200"|"status_green_230"|"status_green_260"|"status_green_300"|"status_green_330"|"status_green_360"|"status_green_400"|"status_green_430"|"status_green_460"|"status_green_500"|"status_green"|"status_green_530"|"status_green_560"|"status_green_600"|"status_green_630"|"status_green_660"|"status_green_700"|"status_green_730"|"status_green_760"|"status_green_800"|"status_green_830"|"status_green_860"|"status_green_900"|"status_grey_100"|"status_grey_130"|"status_grey_160"|"status_grey_200"|"status_grey_230"|"status_grey_260"|"status_grey_300"|"status_grey_330"|"status_grey_360"|"status_grey_400"|"status_grey_430"|"status_grey_460"|"status_grey_500"|"status_grey"|"status_grey_530"|"status_grey_560"|"status_grey_600"|"status_grey_630"|"status_grey_660"|"status_grey_700"|"status_grey_730"|"status_grey_760"|"status_grey_800"|"status_grey_830"|"status_grey_860"|"status_grey_900"|"link_100"|"link_130"|"link_160"|"link_200"|"link_230"|"link_260"|"link_300"|"link_330"|"link_360"|"link_400"|"link_430"|"link_460"|"link_500"|"link"|"link_530"|"link_560"|"link_600"|"link_630"|"link_660"|"link_700"|"link_730"|"link_760"|"link_800"|"link_830"|"link_860"|"link_900"|"link_light_100"|"link_light_130"|"link_light_160"|"link_light_200"|"link_light_230"|"link_light_260"|"link_light_300"|"link_light_330"|"link_light_360"|"link_light_400"|"link_light_430"|"link_light_460"|"link_light_500"|"link_light"|"link_light_530"|"link_light_560"|"link_light_600"|"link_light_630"|"link_light_660"|"link_light_700"|"link_light_730"|"link_light_760"|"link_light_800"|"link_light_830"|"link_light_860"|"link_light_900"|"white_100"|"white_130"|"white_160"|"white_200"|"white_230"|"white_260"|"white_300"|"white_330"|"white_360"|"white_400"|"white_430"|"white_460"|"white_500"|"white"|"white_530"|"white_560"|"white_600"|"white_630"|"white_660"|"white_700"|"white_730"|"white_760"|"white_800"|"white_830"|"white_860"|"white_900"|"black_100"|"black_130"|"black_160"|"black_200"|"black_230"|"black_260"|"black_300"|"black_330"|"black_360"|"black_400"|"black_430"|"black_460"|"black_500"|"black"|"black_530"|"black_560"|"black_600"|"black_630"|"black_660"|"black_700"|"black_730"|"black_760"|"black_800"|"black_830"|"black_860"|"black_900"|"premium_tier_2_purple_500"|"premium_tier_2_purple"|"premium_tier_2_pink_500"|"premium_tier_2_pink"|"premium_tier_1_purple_500"|"premium_tier_1_purple"|"premium_tier_1_blue_500"|"premium_tier_1_blue"|"premium_guild_pink_500"|"premium_guild_pink"|"premium_guild_purple_500"|"premium_guild_purple"|"premium_guild_blue_500"|"premium_guild_blue"|"hypesquad_house_1_500"|"hypesquad_house_1"|"hypesquad_house_2_500"|"hypesquad_house_2"|"hypesquad_house_3_500"|"hypesquad_house_3"|"partner_500"|"partner"|"skype_500"|"skype"|"battlenet_500"|"battlenet"|"steam_500"|"steam"|"lol_500"|"lol"|"twitch_500"|"twitch"|"youtube_500"|"youtube"|"twitter_500"|"twitter"|"reddit_500"|"reddit"|"spotify_500"|"spotify"|"facebook_500"|"facebook"|"samsung_500"|"samsung"|"xbox_500"|"xbox"|"github_500"|"github"|"transparent" - -export class LightcordApiError extends Error { - name:string = "LightcordApiError" +import { ReactElement } from "react"; +import ReactDOM = require("react-dom") +import PluginUtilities from "./PluginUtilities"; + +export default new class Utils { + constructor(){} + + ReactToHTMLElement(ReactElement: ReactElement){ + const element = document.createElement("div") + ReactDOM.render(ReactElement, element) + return element + } + + get PluginUtils(){return PluginUtilities} + + getNestedProps(obj:any, path: string){ + let segments = path.split(".") + for(let seg of segments){ + obj = obj && (seg in obj) ? obj[seg] : undefined + } + return obj + } + + DecimalColorToHex(color:number):string{ + return "#"+color.toString(16).toUpperCase() + } + + HexColorToDecimal(color:string):number{ + color = color.replace(/[#;]/g, "") + let res = parseInt(color, 16) + if(isNaN(res))throw new Error(`Invalid color: ${color}`) + return res + } + + removeDa(className:string):string{ + if(!className)return className + return className.split(" ").filter(e => !e.startsWith("da-")).join(" ") + } + + FindReact(dom:Element, traverseUp:number = 0):React.Component|React.PureComponent{ + // taken from https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging#39165137 + const key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$")); + const domFiber = dom[key]; + if (domFiber == null) return null; + + // react <16 + if (domFiber._currentElement) { + let compFiber = domFiber._currentElement._owner; + for (let i = 0; i < traverseUp; i++) { + compFiber = compFiber._currentElement._owner; + } + return compFiber._instance; + } + + // react 16+ + const GetCompFiber = fiber=>{ + //return fiber._debugOwner; // this also works, but is __DEV__ only + let parentFiber = fiber.return; + while (typeof parentFiber.type == "string") { + parentFiber = parentFiber.return; + } + return parentFiber; + }; + let compFiber = GetCompFiber(domFiber); + for (let i = 0; i < traverseUp; i++) { + compFiber = GetCompFiber(compFiber); + } + return compFiber.stateNode; + } + + hasClass(classNames:string, className:string):boolean{ + if(!classNames || !className)return false + const classnames = classNames.split(" ") + for(let classname of this.removeDa(className).split(" ")){ + if(!classnames.includes(classname))return false + } + return true + } + + formatJSObject(obj:any):string{ + if(["string", "number", "boolean", "bigint", "undefined"].includes(typeof obj))return JSON.stringify(obj) + if(obj === null)return "null" + if(typeof obj === "function")return String(obj) + if(typeof obj === "symbol")return String(obj) + + if(Array.isArray(obj)){ + if(!obj.length)return "[]" + return `[\n ${obj.map(e => this.formatJSObject(e)).join(",\n ")}\n]` + }else{ + const keys = Object.keys(obj) + if(keys.length === 0)return "{}" + return `{\n ${keys.map(key => { + let original = key + if(typeof key === "symbol")key = "["+String(key)+"]" + else{ + if(typeof key === "number")key = String(key) + else{ + console.log(key) + if(isNaN(parseInt(key[0]))){ + key = this.formatJSObject(key) + }else if(/[^\w\d_$]/g.test(key)){ + key = this.formatJSObject(key) + } + } + } + return `${key}: ${this.formatJSObject(obj[original])}` + })}\n}` + } + } + + getColor(color_name:ColorName):string{ + return Lightcord.DiscordModules.constants.Colors[color_name.toUpperCase()] + } + + firstLetterUppercase(str:string):string{ + if(!str)return "" + return str[0].toUpperCase()+str.slice(1) + } + + executeXTimes(func:(index?:number) => result, times:number):result[]{ + let results:result[] = [] + for(let i = 0;times > i;i++){ + results.push(func(i)) + } + return results + } +} + +export type ColorName = "primary_dark_100"|"primary_dark_130"|"primary_dark_160"|"primary_dark_200"|"primary_dark_230"|"primary_dark_260"|"primary_dark_300"|"primary_dark_330"|"primary_dark_360"|"primary_dark_400"|"primary_dark_430"|"primary_dark_460"|"primary_dark_500"|"primary_dark"|"primary_dark_530"|"primary_dark_560"|"primary_dark_600"|"primary_dark_630"|"primary_dark_660"|"primary_dark_700"|"primary_dark_730"|"primary_dark_760"|"primary_dark_800"|"primary_dark_830"|"primary_dark_860"|"primary_dark_900"|"primary_light_100"|"primary_light_130"|"primary_light_160"|"primary_light_200"|"primary_light_230"|"primary_light_260"|"primary_light_300"|"primary_light_330"|"primary_light_360"|"primary_light_400"|"primary_light_430"|"primary_light_460"|"primary_light_500"|"primary_light"|"primary_light_530"|"primary_light_560"|"primary_light_600"|"primary_light_630"|"primary_light_660"|"primary_light_700"|"primary_light_730"|"primary_light_760"|"primary_light_800"|"primary_light_830"|"primary_light_860"|"primary_light_900"|"brand_100"|"brand_130"|"brand_160"|"brand_200"|"brand_230"|"brand_260"|"brand_300"|"brand_330"|"brand_360"|"brand_400"|"brand_430"|"brand_460"|"brand_500"|"brand"|"brand_530"|"brand_560"|"brand_600"|"brand_630"|"brand_660"|"brand_700"|"brand_730"|"brand_760"|"brand_800"|"brand_830"|"brand_860"|"brand_900"|"status_red_100"|"status_red_130"|"status_red_160"|"status_red_200"|"status_red_230"|"status_red_260"|"status_red_300"|"status_red_330"|"status_red_360"|"status_red_400"|"status_red_430"|"status_red_460"|"status_red_500"|"status_red"|"status_red_530"|"status_red_560"|"status_red_600"|"status_red_630"|"status_red_660"|"status_red_700"|"status_red_730"|"status_red_760"|"status_red_800"|"status_red_830"|"status_red_860"|"status_red_900"|"status_yellow_100"|"status_yellow_130"|"status_yellow_160"|"status_yellow_200"|"status_yellow_230"|"status_yellow_260"|"status_yellow_300"|"status_yellow_330"|"status_yellow_360"|"status_yellow_400"|"status_yellow_430"|"status_yellow_460"|"status_yellow_500"|"status_yellow"|"status_yellow_530"|"status_yellow_560"|"status_yellow_600"|"status_yellow_630"|"status_yellow_660"|"status_yellow_700"|"status_yellow_730"|"status_yellow_760"|"status_yellow_800"|"status_yellow_830"|"status_yellow_860"|"status_yellow_900"|"status_green_100"|"status_green_130"|"status_green_160"|"status_green_200"|"status_green_230"|"status_green_260"|"status_green_300"|"status_green_330"|"status_green_360"|"status_green_400"|"status_green_430"|"status_green_460"|"status_green_500"|"status_green"|"status_green_530"|"status_green_560"|"status_green_600"|"status_green_630"|"status_green_660"|"status_green_700"|"status_green_730"|"status_green_760"|"status_green_800"|"status_green_830"|"status_green_860"|"status_green_900"|"status_grey_100"|"status_grey_130"|"status_grey_160"|"status_grey_200"|"status_grey_230"|"status_grey_260"|"status_grey_300"|"status_grey_330"|"status_grey_360"|"status_grey_400"|"status_grey_430"|"status_grey_460"|"status_grey_500"|"status_grey"|"status_grey_530"|"status_grey_560"|"status_grey_600"|"status_grey_630"|"status_grey_660"|"status_grey_700"|"status_grey_730"|"status_grey_760"|"status_grey_800"|"status_grey_830"|"status_grey_860"|"status_grey_900"|"link_100"|"link_130"|"link_160"|"link_200"|"link_230"|"link_260"|"link_300"|"link_330"|"link_360"|"link_400"|"link_430"|"link_460"|"link_500"|"link"|"link_530"|"link_560"|"link_600"|"link_630"|"link_660"|"link_700"|"link_730"|"link_760"|"link_800"|"link_830"|"link_860"|"link_900"|"link_light_100"|"link_light_130"|"link_light_160"|"link_light_200"|"link_light_230"|"link_light_260"|"link_light_300"|"link_light_330"|"link_light_360"|"link_light_400"|"link_light_430"|"link_light_460"|"link_light_500"|"link_light"|"link_light_530"|"link_light_560"|"link_light_600"|"link_light_630"|"link_light_660"|"link_light_700"|"link_light_730"|"link_light_760"|"link_light_800"|"link_light_830"|"link_light_860"|"link_light_900"|"white_100"|"white_130"|"white_160"|"white_200"|"white_230"|"white_260"|"white_300"|"white_330"|"white_360"|"white_400"|"white_430"|"white_460"|"white_500"|"white"|"white_530"|"white_560"|"white_600"|"white_630"|"white_660"|"white_700"|"white_730"|"white_760"|"white_800"|"white_830"|"white_860"|"white_900"|"black_100"|"black_130"|"black_160"|"black_200"|"black_230"|"black_260"|"black_300"|"black_330"|"black_360"|"black_400"|"black_430"|"black_460"|"black_500"|"black"|"black_530"|"black_560"|"black_600"|"black_630"|"black_660"|"black_700"|"black_730"|"black_760"|"black_800"|"black_830"|"black_860"|"black_900"|"premium_tier_2_purple_500"|"premium_tier_2_purple"|"premium_tier_2_pink_500"|"premium_tier_2_pink"|"premium_tier_1_purple_500"|"premium_tier_1_purple"|"premium_tier_1_blue_500"|"premium_tier_1_blue"|"premium_guild_pink_500"|"premium_guild_pink"|"premium_guild_purple_500"|"premium_guild_purple"|"premium_guild_blue_500"|"premium_guild_blue"|"hypesquad_house_1_500"|"hypesquad_house_1"|"hypesquad_house_2_500"|"hypesquad_house_2"|"hypesquad_house_3_500"|"hypesquad_house_3"|"partner_500"|"partner"|"skype_500"|"skype"|"battlenet_500"|"battlenet"|"steam_500"|"steam"|"lol_500"|"lol"|"twitch_500"|"twitch"|"youtube_500"|"youtube"|"twitter_500"|"twitter"|"reddit_500"|"reddit"|"spotify_500"|"spotify"|"facebook_500"|"facebook"|"samsung_500"|"samsung"|"xbox_500"|"xbox"|"github_500"|"github"|"transparent" + +export class LightcordApiError extends Error { + name:string = "LightcordApiError" } \ No newline at end of file diff --git a/LightcordApi/src/modules/WebpackLoader.ts b/LightcordApi/src/modules/WebpackLoader.ts index 0546a09..37ddbe3 100644 --- a/LightcordApi/src/modules/WebpackLoader.ts +++ b/LightcordApi/src/modules/WebpackLoader.ts @@ -1,57 +1,57 @@ -const BDModules:typeof window.BDModules = window.BDModules || require("./BDModules") - -export default new class WebpackLoader { - constructor(){} - - get(id: number):any{ - return BDModules.get(id) - } - find(filter: (mod:any) => boolean):any{ - let result = BDModules.get(filter)[0] - if(!result){ - console.warn(filter, "couldn't find the module.") - } - return result - } - findByUniqueProperties(props:(string|number)[]):any{ - return BDModules.get((mod) => { - if(mod.__esModule && ("default" in mod)){ - let doesMatch = true - for(let prop of props){ - if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false - } - if(doesMatch)return true - } - for(let prop of props){ - if(!Object.prototype.hasOwnProperty.call(mod, prop))return false - } - return true - })[0] - } - filter(filter: (mod:any) => boolean):any[]{ - return BDModules.get(filter) - } - filterByUniqueProperties(props:(string|number)[]):any{ - return BDModules.get((mod) => { - if(mod.__esModule && ("default" in mod)){ - let doesMatch = true - for(let prop of props){ - if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false - } - if(doesMatch)return true - } - for(let prop of props){ - if(!Object.prototype.hasOwnProperty.call(mod, prop))return false - } - return true - }) - } -} - -export class WebpackLoaderError extends Error { - constructor(message:string = ""){ - message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error to the devs. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in our discord server." - super(message) - this.name = "WebpackLoaderError" - } +const BDModules:typeof window.BDModules = window.BDModules || require("./BDModules") + +export default new class WebpackLoader { + constructor(){} + + get(id: number):any{ + return BDModules.get(id) + } + find(filter: (mod:any) => boolean):any{ + let result = BDModules.get(filter)[0] + if(!result){ + console.warn(filter, "couldn't find the module.") + } + return result + } + findByUniqueProperties(props:(string|number)[]):any{ + return BDModules.get((mod) => { + if(mod.__esModule && ("default" in mod)){ + let doesMatch = true + for(let prop of props){ + if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false + } + if(doesMatch)return true + } + for(let prop of props){ + if(!Object.prototype.hasOwnProperty.call(mod, prop))return false + } + return true + })[0] + } + filter(filter: (mod:any) => boolean):any[]{ + return BDModules.get(filter) + } + filterByUniqueProperties(props:(string|number)[]):any{ + return BDModules.get((mod) => { + if(mod.__esModule && ("default" in mod)){ + let doesMatch = true + for(let prop of props){ + if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false + } + if(doesMatch)return true + } + for(let prop of props){ + if(!Object.prototype.hasOwnProperty.call(mod, prop))return false + } + return true + }) + } +} + +export class WebpackLoaderError extends Error { + constructor(message:string = ""){ + message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error to the devs. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in our discord server." + super(message) + this.name = "WebpackLoaderError" + } } \ No newline at end of file diff --git a/LightcordApi/src/modules/cloneNullProto.ts b/LightcordApi/src/modules/cloneNullProto.ts index 5476611..418d3cd 100644 --- a/LightcordApi/src/modules/cloneNullProto.ts +++ b/LightcordApi/src/modules/cloneNullProto.ts @@ -1,11 +1,11 @@ -/** - * Recreate the given object without the __proto__. Useful for better formatting when output in console. - * @param obj The object to recreate - */ -export default function cloneNullProto(obj:Obj):Obj{ - let o = Object.create(null) - Object.keys(obj).forEach(k => { - o[k] = obj[k] - }) - return o +/** + * Recreate the given object without the __proto__. Useful for better formatting when output in console. + * @param obj The object to recreate + */ +export default function cloneNullProto(obj:Obj):Obj{ + let o = Object.create(null) + Object.keys(obj).forEach(k => { + o[k] = obj[k] + }) + return o } \ No newline at end of file diff --git a/LightcordApi/src/modules/environnement.ts b/LightcordApi/src/modules/environnement.ts index be2496b..004d6c6 100644 --- a/LightcordApi/src/modules/environnement.ts +++ b/LightcordApi/src/modules/environnement.ts @@ -1,2 +1,2 @@ -export const isNative:boolean = typeof window.BDModules === "undefined" +export const isNative:boolean = typeof window.BDModules === "undefined" export const isImported:boolean = typeof window.BDModules !== "undefined" \ No newline at end of file diff --git a/LightcordApi/src/modules/lazyLoader.ts b/LightcordApi/src/modules/lazyLoader.ts index 5fe43cf..b2cd70b 100644 --- a/LightcordApi/src/modules/lazyLoader.ts +++ b/LightcordApi/src/modules/lazyLoader.ts @@ -1,84 +1,84 @@ -let cache = new Map() - -export function LazyLoad(getObject: () => T):T{ - if(cache.has(getObject))return cache.get(getObject) - - let mdl = null - let setModule = () => { - if(mdl)return - mdl = getObject() - } - let handler:ProxyHandler<{}> = { - get(target, prop){ - setModule() - return mdl[prop] - }, - set(target, prop, value){ - setModule() - mdl[prop] = value - return true - }, - apply(target, thisArg, args){ - setModule() - mdl.apply(this, args) - }, - construct(target, args){ - setModule() - const prototype = Object.create(mdl.prototype) - handler.apply(target, prototype, args) - return prototype - }, - deleteProperty(target, prop){ - setModule() - if(!(prop in mdl))return false - delete mdl[prop] - return true - }, - enumerate(target){ - setModule() - return Object.keys(mdl) - }, - ownKeys(target) { - setModule() - return Object.keys(mdl) - }, - has(target, prop){ - setModule() - return prop in mdl - }, - defineProperty(target, prop, attributes){ - setModule() - return Object.defineProperty(mdl, prop, attributes) - }, - getOwnPropertyDescriptor(target, prop){ - setModule() - return Object.getOwnPropertyDescriptor(mdl, prop) - }, - getPrototypeOf(target){ - setModule() - return Object.getPrototypeOf(mdl) - }, - setPrototypeOf(target, proto){ - setModule() - try{ - Object.setPrototypeOf(mdl, proto) - return true - }catch(e){ - return false - } - }, - isExtensible(target){ - setModule() - return Object.isExtensible(mdl) - }, - preventExtensions(target){ - setModule() - Object.preventExtensions(mdl) - return true - } - } - const proxy = new Proxy({}, handler) - - cache.set(getObject, proxy) - return proxy as T +let cache = new Map() + +export function LazyLoad(getObject: () => T):T{ + if(cache.has(getObject))return cache.get(getObject) + + let mdl = null + let setModule = () => { + if(mdl)return + mdl = getObject() + } + let handler:ProxyHandler<{}> = { + get(target, prop){ + setModule() + return mdl[prop] + }, + set(target, prop, value){ + setModule() + mdl[prop] = value + return true + }, + apply(target, thisArg, args){ + setModule() + mdl.apply(this, args) + }, + construct(target, args){ + setModule() + const prototype = Object.create(mdl.prototype) + handler.apply(target, prototype, args) + return prototype + }, + deleteProperty(target, prop){ + setModule() + if(!(prop in mdl))return false + delete mdl[prop] + return true + }, + enumerate(target){ + setModule() + return Object.keys(mdl) + }, + ownKeys(target) { + setModule() + return Object.keys(mdl) + }, + has(target, prop){ + setModule() + return prop in mdl + }, + defineProperty(target, prop, attributes){ + setModule() + return Object.defineProperty(mdl, prop, attributes) + }, + getOwnPropertyDescriptor(target, prop){ + setModule() + return Object.getOwnPropertyDescriptor(mdl, prop) + }, + getPrototypeOf(target){ + setModule() + return Object.getPrototypeOf(mdl) + }, + setPrototypeOf(target, proto){ + setModule() + try{ + Object.setPrototypeOf(mdl, proto) + return true + }catch(e){ + return false + } + }, + isExtensible(target){ + setModule() + return Object.isExtensible(mdl) + }, + preventExtensions(target){ + setModule() + Object.preventExtensions(mdl) + return true + } + } + const proxy = new Proxy({}, handler) + + cache.set(getObject, proxy) + return proxy as T } \ No newline at end of file diff --git a/LightcordApi/src/modules/patchers.ts b/LightcordApi/src/modules/patchers.ts index 516c69c..51fee06 100644 --- a/LightcordApi/src/modules/patchers.ts +++ b/LightcordApi/src/modules/patchers.ts @@ -1,232 +1,232 @@ -import Utils from "./Utils" -import Notices, { notices } from "../components/private/Notices" -import { isNative } from "./environnement"; -import WebpackLoader from "./WebpackLoader"; - -export function patch(){ - /** START NOTICE */ - getModule(e => e.default && e.default.displayName === "ConnectedAppView") - .then(async (mod) => { - const appClasses = await getModule(e => e.hasNotice); - const buildRender = original => { - return function render(){ - const returnValue = original.call(this, ...arguments) - const newchildren = [] - let children = returnValue.props.children[1].props.children - if(!Array.isArray(children))children = [children] - - newchildren.push(children[0]) - newchildren.push(React.createElement(Notices, {container: this})) - newchildren.push(children[1]) - returnValue.props.children[1].props.children = newchildren - - returnValue.props.children[1].props.children[2].props.children[0].props.render = buildRenderChannelSidebar(returnValue.props.children[1].props.children[2].props.children[0].props.render) - - return returnValue - } - } - const buildRenderChannelSidebar = original => { - return function renderChannelSidebar(){ - const returnValue = original.call(this, ...arguments) - - const hasNotice = notices.length > 0 - if(!hasNotice)return returnValue - if(!Utils.hasClass(returnValue.props.className, appClasses.hasNotice)){ - returnValue.props.className += " "+Utils.removeDa(appClasses.hasNotice) - } - - return returnValue - } - } - mod.default.prototype.render = buildRender(mod.default.prototype.render); - (async function(){ - const base = document.querySelector("."+Utils.removeDa(appClasses.base)) - if(!base)throw new Error(`Could not find base here`) - const elem = Utils.FindReact(base) as any - elem.render = buildRender(elem.render) - elem.forceUpdate() - })() - }) - /** END NOTICE */ - - if(isNative){ - /** START USERPOPOUT PATCH */ - awaitLogin() - .then(async () => { - let UserPopout = await getModule(e => e.default && e.default.displayName === "FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))") - const userModule = await getModule(e => e.default && e.default.getCurrentUser) - const render1 = new UserPopout.default({userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true}).render() - const PopoutProps = render1.props - const render2 = render1.type.render(PopoutProps, null) - const render3 = new render2.type(render2.props).render() - const UserPopoutComponent = render3.type - if(!UserPopoutComponent)throw new Error(`Couldn't find the UserPopoutComponent component.`) - - const render = UserPopoutComponent.prototype.render - UserPopoutComponent.prototype.render = function(){ - const returnValue = render.call(this, ...arguments) - try{ - returnValue.props.children.props["data-user-id"] = this.props.user.id - }catch(e){ - console.error(e) - } - return returnValue - } - }) - /** END USERPOPOUT PATCH*/ - - /** START USERPROFILE PATCH */ - awaitLogin() - .then(async () => { - let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile") - const userModule = await getModule(e => e.default && e.default.getCurrentUser) - const render1 = new UserProfile.default({ - user: userModule.default.getCurrentUser() - }).render() - const render2 = new render1.type(render1.props).render() - const render3 = render2.type.render(render2.props, null) - const render4 = new render3.type(render3.props).render() - const UserProfileComponent = render4.type - if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`) - - const render = UserProfileComponent.prototype.render - UserProfileComponent.prototype.render = function(){ - const returnValue = render.call(this, ...arguments) - console.log(returnValue) - try{ - returnValue.props.children.props["data-user-id"] = this.props.user.id - }catch(e){ - console.error(e) - } - return returnValue - } - }) - /** END USERPROFILE PATCH */ - - /** START WEBHOOK PATCH */ -/* - let usedWebhooks = {} - - getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end) - .then(RequestModule => { - const end = RequestModule.Request.prototype.end - RequestModule.Request.prototype.end = function(){ - if(this.url.endsWith("/messages") && /\/channels\/\d+\/messages/g.test(this.url) && this.method === "POST"){ // sending message - let channelId = this.url.split("/channels/")[1].split("/messages")[0] - - if(usedWebhooks[channelId]){ // webhook is availlable - let webhook = usedWebhooks[channelId] - let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true` - this.url = url - } - } - - return end.call(this, ...arguments) - } - }) - getModule(e => e.default && e.default.displayName === "Webhook") - .then(webhookComponent => { - const renderEdit = webhookComponent.default.prototype.renderEdit - webhookComponent.default.prototype.renderEdit = function(){ - const webhook = this.props.webhook - let returnValue = renderEdit.call(this, ...arguments) - returnValue.props.children = [returnValue.props.children] - let message = usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id ? "Stop talking with this webhook" : "Talk with this webhook" - - returnValue.props.children.push(React.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "green", wrapper: false, onClick(){ - if(usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id){ - delete usedWebhooks[webhook.channel_id] - }else{ - usedWebhooks[webhook.channel_id] = { - id: webhook.id, - token: webhook.token - } - } - webhookPanels.forEach(e => e()) - }}, message)) - - return returnValue - } - }) - - let webhookPanels = [] - let getComp = (comp) => { - class SettingsWebhooks extends React.PureComponent { - constructor(props){ - super(props) - } - - componentWillMount(){ - this.id = uuid() - this.component = new comp(this.props) - let func = () => { - this.component.forceUpdate() - } - func.id = this.id - webhookPanels.push(func) - } - - componentWillUnmount(){ - this.component = null - webhookPanels = webhookPanels.filter(e => e.id !== this.id) - } - - render(){ - return this.component.render() - } - - static displayName = "SettingsWebhooks" - } - - return SettingsWebhooks - } - getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)") - .then(webhooksComponents => { - let comp = webhooksComponents.default - - webhooksComponents.default = getComp(comp) - - WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))") - .forEach(mod => { - mod.default = getComp(mod.default) - }) - })*/ - /** END WEBHOOK PATCH */ - } - - // TODO: Add in app-notifications / confirmations. - /** START IN-APP NOTIFICATIONS */ - //getModule(e => true) - /** END IN-APP NOTIFICATIONS */ -} - -function getModule(filter: (mod:any) => boolean):Promise{ - return new Promise((resolve) => { - window.Lightcord.Api.ensureExported(filter) - .then(resolve) - .catch(err => { - console.error("[LIGHTCORD]", err, filter) - }) - }) -} - -let hasCompletedLogin = false -let loginPromise:Promise -function awaitLogin():Promise{ - if(hasCompletedLogin)return Promise.resolve() - if(loginPromise)return loginPromise - - return loginPromise = new Promise((resolve) => { - let isResolved = false - window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => { - if(isResolved)return - hasCompletedLogin = true - resolve() - isResolved = true - }) - }) -} -window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => { - hasCompletedLogin = false - loginPromise = undefined +import Utils from "./Utils" +import Notices, { notices } from "../components/private/Notices" +import { isNative } from "./environnement"; +import WebpackLoader from "./WebpackLoader"; + +export function patch(){ + /** START NOTICE */ + getModule(e => e.default && e.default.displayName === "ConnectedAppView") + .then(async (mod) => { + const appClasses = await getModule(e => e.hasNotice); + const buildRender = original => { + return function render(){ + const returnValue = original.call(this, ...arguments) + const newchildren = [] + let children = returnValue.props.children[1].props.children + if(!Array.isArray(children))children = [children] + + newchildren.push(children[0]) + newchildren.push(React.createElement(Notices, {container: this})) + newchildren.push(children[1]) + returnValue.props.children[1].props.children = newchildren + + returnValue.props.children[1].props.children[2].props.children[0].props.render = buildRenderChannelSidebar(returnValue.props.children[1].props.children[2].props.children[0].props.render) + + return returnValue + } + } + const buildRenderChannelSidebar = original => { + return function renderChannelSidebar(){ + const returnValue = original.call(this, ...arguments) + + const hasNotice = notices.length > 0 + if(!hasNotice)return returnValue + if(!Utils.hasClass(returnValue.props.className, appClasses.hasNotice)){ + returnValue.props.className += " "+Utils.removeDa(appClasses.hasNotice) + } + + return returnValue + } + } + mod.default.prototype.render = buildRender(mod.default.prototype.render); + (async function(){ + const base = document.querySelector("."+Utils.removeDa(appClasses.base)) + if(!base)throw new Error(`Could not find base here`) + const elem = Utils.FindReact(base) as any + elem.render = buildRender(elem.render) + elem.forceUpdate() + })() + }) + /** END NOTICE */ + + if(isNative){ + /** START USERPOPOUT PATCH */ + awaitLogin() + .then(async () => { + let UserPopout = await getModule(e => e.default && e.default.displayName === "FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))") + const userModule = await getModule(e => e.default && e.default.getCurrentUser) + const render1 = new UserPopout.default({userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true}).render() + const PopoutProps = render1.props + const render2 = render1.type.render(PopoutProps, null) + const render3 = new render2.type(render2.props).render() + const UserPopoutComponent = render3.type + if(!UserPopoutComponent)throw new Error(`Couldn't find the UserPopoutComponent component.`) + + const render = UserPopoutComponent.prototype.render + UserPopoutComponent.prototype.render = function(){ + const returnValue = render.call(this, ...arguments) + try{ + returnValue.props.children.props["data-user-id"] = this.props.user.id + }catch(e){ + console.error(e) + } + return returnValue + } + }) + /** END USERPOPOUT PATCH*/ + + /** START USERPROFILE PATCH */ + awaitLogin() + .then(async () => { + let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile") + const userModule = await getModule(e => e.default && e.default.getCurrentUser) + const render1 = new UserProfile.default({ + user: userModule.default.getCurrentUser() + }).render() + const render2 = new render1.type(render1.props).render() + const render3 = render2.type.render(render2.props, null) + const render4 = new render3.type(render3.props).render() + const UserProfileComponent = render4.type + if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`) + + const render = UserProfileComponent.prototype.render + UserProfileComponent.prototype.render = function(){ + const returnValue = render.call(this, ...arguments) + console.log(returnValue) + try{ + returnValue.props.children.props["data-user-id"] = this.props.user.id + }catch(e){ + console.error(e) + } + return returnValue + } + }) + /** END USERPROFILE PATCH */ + + /** START WEBHOOK PATCH */ +/* + let usedWebhooks = {} + + getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end) + .then(RequestModule => { + const end = RequestModule.Request.prototype.end + RequestModule.Request.prototype.end = function(){ + if(this.url.endsWith("/messages") && /\/channels\/\d+\/messages/g.test(this.url) && this.method === "POST"){ // sending message + let channelId = this.url.split("/channels/")[1].split("/messages")[0] + + if(usedWebhooks[channelId]){ // webhook is availlable + let webhook = usedWebhooks[channelId] + let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true` + this.url = url + } + } + + return end.call(this, ...arguments) + } + }) + getModule(e => e.default && e.default.displayName === "Webhook") + .then(webhookComponent => { + const renderEdit = webhookComponent.default.prototype.renderEdit + webhookComponent.default.prototype.renderEdit = function(){ + const webhook = this.props.webhook + let returnValue = renderEdit.call(this, ...arguments) + returnValue.props.children = [returnValue.props.children] + let message = usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id ? "Stop talking with this webhook" : "Talk with this webhook" + + returnValue.props.children.push(React.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "green", wrapper: false, onClick(){ + if(usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id){ + delete usedWebhooks[webhook.channel_id] + }else{ + usedWebhooks[webhook.channel_id] = { + id: webhook.id, + token: webhook.token + } + } + webhookPanels.forEach(e => e()) + }}, message)) + + return returnValue + } + }) + + let webhookPanels = [] + let getComp = (comp) => { + class SettingsWebhooks extends React.PureComponent { + constructor(props){ + super(props) + } + + componentWillMount(){ + this.id = uuid() + this.component = new comp(this.props) + let func = () => { + this.component.forceUpdate() + } + func.id = this.id + webhookPanels.push(func) + } + + componentWillUnmount(){ + this.component = null + webhookPanels = webhookPanels.filter(e => e.id !== this.id) + } + + render(){ + return this.component.render() + } + + static displayName = "SettingsWebhooks" + } + + return SettingsWebhooks + } + getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)") + .then(webhooksComponents => { + let comp = webhooksComponents.default + + webhooksComponents.default = getComp(comp) + + WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))") + .forEach(mod => { + mod.default = getComp(mod.default) + }) + })*/ + /** END WEBHOOK PATCH */ + } + + // TODO: Add in app-notifications / confirmations. + /** START IN-APP NOTIFICATIONS */ + //getModule(e => true) + /** END IN-APP NOTIFICATIONS */ +} + +function getModule(filter: (mod:any) => boolean):Promise{ + return new Promise((resolve) => { + window.Lightcord.Api.ensureExported(filter) + .then(resolve) + .catch(err => { + console.error("[LIGHTCORD]", err, filter) + }) + }) +} + +let hasCompletedLogin = false +let loginPromise:Promise +function awaitLogin():Promise{ + if(hasCompletedLogin)return Promise.resolve() + if(loginPromise)return loginPromise + + return loginPromise = new Promise((resolve) => { + let isResolved = false + window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => { + if(isResolved)return + hasCompletedLogin = true + resolve() + isResolved = true + }) + }) +} +window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => { + hasCompletedLogin = false + loginPromise = undefined }) \ No newline at end of file diff --git a/LightcordApi/tsconfig.json b/LightcordApi/tsconfig.json index 8d10262..e243ab0 100644 --- a/LightcordApi/tsconfig.json +++ b/LightcordApi/tsconfig.json @@ -1,21 +1,21 @@ -{ - "compilerOptions": { - "noImplicitAny": false, - "module": "CommonJS", - "target": "ES2017", - "jsx": "react", - "outDir": "./dist", - "resolveJsonModule": true, - "rootDir": "./src", - "sourceMap": true, - "declaration": true, - "allowJs": true - }, - "exclude": [ - "./js/**", - "./webpack.config.js", - "./dist/**", - "./prod.config.js", - "./docs" - ] +{ + "compilerOptions": { + "noImplicitAny": false, + "module": "CommonJS", + "target": "ES2017", + "jsx": "react", + "outDir": "./dist", + "resolveJsonModule": true, + "rootDir": "./src", + "sourceMap": true, + "declaration": true, + "allowJs": true + }, + "exclude": [ + "./js/**", + "./webpack.config.js", + "./dist/**", + "./prod.config.js", + "./docs" + ] } \ No newline at end of file diff --git a/LightcordApi/webpack.config.js b/LightcordApi/webpack.config.js index 50fe1ea..322d686 100644 --- a/LightcordApi/webpack.config.js +++ b/LightcordApi/webpack.config.js @@ -1,72 +1,72 @@ -const path = require("path"); -const TerserPlugin = require("terser-webpack-plugin") -const child_process = require("child_process") - -module.exports = { - mode: "development", - target: "node", - devtool: "inline-source-map", - entry: "./src/index.ts", - output: { - filename: "main.js", - path: path.resolve(__dirname, "js"), - library: "LightcordApi", - libraryTarget: "commonjs2" - }, - externals: { - electron: `electron`, - fs: `fs`, - path: `path`, - events: `events`, - rimraf: `rimraf`, - yauzl: `yauzl`, - mkdirp: `mkdirp`, - request: `request`, - "node-fetch": "node-fetch", - "uuid/v1": "uuid/v1", - "uuid/v4": "uuid/v4", - "powercord/webpack": "powercord/webpack" - }, - resolve: { - extensions: [".js", ".jsx", ".json", ".ts", ".tsx"], - alias: { - "react$": path.resolve(__dirname, "src", "alias", "react.js"), - "react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.js") - } - }, - module: { - rules: [{ - test: /\.jsx?$/, - loader: "babel-loader", - exclude: /node_modules/, - query: { - presets: [ - ["@babel/env", { - targets: { - node: "12.8.1", - chrome: "78" - } - }], "@babel/react" - ] - } - }, { - test: /\.tsx?$/, - use: 'ts-loader', - exclude: /node_modules/, - }] - }, - optimization: { - minimizer: [ - new TerserPlugin({ - cache: true, - parallel: true, - sourceMap: true, - terserOptions: { - mangle: false, - keep_classnames: true, - keep_fnames: true - } - }), - ] - } +const path = require("path"); +const TerserPlugin = require("terser-webpack-plugin") +const child_process = require("child_process") + +module.exports = { + mode: "development", + target: "node", + devtool: "inline-source-map", + entry: "./src/index.ts", + output: { + filename: "main.js", + path: path.resolve(__dirname, "js"), + library: "LightcordApi", + libraryTarget: "commonjs2" + }, + externals: { + electron: `electron`, + fs: `fs`, + path: `path`, + events: `events`, + rimraf: `rimraf`, + yauzl: `yauzl`, + mkdirp: `mkdirp`, + request: `request`, + "node-fetch": "node-fetch", + "uuid/v1": "uuid/v1", + "uuid/v4": "uuid/v4", + "powercord/webpack": "powercord/webpack" + }, + resolve: { + extensions: [".js", ".jsx", ".json", ".ts", ".tsx"], + alias: { + "react$": path.resolve(__dirname, "src", "alias", "react.js"), + "react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.js") + } + }, + module: { + rules: [{ + test: /\.jsx?$/, + loader: "babel-loader", + exclude: /node_modules/, + query: { + presets: [ + ["@babel/env", { + targets: { + node: "12.8.1", + chrome: "78" + } + }], "@babel/react" + ] + } + }, { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }] + }, + optimization: { + minimizer: [ + new TerserPlugin({ + cache: true, + parallel: true, + sourceMap: true, + terserOptions: { + mangle: false, + keep_classnames: true, + keep_fnames: true + } + }), + ] + } }; \ No newline at end of file diff --git a/afterbuild.js b/afterbuild.js index 1abf386..4c202b6 100644 --- a/afterbuild.js +++ b/afterbuild.js @@ -1,64 +1,64 @@ -const fs = require("fs") -const fsAsync = fs.promises -const yazl = require("yazl") -const __path = require("path") - -const buildsPaths = __path.join(__dirname, "builds") -const folders = [ - "lightcord-win32-ia32", - "lightcord-linux-x64", - "lightcord-darwin-x64" -] -folders.forEach(folder => { - const path = __path.join(buildsPaths, folder) - if(!fs.existsSync(path))return console.warn(`\x1b[33mCan't pack build ${folder} because it doesn't exist.\x1b[0m`) - const zipPath = __path.join(buildsPaths, folder+".zip") - if(fs.existsSync(zipPath)){ - console.warn(`Deleting ${zipPath}.`) - fs.unlinkSync(zipPath) - } - const zip = new yazl.ZipFile() - zip.outputStream.pipe(fs.createWriteStream(zipPath)) - - const platform = folder.split("-")[1] - processNextDir(path, zip, platform) - .then(() => { - console.log(`Zipped ${platform}.`) - zip.end() - }) -}) - -async function processNextDir(dir, zip, platform, bpath = dir){ - if(dir.replace(bpath, ""))zip.addEmptyDirectory(dir.replace(bpath, "").slice(1)) - await Promise.all(fs.readdirSync(dir, {withFileTypes: true}) - .map(async file => { - let path = __path.join(dir, file.name) - if(file.isDirectory()){ - return await processNextDir(path, zip, platform, bpath) - }else if(file.isFile()){ - if(!path.includes("node_modules")){ - if(platform === "win32"){ - if(file.name.endsWith("_linux.node"))return - if(file.name.endsWith("_darwin.node"))return - if(file.name.endsWith(".dylib"))return - if(file.name.endsWith(".so.4"))return - }else if(platform === "linux"){ - if(file.name.endsWith("_win32.node"))return - if(file.name.endsWith("_darwin.node"))return - if(file.name.endsWith(".dylib"))return - if(file.name.endsWith(".dll"))return - }else if(platform === "darwin"){ - if(file.name.endsWith("_linux.node"))return - if(file.name.endsWith("_win32.node"))return - if(file.name.endsWith(".dll"))return - if(file.name.endsWith(".so.4"))return - } - } - let stat = fs.statSync(path) - zip.addBuffer(await fsAsync.readFile(path), __path.relative(bpath, path), { - mode: stat.mode, - mtime: stat.mtime - }) - } - })) +const fs = require("fs") +const fsAsync = fs.promises +const yazl = require("yazl") +const __path = require("path") + +const buildsPaths = __path.join(__dirname, "builds") +const folders = [ + "lightcord-win32-ia32", + "lightcord-linux-x64", + "lightcord-darwin-x64" +] +folders.forEach(folder => { + const path = __path.join(buildsPaths, folder) + if(!fs.existsSync(path))return console.warn(`\x1b[33mCan't pack build ${folder} because it doesn't exist.\x1b[0m`) + const zipPath = __path.join(buildsPaths, folder+".zip") + if(fs.existsSync(zipPath)){ + console.warn(`Deleting ${zipPath}.`) + fs.unlinkSync(zipPath) + } + const zip = new yazl.ZipFile() + zip.outputStream.pipe(fs.createWriteStream(zipPath)) + + const platform = folder.split("-")[1] + processNextDir(path, zip, platform) + .then(() => { + console.log(`Zipped ${platform}.`) + zip.end() + }) +}) + +async function processNextDir(dir, zip, platform, bpath = dir){ + if(dir.replace(bpath, ""))zip.addEmptyDirectory(dir.replace(bpath, "").slice(1)) + await Promise.all(fs.readdirSync(dir, {withFileTypes: true}) + .map(async file => { + let path = __path.join(dir, file.name) + if(file.isDirectory()){ + return await processNextDir(path, zip, platform, bpath) + }else if(file.isFile()){ + if(!path.includes("node_modules")){ + if(platform === "win32"){ + if(file.name.endsWith("_linux.node"))return + if(file.name.endsWith("_darwin.node"))return + if(file.name.endsWith(".dylib"))return + if(file.name.endsWith(".so.4"))return + }else if(platform === "linux"){ + if(file.name.endsWith("_win32.node"))return + if(file.name.endsWith("_darwin.node"))return + if(file.name.endsWith(".dylib"))return + if(file.name.endsWith(".dll"))return + }else if(platform === "darwin"){ + if(file.name.endsWith("_linux.node"))return + if(file.name.endsWith("_win32.node"))return + if(file.name.endsWith(".dll"))return + if(file.name.endsWith(".so.4"))return + } + } + let stat = fs.statSync(path) + zip.addBuffer(await fsAsync.readFile(path), __path.relative(bpath, path), { + mode: stat.mode, + mtime: stat.mtime + }) + } + })) } \ No newline at end of file diff --git a/build.js b/build.js index 07a45ca..9d57170 100644 --- a/build.js +++ b/build.js @@ -1,275 +1,275 @@ -const child_process = require("child_process") -const path = require("path") -const terser = require("terser") -const util = require("util") - -const production = true -const includeSourcesMaps = true - -let fs = require("fs") - -console.log = (...args) => { - process.stdout.write(Buffer.from(util.formatWithOptions({colors: true}, ...args)+"\n", "binary").toString("utf8")) -} -console.info = (...args) => { - console.log(`\x1b[34m[INFO]\x1b[0m`, ...args) -} -let commit = child_process.execSync("git rev-parse HEAD").toString().split("\n")[0].trim() -console.info(`Obtained commit ${commit} for the build`) - -async function processNextDir(folder, folders, predicate, compile, ignoreModules){ - if(typeof ignoreModules === "undefined")ignoreModules = false - let files = fs.readdirSync(folder, {withFileTypes: true}) - for(let file of files){ - if(file.isFile()){ - let isMinified = file.name.endsWith(".min.js") || file.name.endsWith(".min.css") - let filepath = path.join(folder, file.name) - let type = file.name.split(".").pop().toLowerCase() - if(type === file.name)type = "" - if([ - "ts", - "md", - "gitignore", - "map" - ].includes(type)){ - console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of type ${type}\x1b[0m`) - continue - } - if([ - "tsconfig.json", - "webpack.config.js" - ].includes(file.name)){ - console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of name ${file.name}\x1b[0m`) - continue - } - if(folders.exclude && folders.exclude.test(filepath)){ - console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because regex\x1b[0m`) - continue - } - let hasMinifiedVersion = (type === "js" || type === "css") && !isMinified && files.find(f => { - return f.name === file.name.split(".").slice(0, -1).join(".")+".min."+type - }) - if(hasMinifiedVersion){ - console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because it has a minified version.\x1b[0m`) - continue - } - if(!isMinified && predicate(filepath) && filepath.split(/[\\/]+/).reverse()[1] !== "js"){ - await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..") - }else{ - if(["js", "css"].includes(type)){ - if(!includeSourcesMaps){ - console.log(`We don't include sourcemap for this build. Skipping ${file.name}.`) - return await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) - } - let fileContent = (await fs.promises.readFile(filepath, "utf8")) - let sourceMap = fileContent.split(/[\n\r]+/g).pop() - if(!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")){ - console.log(`This file doesn't have sourcemap. ${file.name}.`) - await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) - continue - } - let sourceMapContent - if(sourceMap.slice(21).startsWith("data:")){ - console.log(`Extracting sourcemap from data uri. From file ${file.name}.`) - sourceMapContent = Buffer.from(sourceMap.split("=").slice(1).join("="), "base64").toString("utf-8") - }else{ - console.log(`Extracting sourcemap from file ${file.name}.map.`) - await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) - sourceMapContent = await fs.promises.readFile(path.join(folder, sourceMap.slice(21)), "utf8") - } - sourceMapContent = JSON.parse(sourceMapContent) - sourceMapContent.sourcesContent = [] - let sourceMapPath = filepath + ".map" - fileContent = fileContent - // source map - .replace(sourceMap, "//# sourceMappingURL="+filepath.split(/[\\\/]+/g).pop()+".map") - await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir), fileContent) - await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir)+".map", JSON.stringify(sourceMapContent)) - }else{ - await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) - } - } - }else if(file.isDirectory()){ - if(ignoreModules && file.name === "node_modules")continue - if(folders.exclude && folders.exclude.test(path.join(folder, file.name)))continue - await fs.promises.mkdir(path.join(folder, file.name).replace(folders.startDir, folders.newDir), {recursive: true}) - await processNextDir(path.join(folder, file.name), ...Array.from(arguments).slice(1)) - } - } -} - -async function main(){ - let startTimestamp = Date.now() - console.info("Starting build") - - console.info("Reseting existent directory...") - await fs.promises.rmdir("./distApp", {"recursive": true}) - await fs.promises.mkdir(__dirname+"/distApp/dist", {"recursive": true}) - - console.info("Executing command `npm run compile`") - child_process.execSync("npm run compile", { - encoding: "binary", - stdio: "inherit" - }) - - let startDir = path.join(__dirname, "./dist") - let newDir = path.join(__dirname, "./distApp/dist") - console.info("No error detected. Copying files from "+startDir+".") - await fs.promises.mkdir(startDir, {recursive: true}) - - await processNextDir(startDir, { - startDir, - newDir - }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { - console.info(`Minifying ${filepath} to ${newpath}`) - - if(filepath.endsWith("git.js")){ - await fs.promises.writeFile(newpath, terser.minify(fs.readFileSync(filepath, "utf8").replace(/"{commit}"/g, `"${commit}"`)).code, "utf8") - }else{ - await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") - } - }, true).then(() => { - console.info(`Copied files and minified them from ${startDir}.`) - }) - - await processNextDir(path.join(__dirname, "modules"), { - startDir: path.join(__dirname, "modules"), - newDir: path.join(__dirname, "distApp", "modules"), - exclude: /discord_spellcheck/g - }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { - console.info(`Minifying ${filepath} to ${newpath}`) - await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") - }, true).then(() => { - console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`) - }) - - await Promise.all((await fs.promises.readdir(path.join(__dirname, "distApp", "modules"))).map(async mdl => { - let dir = path.join(__dirname, "distApp", "modules", mdl) - - if(!fs.existsSync(path.join(dir, "package.json"))){ - if(mdl === "discord_desktop_core"){ - dir = path.join(dir, "core") - }else{ - return - } - } - - console.info(`Installing modules for ${mdl}`) - child_process.execSync("npm install --only=prod", { - encoding: "binary", - cwd: dir, - stdio: "inherit" - }) - })) - - await fs.promises.mkdir(path.join(__dirname, "distApp", "modules", "discord_spellcheck"), {recursive: true}) - await processNextDir(path.join(__dirname, "modules", "discord_spellcheck"), { - startDir: path.join(__dirname, "modules", "discord_spellcheck"), - newDir: path.join(__dirname, "distApp", "modules", "discord_spellcheck") - }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { - console.info(`Minifying ${filepath} to ${newpath}`) - await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") - }, false).then(() => { - console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`) - }) - - await processNextDir(path.join(__dirname, "LightcordApi"), { - startDir: path.join(__dirname, "LightcordApi"), - newDir: path.join(__dirname, "distApp", "LightcordApi"), - exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g - }, ((filepath) => filepath.endsWith(".js") && (!production ? !filepath.includes("node_modules") : true)), async (filepath, newpath) => { - await fs.promises.copyFile(filepath, newpath) - }, true).then(() => { - console.info(`Copied files and minified them from ${path.join(__dirname, "LightcordApi")}.`) - }) - - child_process.execSync("npm install --only=prod", { - encoding: "binary", - cwd: path.join(__dirname, "distApp", "LightcordApi"), - stdio: "inherit" - }) - - function processDJS(dir){ - fs.mkdirSync(path.join(__dirname, "distApp", "DiscordJS", dir), {recursive: true}) - return processNextDir(path.join(__dirname, "DiscordJS", dir), { - startDir: path.join(__dirname, "DiscordJS", dir), - newDir: path.join(__dirname, "distApp", "DiscordJS", dir), - exclude: /node_modules/g - }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { - console.info(`Minifying ${filepath} to ${newpath}`) - await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") - }).then(() => { - console.info(`Copied files and minified them from ${path.join(__dirname, "DiscordJS", dir)}.`) - }) - } - async function copyFileDJS(file){ - await fs.promises.writeFile(path.join(__dirname, "distApp", "DiscordJS", file), await fs.promises.readFile(path.join(__dirname, "DiscordJS", file))) - } - - await processDJS("dist") - await copyFileDJS("package.json") - - child_process.execSync("npm install --only=prod", { - encoding: "binary", - cwd: path.join(__dirname, "distApp", "DiscordJS"), - stdio: "inherit" - }) - - fs.mkdirSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist"), {recursive: true}) - const BDPackageJSON = require("./BetterDiscordApp/package.json") - fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "package.json"), JSON.stringify(BDPackageJSON), "utf8") - const files = [ - "index.min.js", - "style.min.css" - ] - files.forEach(e => { - files.push(e + ".map") - }) - files.forEach(e => { - const pth = path.join(__dirname, "BetterDiscordApp", "dist", e) - if(!fs.existsSync(pth))return console.error(`\x1b[31mFile ${pth} from betterdiscord does not exist.\x1b[0m`) - if(e.endsWith(".map")){ - const data = JSON.parse(fs.readFileSync(pth, "utf8")) - data.sourcesContent = [] - fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e), JSON.stringify(data)) - }else{ - fs.copyFileSync(pth, path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e)) - } - }) - - await fs.promises.mkdir(path.join(__dirname, "distApp", "splash", "videos"), {recursive: true}) - await processNextDir(path.join(__dirname, "splash"), { - startDir: path.join(__dirname, "splash"), - newDir: path.join(__dirname, "distApp", "splash"), - exclude: /node_modules/g - }, (filepath) => { - if(filepath.endsWith(".js"))return true - return false - }, async (filepath, newpath) => { - console.info(`Minifying ${filepath} to ${newpath}`) - await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") - }).then(() => { - console.info(`Copied files and minified them from ${path.join(__dirname, "splash")}.`) - }) - fs.writeFileSync(path.join(__dirname, "distApp", "LICENSE"), fs.readFileSync(path.join(__dirname, "LICENSE"))) - - let packageJSON = require("./package.json") - packageJSON.scripts["build:electron_linux"] = packageJSON.scripts["build:electron_linux"].replace("./distApp", ".") - packageJSON.scripts["build:electron_win"] = packageJSON.scripts["build:electron_win"].replace("./distApp", ".") - packageJSON.scripts["build:electron_darwin"] = packageJSON.scripts["build:electron_darwin"].replace("./distApp", ".") - - fs.writeFileSync(path.join(__dirname, "distApp", "package.json"), JSON.stringify(packageJSON), "utf8") - - console.info(`Installing ${Object.keys(packageJSON.dependencies).length} packages...`) - child_process.execSync("npm install --only=prod", { - encoding: "binary", - cwd: path.join(__dirname, "distApp"), - stdio: "inherit" - }) - console.info("Build took "+(Date.now() - startTimestamp) +"ms.") -} -main() -.catch(err => { - console.error(err) - process.exit(1) +const child_process = require("child_process") +const path = require("path") +const terser = require("terser") +const util = require("util") + +const production = true +const includeSourcesMaps = true + +let fs = require("fs") + +console.log = (...args) => { + process.stdout.write(Buffer.from(util.formatWithOptions({colors: true}, ...args)+"\n", "binary").toString("utf8")) +} +console.info = (...args) => { + console.log(`\x1b[34m[INFO]\x1b[0m`, ...args) +} +let commit = child_process.execSync("git rev-parse HEAD").toString().split("\n")[0].trim() +console.info(`Obtained commit ${commit} for the build`) + +async function processNextDir(folder, folders, predicate, compile, ignoreModules){ + if(typeof ignoreModules === "undefined")ignoreModules = false + let files = fs.readdirSync(folder, {withFileTypes: true}) + for(let file of files){ + if(file.isFile()){ + let isMinified = file.name.endsWith(".min.js") || file.name.endsWith(".min.css") + let filepath = path.join(folder, file.name) + let type = file.name.split(".").pop().toLowerCase() + if(type === file.name)type = "" + if([ + "ts", + "md", + "gitignore", + "map" + ].includes(type)){ + console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of type ${type}\x1b[0m`) + continue + } + if([ + "tsconfig.json", + "webpack.config.js" + ].includes(file.name)){ + console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of name ${file.name}\x1b[0m`) + continue + } + if(folders.exclude && folders.exclude.test(filepath)){ + console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because regex\x1b[0m`) + continue + } + let hasMinifiedVersion = (type === "js" || type === "css") && !isMinified && files.find(f => { + return f.name === file.name.split(".").slice(0, -1).join(".")+".min."+type + }) + if(hasMinifiedVersion){ + console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because it has a minified version.\x1b[0m`) + continue + } + if(!isMinified && predicate(filepath) && filepath.split(/[\\/]+/).reverse()[1] !== "js"){ + await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..") + }else{ + if(["js", "css"].includes(type)){ + if(!includeSourcesMaps){ + console.log(`We don't include sourcemap for this build. Skipping ${file.name}.`) + return await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) + } + let fileContent = (await fs.promises.readFile(filepath, "utf8")) + let sourceMap = fileContent.split(/[\n\r]+/g).pop() + if(!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")){ + console.log(`This file doesn't have sourcemap. ${file.name}.`) + await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) + continue + } + let sourceMapContent + if(sourceMap.slice(21).startsWith("data:")){ + console.log(`Extracting sourcemap from data uri. From file ${file.name}.`) + sourceMapContent = Buffer.from(sourceMap.split("=").slice(1).join("="), "base64").toString("utf-8") + }else{ + console.log(`Extracting sourcemap from file ${file.name}.map.`) + await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) + sourceMapContent = await fs.promises.readFile(path.join(folder, sourceMap.slice(21)), "utf8") + } + sourceMapContent = JSON.parse(sourceMapContent) + sourceMapContent.sourcesContent = [] + let sourceMapPath = filepath + ".map" + fileContent = fileContent + // source map + .replace(sourceMap, "//# sourceMappingURL="+filepath.split(/[\\\/]+/g).pop()+".map") + await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir), fileContent) + await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir)+".map", JSON.stringify(sourceMapContent)) + }else{ + await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) + } + } + }else if(file.isDirectory()){ + if(ignoreModules && file.name === "node_modules")continue + if(folders.exclude && folders.exclude.test(path.join(folder, file.name)))continue + await fs.promises.mkdir(path.join(folder, file.name).replace(folders.startDir, folders.newDir), {recursive: true}) + await processNextDir(path.join(folder, file.name), ...Array.from(arguments).slice(1)) + } + } +} + +async function main(){ + let startTimestamp = Date.now() + console.info("Starting build") + + console.info("Reseting existent directory...") + await fs.promises.rmdir("./distApp", {"recursive": true}) + await fs.promises.mkdir(__dirname+"/distApp/dist", {"recursive": true}) + + console.info("Executing command `npm run compile`") + child_process.execSync("npm run compile", { + encoding: "binary", + stdio: "inherit" + }) + + let startDir = path.join(__dirname, "./dist") + let newDir = path.join(__dirname, "./distApp/dist") + console.info("No error detected. Copying files from "+startDir+".") + await fs.promises.mkdir(startDir, {recursive: true}) + + await processNextDir(startDir, { + startDir, + newDir + }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { + console.info(`Minifying ${filepath} to ${newpath}`) + + if(filepath.endsWith("git.js")){ + await fs.promises.writeFile(newpath, terser.minify(fs.readFileSync(filepath, "utf8").replace(/"{commit}"/g, `"${commit}"`)).code, "utf8") + }else{ + await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") + } + }, true).then(() => { + console.info(`Copied files and minified them from ${startDir}.`) + }) + + await processNextDir(path.join(__dirname, "modules"), { + startDir: path.join(__dirname, "modules"), + newDir: path.join(__dirname, "distApp", "modules"), + exclude: /discord_spellcheck/g + }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { + console.info(`Minifying ${filepath} to ${newpath}`) + await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") + }, true).then(() => { + console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`) + }) + + await Promise.all((await fs.promises.readdir(path.join(__dirname, "distApp", "modules"))).map(async mdl => { + let dir = path.join(__dirname, "distApp", "modules", mdl) + + if(!fs.existsSync(path.join(dir, "package.json"))){ + if(mdl === "discord_desktop_core"){ + dir = path.join(dir, "core") + }else{ + return + } + } + + console.info(`Installing modules for ${mdl}`) + child_process.execSync("npm install --only=prod", { + encoding: "binary", + cwd: dir, + stdio: "inherit" + }) + })) + + await fs.promises.mkdir(path.join(__dirname, "distApp", "modules", "discord_spellcheck"), {recursive: true}) + await processNextDir(path.join(__dirname, "modules", "discord_spellcheck"), { + startDir: path.join(__dirname, "modules", "discord_spellcheck"), + newDir: path.join(__dirname, "distApp", "modules", "discord_spellcheck") + }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { + console.info(`Minifying ${filepath} to ${newpath}`) + await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") + }, false).then(() => { + console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`) + }) + + await processNextDir(path.join(__dirname, "LightcordApi"), { + startDir: path.join(__dirname, "LightcordApi"), + newDir: path.join(__dirname, "distApp", "LightcordApi"), + exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g + }, ((filepath) => filepath.endsWith(".js") && (!production ? !filepath.includes("node_modules") : true)), async (filepath, newpath) => { + await fs.promises.copyFile(filepath, newpath) + }, true).then(() => { + console.info(`Copied files and minified them from ${path.join(__dirname, "LightcordApi")}.`) + }) + + child_process.execSync("npm install --only=prod", { + encoding: "binary", + cwd: path.join(__dirname, "distApp", "LightcordApi"), + stdio: "inherit" + }) + + function processDJS(dir){ + fs.mkdirSync(path.join(__dirname, "distApp", "DiscordJS", dir), {recursive: true}) + return processNextDir(path.join(__dirname, "DiscordJS", dir), { + startDir: path.join(__dirname, "DiscordJS", dir), + newDir: path.join(__dirname, "distApp", "DiscordJS", dir), + exclude: /node_modules/g + }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { + console.info(`Minifying ${filepath} to ${newpath}`) + await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") + }).then(() => { + console.info(`Copied files and minified them from ${path.join(__dirname, "DiscordJS", dir)}.`) + }) + } + async function copyFileDJS(file){ + await fs.promises.writeFile(path.join(__dirname, "distApp", "DiscordJS", file), await fs.promises.readFile(path.join(__dirname, "DiscordJS", file))) + } + + await processDJS("dist") + await copyFileDJS("package.json") + + child_process.execSync("npm install --only=prod", { + encoding: "binary", + cwd: path.join(__dirname, "distApp", "DiscordJS"), + stdio: "inherit" + }) + + fs.mkdirSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist"), {recursive: true}) + const BDPackageJSON = require("./BetterDiscordApp/package.json") + fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "package.json"), JSON.stringify(BDPackageJSON), "utf8") + const files = [ + "index.min.js", + "style.min.css" + ] + files.forEach(e => { + files.push(e + ".map") + }) + files.forEach(e => { + const pth = path.join(__dirname, "BetterDiscordApp", "dist", e) + if(!fs.existsSync(pth))return console.error(`\x1b[31mFile ${pth} from betterdiscord does not exist.\x1b[0m`) + if(e.endsWith(".map")){ + const data = JSON.parse(fs.readFileSync(pth, "utf8")) + data.sourcesContent = [] + fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e), JSON.stringify(data)) + }else{ + fs.copyFileSync(pth, path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e)) + } + }) + + await fs.promises.mkdir(path.join(__dirname, "distApp", "splash", "videos"), {recursive: true}) + await processNextDir(path.join(__dirname, "splash"), { + startDir: path.join(__dirname, "splash"), + newDir: path.join(__dirname, "distApp", "splash"), + exclude: /node_modules/g + }, (filepath) => { + if(filepath.endsWith(".js"))return true + return false + }, async (filepath, newpath) => { + console.info(`Minifying ${filepath} to ${newpath}`) + await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") + }).then(() => { + console.info(`Copied files and minified them from ${path.join(__dirname, "splash")}.`) + }) + fs.writeFileSync(path.join(__dirname, "distApp", "LICENSE"), fs.readFileSync(path.join(__dirname, "LICENSE"))) + + let packageJSON = require("./package.json") + packageJSON.scripts["build:electron_linux"] = packageJSON.scripts["build:electron_linux"].replace("./distApp", ".") + packageJSON.scripts["build:electron_win"] = packageJSON.scripts["build:electron_win"].replace("./distApp", ".") + packageJSON.scripts["build:electron_darwin"] = packageJSON.scripts["build:electron_darwin"].replace("./distApp", ".") + + fs.writeFileSync(path.join(__dirname, "distApp", "package.json"), JSON.stringify(packageJSON), "utf8") + + console.info(`Installing ${Object.keys(packageJSON.dependencies).length} packages...`) + child_process.execSync("npm install --only=prod", { + encoding: "binary", + cwd: path.join(__dirname, "distApp"), + stdio: "inherit" + }) + console.info("Build took "+(Date.now() - startTimestamp) +"ms.") +} +main() +.catch(err => { + console.error(err) + process.exit(1) }) \ No newline at end of file diff --git a/build_electron.js b/build_electron.js index 67eb4ee..b1d0ed0 100644 --- a/build_electron.js +++ b/build_electron.js @@ -1,119 +1,119 @@ -const spawn = require("cross-spawn") -const path = require("path") -const { existsSync, promises: fsPromises } = require("fs") - -const supportedPlatforms = [] -const Platforms = { - linux: { - name: "linux", - run: () => { - return awaitExec("npm", ["run", "build:electron_linux"]) - } - }, - win: { - name: "win", - run: () => { - return awaitExec("npm", ["run", "build:electron_win"]) - } - }, - mac: { - name: "mac", - run: () => { - return awaitExec("npm", ["run", "build:electron_darwin"]) - } - }, - mac_experimental: { - name: "mac", - experimental: true, - run: async () => { - const basePath = path.join(__dirname, "..", "lightcord-darwin-x64") - const nextPath = path.join(__dirname, "builds", "lightcord-darwin-x64") - if(existsSync(nextPath)){ - console.log(`Cleaning ${nextPath}.`) - await fsPromises.rmdir(nextPath, {recursive: true}) - } - console.log(`Copying files from ${basePath}.`) - let nextDir = async (pth) => { - const newPath = pth.replace(basePath, nextPath) - await fsPromises.mkdir(newPath) - for(let file of await fsPromises.readdir(pth, {withFileTypes: true})){ - const filePath = path.join(pth, file.name) - const newFilePath = path.join(newPath, file.name) - if(file.isFile()){ - await fsPromises.copyFile(filePath, newFilePath) - }else if(file.isDirectory()){ - await nextDir(filePath) - } - } - } - await nextDir(basePath) - console.log(`Files are copied. Erasing current bundle if existing.`) - const asarPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar") - if(existsSync(asarPath))await fsPromises.unlink(asarPath) - const asarUnpackPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar.unpacked") - if(existsSync(asarUnpackPath))await fsPromises.rmdir(asarUnpackPath, {recursive: true}) - const asar = require("asar") - await asar.createPackageWithOptions(path.join(__dirname, "distApp"), asarPath, { - unpack: "*.{node,dylib,so.4,dll}", - unpackDir: asarUnpackPath - }) - const iconPath = path.join(__dirname, "app_icon_darwin.icns") - if(existsSync(iconPath)){ - console.log(`Setting icon.`) - const newIconPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "electron.icns") - await fsPromises.copyFile(iconPath, newIconPath) - } - } - } -} - -switch(process.platform){ - case "win32": - supportedPlatforms.push(Platforms.win) - supportedPlatforms.push(Platforms.linux) - if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){ - supportedPlatforms.push(Platforms.mac_experimental) - } - break - case "linux": - supportedPlatforms.push(Platforms.linux) - if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){ - supportedPlatforms.push(Platforms.mac_experimental) - } - break - case "darwin": - supportedPlatforms.push(Platforms.mac) - supportedPlatforms.push(Platforms.linux) - break -} - -(async function(){ - console.log(`[\x1b[33mINFO\x1b[0m] Will build platforms \x1b[34m${supportedPlatforms.map(e => e.name).join("\x1b[0m, \x1b[34m")}\x1b[0m`) - for(let platform of supportedPlatforms){ - console.log(`[\x1b[33mINFO\x1b[0m] Building platform ${platform.name}`) - if(platform.experimental)console.warn(`[\x1b[33mWARN\x1b[0m] This platform is experimental`) - await platform.run() - } -})().catch(err => { - console.error(`Couldn't package app for electrons. Error: ${err}`) -}) - -function awaitExec(command, args = []){ - return new Promise((resolve, reject) => { - const child = spawn.spawn(command, args, { - env: process.env, - cwd: process.cwd(), - stdio: "inherit" - }) - child.on("close", (code) => { - console.log() - console.log() - console.log(`Command ${command}${args.length > 0 ? " " + args.join(" ") : ""} ended with code ${code}.`) - if(code !== 0){ - console.error("\x1b[31mFAILURE\x1b[0m Command failed. See logs above.") - return reject(code) - } - resolve() - }) - }) +const spawn = require("cross-spawn") +const path = require("path") +const { existsSync, promises: fsPromises } = require("fs") + +const supportedPlatforms = [] +const Platforms = { + linux: { + name: "linux", + run: () => { + return awaitExec("npm", ["run", "build:electron_linux"]) + } + }, + win: { + name: "win", + run: () => { + return awaitExec("npm", ["run", "build:electron_win"]) + } + }, + mac: { + name: "mac", + run: () => { + return awaitExec("npm", ["run", "build:electron_darwin"]) + } + }, + mac_experimental: { + name: "mac", + experimental: true, + run: async () => { + const basePath = path.join(__dirname, "..", "lightcord-darwin-x64") + const nextPath = path.join(__dirname, "builds", "lightcord-darwin-x64") + if(existsSync(nextPath)){ + console.log(`Cleaning ${nextPath}.`) + await fsPromises.rmdir(nextPath, {recursive: true}) + } + console.log(`Copying files from ${basePath}.`) + let nextDir = async (pth) => { + const newPath = pth.replace(basePath, nextPath) + await fsPromises.mkdir(newPath) + for(let file of await fsPromises.readdir(pth, {withFileTypes: true})){ + const filePath = path.join(pth, file.name) + const newFilePath = path.join(newPath, file.name) + if(file.isFile()){ + await fsPromises.copyFile(filePath, newFilePath) + }else if(file.isDirectory()){ + await nextDir(filePath) + } + } + } + await nextDir(basePath) + console.log(`Files are copied. Erasing current bundle if existing.`) + const asarPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar") + if(existsSync(asarPath))await fsPromises.unlink(asarPath) + const asarUnpackPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar.unpacked") + if(existsSync(asarUnpackPath))await fsPromises.rmdir(asarUnpackPath, {recursive: true}) + const asar = require("asar") + await asar.createPackageWithOptions(path.join(__dirname, "distApp"), asarPath, { + unpack: "*.{node,dylib,so.4,dll}", + unpackDir: asarUnpackPath + }) + const iconPath = path.join(__dirname, "app_icon_darwin.icns") + if(existsSync(iconPath)){ + console.log(`Setting icon.`) + const newIconPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "electron.icns") + await fsPromises.copyFile(iconPath, newIconPath) + } + } + } +} + +switch(process.platform){ + case "win32": + supportedPlatforms.push(Platforms.win) + supportedPlatforms.push(Platforms.linux) + if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){ + supportedPlatforms.push(Platforms.mac_experimental) + } + break + case "linux": + supportedPlatforms.push(Platforms.linux) + if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){ + supportedPlatforms.push(Platforms.mac_experimental) + } + break + case "darwin": + supportedPlatforms.push(Platforms.mac) + supportedPlatforms.push(Platforms.linux) + break +} + +(async function(){ + console.log(`[\x1b[33mINFO\x1b[0m] Will build platforms \x1b[34m${supportedPlatforms.map(e => e.name).join("\x1b[0m, \x1b[34m")}\x1b[0m`) + for(let platform of supportedPlatforms){ + console.log(`[\x1b[33mINFO\x1b[0m] Building platform ${platform.name}`) + if(platform.experimental)console.warn(`[\x1b[33mWARN\x1b[0m] This platform is experimental`) + await platform.run() + } +})().catch(err => { + console.error(`Couldn't package app for electrons. Error: ${err}`) +}) + +function awaitExec(command, args = []){ + return new Promise((resolve, reject) => { + const child = spawn.spawn(command, args, { + env: process.env, + cwd: process.cwd(), + stdio: "inherit" + }) + child.on("close", (code) => { + console.log() + console.log() + console.log(`Command ${command}${args.length > 0 ? " " + args.join(" ") : ""} ended with code ${code}.`) + if(code !== 0){ + console.error("\x1b[31mFAILURE\x1b[0m Command failed. See logs above.") + return reject(code) + } + resolve() + }) + }) } \ No newline at end of file diff --git a/compile.js b/compile.js index fed54b2..fbcecda 100644 --- a/compile.js +++ b/compile.js @@ -1,30 +1,30 @@ -const spawn = require("cross-spawn") -const { join } = require("path") - -/** Main Project */ -spawnSync("tsc") -/** BetterDiscord */ -spawnSync("npm run build", join(__dirname, "BetterDiscordApp")) -spawnSync("npm run build-prod", join(__dirname, "BetterDiscordApp")) -spawnSync("npm run minify-css", join(__dirname, "BetterDiscordApp")) -/** DiscordJS */ -spawnSync("npm run build", join(__dirname, "DiscordJS")) -/** LightcordApi */ -spawnSync("npm run build", join(__dirname, "LightcordApi")) -spawnSync("npm run build-prod", join(__dirname, "LightcordApi")) -spawnSync("tsc", join(__dirname, "LightcordApi")) - - -function spawnSync(args, cwd){ - args = args.split(" ") - let command = args.shift() - return spawn(command, args, { - cwd: cwd || process.cwd(), - env: process.env, - stdio: "inherit" - }) -} - -process.on("beforeExit", () => { - console.log(`Exiting compilation`) +const spawn = require("cross-spawn") +const { join } = require("path") + +/** Main Project */ +spawnSync("tsc") +/** BetterDiscord */ +spawnSync("npm run build", join(__dirname, "BetterDiscordApp")) +spawnSync("npm run build-prod", join(__dirname, "BetterDiscordApp")) +spawnSync("npm run minify-css", join(__dirname, "BetterDiscordApp")) +/** DiscordJS */ +spawnSync("npm run build", join(__dirname, "DiscordJS")) +/** LightcordApi */ +spawnSync("npm run build", join(__dirname, "LightcordApi")) +spawnSync("npm run build-prod", join(__dirname, "LightcordApi")) +spawnSync("tsc", join(__dirname, "LightcordApi")) + + +function spawnSync(args, cwd){ + args = args.split(" ") + let command = args.shift() + return spawn(command, args, { + cwd: cwd || process.cwd(), + env: process.env, + stdio: "inherit" + }) +} + +process.on("beforeExit", () => { + console.log(`Exiting compilation`) }) \ No newline at end of file diff --git a/downloadNativeModules.js b/downloadNativeModules.js index b2cdcc9..814707d 100644 --- a/downloadNativeModules.js +++ b/downloadNativeModules.js @@ -1,164 +1,164 @@ -const fetch = require("node-fetch") -const yauzl = require("yauzl") -const tmp = require("tmp") -const fs = require("fs") -const util = require('util') -const { join, dirname } = require("path") -const streamPipeline = util.promisify(require('stream').pipeline) - -const API_URL = "https://discord.com/api" -const branch = "stable" -const platforms = ["win", "linux", "osx"] -const platformsNormalized = { - win: "win32", - linux: "linux", - osx: "darwin" -} - -const manuallyDownloads = [] -const patchedJS = [] - -;(async function(){ - for(let platform of platforms){ - console.log(`[\x1b[32mINFO\x1b[0m] Downloading modules for ${platform} (${branch})`) - const version = await fetch(`${API_URL}/updates/${branch}?platform=${platform}`) - .then(res => res.json()).then(res => res.name) - console.log(`[\x1b[32mINFO\x1b[0m] Obtained version ${version} (${platform})`) - const modules = await fetch(`${API_URL}/modules/${branch}/versions.json?host_version=${version}&platform=${platform}`) - .then(res => res.json()) - console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${Object.keys(modules).length} modules.`) - for(const module in modules){ - if(["discord_desktop_core"].includes(module))continue - const moduleVersion = modules[module] - console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${module} v${moduleVersion}.`) - const file = tmp.fileSync({ - prefix: module+"-", - postfix: ".zip" - }) - const res = await fetch(`${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`, { - redirect: "follow" - }) - if(res.status !== 200)throw new Error(`res.status !== 200. ${res.status} ${module} v${moduleVersion} ${branch} ${platform}`) - await streamPipeline(res.body, fs.createWriteStream(file.name)) - /** - * @type {yauzl.ZipFile} - */ - const zipFile = await new Promise((resolve, reject) => { - yauzl.open(file.name, {lazyEntries: true, autoClose: true}, (err, zip) => { - if(err)return reject(err) - resolve(zip) - }) - }) - const modulePath = join(__dirname, "modules", module) - const exists = fs.existsSync(modulePath) - let hasNode = false - let hasBinaries = ["discord_hook", "discord_modules"].includes(module) - let hasNativeDependencies = ["discord_voice", "discord_krisp"].includes(module) - if(!exists){ - console.warn(`[\x1b[33mWARN\x1b[0m] Downloading whole module because it doesn't exists in your files.`) - fs.mkdirSync(modulePath) - } - await new Promise((resolve) => { - zipFile.readEntry() - zipFile.on("entry", function(entry) { - const fileName = entry.fileName.toString("utf8") - if(fileName.endsWith("/")){ - const folderPath = join(modulePath, fileName) - if(!fs.existsSync(folderPath))fs.mkdirSync(folderPath, {recursive: true}) - zipFile.readEntry(); - }else{ - if(!exists){ - const filePath = join(modulePath, fileName) - zipFile.openReadStream(entry, function(err, readStream) { - if (err) throw err; - readStream.on("end", function() { - let content = fs.readFileSync(filePath, "utf-8") - if(content.includes(module+".node")){ - content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`) - fs.writeFileSync(filePath, content) - } - zipFile.readEntry(); - }); - readStream.pipe(fs.createWriteStream(filePath)); - }); - }else{ - let filePath = join(modulePath, fileName) - if(filePath.endsWith(".asar"))return zipFile.readEntry(); - if(fileName.endsWith(".node")){ - hasNode = true - filePath = filePath.replace(".node", "_"+platformsNormalized[platform]+".node") - extractFile() - }else if(join(filePath, "..") === modulePath && filePath.endsWith(".js")){ - if(patchedJS.includes(filePath))return zipFile.readEntry(); - patchedJS.push(filePath) - extractFile(() => { - setTimeout(() => { - let content = fs.readFileSync(filePath, "utf-8") - if(content.includes(module+".node")){ - content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`) - fs.writeFileSync(filePath, content) - } - }, 10); - }) - }else if(hasBinaries && ["exe", "dll"].map(e => { - return filePath.endsWith("."+e) - }).includes(true)){ // binaries - extractFile() - }else if(hasNativeDependencies && ["so.4", "dll", "dylib", "thw"].map(e => { - return filePath.endsWith("."+e) - }).includes(true)){ - extractFile() - }else{ - zipFile.readEntry(); - } - async function extractFile(onEnd){ - const dir = dirname(filePath) - if(!fs.existsSync(dir))await fs.promises.mkdir(dir, {recursive: true}) - zipFile.openReadStream(entry, function(err, readStream) { - if(err)throw err; - readStream.on("end", function() { - if(onEnd)onEnd() - zipFile.readEntry(); - }).on("error", (err) => { - zipFile.close() - console.error(err) - console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`) - console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) - resolve() - zipFile.readEntry(); - manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) - }) - try{ - readStream.pipe(fs.createWriteStream(filePath)); - }catch(err){ - zipFile.close() - console.error(err) - console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`) - console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) - resolve() - zipFile.readEntry(); - manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) - } - }); - } - } - } - }).on("end", () => resolve()) - .on("error", (err) => { - zipFile.close() - console.error(err) - console.error(`\x1b[31mERROR\x1b[0m] Skipping files because of error.`) - console.error(`\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) - manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) - }) - }) - if(hasNode){ - console.log(`[\x1b[32mINFO\x1b[0m] .node files are now in ${module}.`) - } - } - } - if(manuallyDownloads.length > 0){ - console.error(`[\x1b[31mERROR\x1b[0m] Couldn't download some modules. Manual links:`) - manuallyDownloads.forEach(console.log) - } +const fetch = require("node-fetch") +const yauzl = require("yauzl") +const tmp = require("tmp") +const fs = require("fs") +const util = require('util') +const { join, dirname } = require("path") +const streamPipeline = util.promisify(require('stream').pipeline) + +const API_URL = "https://discord.com/api" +const branch = "stable" +const platforms = ["win", "linux", "osx"] +const platformsNormalized = { + win: "win32", + linux: "linux", + osx: "darwin" +} + +const manuallyDownloads = [] +const patchedJS = [] + +;(async function(){ + for(let platform of platforms){ + console.log(`[\x1b[32mINFO\x1b[0m] Downloading modules for ${platform} (${branch})`) + const version = await fetch(`${API_URL}/updates/${branch}?platform=${platform}`) + .then(res => res.json()).then(res => res.name) + console.log(`[\x1b[32mINFO\x1b[0m] Obtained version ${version} (${platform})`) + const modules = await fetch(`${API_URL}/modules/${branch}/versions.json?host_version=${version}&platform=${platform}`) + .then(res => res.json()) + console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${Object.keys(modules).length} modules.`) + for(const module in modules){ + if(["discord_desktop_core"].includes(module))continue + const moduleVersion = modules[module] + console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${module} v${moduleVersion}.`) + const file = tmp.fileSync({ + prefix: module+"-", + postfix: ".zip" + }) + const res = await fetch(`${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`, { + redirect: "follow" + }) + if(res.status !== 200)throw new Error(`res.status !== 200. ${res.status} ${module} v${moduleVersion} ${branch} ${platform}`) + await streamPipeline(res.body, fs.createWriteStream(file.name)) + /** + * @type {yauzl.ZipFile} + */ + const zipFile = await new Promise((resolve, reject) => { + yauzl.open(file.name, {lazyEntries: true, autoClose: true}, (err, zip) => { + if(err)return reject(err) + resolve(zip) + }) + }) + const modulePath = join(__dirname, "modules", module) + const exists = fs.existsSync(modulePath) + let hasNode = false + let hasBinaries = ["discord_hook", "discord_modules"].includes(module) + let hasNativeDependencies = ["discord_voice", "discord_krisp"].includes(module) + if(!exists){ + console.warn(`[\x1b[33mWARN\x1b[0m] Downloading whole module because it doesn't exists in your files.`) + fs.mkdirSync(modulePath) + } + await new Promise((resolve) => { + zipFile.readEntry() + zipFile.on("entry", function(entry) { + const fileName = entry.fileName.toString("utf8") + if(fileName.endsWith("/")){ + const folderPath = join(modulePath, fileName) + if(!fs.existsSync(folderPath))fs.mkdirSync(folderPath, {recursive: true}) + zipFile.readEntry(); + }else{ + if(!exists){ + const filePath = join(modulePath, fileName) + zipFile.openReadStream(entry, function(err, readStream) { + if (err) throw err; + readStream.on("end", function() { + let content = fs.readFileSync(filePath, "utf-8") + if(content.includes(module+".node")){ + content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`) + fs.writeFileSync(filePath, content) + } + zipFile.readEntry(); + }); + readStream.pipe(fs.createWriteStream(filePath)); + }); + }else{ + let filePath = join(modulePath, fileName) + if(filePath.endsWith(".asar"))return zipFile.readEntry(); + if(fileName.endsWith(".node")){ + hasNode = true + filePath = filePath.replace(".node", "_"+platformsNormalized[platform]+".node") + extractFile() + }else if(join(filePath, "..") === modulePath && filePath.endsWith(".js")){ + if(patchedJS.includes(filePath))return zipFile.readEntry(); + patchedJS.push(filePath) + extractFile(() => { + setTimeout(() => { + let content = fs.readFileSync(filePath, "utf-8") + if(content.includes(module+".node")){ + content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`) + fs.writeFileSync(filePath, content) + } + }, 10); + }) + }else if(hasBinaries && ["exe", "dll"].map(e => { + return filePath.endsWith("."+e) + }).includes(true)){ // binaries + extractFile() + }else if(hasNativeDependencies && ["so.4", "dll", "dylib", "thw"].map(e => { + return filePath.endsWith("."+e) + }).includes(true)){ + extractFile() + }else{ + zipFile.readEntry(); + } + async function extractFile(onEnd){ + const dir = dirname(filePath) + if(!fs.existsSync(dir))await fs.promises.mkdir(dir, {recursive: true}) + zipFile.openReadStream(entry, function(err, readStream) { + if(err)throw err; + readStream.on("end", function() { + if(onEnd)onEnd() + zipFile.readEntry(); + }).on("error", (err) => { + zipFile.close() + console.error(err) + console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`) + console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) + resolve() + zipFile.readEntry(); + manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) + }) + try{ + readStream.pipe(fs.createWriteStream(filePath)); + }catch(err){ + zipFile.close() + console.error(err) + console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`) + console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) + resolve() + zipFile.readEntry(); + manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) + } + }); + } + } + } + }).on("end", () => resolve()) + .on("error", (err) => { + zipFile.close() + console.error(err) + console.error(`\x1b[31mERROR\x1b[0m] Skipping files because of error.`) + console.error(`\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) + manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) + }) + }) + if(hasNode){ + console.log(`[\x1b[32mINFO\x1b[0m] .node files are now in ${module}.`) + } + } + } + if(manuallyDownloads.length > 0){ + console.error(`[\x1b[31mERROR\x1b[0m] Couldn't download some modules. Manual links:`) + manuallyDownloads.forEach(console.log) + } })() \ No newline at end of file diff --git a/installSubModules.js b/installSubModules.js index b77d44d..46d4879 100644 --- a/installSubModules.js +++ b/installSubModules.js @@ -1,54 +1,54 @@ -const child_process = require("child_process") -const fs = require("fs") -const path = require("path") - -const MODULES_DIRNAME = path.join(__dirname, "modules") - -fs.readdirSync(MODULES_DIRNAME, {withFileTypes: true}) -.forEach(e => { - if(!e.isDirectory())return - const MODULE_DIRNAME = path.join(MODULES_DIRNAME, e.name) - if(!fs.existsSync(path.join(MODULE_DIRNAME, "package.json")))return - if(e.name === "discord_spellcheck")return - - console.log(`Installing modules in ${e.name}.`) - - child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { - cwd: MODULE_DIRNAME, - env: process.env, - stdio: "inherit" - }).on("error", (err) => { - console.error(err) - process.exit(1) - }) -}) - - -const MODULE_DIRNAME = path.join(__dirname, "modules", "discord_desktop_core", "core") -const BETTERDISCORD_DIRNAME = path.join(__dirname, "BetterDiscordApp") -const DISCORDJS_DIRNAME = path.join(__dirname, "DiscordJS") - -child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { - cwd: MODULE_DIRNAME, - env: process.env, - stdio: "inherit" -}).on("error", (err) => { - console.error(err) - process.exit(1) -}) - -child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { - cwd: BETTERDISCORD_DIRNAME, - env: process.env, - stdio: "inherit" -}).on("error", (err) => { - console.error(err) - process.exit(1) -}) - - -child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { - cwd: DISCORDJS_DIRNAME, - env: process.env, - stdio: "inherit" +const child_process = require("child_process") +const fs = require("fs") +const path = require("path") + +const MODULES_DIRNAME = path.join(__dirname, "modules") + +fs.readdirSync(MODULES_DIRNAME, {withFileTypes: true}) +.forEach(e => { + if(!e.isDirectory())return + const MODULE_DIRNAME = path.join(MODULES_DIRNAME, e.name) + if(!fs.existsSync(path.join(MODULE_DIRNAME, "package.json")))return + if(e.name === "discord_spellcheck")return + + console.log(`Installing modules in ${e.name}.`) + + child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { + cwd: MODULE_DIRNAME, + env: process.env, + stdio: "inherit" + }).on("error", (err) => { + console.error(err) + process.exit(1) + }) +}) + + +const MODULE_DIRNAME = path.join(__dirname, "modules", "discord_desktop_core", "core") +const BETTERDISCORD_DIRNAME = path.join(__dirname, "BetterDiscordApp") +const DISCORDJS_DIRNAME = path.join(__dirname, "DiscordJS") + +child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { + cwd: MODULE_DIRNAME, + env: process.env, + stdio: "inherit" +}).on("error", (err) => { + console.error(err) + process.exit(1) +}) + +child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { + cwd: BETTERDISCORD_DIRNAME, + env: process.env, + stdio: "inherit" +}).on("error", (err) => { + console.error(err) + process.exit(1) +}) + + +child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { + cwd: DISCORDJS_DIRNAME, + env: process.env, + stdio: "inherit" }) \ No newline at end of file diff --git a/modules/discord_cloudsync/index.js b/modules/discord_cloudsync/index.js index 218dd09..09ca918 100644 --- a/modules/discord_cloudsync/index.js +++ b/modules/discord_cloudsync/index.js @@ -1,36 +1,36 @@ -const EventEmitter = require('events'); -const {CloudSync: CloudSyncNative} = require('./discord_cloudsync_'+process.platform+'.node'); - -function makeCallback(resolve, reject) { - return (err, result) => { - if (err != null && err !== '') { - reject(new Error(JSON.parse(err))); - } else { - resolve(result != null && result !== '' ? JSON.parse(result) : null); - } - }; -} - -class CloudSync extends EventEmitter { - constructor() { - super(); - - this._cloudSync = new CloudSyncNative(state => this.emit('state', JSON.parse(state))); - } - - sync(id, config) { - return new Promise((resolve, reject) => - this._cloudSync.command(JSON.stringify({type: 'SYNC', id, config}), makeCallback(resolve, reject)) - ); - } -} - -function cloudSyncConstructor() { - const instance = new CloudSync(); - return { - on: instance.on.bind(instance), - sync: instance.sync.bind(instance), - }; -} - -module.exports = cloudSyncConstructor; +const EventEmitter = require('events'); +const {CloudSync: CloudSyncNative} = require('./discord_cloudsync_'+process.platform+'.node'); + +function makeCallback(resolve, reject) { + return (err, result) => { + if (err != null && err !== '') { + reject(new Error(JSON.parse(err))); + } else { + resolve(result != null && result !== '' ? JSON.parse(result) : null); + } + }; +} + +class CloudSync extends EventEmitter { + constructor() { + super(); + + this._cloudSync = new CloudSyncNative(state => this.emit('state', JSON.parse(state))); + } + + sync(id, config) { + return new Promise((resolve, reject) => + this._cloudSync.command(JSON.stringify({type: 'SYNC', id, config}), makeCallback(resolve, reject)) + ); + } +} + +function cloudSyncConstructor() { + const instance = new CloudSync(); + return { + on: instance.on.bind(instance), + sync: instance.sync.bind(instance), + }; +} + +module.exports = cloudSyncConstructor; diff --git a/modules/discord_cloudsync/manifest.json b/modules/discord_cloudsync/manifest.json index 013f4cd..d3d1741 100644 --- a/modules/discord_cloudsync/manifest.json +++ b/modules/discord_cloudsync/manifest.json @@ -1,7 +1,7 @@ -{ - "files": [ - "discord_cloudsync.node", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_cloudsync.node", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/BetterDiscord/betterdiscord.js b/modules/discord_desktop_core/core/app/BetterDiscord/betterdiscord.js index 5cfa579..a796162 100644 --- a/modules/discord_desktop_core/core/app/BetterDiscord/betterdiscord.js +++ b/modules/discord_desktop_core/core/app/BetterDiscord/betterdiscord.js @@ -1,12 +1,12 @@ -const electron = require("electron") -const { useShim } = require("./patchNotifications"); -const appSettings = electron.remote.getGlobal("appSettings") - -module.exports = { - NotificationsUseShim: (value) => { - if(![true, false].includes(value))return - appSettings.set("DEFAULT_NOTIFICATIONS", !value) - appSettings.save() - useShim(value) - } +const electron = require("electron") +const { useShim } = require("./patchNotifications"); +const appSettings = electron.remote.getGlobal("appSettings") + +module.exports = { + NotificationsUseShim: (value) => { + if(![true, false].includes(value))return + appSettings.set("DEFAULT_NOTIFICATIONS", !value) + appSettings.save() + useShim(value) + } } \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/BetterDiscord/index.js b/modules/discord_desktop_core/core/app/BetterDiscord/index.js index d3b5dc2..212632e 100644 --- a/modules/discord_desktop_core/core/app/BetterDiscord/index.js +++ b/modules/discord_desktop_core/core/app/BetterDiscord/index.js @@ -1,1292 +1,1292 @@ -const ModuleLoader = require("./loaders/modules") -const { EventEmitter } = require("events") -const Logger = require("./Logger") -const fs = require("fs") -const path = require("path") -const electron = require("electron") -const fetch = require("node-fetch").default -const uuid = require("uuid/v4") - -const isPackaged = __filename.includes("app.asar") - -const events = exports.events = new EventEmitter() -const logger = exports.logger = new Logger("Lightcord") -const appSettings = electron.remote.getGlobal("appSettings") - -let hasInit = false -let tries = 0 - -const browserWindow = electron.remote.getCurrentWindow() -const UserAgent = electron.ipcRenderer.sendSync("LIGHTCORD_GET_USER_AGENT").replace(/lightcord\/[^ ]+/g, "discord/"+require("../discord_native/renderer/app").getVersion()) -browserWindow.webContents.userAgent = UserAgent - -exports.init = function(){ - if(hasInit == true){ - console.warn(new Error("Lightcord has already inited.")) - return - } - formatLogger.log("The app is", isPackaged ? "packaged." : "not packaged.") - - hasInit = true - let readyInterval = setInterval(()=>{ - events.emit("debug", `[INIT] try ${tries++} loading Lightcord`) - try{ - if(!global.webpackJsonp)return - if(!ModuleLoader.get(4))return - clearInterval(readyInterval) - privateInit() - .then(() => { - console.log("Finished loading Lightcord.") - }) - }catch(e){ - console.error(e) - } - }, 100) -} - -let hasPrivateInit = false - -async function privateInit(){ - if(!hasInit)return - if(hasPrivateInit)return - hasPrivateInit = true - installReactDevtools() - let cached = require.cache[path.join(__dirname, "loaders", "modules.js")] - if(cached){ - cached.exports = window.BDModules - } - - //disabling sentry - ModuleLoader.get(e => e.getCurrentHub)[0].getCurrentHub().getClient().getOptions().enabled = false - - // setting react in require cache - const React = ModuleLoader.get(e => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].find(k => !e[k]))[0] - window.React = React - - const ReactDOM = ModuleLoader.get(e => e.findDOMNode)[0] - window.ReactDOM = ReactDOM - - //stop here if betterdiscord is disabled. - if(electron.remote.process.argv.includes("--disable-betterdiscord")){ - let formComponents - let margins - class LightcordSettings extends React.Component { - render(){ - if(!formComponents)formComponents = ModuleLoader.get(e => e.FormSection)[0] - if(!margins)margins = ModuleLoader.get(e => e.marginTop60)[0] - - let button = require("./Button").default - - return React.createElement("div", {}, [ - React.createElement(formComponents.FormSection, { - className: "", - tag: "h2", - title: "Lightcord's Settings" - }, React.createElement(button, { - color: "yellow", - look: "ghost", - size: "medium", - hoverColor: "red", - onClick: () => { - console.log("Should relaunch") - electron.remote.app.relaunch({ - args: electron.remote.process.argv.slice(1).filter(e => e !== "--disable-betterdiscord") - }) - electron.remote.app.quit() - }, - wrapper: true - }, "Relaunch with BetterDiscord")) - ]) - } - } - - // fix notifications here - let dispatcher = ModuleLoader.get(m=>m.Dispatcher&&m.default&&m.default.dispatch)[0].default - dispatcher.subscribe("USER_SETTINGS_UPDATE", (data) => { - DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme) - }) - - let constants = ModuleLoader.get(m=>m.API_HOST)[0] - - // add menu to re enable BetterDiscord - constants.UserSettingsSections = Object.freeze(Object.assign({}, constants.UserSettingsSections, {LIGHTCORD: "Lightcord"})) - - ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections) - .then(settingModule => { - - let getPredicateSections = settingModule.default.prototype.getPredicateSections - settingModule.default.prototype.getPredicateSections = function(){ - let result = getPredicateSections.call(this, ...arguments) - if(result[1].section === "My Account"){ // user settings, not guild settings - let poped = [] - - poped.push(result.pop()) - poped.push(result.pop()) - poped.push(result.pop()) - poped.push(result.pop()) - - result.push({ - section: "HEADER", - label: "Lightcord" - }, { - section: constants.UserSettingsSections.LIGHTCORD, - label: "Lightcord", - element: LightcordSettings - }, { - section: "DIVIDER" - }) - - while(poped[0]){ - result.push(poped.pop()) - } - } - return result - } - }) - - return - } - - let createSoundOriginal = ModuleLoader.get((e) => e.createSound)[0].createSound - ModuleLoader.get((e) => e.createSound)[0].createSound = function(sound){ - let isCalling = sound === "call_ringing_beat" || sound === "call_ringing" - if(isCalling){ - let returned = createSoundOriginal.call(this, ...arguments) - Object.defineProperty(returned, "name", { - get(){ - return window.Lightcord.Settings.callRingingBeat ? "call_ringing_beat" : "call_ringing" - }, - set(data){ - console.log("Attempting to set call_ringing value. Canceling", data) - }, - configurable: false - }) - return returned - }else{ - return createSoundOriginal(...arguments) - } - } - - let constants = ModuleLoader.get(m=>m.API_HOST)[0] - let dispatcher = ModuleLoader.get(m=>m.Dispatcher&&m.default&&m.default.dispatch)[0].default - require(formatMinified(path.join(__dirname, "../../../../../BetterDiscordApp/dist/style{min}.css"))) - require("./lightcord.css") - - function getCurrentHypesquad(){ - let user = ModuleLoader.get(e => e.default && e.default.getCurrentUser)[0].default.getCurrentUser() - if(!user)return undefined - if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_1))return "1" - if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_2))return "2" - if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_3))return "3" - return undefined - } - - window.$ = window.jQuery = require("./jquery.min.js") - require("./ace.js") - - if(!fs.existsSync(BetterDiscordConfig.dataPath))fs.mkdirSync(BetterDiscordConfig.dataPath, {recursive: true}) - let pluginPath = path.join(BetterDiscordConfig.dataPath, "plugins") - let themePath = path.join(BetterDiscordConfig.dataPath, "themes") - console.log(`Plugins: ${pluginPath}\nThemes: ${themePath}`) - if(!fs.existsSync(pluginPath)){ - fs.mkdirSync(pluginPath, {recursive: true}) - - /** Downloads Util Plugins So the user don't have to install it manually */ - - /** ZeresPluginLibrary */ - const ZeresPluginLibraryPath = path.join(pluginPath, "0PluginLibrary.plugin.js") - fetch("https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js") - .then(async res => { - if(res.status !== 200)return - const content = await res.buffer() - fs.writeFileSync(ZeresPluginLibraryPath, content) - }) - - BetterDiscordConfig.haveInstalledDefault = true // Inform User about what we just did - } - if(!fs.existsSync(themePath)){ - fs.mkdirSync(themePath, {recursive: true}) - - /** Downloads Basic Themes to guide user and showcase features */ - - /** Discord Dark */ - const DarkDiscordPath = path.join(themePath, "DarkDiscord.theme.css") - fetch("https://raw.githubusercontent.com/hellbound1337/dark-discord/master/DarkDiscord.theme.css") - .then(async res => { - if(res.status !== 200)return - const content = await res.buffer() - fs.writeFileSync(DarkDiscordPath, content) - }) - - /** Glasscord Example */ - const GlasscordExamplePath = path.join(themePath, "glasscord_example.theme.css") - fetch("https://raw.githubusercontent.com/AryToNeX/Glasscord/master/extras/discord_example_theme/discord_example.theme.css") - .then(async res => { - if(res.status !== 200)return - const content = await res.buffer() - fs.writeFileSync(GlasscordExamplePath, content) - }) - - BetterDiscordConfig.haveInstalledDefault = true // Inform User about what we just did - } - - // setting Discord Internal Developer Mode for developement and test purposes. - let developerModule = ModuleLoader.get(e => e.default && typeof e.default === "object" && ("isDeveloper" in e.default))[0] - if(developerModule){ - Object.defineProperty(developerModule.default, "isDeveloper", { - get(){return !!window.Lightcord.Settings.devMode}, - set(data){return !!window.Lightcord.Settings.devMode} - }) - } - - /** - * @type {typeof import("../../../../../DiscordJS").default} - */ - let DiscordJS - try{ - DiscordJS = require("../../../../../DiscordJS").default - }catch(err){ - console.error(err) - DiscordJS = null - } - /*let Authorization = appSettings.get("LIGHTCORD_AUTH", false) - let shouldShowPrompt = Authorization === false - - if(typeof Authorization !== "string"){ - Authorization = null - appSettings.set("LIGHTCORD_AUTH", null) - appSettings.save() - }*/ - - let cloneNullProto = (obj) => { // recreate object without __proto__ - let o = Object.create(null) - Object.keys(obj).forEach(k => { - o[k] = obj[k] - }) - return o - } - - window.Lightcord = cloneNullProto({ - DiscordModules: cloneNullProto({ - dispatcher, - constants - }), - Settings: cloneNullProto({ - devMode: false, - callRingingBeat: true - }), - Api: cloneNullProto({/* - get Authorization(){ - return Authorization - }, - set Authorization(data){ - if(typeof data !== "string" && data !== null)return Authorization - appSettings.set("LIGHTCORD_AUTH", Authorization = data) - appSettings.save() - },*/ - Authorization: null, - ensureExported, - cloneNullProto - }), - BetterDiscord: cloneNullProto({ // Global BetterDiscord's exported modules - - }) - }) - - dispatcher.subscribe("USER_SETTINGS_UPDATE", (data) => { - DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme) - }) - - require(formatMinified("lightcordapi/js/main{min}.js")) - - /* - if(shouldShowPrompt){ - let onConn = (ev) => { - console.log(`Showing auth window.`, ev) - shouldShowPrompt = false - dispatcher.unsubscribe(constants.ActionTypes.CONNECTION_OPEN || "CONNECTION_OPEN", onConn) - - const options = { - width: 500, - height: 550, - backgroundColor: "#202225", - show: true, - resizable: false, - maximizable: false, - minimizable: false, - frame: false, - center: false, - webPreferences: { - nodeIntegration: false, - preload: path.join(__dirname, "auth", "preload.js"), - webviewTag: true - }, - parent: electron.remote.getCurrentWindow() - }; - options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2); - options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2); - - const authWindow = new electron.remote.BrowserWindow(options) - - authWindow.webContents.session.protocol.registerFileProtocol("lightcord", (req, callback) => { - const parsedURL = new URL("http://lightcord.xyz/"+req.url.split("://")[1]) - - let file - if(req.method !== "GET"){ - file = "404.html" - }else{ - if(parsedURL.pathname === "/index.html"){ - file = "index.html" - }else if(parsedURL.pathname === "/index.css"){ - file = "index.css" - }else if(parsedURL.pathname === "/login/callback"){ - authWindow.close() - console.log(parsedURL.searchParams) - Authorization = parsedURL.searchParams.get("auth") - authWindow = null - return - } - } - - if(!file){ - file = "404.html" - } - - callback(path.join(__dirname, "auth", file)) - }, (err) => { - if(err)console.error(err) - }) - - electron.remote.getCurrentWindow().webContents.on("devtools-reload-page", () => { - electron.remote.protocol.unregisterProtocol("lightcord") - }) - - authWindow.on("close", () => { - electron.remote.protocol.unregisterProtocol("lightcord") - }) - - authWindow.loadURL("lightcord://index.html") - } - dispatcher.subscribe(constants.ActionTypes.CONNECTION_OPEN || "CONNECTION_OPEN", onConn) - }*/ - - const BetterDiscord = window.BetterDiscord = window.mainCore = new(require(formatMinified("../../../../../BetterDiscordApp/dist/index{min}.js")).default)(BetterDiscordConfig, require("./betterdiscord")) - - const Utils = window.Lightcord.BetterDiscord.Utils - const DOMTools = window.Lightcord.BetterDiscord.DOM - - let isBot = false - dispatcher.subscribe("LOGOUT", () => { - isBot = false - }) - ;(async function(){ - const gatewayModule = await ensureExported(e => e.default && e.default.prototype && e.default.prototype._handleDispatch) - if(!gatewayModule)return - let _handleDispatch = gatewayModule.default.prototype._handleDispatch - gatewayModule.default.prototype._handleDispatch = function(data, event, props){ - if(event === "READY"){ - console.log(...arguments) - if(false){ - dispatcher.dispatch({ - type: "LOGOUT" - }) - BdApi.showToast(data.user.username+"#"+data.user.discriminator+": This account is blacklisted from Lightcord.", { - type: "error", - timeout: 10000 - }) - appSettings.get("­", true) - appSettings.save() - return - } - isBot = data.user.bot - if(data.user.bot){ - logger.log(`Logged in as a bot, spoofing user...`) - data.user.bot = false - data.user.premium = true - data.user.premium_type = 1 - data.user.email = data.user.email || uuid()+"@lightcord.xyz" // filler email, not a real one - data.experiments = data.experiments || [] - data.guild_experiments = data.guild_experiments || []; - data.connected_accounts = data.connected_accounts || []; - data.relationships = data.relationships || []; - data.notes = data.notes || {}; - data.user_feed_settings = data.user_feed_settings || []; - data.analytics_tokens = data.analytics_tokens || []; - data.analytics_token = data.analytics_token || "" - data.private_channels = data.private_channels || []; - data.read_state = data.read_state || { - entries: [], - partial: false, - version: 19438 - } - data.consents = data.consents || { - personalization: false - } - data.tutorial = data.tutorial || null - data.user_settings = Object.assign(data.user_settings || {}, { - afk_timeout: 600, - allow_accessibility_detection: false, - animate_emoji: true, - contact_sync_enabled: false, - convert_emoticons: true, - custom_status: null, - default_guilds_restricted: false, - detect_platform_accounts: false, - developer_mode: true, - disable_games_tab: true, - enable_tts_command: true, - explicit_content_filter: 0, - friend_source_flags: { - all: false, - mutual_friends: false, - mutual_guilds: false - }, - gif_auto_play: true, - guild_folders: [], - guild_positions: [], - inline_attachment_media: true, - inline_embed_media: true, - message_display_compact: false, - native_phone_integration_enabled: false, - render_embeds: true, - render_reactions: true, - restricted_guilds: [], - show_current_game: false, - stream_notifications_enabled: false - }, data.user_settings || {}) - data.user_guild_settings = data.user_guild_settings || { - entries: [], - version: 0, - partial: false - } - data.friend_suggestion_count = data.friend_suggestion_count || 0 - data.presences = data.presences || [] - browserWindow.webContents.userAgent = `DiscordBot (https://github.com/lightcord/lightcord, v${electron.remote.getGlobal("BuildInfo").version})` - }else{ - browserWindow.webContents.userAgent = UserAgent - logger.log(`Logged in as an user. Skipping user spoofing.`) - } - } - let returnValue = _handleDispatch.call(this, ...arguments) - if(event === "READY" && DiscordJS){ - try{ - DiscordJS.client.emit("self.ready", data) - }catch(e){ - console.error("[DiscordJS Error]", e) - } - } - return returnValue - } - dispatcher.subscribe("LOGOUT", () => { - isBot = false - }) - function cancelGatewayPrototype(methodName){ - if(gatewayModule.default.prototype[methodName]){ - const original = gatewayModule.default.prototype[methodName] - gatewayModule.default.prototype[methodName] = function(){ - if(!isBot)return original.call(this, ...arguments) - } - }else{ - logger.warn(`Couldn't find ${methodName} on gateway.`) - } - } - cancelGatewayPrototype("updateGuildSubscriptions") - cancelGatewayPrototype("callConnect") - cancelGatewayPrototype("lobbyConnect") - cancelGatewayPrototype("lobbyDisconnect") - cancelGatewayPrototype("lobbyVoiceStatesUpdate") - cancelGatewayPrototype("streamCreate") - cancelGatewayPrototype("streamWatch") - cancelGatewayPrototype("streamPing") - cancelGatewayPrototype("streamDelete") - cancelGatewayPrototype("streamSetPaused") - - const requestGuildMembers = gatewayModule.default.prototype.requestGuildMembers - gatewayModule.default.prototype.requestGuildMembers = function(){ // TODO: requestGuildMembers patch for bots. - /*if(!isBot)*/return requestGuildMembers.call(this, ...arguments) - console.log(arguments) - } - - const hasUnreadModules = BDModules.get(e => e.default && e.default.hasUnread) - hasUnreadModules.forEach((mod) => { - const hasUnread = mod.default.hasUnread - mod.default.hasUnread = function(){ - if(isBot)return false - return hasUnread.call(this, ...arguments) - } - for (const fName of ['ack']) { - console.log(fName, mod[fName]) - if(!mod || !mod[fName]){ - logger.warn("Couldn't find prop "+fName+" in ackmodule1") - continue - } - let original = mod[fName] - mod[fName] = function(){ - if(!isBot)return original.call(this, ...arguments) - } - } - if(mod.getAckTimestamp){ - let getAckTimestamp = mod.getAckTimestamp - mod.getAckTimestamp = function(){ - if(!isBot)return getAckTimestamp.call(this, ...arguments) - return NaN - } - } - }) - const ackModule = BDModules.get(e => e.ackCategory)[0] - if(ackModule){ - for (const fName of ['ack', 'ackCategory', 'localAck', 'ackGuild']) { - console.log(fName, ackModule[fName]) - if(!ackModule || !ackModule[fName]){ - logger.warn("Couldn't find prop "+fName+" in ackmodule2") - continue - } - let original = ackModule[fName] - ackModule[fName] = function(){ - if(!isBot)return original.call(this, ...arguments) - } - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const getTokenModule = ModuleLoader.get(e => e.default && e.default.getToken)[0] - if(getTokenModule){ - const getToken = getTokenModule.default.getToken - getTokenModule.default.getToken = function(){ - const token = getToken.call(this) - if(!token)return token - if(isBot)return token.startsWith("Bot ") ? token : "Bot " + token - return token - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const relationshipsModule = BDModules.get(e => e.default && e.default.fetchRelationships)[0] - if(relationshipsModule){ - const fetchRelationships = relationshipsModule.default.fetchRelationships - relationshipsModule.default.fetchRelationships = function(){ - if(!isBot)return fetchRelationships.call(this, ...arguments) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.LOAD_RELATIONSHIPS_SUCCESS, - relationships: [] - }) - }) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const consentModule = BDModules.get(e => e.fetchConsents)[0] - if(consentModule){ - const fetchConsents = consentModule.fetchConsents - consentModule.fetchConsents = function(){ - if(!isBot)return fetchConsents.call(this, ...arguments) - setImmediate(()=>{ - dispatcher.dispatch({ - type: constants.ActionTypes.UPDATE_CONSENTS, - consents: { - personalization: false, - usage_statistics: false - } - }) - }) - } - const setConsents = consentModule.setConsents - consentModule.setConsents = function(){ - if(!isBot)return setConsents.call(this, ...arguments) - return Promise.reject(new Error("Lightcord bot emulation cannot change this setting.")) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const harvestModule = BDModules.get(e => e.getHarvestStatus)[0] - if(harvestModule){ - const getHarvestStatus = harvestModule.getHarvestStatus - harvestModule.getHarvestStatus = function(){ - if(!isBot)return getHarvestStatus.call(this, ...arguments) - return Promise.resolve({ - requestingHarvest: false, - currentHarvestRequest: null - }) - } - const requestHarvest = harvestModule.requestHarvest - harvestModule.requestHarvest = function(){ - if(!isBot)return requestHarvest.call(this, ...arguments) - return Promise.reject() - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const harvestDisabledModule = BDModules.get(e => e.getSanitizedRestrictedGuilds)[0] - if(harvestDisabledModule){ - const harvestDisabled = harvestDisabledModule.harvestDisabled - harvestDisabledModule.harvestDisabled = function(){ - if(!isBot)return harvestDisabled.call(this, ...arguments) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const settingModule = BDModules.get(e => e.default && e.default.updateRemoteSettings)[0] - if(settingModule){ - const updateRemoteSettings = settingModule.default.updateRemoteSettings - settingModule.default.updateRemoteSettings = function(){ - if(isBot)return Promise.resolve() - return updateRemoteSettings.call(this, ...arguments) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const oauth2Module = BDModules.get(e => e.default && Object.keys(e.default).length === 2 && e.default.fetch && e.default.delete)[0] - if(oauth2Module){ - const fetch = oauth2Module.default.fetch - oauth2Module.default.fetch = function(){ - if(!isBot)return fetch.call(this, ...arguments) - setImmediate(()=>{ - dispatcher.dispatch({ - type: constants.ActionTypes.USER_AUTHORIZED_APPS_UPDATE, - apps: [] - }) - }) - } - const deleteFunc = oauth2Module.delete - oauth2Module.delete = function(){ - if(!isBot)return deleteFunc.call(this, ...arguments) - oauth2Module.fetch() - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const paymentModule = BDModules.get(e => e.fetchPaymentSources)[0] - if(paymentModule){ - const fetchPaymentSources = paymentModule.fetchPaymentSources - paymentModule.fetchPaymentSources = function(){ - if(!isBot)return fetchPaymentSources.call(this, ...arguments) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.BILLING_PAYMENT_SOURCES_FETCH_START - }) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.BILLING_PAYMENT_SOURCES_FETCH_SUCCESS, - paymentSources: [] - }) - }) - }) - } - const fetchPayments = paymentModule.fetchPayments - paymentModule.fetchPayments = function(){ - if(!isBot)return fetchPayments.call(this, ...arguments) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.BILLING_PAYMENTS_FETCH_START - }) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.BILLING_PAYMENTS_FETCH_SUCCESS, - payments: [] - }) - }) - }) - } - const fetchSubscriptions = paymentModule.fetchSubscriptions - paymentModule.fetchSubscriptions = function(){ - if(!isBot)return fetchSubscriptions.call(this, ...arguments) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.BILLING_SUBSCRIPTION_FETCH_START - }) - setImmediate(() => { - const subs = [ - { - "id": "123456789", - "type": 1, - "created_at": "2020-06-00T00:00:00.000000", - "canceled_at": null, - "current_period_start": "2020-06-00:00:00.000000", - "current_period_end": "2100-06-00:00:00.000000", - "status": 1, - "payment_source_id": null, - "payment_gateway": null, - "payment_gateway_plan_id": "premium_year", - "plan_id": "511651860671627264", - "items": [ - { - "id": "123456789", - "plan_id": "511651860671627264", - "quantity": 1 - } - ], - "currency": "usd" - } - ] - resolve({ - body: subs - }) - dispatcher.dispatch({ - type: constants.ActionTypes.BILLING_SUBSCRIPTION_FETCH_SUCCESS, - subscriptions: subs - }) - }) - }) - let resolve - return new Promise((res) => (resolve = res)) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const markServerReadShortcut = BDModules.get(e => e.MARK_SERVER_READ)[0] - if(markServerReadShortcut){ - let action = markServerReadShortcut.MARK_SERVER_READ.action - markServerReadShortcut.MARK_SERVER_READ.action = function(){ - if(isBot)return - return action.call(this, ...arguments) - } - markServerReadShortcut.default && markServerReadShortcut.default.MARK_SERVER_READ && (markServerReadShortcut.default.MARK_SERVER_READ.action = markServerReadShortcut.MARK_SERVER_READ.action) - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const applicationStatisticModule = BDModules.get(e => e.fetchActivityStatistics)[0] - if(applicationStatisticModule){ - const fetchActivityStatistics = applicationStatisticModule.fetchActivityStatistics - applicationStatisticModule.fetchActivityStatistics = function(){ - if(!isBot)return fetchActivityStatistics.call(this, ...arguments) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.USER_ACTIVITY_STATISTICS_FETCH_SUCCESS, - statistics: [] - }) - }) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const subsInvoiceModule = BDModules.get(e => e.fetchSubscriptionInvoicePreview)[0] - if(subsInvoiceModule){ - function adapt(data){ - return { - id: data.id, - invoiceItems: data.invoice_items.map(function(e) { - return { - id: e.id, - subscriptionPlanId: e.subscription_plan_id, - subscriptionPlanPrice: e.subscription_plan_price, - amount: e.amount, - quantity: e.quantity, - discounts: e.discounts - } - }), - total: data.total, - subtotal: data.subtotal, - currency: data.currency, - tax: data.tax, - taxInclusive: data.tax_inclusive, - subscriptionPeriodStart: new Date(data.subscription_period_start), - subscriptionPeriodEnd: new Date(data.subscription_period_end) - } - } - const fetchSubscriptionInvoicePreview = subsInvoiceModule.fetchSubscriptionInvoicePreview - subsInvoiceModule.fetchSubscriptionInvoicePreview = function(){ - if(!isBot)return fetchSubscriptionInvoicePreview.call(this, ...arguments) - const arg1 = arguments[0] - if(!arg1 || !arg1.subscriptionId || arg1.subscriptionId === "123456789"){ - return new Promise((resolve, reject) => { - let data = adapt({ - "id": "123456789", - "invoice_items": [{ - "id": "123456789", - "amount": 0, - "discounts": [], - "subscription_plan_id": "511651860671627264", - "subscription_plan_price": 0, - "quantity": 1, - "proration": false - }], - "total": 100, - "subtotal": 100, - "currency": "usd", - "tax": 0, - "tax_inclusive": true, - "subscription_period_start": "2020-06-00:00:00.000000", - "subscription_period_end": "2100-06-00:00:00.000000" - }) - console.log(data) - resolve(data) - }) - } - return fetchSubscriptionInvoicePreview.call(this, ...arguments) - } - const useSubscriptionInvoice = subsInvoiceModule.useSubscriptionInvoice - subsInvoiceModule.useSubscriptionInvoice = function(){ - if(!isBot)return useSubscriptionInvoice.call(this, ...arguments) - return useSubscriptionInvoice.call(this, Object.assign(arguments[0], {preventFetch: true}), ...Array.from(arguments).slice(1)) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const subsModule = BDModules.get(e => e.fetchUserPremiumGuildSubscriptionSlots)[0] - if(subsModule){ - const fetchUserPremiumGuildSubscriptionSlots = subsModule.fetchUserPremiumGuildSubscriptionSlots - subsModule.fetchUserPremiumGuildSubscriptionSlots = function(){ - if(!isBot)return fetchUserPremiumGuildSubscriptionSlots.call(this, ...arguments) - setImmediate(()=>{ - dispatcher.dispatch({ - type: constants.ActionTypes.USER_PREMIUM_GUILD_SUBSCRIPTION_SLOTS_FETCH_SUCCESS, - userPremiumGuildSubscriptionSlots: [] - }) - }) - } - const fetchPremiumSubscriptionCooldown = subsModule.fetchPremiumSubscriptionCooldown - subsModule.fetchPremiumSubscriptionCooldown = function(){ - if(!isBot)return fetchPremiumSubscriptionCooldown.call(this, ...arguments) - return new Promise((resolve, reject) => { - reject(new Error("Lightcord bot emulation cannot use Server Boosts")) - }) - } - const fetchPremiumSubscriptions = subsModule.fetchPremiumSubscriptions - subsModule.fetchPremiumSubscriptions = function(){ - if(!isBot)return fetchPremiumSubscriptions.call(this, ...arguments) - return new Promise((resolve, reject) => { - reject(new Error("Lightcord bot emulation cannot use Server Boosts")) - }) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const entitlementsModule = BDModules.get(e => e.fetchUserEntitlementsForApplication)[0] - if(entitlementsModule){ - const fetchUserEntitlementsForApplication = entitlementsModule.fetchUserEntitlementsForApplication - entitlementsModule.fetchUserEntitlementsForApplication = function(){ - if(!isBot)return fetchUserEntitlementsForApplication.call(this, ...arguments) - let resolve - setImmediate(()=>{ - dispatcher.dispatch({ - type: constants.ActionTypes.ENTITLEMENT_FETCH_APPLICATION_START, - applicationId: arguments[0] - }) - setImmediate(()=>{ - resolve([]) - dispatcher.dispatch({ - type: constants.ActionTypes.ENTITLEMENT_FETCH_APPLICATION_SUCCESS, - applicationId: arguments[0], - entitlements: [] - }) - }) - }) - return new Promise((res) => (resolve = res)) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const giftModule1 = BDModules.get(e => e.fetchGiftableEntitlements)[0] - if(giftModule1){ - const fetchGiftableEntitlements = giftModule1.fetchGiftableEntitlements - giftModule1.fetchGiftableEntitlements = function(){ - if(!isBot)return fetchGiftableEntitlements.call(this, ...arguments) - dispatcher.dispatch({ - type: constants.ActionTypes.ENTITLEMENTS_GIFTABLE_FETCH - }) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.ENTITLEMENTS_GIFTABLE_FETCH_SUCCESS, - entitlements: [] - }) - }) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const libraryModule = BDModules.get(e => e.fetchLibrary)[0] - if(libraryModule){ - const fetchLibrary = libraryModule.fetchLibrary - libraryModule.fetchLibrary = function(){ - if(!isBot)return fetchLibrary.call(this, ...arguments) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.LIBRARY_FETCH_SUCCESS, - libraryApplications: [] - }) - }) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const hypesquadModule = BDModules.get(e => e.default && e.default.joinHypeSquadOnline)[0] - if(hypesquadModule){ - const joinHypeSquadOnline = hypesquadModule.default.joinHypeSquadOnline - hypesquadModule.default.joinHypeSquadOnline = function(){ - if(!isBot)return joinHypeSquadOnline.call(this, ...arguments) - return Promise.reject(new Error("Lightcord bot emulation cannot join hypesquad.")) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const HouseSelectionModal = BDModules.get(e => e.default && e.default.displayName === "HouseSelectionModal")[0] - const RadioGroup = BDModules.get(e => e.default && e.default.displayName === "RadioGroup")[0] - - if(HouseSelectionModal && RadioGroup){ - const defaultFunc = HouseSelectionModal.default - HouseSelectionModal.default = function(){ - let returnValue = new defaultFunc(...arguments) - - let hypesquadValue = getCurrentHypesquad() || "3" - const renderHeaderCopy = returnValue.renderHeaderCopy - returnValue.renderHeaderCopy = function(){ - return "Hypesquad" - } - const renderPrimaryAction = returnValue.renderPrimaryAction - returnValue.renderPrimaryAction = function(){ - const renderValue = renderPrimaryAction.call(returnValue, ...arguments) - - if(!returnValue.state.hasSubmittedHouse){ - renderValue.props.children = "Submit" - }else{ - renderValue.props.children = "Close" - } - if(!isBot){ - renderValue.props.disabled = false - }else{ - renderValue.props.disabled = true - } - const onClick = renderValue.props.onClick - renderValue.props.onClick = (ev) => { - if(!returnValue.state.hasSubmittedHouse){ - returnValue.handleSubmitButtonClick.call(returnValue, ...arguments) - }else{ - onClick.call(this, ...arguments) - } - } - return renderValue - } - const getSelectedHouseID = returnValue.getSelectedHouseID - returnValue.getSelectedHouseID = function(){ - return "HOUSE_"+hypesquadValue - } - const renderContent = returnValue.renderContent - returnValue.renderContent = function(){ - if(!isBot){ - if(this.state.hasSubmittedHouse)return this.renderQuizResult(); - - let component = React.createElement(class RadioContainer extends React.PureComponent { - constructor(props){ - super(props) - } - - render(){ - return React.createElement("div", { - style: { - margin: "0 auto", - width: "75%" - } - }, React.createElement(RadioGroup.default, { - disabled: false, - value: hypesquadValue, - options: [ - { - value: "1", - name: "Bravery", - desc: "The Bravery house" - }, - { - value: "2", - name: "Brillance", - desc: "The Brillance house" - }, - { - value: "3", - name: "Balance", - desc: "The Balance house" - } - ], - onChange: (ev) => { - hypesquadValue = ev.value - this.forceUpdate() - } - })) - } - }, {}) - return component - }else{ - let component = React.createElement(class BotWarning extends React.PureComponent { - constructor(props){ - super(props) - } - - render(){ - return React.createElement("div", { - style: { - margin: "0 auto", - width: "75%" - } - }, [ - React.createElement("h2", { - style: { - color: "var(--text-normal)" - } - }, "Bots cannot use Hypesquad.") - ]) - } - }, {}) - return component - } - } - return returnValue - } - }else{ - logger.warn(new Error("Couldn't find module here"), HouseSelectionModal, RadioGroup) - } - const mentionModule = BDModules.get(e => e.default && e.default.fetchRecentMentions)[0] - if(mentionModule){ - const fetchRecentMentions = mentionModule.default.fetchRecentMentions - mentionModule.default.fetchRecentMentions = function(e, t, n, i, s){ - if(!isBot)return fetchRecentMentions.call(this, ...arguments) - if(!n)n = null - dispatcher.dirtyDispatch({ - type: constants.ActionTypes.LOAD_RECENT_MENTIONS, - guildId: n - }) - setImmediate(() => { - dispatcher.dispatch({ - type: constants.ActionTypes.LOAD_RECENT_MENTIONS_SUCCESS, - messages: [], - isAfter: null != e, - hasMoreAfter: false - }) - }) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const templateModule = BDModules.get(e => e.default && e.default.loadTemplatesForGuild)[0] - if(templateModule){ - const loadTemplatesForGuild = templateModule.default.loadTemplatesForGuild - templateModule.default.loadTemplatesForGuild = function(){ - if(!isBot)return loadTemplatesForGuild.call(this, ...arguments) - return Promise.reject(new Error("Lightcord bot emulation cannot use Guild Templates")) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const searchModule = BDModules.get(e => e.default && e.default.prototype && e.default.prototype.retryLater)[0] - if(searchModule){ - const fetch = searchModule.default.prototype.fetch - searchModule.default.prototype.fetch = function(e, t, n){ - if(!isBot)return fetch.call(this, ...arguments) - n(new Error("Lightcord bot emulation cannot search in guild.")) - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - const inviteModule = BDModules.get(e => e.default && e.default.acceptInvite)[0] - if(inviteModule){ - const acceptInvite = inviteModule.default.acceptInvite - inviteModule.default.acceptInvite = function(code, location, extraOptions){ - if(!isBot)return acceptInvite.call(this, ...arguments) - dispatcher.dispatch({ - type: "INVITE_ACCEPT_FAILURE", - code - }) - Utils.showToast("Lightcord Bot Emulation cannot join guilds.", {type: "error"}) - return Promise.reject("Lightcord Bot Emulation cannot join guilds.") - } - }else{ - logger.warn(new Error("Couldn't find module here")) - } - })().catch(console.error.bind(console, `%c[Error Bot shit]`, "color:red")) - - Utils.monkeyPatch(await ensureExported(e => e.default && e.default.displayName == "AuthBox"), "default", {after: (data) => { - const children = Utils.getNestedProp(data.returnValue, "props.children.props.children.props.children") - children.push(React.createElement(require("./tokenLogin").default, {})) - }}) - let [ - authBoxExpanded - ] = [ - BDModules.get(e => e.authBoxExpanded && typeof e.authBoxExpanded === "string")[0] - ] - DOMTools.addStyle("tokenLoginPatch", `.${authBoxExpanded ? Utils.removeDa(authBoxExpanded.authBoxExpanded) : "authBoxExpanded-2jqaBe"} { - width: 900px; -}`) - - await ensureGuildClasses() - BetterDiscord.init() - - events.emit("ready") -} - -function installReactDevtools(){ - let reactDevToolsPath = ""; - if (process.platform === "win32") reactDevToolsPath = path.resolve(process.env.LOCALAPPDATA, "Google/Chrome/User Data"); - else if (process.platform === "linux") reactDevToolsPath = path.resolve(process.env.HOME, ".config/google-chrome"); - else if (process.platform === "darwin") reactDevToolsPath = path.resolve(process.env.HOME, "Library/Application Support/Google/Chrome"); - else reactDevToolsPath = path.resolve(process.env.HOME, ".config/chromium"); - reactDevToolsPath = path.join(reactDevToolsPath, "Default", "Extensions", "fmkadmapgofadopljbjfkapdkoienihi") - if (fs.existsSync(reactDevToolsPath)) { - const versions = fs.readdirSync(reactDevToolsPath); - reactDevToolsPath = path.resolve(reactDevToolsPath, versions[versions.length - 1]); - } - if(fs.existsSync(reactDevToolsPath)){ - const webContents = electron.remote.getCurrentWebContents() - const BrowserWindow = electron.remote.BrowserWindow - setImmediate(() => webContents.on("devtools-opened", devToolsListener)); - if (webContents.isDevToolsOpened()) devToolsListener(); - - function devToolsListener(){ - logger.log(`Installing React Devtools`) - BrowserWindow.removeDevToolsExtension("React Developer Tools"); - const didInstall = BrowserWindow.addDevToolsExtension(reactDevToolsPath); - - if (didInstall) logger.log("React DevTools", "Successfully installed react devtools."); - else logger.log("React DevTools", "Couldn't find react devtools."); - } - }else{ - console.warn(new Error(`React Devtools could not be found.`)) - } -} - -require.extensions[".css"] = (m, filename) => { - let content = fs.readFileSync(filename, "binary") - let style = document.createElement("style") - style.id = btoa(filename) - style.innerHTML = content - document.head.appendChild(style) - m.exports = { - id: style.id, - remove(){ - return style.remove() - } - } - return m.exports -} - -let zlib = require("zlib") -let tmp = require("tmp") - -require.extensions[".jsbr"] = (m, filename) => { - if(!zlib)zlib = require("zlib") - if(!tmp)tmp = require("tmp") - let tmpFile = tmp.fileSync() - - fs.writeFileSync(tmpFile.name+".js", zlib.brotliDecompressSync(fs.readFileSync(filename))) - return require.extensions[".js"](m, tmpFile.name+".js") -} -require.extensions[".txt"] = (m, filename) => { - m.exports = fs.readFileSync(filename, "utf8") - return m.exports -} - -const LightcordBDFolder = path.join(electron.remote.app.getPath("appData"), "Lightcord_BD") - -const BetterDiscordConfig = window.BetterDiscordConfig = { - "branch": "lightcord", - dataPath: LightcordBDFolder+"/", - os: process.platform, - latestVersion: "0.3.4", - version: "0.3.4" -} - -function ensureGuildClasses(){ - return new Promise((resolve) => { - let classs = getGuildClasses() - if(classs && classs.wrapper)return resolve() - - let intergay = setInterval(() => { - classs = getGuildClasses() - if(classs && classs.wrapper){ - clearInterval(intergay) - resolve() - return - } - }, 200); - }) -} - -var ensureExported = global.ensureExported = function ensureExported(filter, maxTime = 500){ - let tried = 0 - return new Promise((resolve, reject) => { - let mod = ModuleLoader.get(filter)[0] - if(mod)return resolve(mod) - tried++ - - let interval = setInterval(() => { - if(tried > maxTime){ - clearInterval(interval) - reject(new Error("Could not find the module with the given filter.")) - return - } - mod = ModuleLoader.get(filter)[0] - if(mod){ - clearInterval(interval) - resolve(mod) - return - } - tried++ - }, 100); - }) -} -let Notifications = require("./patchNotifications") -Notifications.useShim(!appSettings.get("DEFAULT_NOTIFICATIONS", true)) - -function getGuildClasses() { - const guildsWrapper = ModuleLoader.get(e => e.wrapper && e.unreadMentionsBar)[0]; - const guilds = ModuleLoader.get(e => e.guildsError && e.selected)[0] - const pill = ModuleLoader.get(e => e.blobContainer)[0] - return Object.assign({}, guildsWrapper, guilds, pill); -} - -const originalResolve = path.resolve -const originalJoin = path.join - -const BetterDiscordFolder = function() { - if (process.env.injDir) return path.resolve(process.env.injDir); - switch (process.platform) { - case "win32": - return path.resolve(process.env.appdata, "BetterDiscord/"); - case "darwin": - return path.resolve(process.env.HOME, "Library/Preferences/", "BetterDiscord/"); - default: - return path.resolve(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : process.env.HOME + "/.config", "BetterDiscord/"); - } -}() - -path.resolve = (...args) => { // Patching BetterDiscord folder by Lightcord's BetterDiscord folder - let resp = originalResolve.call(path, ...args) - if(resp.startsWith(BetterDiscordFolder))resp = resp.replace(BetterDiscordFolder, LightcordBDFolder) - return resp -} -path.join = (...args) => { // Patching BetterDiscord folder by Lightcord's BetterDiscord folder - let resp = originalJoin.call(path, ...args) - if(resp.startsWith(BetterDiscordFolder))resp = resp.replace(BetterDiscordFolder, LightcordBDFolder) - return resp -} - -path.originalResolve = originalResolve - -let blacklist -function isBlacklisted(id){ - if(!blacklist)blacklist = require("./blacklist.txt").split(/[\n\r]+/g).map((line, index, lines) => { - let id = "" - let comment = "" - line.split("#").forEach((idOrComment, index, array) => { - idOrComment = idOrComment.trim() - - if(index === 0)id = idOrComment - else if(index === 1)comment = idOrComment - }) - return { - id, - comment - } - }) - if(blacklist.find(e => e.id === id))return true - return false -} - -const formatLogger = new Logger("RequireFormat") -function formatMinified(path){ - let result = path.replace("{min}", isPackaged ? ".min": "") - return result -} - - -window.ohgodohfuck = function(){ - let style=document.createElement("style");style.innerHTML=`html:after{content:"";position:absolute;top:0;left:0 ;width:100vw;height:100vh;background-image:url("https://media.giphy.com/media/l378vg4Pm9LGnmD6M/giphy.gif");background-size:cover;background-position:center;background-color:transparent !important;opacity:0.9;mix-blend-mode:hue;z-index:999999999999;pointer-events:none}@keyframes ohgodohfuck{from{transform:rotateZ(0deg)}to{transform:rotateZ(360deg)}}#app-mount{animation:ohgodohfuck 5s infinite alternate}`;document.body.append(style);setTimeout(()=>document.body.removeChild(style),5000); +const ModuleLoader = require("./loaders/modules") +const { EventEmitter } = require("events") +const Logger = require("./Logger") +const fs = require("fs") +const path = require("path") +const electron = require("electron") +const fetch = require("node-fetch").default +const uuid = require("uuid/v4") + +const isPackaged = __filename.includes("app.asar") + +const events = exports.events = new EventEmitter() +const logger = exports.logger = new Logger("Lightcord") +const appSettings = electron.remote.getGlobal("appSettings") + +let hasInit = false +let tries = 0 + +const browserWindow = electron.remote.getCurrentWindow() +const UserAgent = electron.ipcRenderer.sendSync("LIGHTCORD_GET_USER_AGENT").replace(/lightcord\/[^ ]+/g, "discord/"+require("../discord_native/renderer/app").getVersion()) +browserWindow.webContents.userAgent = UserAgent + +exports.init = function(){ + if(hasInit == true){ + console.warn(new Error("Lightcord has already inited.")) + return + } + formatLogger.log("The app is", isPackaged ? "packaged." : "not packaged.") + + hasInit = true + let readyInterval = setInterval(()=>{ + events.emit("debug", `[INIT] try ${tries++} loading Lightcord`) + try{ + if(!global.webpackJsonp)return + if(!ModuleLoader.get(4))return + clearInterval(readyInterval) + privateInit() + .then(() => { + console.log("Finished loading Lightcord.") + }) + }catch(e){ + console.error(e) + } + }, 100) +} + +let hasPrivateInit = false + +async function privateInit(){ + if(!hasInit)return + if(hasPrivateInit)return + hasPrivateInit = true + installReactDevtools() + let cached = require.cache[path.join(__dirname, "loaders", "modules.js")] + if(cached){ + cached.exports = window.BDModules + } + + //disabling sentry + ModuleLoader.get(e => e.getCurrentHub)[0].getCurrentHub().getClient().getOptions().enabled = false + + // setting react in require cache + const React = ModuleLoader.get(e => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].find(k => !e[k]))[0] + window.React = React + + const ReactDOM = ModuleLoader.get(e => e.findDOMNode)[0] + window.ReactDOM = ReactDOM + + //stop here if betterdiscord is disabled. + if(electron.remote.process.argv.includes("--disable-betterdiscord")){ + let formComponents + let margins + class LightcordSettings extends React.Component { + render(){ + if(!formComponents)formComponents = ModuleLoader.get(e => e.FormSection)[0] + if(!margins)margins = ModuleLoader.get(e => e.marginTop60)[0] + + let button = require("./Button").default + + return React.createElement("div", {}, [ + React.createElement(formComponents.FormSection, { + className: "", + tag: "h2", + title: "Lightcord's Settings" + }, React.createElement(button, { + color: "yellow", + look: "ghost", + size: "medium", + hoverColor: "red", + onClick: () => { + console.log("Should relaunch") + electron.remote.app.relaunch({ + args: electron.remote.process.argv.slice(1).filter(e => e !== "--disable-betterdiscord") + }) + electron.remote.app.quit() + }, + wrapper: true + }, "Relaunch with BetterDiscord")) + ]) + } + } + + // fix notifications here + let dispatcher = ModuleLoader.get(m=>m.Dispatcher&&m.default&&m.default.dispatch)[0].default + dispatcher.subscribe("USER_SETTINGS_UPDATE", (data) => { + DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme) + }) + + let constants = ModuleLoader.get(m=>m.API_HOST)[0] + + // add menu to re enable BetterDiscord + constants.UserSettingsSections = Object.freeze(Object.assign({}, constants.UserSettingsSections, {LIGHTCORD: "Lightcord"})) + + ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections) + .then(settingModule => { + + let getPredicateSections = settingModule.default.prototype.getPredicateSections + settingModule.default.prototype.getPredicateSections = function(){ + let result = getPredicateSections.call(this, ...arguments) + if(result[1].section === "My Account"){ // user settings, not guild settings + let poped = [] + + poped.push(result.pop()) + poped.push(result.pop()) + poped.push(result.pop()) + poped.push(result.pop()) + + result.push({ + section: "HEADER", + label: "Lightcord" + }, { + section: constants.UserSettingsSections.LIGHTCORD, + label: "Lightcord", + element: LightcordSettings + }, { + section: "DIVIDER" + }) + + while(poped[0]){ + result.push(poped.pop()) + } + } + return result + } + }) + + return + } + + let createSoundOriginal = ModuleLoader.get((e) => e.createSound)[0].createSound + ModuleLoader.get((e) => e.createSound)[0].createSound = function(sound){ + let isCalling = sound === "call_ringing_beat" || sound === "call_ringing" + if(isCalling){ + let returned = createSoundOriginal.call(this, ...arguments) + Object.defineProperty(returned, "name", { + get(){ + return window.Lightcord.Settings.callRingingBeat ? "call_ringing_beat" : "call_ringing" + }, + set(data){ + console.log("Attempting to set call_ringing value. Canceling", data) + }, + configurable: false + }) + return returned + }else{ + return createSoundOriginal(...arguments) + } + } + + let constants = ModuleLoader.get(m=>m.API_HOST)[0] + let dispatcher = ModuleLoader.get(m=>m.Dispatcher&&m.default&&m.default.dispatch)[0].default + require(formatMinified(path.join(__dirname, "../../../../../BetterDiscordApp/dist/style{min}.css"))) + require("./lightcord.css") + + function getCurrentHypesquad(){ + let user = ModuleLoader.get(e => e.default && e.default.getCurrentUser)[0].default.getCurrentUser() + if(!user)return undefined + if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_1))return "1" + if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_2))return "2" + if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_3))return "3" + return undefined + } + + window.$ = window.jQuery = require("./jquery.min.js") + require("./ace.js") + + if(!fs.existsSync(BetterDiscordConfig.dataPath))fs.mkdirSync(BetterDiscordConfig.dataPath, {recursive: true}) + let pluginPath = path.join(BetterDiscordConfig.dataPath, "plugins") + let themePath = path.join(BetterDiscordConfig.dataPath, "themes") + console.log(`Plugins: ${pluginPath}\nThemes: ${themePath}`) + if(!fs.existsSync(pluginPath)){ + fs.mkdirSync(pluginPath, {recursive: true}) + + /** Downloads Util Plugins So the user don't have to install it manually */ + + /** ZeresPluginLibrary */ + const ZeresPluginLibraryPath = path.join(pluginPath, "0PluginLibrary.plugin.js") + fetch("https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js") + .then(async res => { + if(res.status !== 200)return + const content = await res.buffer() + fs.writeFileSync(ZeresPluginLibraryPath, content) + }) + + BetterDiscordConfig.haveInstalledDefault = true // Inform User about what we just did + } + if(!fs.existsSync(themePath)){ + fs.mkdirSync(themePath, {recursive: true}) + + /** Downloads Basic Themes to guide user and showcase features */ + + /** Discord Dark */ + const DarkDiscordPath = path.join(themePath, "DarkDiscord.theme.css") + fetch("https://raw.githubusercontent.com/hellbound1337/dark-discord/master/DarkDiscord.theme.css") + .then(async res => { + if(res.status !== 200)return + const content = await res.buffer() + fs.writeFileSync(DarkDiscordPath, content) + }) + + /** Glasscord Example */ + const GlasscordExamplePath = path.join(themePath, "glasscord_example.theme.css") + fetch("https://raw.githubusercontent.com/AryToNeX/Glasscord/master/extras/discord_example_theme/discord_example.theme.css") + .then(async res => { + if(res.status !== 200)return + const content = await res.buffer() + fs.writeFileSync(GlasscordExamplePath, content) + }) + + BetterDiscordConfig.haveInstalledDefault = true // Inform User about what we just did + } + + // setting Discord Internal Developer Mode for developement and test purposes. + let developerModule = ModuleLoader.get(e => e.default && typeof e.default === "object" && ("isDeveloper" in e.default))[0] + if(developerModule){ + Object.defineProperty(developerModule.default, "isDeveloper", { + get(){return !!window.Lightcord.Settings.devMode}, + set(data){return !!window.Lightcord.Settings.devMode} + }) + } + + /** + * @type {typeof import("../../../../../DiscordJS").default} + */ + let DiscordJS + try{ + DiscordJS = require("../../../../../DiscordJS").default + }catch(err){ + console.error(err) + DiscordJS = null + } + /*let Authorization = appSettings.get("LIGHTCORD_AUTH", false) + let shouldShowPrompt = Authorization === false + + if(typeof Authorization !== "string"){ + Authorization = null + appSettings.set("LIGHTCORD_AUTH", null) + appSettings.save() + }*/ + + let cloneNullProto = (obj) => { // recreate object without __proto__ + let o = Object.create(null) + Object.keys(obj).forEach(k => { + o[k] = obj[k] + }) + return o + } + + window.Lightcord = cloneNullProto({ + DiscordModules: cloneNullProto({ + dispatcher, + constants + }), + Settings: cloneNullProto({ + devMode: false, + callRingingBeat: true + }), + Api: cloneNullProto({/* + get Authorization(){ + return Authorization + }, + set Authorization(data){ + if(typeof data !== "string" && data !== null)return Authorization + appSettings.set("LIGHTCORD_AUTH", Authorization = data) + appSettings.save() + },*/ + Authorization: null, + ensureExported, + cloneNullProto + }), + BetterDiscord: cloneNullProto({ // Global BetterDiscord's exported modules + + }) + }) + + dispatcher.subscribe("USER_SETTINGS_UPDATE", (data) => { + DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme) + }) + + require(formatMinified("lightcordapi/js/main{min}.js")) + + /* + if(shouldShowPrompt){ + let onConn = (ev) => { + console.log(`Showing auth window.`, ev) + shouldShowPrompt = false + dispatcher.unsubscribe(constants.ActionTypes.CONNECTION_OPEN || "CONNECTION_OPEN", onConn) + + const options = { + width: 500, + height: 550, + backgroundColor: "#202225", + show: true, + resizable: false, + maximizable: false, + minimizable: false, + frame: false, + center: false, + webPreferences: { + nodeIntegration: false, + preload: path.join(__dirname, "auth", "preload.js"), + webviewTag: true + }, + parent: electron.remote.getCurrentWindow() + }; + options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2); + options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2); + + const authWindow = new electron.remote.BrowserWindow(options) + + authWindow.webContents.session.protocol.registerFileProtocol("lightcord", (req, callback) => { + const parsedURL = new URL("http://lightcord.xyz/"+req.url.split("://")[1]) + + let file + if(req.method !== "GET"){ + file = "404.html" + }else{ + if(parsedURL.pathname === "/index.html"){ + file = "index.html" + }else if(parsedURL.pathname === "/index.css"){ + file = "index.css" + }else if(parsedURL.pathname === "/login/callback"){ + authWindow.close() + console.log(parsedURL.searchParams) + Authorization = parsedURL.searchParams.get("auth") + authWindow = null + return + } + } + + if(!file){ + file = "404.html" + } + + callback(path.join(__dirname, "auth", file)) + }, (err) => { + if(err)console.error(err) + }) + + electron.remote.getCurrentWindow().webContents.on("devtools-reload-page", () => { + electron.remote.protocol.unregisterProtocol("lightcord") + }) + + authWindow.on("close", () => { + electron.remote.protocol.unregisterProtocol("lightcord") + }) + + authWindow.loadURL("lightcord://index.html") + } + dispatcher.subscribe(constants.ActionTypes.CONNECTION_OPEN || "CONNECTION_OPEN", onConn) + }*/ + + const BetterDiscord = window.BetterDiscord = window.mainCore = new(require(formatMinified("../../../../../BetterDiscordApp/dist/index{min}.js")).default)(BetterDiscordConfig, require("./betterdiscord")) + + const Utils = window.Lightcord.BetterDiscord.Utils + const DOMTools = window.Lightcord.BetterDiscord.DOM + + let isBot = false + dispatcher.subscribe("LOGOUT", () => { + isBot = false + }) + ;(async function(){ + const gatewayModule = await ensureExported(e => e.default && e.default.prototype && e.default.prototype._handleDispatch) + if(!gatewayModule)return + let _handleDispatch = gatewayModule.default.prototype._handleDispatch + gatewayModule.default.prototype._handleDispatch = function(data, event, props){ + if(event === "READY"){ + console.log(...arguments) + if(false){ + dispatcher.dispatch({ + type: "LOGOUT" + }) + BdApi.showToast(data.user.username+"#"+data.user.discriminator+": This account is blacklisted from Lightcord.", { + type: "error", + timeout: 10000 + }) + appSettings.get("­", true) + appSettings.save() + return + } + isBot = data.user.bot + if(data.user.bot){ + logger.log(`Logged in as a bot, spoofing user...`) + data.user.bot = false + data.user.premium = true + data.user.premium_type = 1 + data.user.email = data.user.email || uuid()+"@lightcord.xyz" // filler email, not a real one + data.experiments = data.experiments || [] + data.guild_experiments = data.guild_experiments || []; + data.connected_accounts = data.connected_accounts || []; + data.relationships = data.relationships || []; + data.notes = data.notes || {}; + data.user_feed_settings = data.user_feed_settings || []; + data.analytics_tokens = data.analytics_tokens || []; + data.analytics_token = data.analytics_token || "" + data.private_channels = data.private_channels || []; + data.read_state = data.read_state || { + entries: [], + partial: false, + version: 19438 + } + data.consents = data.consents || { + personalization: false + } + data.tutorial = data.tutorial || null + data.user_settings = Object.assign(data.user_settings || {}, { + afk_timeout: 600, + allow_accessibility_detection: false, + animate_emoji: true, + contact_sync_enabled: false, + convert_emoticons: true, + custom_status: null, + default_guilds_restricted: false, + detect_platform_accounts: false, + developer_mode: true, + disable_games_tab: true, + enable_tts_command: true, + explicit_content_filter: 0, + friend_source_flags: { + all: false, + mutual_friends: false, + mutual_guilds: false + }, + gif_auto_play: true, + guild_folders: [], + guild_positions: [], + inline_attachment_media: true, + inline_embed_media: true, + message_display_compact: false, + native_phone_integration_enabled: false, + render_embeds: true, + render_reactions: true, + restricted_guilds: [], + show_current_game: false, + stream_notifications_enabled: false + }, data.user_settings || {}) + data.user_guild_settings = data.user_guild_settings || { + entries: [], + version: 0, + partial: false + } + data.friend_suggestion_count = data.friend_suggestion_count || 0 + data.presences = data.presences || [] + browserWindow.webContents.userAgent = `DiscordBot (https://github.com/lightcord/lightcord, v${electron.remote.getGlobal("BuildInfo").version})` + }else{ + browserWindow.webContents.userAgent = UserAgent + logger.log(`Logged in as an user. Skipping user spoofing.`) + } + } + let returnValue = _handleDispatch.call(this, ...arguments) + if(event === "READY" && DiscordJS){ + try{ + DiscordJS.client.emit("self.ready", data) + }catch(e){ + console.error("[DiscordJS Error]", e) + } + } + return returnValue + } + dispatcher.subscribe("LOGOUT", () => { + isBot = false + }) + function cancelGatewayPrototype(methodName){ + if(gatewayModule.default.prototype[methodName]){ + const original = gatewayModule.default.prototype[methodName] + gatewayModule.default.prototype[methodName] = function(){ + if(!isBot)return original.call(this, ...arguments) + } + }else{ + logger.warn(`Couldn't find ${methodName} on gateway.`) + } + } + cancelGatewayPrototype("updateGuildSubscriptions") + cancelGatewayPrototype("callConnect") + cancelGatewayPrototype("lobbyConnect") + cancelGatewayPrototype("lobbyDisconnect") + cancelGatewayPrototype("lobbyVoiceStatesUpdate") + cancelGatewayPrototype("streamCreate") + cancelGatewayPrototype("streamWatch") + cancelGatewayPrototype("streamPing") + cancelGatewayPrototype("streamDelete") + cancelGatewayPrototype("streamSetPaused") + + const requestGuildMembers = gatewayModule.default.prototype.requestGuildMembers + gatewayModule.default.prototype.requestGuildMembers = function(){ // TODO: requestGuildMembers patch for bots. + /*if(!isBot)*/return requestGuildMembers.call(this, ...arguments) + console.log(arguments) + } + + const hasUnreadModules = BDModules.get(e => e.default && e.default.hasUnread) + hasUnreadModules.forEach((mod) => { + const hasUnread = mod.default.hasUnread + mod.default.hasUnread = function(){ + if(isBot)return false + return hasUnread.call(this, ...arguments) + } + for (const fName of ['ack']) { + console.log(fName, mod[fName]) + if(!mod || !mod[fName]){ + logger.warn("Couldn't find prop "+fName+" in ackmodule1") + continue + } + let original = mod[fName] + mod[fName] = function(){ + if(!isBot)return original.call(this, ...arguments) + } + } + if(mod.getAckTimestamp){ + let getAckTimestamp = mod.getAckTimestamp + mod.getAckTimestamp = function(){ + if(!isBot)return getAckTimestamp.call(this, ...arguments) + return NaN + } + } + }) + const ackModule = BDModules.get(e => e.ackCategory)[0] + if(ackModule){ + for (const fName of ['ack', 'ackCategory', 'localAck', 'ackGuild']) { + console.log(fName, ackModule[fName]) + if(!ackModule || !ackModule[fName]){ + logger.warn("Couldn't find prop "+fName+" in ackmodule2") + continue + } + let original = ackModule[fName] + ackModule[fName] = function(){ + if(!isBot)return original.call(this, ...arguments) + } + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const getTokenModule = ModuleLoader.get(e => e.default && e.default.getToken)[0] + if(getTokenModule){ + const getToken = getTokenModule.default.getToken + getTokenModule.default.getToken = function(){ + const token = getToken.call(this) + if(!token)return token + if(isBot)return token.startsWith("Bot ") ? token : "Bot " + token + return token + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const relationshipsModule = BDModules.get(e => e.default && e.default.fetchRelationships)[0] + if(relationshipsModule){ + const fetchRelationships = relationshipsModule.default.fetchRelationships + relationshipsModule.default.fetchRelationships = function(){ + if(!isBot)return fetchRelationships.call(this, ...arguments) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.LOAD_RELATIONSHIPS_SUCCESS, + relationships: [] + }) + }) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const consentModule = BDModules.get(e => e.fetchConsents)[0] + if(consentModule){ + const fetchConsents = consentModule.fetchConsents + consentModule.fetchConsents = function(){ + if(!isBot)return fetchConsents.call(this, ...arguments) + setImmediate(()=>{ + dispatcher.dispatch({ + type: constants.ActionTypes.UPDATE_CONSENTS, + consents: { + personalization: false, + usage_statistics: false + } + }) + }) + } + const setConsents = consentModule.setConsents + consentModule.setConsents = function(){ + if(!isBot)return setConsents.call(this, ...arguments) + return Promise.reject(new Error("Lightcord bot emulation cannot change this setting.")) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const harvestModule = BDModules.get(e => e.getHarvestStatus)[0] + if(harvestModule){ + const getHarvestStatus = harvestModule.getHarvestStatus + harvestModule.getHarvestStatus = function(){ + if(!isBot)return getHarvestStatus.call(this, ...arguments) + return Promise.resolve({ + requestingHarvest: false, + currentHarvestRequest: null + }) + } + const requestHarvest = harvestModule.requestHarvest + harvestModule.requestHarvest = function(){ + if(!isBot)return requestHarvest.call(this, ...arguments) + return Promise.reject() + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const harvestDisabledModule = BDModules.get(e => e.getSanitizedRestrictedGuilds)[0] + if(harvestDisabledModule){ + const harvestDisabled = harvestDisabledModule.harvestDisabled + harvestDisabledModule.harvestDisabled = function(){ + if(!isBot)return harvestDisabled.call(this, ...arguments) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const settingModule = BDModules.get(e => e.default && e.default.updateRemoteSettings)[0] + if(settingModule){ + const updateRemoteSettings = settingModule.default.updateRemoteSettings + settingModule.default.updateRemoteSettings = function(){ + if(isBot)return Promise.resolve() + return updateRemoteSettings.call(this, ...arguments) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const oauth2Module = BDModules.get(e => e.default && Object.keys(e.default).length === 2 && e.default.fetch && e.default.delete)[0] + if(oauth2Module){ + const fetch = oauth2Module.default.fetch + oauth2Module.default.fetch = function(){ + if(!isBot)return fetch.call(this, ...arguments) + setImmediate(()=>{ + dispatcher.dispatch({ + type: constants.ActionTypes.USER_AUTHORIZED_APPS_UPDATE, + apps: [] + }) + }) + } + const deleteFunc = oauth2Module.delete + oauth2Module.delete = function(){ + if(!isBot)return deleteFunc.call(this, ...arguments) + oauth2Module.fetch() + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const paymentModule = BDModules.get(e => e.fetchPaymentSources)[0] + if(paymentModule){ + const fetchPaymentSources = paymentModule.fetchPaymentSources + paymentModule.fetchPaymentSources = function(){ + if(!isBot)return fetchPaymentSources.call(this, ...arguments) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.BILLING_PAYMENT_SOURCES_FETCH_START + }) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.BILLING_PAYMENT_SOURCES_FETCH_SUCCESS, + paymentSources: [] + }) + }) + }) + } + const fetchPayments = paymentModule.fetchPayments + paymentModule.fetchPayments = function(){ + if(!isBot)return fetchPayments.call(this, ...arguments) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.BILLING_PAYMENTS_FETCH_START + }) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.BILLING_PAYMENTS_FETCH_SUCCESS, + payments: [] + }) + }) + }) + } + const fetchSubscriptions = paymentModule.fetchSubscriptions + paymentModule.fetchSubscriptions = function(){ + if(!isBot)return fetchSubscriptions.call(this, ...arguments) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.BILLING_SUBSCRIPTION_FETCH_START + }) + setImmediate(() => { + const subs = [ + { + "id": "123456789", + "type": 1, + "created_at": "2020-06-00T00:00:00.000000", + "canceled_at": null, + "current_period_start": "2020-06-00:00:00.000000", + "current_period_end": "2100-06-00:00:00.000000", + "status": 1, + "payment_source_id": null, + "payment_gateway": null, + "payment_gateway_plan_id": "premium_year", + "plan_id": "511651860671627264", + "items": [ + { + "id": "123456789", + "plan_id": "511651860671627264", + "quantity": 1 + } + ], + "currency": "usd" + } + ] + resolve({ + body: subs + }) + dispatcher.dispatch({ + type: constants.ActionTypes.BILLING_SUBSCRIPTION_FETCH_SUCCESS, + subscriptions: subs + }) + }) + }) + let resolve + return new Promise((res) => (resolve = res)) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const markServerReadShortcut = BDModules.get(e => e.MARK_SERVER_READ)[0] + if(markServerReadShortcut){ + let action = markServerReadShortcut.MARK_SERVER_READ.action + markServerReadShortcut.MARK_SERVER_READ.action = function(){ + if(isBot)return + return action.call(this, ...arguments) + } + markServerReadShortcut.default && markServerReadShortcut.default.MARK_SERVER_READ && (markServerReadShortcut.default.MARK_SERVER_READ.action = markServerReadShortcut.MARK_SERVER_READ.action) + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const applicationStatisticModule = BDModules.get(e => e.fetchActivityStatistics)[0] + if(applicationStatisticModule){ + const fetchActivityStatistics = applicationStatisticModule.fetchActivityStatistics + applicationStatisticModule.fetchActivityStatistics = function(){ + if(!isBot)return fetchActivityStatistics.call(this, ...arguments) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.USER_ACTIVITY_STATISTICS_FETCH_SUCCESS, + statistics: [] + }) + }) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const subsInvoiceModule = BDModules.get(e => e.fetchSubscriptionInvoicePreview)[0] + if(subsInvoiceModule){ + function adapt(data){ + return { + id: data.id, + invoiceItems: data.invoice_items.map(function(e) { + return { + id: e.id, + subscriptionPlanId: e.subscription_plan_id, + subscriptionPlanPrice: e.subscription_plan_price, + amount: e.amount, + quantity: e.quantity, + discounts: e.discounts + } + }), + total: data.total, + subtotal: data.subtotal, + currency: data.currency, + tax: data.tax, + taxInclusive: data.tax_inclusive, + subscriptionPeriodStart: new Date(data.subscription_period_start), + subscriptionPeriodEnd: new Date(data.subscription_period_end) + } + } + const fetchSubscriptionInvoicePreview = subsInvoiceModule.fetchSubscriptionInvoicePreview + subsInvoiceModule.fetchSubscriptionInvoicePreview = function(){ + if(!isBot)return fetchSubscriptionInvoicePreview.call(this, ...arguments) + const arg1 = arguments[0] + if(!arg1 || !arg1.subscriptionId || arg1.subscriptionId === "123456789"){ + return new Promise((resolve, reject) => { + let data = adapt({ + "id": "123456789", + "invoice_items": [{ + "id": "123456789", + "amount": 0, + "discounts": [], + "subscription_plan_id": "511651860671627264", + "subscription_plan_price": 0, + "quantity": 1, + "proration": false + }], + "total": 100, + "subtotal": 100, + "currency": "usd", + "tax": 0, + "tax_inclusive": true, + "subscription_period_start": "2020-06-00:00:00.000000", + "subscription_period_end": "2100-06-00:00:00.000000" + }) + console.log(data) + resolve(data) + }) + } + return fetchSubscriptionInvoicePreview.call(this, ...arguments) + } + const useSubscriptionInvoice = subsInvoiceModule.useSubscriptionInvoice + subsInvoiceModule.useSubscriptionInvoice = function(){ + if(!isBot)return useSubscriptionInvoice.call(this, ...arguments) + return useSubscriptionInvoice.call(this, Object.assign(arguments[0], {preventFetch: true}), ...Array.from(arguments).slice(1)) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const subsModule = BDModules.get(e => e.fetchUserPremiumGuildSubscriptionSlots)[0] + if(subsModule){ + const fetchUserPremiumGuildSubscriptionSlots = subsModule.fetchUserPremiumGuildSubscriptionSlots + subsModule.fetchUserPremiumGuildSubscriptionSlots = function(){ + if(!isBot)return fetchUserPremiumGuildSubscriptionSlots.call(this, ...arguments) + setImmediate(()=>{ + dispatcher.dispatch({ + type: constants.ActionTypes.USER_PREMIUM_GUILD_SUBSCRIPTION_SLOTS_FETCH_SUCCESS, + userPremiumGuildSubscriptionSlots: [] + }) + }) + } + const fetchPremiumSubscriptionCooldown = subsModule.fetchPremiumSubscriptionCooldown + subsModule.fetchPremiumSubscriptionCooldown = function(){ + if(!isBot)return fetchPremiumSubscriptionCooldown.call(this, ...arguments) + return new Promise((resolve, reject) => { + reject(new Error("Lightcord bot emulation cannot use Server Boosts")) + }) + } + const fetchPremiumSubscriptions = subsModule.fetchPremiumSubscriptions + subsModule.fetchPremiumSubscriptions = function(){ + if(!isBot)return fetchPremiumSubscriptions.call(this, ...arguments) + return new Promise((resolve, reject) => { + reject(new Error("Lightcord bot emulation cannot use Server Boosts")) + }) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const entitlementsModule = BDModules.get(e => e.fetchUserEntitlementsForApplication)[0] + if(entitlementsModule){ + const fetchUserEntitlementsForApplication = entitlementsModule.fetchUserEntitlementsForApplication + entitlementsModule.fetchUserEntitlementsForApplication = function(){ + if(!isBot)return fetchUserEntitlementsForApplication.call(this, ...arguments) + let resolve + setImmediate(()=>{ + dispatcher.dispatch({ + type: constants.ActionTypes.ENTITLEMENT_FETCH_APPLICATION_START, + applicationId: arguments[0] + }) + setImmediate(()=>{ + resolve([]) + dispatcher.dispatch({ + type: constants.ActionTypes.ENTITLEMENT_FETCH_APPLICATION_SUCCESS, + applicationId: arguments[0], + entitlements: [] + }) + }) + }) + return new Promise((res) => (resolve = res)) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const giftModule1 = BDModules.get(e => e.fetchGiftableEntitlements)[0] + if(giftModule1){ + const fetchGiftableEntitlements = giftModule1.fetchGiftableEntitlements + giftModule1.fetchGiftableEntitlements = function(){ + if(!isBot)return fetchGiftableEntitlements.call(this, ...arguments) + dispatcher.dispatch({ + type: constants.ActionTypes.ENTITLEMENTS_GIFTABLE_FETCH + }) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.ENTITLEMENTS_GIFTABLE_FETCH_SUCCESS, + entitlements: [] + }) + }) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const libraryModule = BDModules.get(e => e.fetchLibrary)[0] + if(libraryModule){ + const fetchLibrary = libraryModule.fetchLibrary + libraryModule.fetchLibrary = function(){ + if(!isBot)return fetchLibrary.call(this, ...arguments) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.LIBRARY_FETCH_SUCCESS, + libraryApplications: [] + }) + }) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const hypesquadModule = BDModules.get(e => e.default && e.default.joinHypeSquadOnline)[0] + if(hypesquadModule){ + const joinHypeSquadOnline = hypesquadModule.default.joinHypeSquadOnline + hypesquadModule.default.joinHypeSquadOnline = function(){ + if(!isBot)return joinHypeSquadOnline.call(this, ...arguments) + return Promise.reject(new Error("Lightcord bot emulation cannot join hypesquad.")) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const HouseSelectionModal = BDModules.get(e => e.default && e.default.displayName === "HouseSelectionModal")[0] + const RadioGroup = BDModules.get(e => e.default && e.default.displayName === "RadioGroup")[0] + + if(HouseSelectionModal && RadioGroup){ + const defaultFunc = HouseSelectionModal.default + HouseSelectionModal.default = function(){ + let returnValue = new defaultFunc(...arguments) + + let hypesquadValue = getCurrentHypesquad() || "3" + const renderHeaderCopy = returnValue.renderHeaderCopy + returnValue.renderHeaderCopy = function(){ + return "Hypesquad" + } + const renderPrimaryAction = returnValue.renderPrimaryAction + returnValue.renderPrimaryAction = function(){ + const renderValue = renderPrimaryAction.call(returnValue, ...arguments) + + if(!returnValue.state.hasSubmittedHouse){ + renderValue.props.children = "Submit" + }else{ + renderValue.props.children = "Close" + } + if(!isBot){ + renderValue.props.disabled = false + }else{ + renderValue.props.disabled = true + } + const onClick = renderValue.props.onClick + renderValue.props.onClick = (ev) => { + if(!returnValue.state.hasSubmittedHouse){ + returnValue.handleSubmitButtonClick.call(returnValue, ...arguments) + }else{ + onClick.call(this, ...arguments) + } + } + return renderValue + } + const getSelectedHouseID = returnValue.getSelectedHouseID + returnValue.getSelectedHouseID = function(){ + return "HOUSE_"+hypesquadValue + } + const renderContent = returnValue.renderContent + returnValue.renderContent = function(){ + if(!isBot){ + if(this.state.hasSubmittedHouse)return this.renderQuizResult(); + + let component = React.createElement(class RadioContainer extends React.PureComponent { + constructor(props){ + super(props) + } + + render(){ + return React.createElement("div", { + style: { + margin: "0 auto", + width: "75%" + } + }, React.createElement(RadioGroup.default, { + disabled: false, + value: hypesquadValue, + options: [ + { + value: "1", + name: "Bravery", + desc: "The Bravery house" + }, + { + value: "2", + name: "Brillance", + desc: "The Brillance house" + }, + { + value: "3", + name: "Balance", + desc: "The Balance house" + } + ], + onChange: (ev) => { + hypesquadValue = ev.value + this.forceUpdate() + } + })) + } + }, {}) + return component + }else{ + let component = React.createElement(class BotWarning extends React.PureComponent { + constructor(props){ + super(props) + } + + render(){ + return React.createElement("div", { + style: { + margin: "0 auto", + width: "75%" + } + }, [ + React.createElement("h2", { + style: { + color: "var(--text-normal)" + } + }, "Bots cannot use Hypesquad.") + ]) + } + }, {}) + return component + } + } + return returnValue + } + }else{ + logger.warn(new Error("Couldn't find module here"), HouseSelectionModal, RadioGroup) + } + const mentionModule = BDModules.get(e => e.default && e.default.fetchRecentMentions)[0] + if(mentionModule){ + const fetchRecentMentions = mentionModule.default.fetchRecentMentions + mentionModule.default.fetchRecentMentions = function(e, t, n, i, s){ + if(!isBot)return fetchRecentMentions.call(this, ...arguments) + if(!n)n = null + dispatcher.dirtyDispatch({ + type: constants.ActionTypes.LOAD_RECENT_MENTIONS, + guildId: n + }) + setImmediate(() => { + dispatcher.dispatch({ + type: constants.ActionTypes.LOAD_RECENT_MENTIONS_SUCCESS, + messages: [], + isAfter: null != e, + hasMoreAfter: false + }) + }) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const templateModule = BDModules.get(e => e.default && e.default.loadTemplatesForGuild)[0] + if(templateModule){ + const loadTemplatesForGuild = templateModule.default.loadTemplatesForGuild + templateModule.default.loadTemplatesForGuild = function(){ + if(!isBot)return loadTemplatesForGuild.call(this, ...arguments) + return Promise.reject(new Error("Lightcord bot emulation cannot use Guild Templates")) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const searchModule = BDModules.get(e => e.default && e.default.prototype && e.default.prototype.retryLater)[0] + if(searchModule){ + const fetch = searchModule.default.prototype.fetch + searchModule.default.prototype.fetch = function(e, t, n){ + if(!isBot)return fetch.call(this, ...arguments) + n(new Error("Lightcord bot emulation cannot search in guild.")) + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + const inviteModule = BDModules.get(e => e.default && e.default.acceptInvite)[0] + if(inviteModule){ + const acceptInvite = inviteModule.default.acceptInvite + inviteModule.default.acceptInvite = function(code, location, extraOptions){ + if(!isBot)return acceptInvite.call(this, ...arguments) + dispatcher.dispatch({ + type: "INVITE_ACCEPT_FAILURE", + code + }) + Utils.showToast("Lightcord Bot Emulation cannot join guilds.", {type: "error"}) + return Promise.reject("Lightcord Bot Emulation cannot join guilds.") + } + }else{ + logger.warn(new Error("Couldn't find module here")) + } + })().catch(console.error.bind(console, `%c[Error Bot shit]`, "color:red")) + + Utils.monkeyPatch(await ensureExported(e => e.default && e.default.displayName == "AuthBox"), "default", {after: (data) => { + const children = Utils.getNestedProp(data.returnValue, "props.children.props.children.props.children") + children.push(React.createElement(require("./tokenLogin").default, {})) + }}) + let [ + authBoxExpanded + ] = [ + BDModules.get(e => e.authBoxExpanded && typeof e.authBoxExpanded === "string")[0] + ] + DOMTools.addStyle("tokenLoginPatch", `.${authBoxExpanded ? Utils.removeDa(authBoxExpanded.authBoxExpanded) : "authBoxExpanded-2jqaBe"} { + width: 900px; +}`) + + await ensureGuildClasses() + BetterDiscord.init() + + events.emit("ready") +} + +function installReactDevtools(){ + let reactDevToolsPath = ""; + if (process.platform === "win32") reactDevToolsPath = path.resolve(process.env.LOCALAPPDATA, "Google/Chrome/User Data"); + else if (process.platform === "linux") reactDevToolsPath = path.resolve(process.env.HOME, ".config/google-chrome"); + else if (process.platform === "darwin") reactDevToolsPath = path.resolve(process.env.HOME, "Library/Application Support/Google/Chrome"); + else reactDevToolsPath = path.resolve(process.env.HOME, ".config/chromium"); + reactDevToolsPath = path.join(reactDevToolsPath, "Default", "Extensions", "fmkadmapgofadopljbjfkapdkoienihi") + if (fs.existsSync(reactDevToolsPath)) { + const versions = fs.readdirSync(reactDevToolsPath); + reactDevToolsPath = path.resolve(reactDevToolsPath, versions[versions.length - 1]); + } + if(fs.existsSync(reactDevToolsPath)){ + const webContents = electron.remote.getCurrentWebContents() + const BrowserWindow = electron.remote.BrowserWindow + setImmediate(() => webContents.on("devtools-opened", devToolsListener)); + if (webContents.isDevToolsOpened()) devToolsListener(); + + function devToolsListener(){ + logger.log(`Installing React Devtools`) + BrowserWindow.removeDevToolsExtension("React Developer Tools"); + const didInstall = BrowserWindow.addDevToolsExtension(reactDevToolsPath); + + if (didInstall) logger.log("React DevTools", "Successfully installed react devtools."); + else logger.log("React DevTools", "Couldn't find react devtools."); + } + }else{ + console.warn(new Error(`React Devtools could not be found.`)) + } +} + +require.extensions[".css"] = (m, filename) => { + let content = fs.readFileSync(filename, "binary") + let style = document.createElement("style") + style.id = btoa(filename) + style.innerHTML = content + document.head.appendChild(style) + m.exports = { + id: style.id, + remove(){ + return style.remove() + } + } + return m.exports +} + +let zlib = require("zlib") +let tmp = require("tmp") + +require.extensions[".jsbr"] = (m, filename) => { + if(!zlib)zlib = require("zlib") + if(!tmp)tmp = require("tmp") + let tmpFile = tmp.fileSync() + + fs.writeFileSync(tmpFile.name+".js", zlib.brotliDecompressSync(fs.readFileSync(filename))) + return require.extensions[".js"](m, tmpFile.name+".js") +} +require.extensions[".txt"] = (m, filename) => { + m.exports = fs.readFileSync(filename, "utf8") + return m.exports +} + +const LightcordBDFolder = path.join(electron.remote.app.getPath("appData"), "Lightcord_BD") + +const BetterDiscordConfig = window.BetterDiscordConfig = { + "branch": "lightcord", + dataPath: LightcordBDFolder+"/", + os: process.platform, + latestVersion: "0.3.4", + version: "0.3.4" +} + +function ensureGuildClasses(){ + return new Promise((resolve) => { + let classs = getGuildClasses() + if(classs && classs.wrapper)return resolve() + + let intergay = setInterval(() => { + classs = getGuildClasses() + if(classs && classs.wrapper){ + clearInterval(intergay) + resolve() + return + } + }, 200); + }) +} + +var ensureExported = global.ensureExported = function ensureExported(filter, maxTime = 500){ + let tried = 0 + return new Promise((resolve, reject) => { + let mod = ModuleLoader.get(filter)[0] + if(mod)return resolve(mod) + tried++ + + let interval = setInterval(() => { + if(tried > maxTime){ + clearInterval(interval) + reject(new Error("Could not find the module with the given filter.")) + return + } + mod = ModuleLoader.get(filter)[0] + if(mod){ + clearInterval(interval) + resolve(mod) + return + } + tried++ + }, 100); + }) +} +let Notifications = require("./patchNotifications") +Notifications.useShim(!appSettings.get("DEFAULT_NOTIFICATIONS", true)) + +function getGuildClasses() { + const guildsWrapper = ModuleLoader.get(e => e.wrapper && e.unreadMentionsBar)[0]; + const guilds = ModuleLoader.get(e => e.guildsError && e.selected)[0] + const pill = ModuleLoader.get(e => e.blobContainer)[0] + return Object.assign({}, guildsWrapper, guilds, pill); +} + +const originalResolve = path.resolve +const originalJoin = path.join + +const BetterDiscordFolder = function() { + if (process.env.injDir) return path.resolve(process.env.injDir); + switch (process.platform) { + case "win32": + return path.resolve(process.env.appdata, "BetterDiscord/"); + case "darwin": + return path.resolve(process.env.HOME, "Library/Preferences/", "BetterDiscord/"); + default: + return path.resolve(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : process.env.HOME + "/.config", "BetterDiscord/"); + } +}() + +path.resolve = (...args) => { // Patching BetterDiscord folder by Lightcord's BetterDiscord folder + let resp = originalResolve.call(path, ...args) + if(resp.startsWith(BetterDiscordFolder))resp = resp.replace(BetterDiscordFolder, LightcordBDFolder) + return resp +} +path.join = (...args) => { // Patching BetterDiscord folder by Lightcord's BetterDiscord folder + let resp = originalJoin.call(path, ...args) + if(resp.startsWith(BetterDiscordFolder))resp = resp.replace(BetterDiscordFolder, LightcordBDFolder) + return resp +} + +path.originalResolve = originalResolve + +let blacklist +function isBlacklisted(id){ + if(!blacklist)blacklist = require("./blacklist.txt").split(/[\n\r]+/g).map((line, index, lines) => { + let id = "" + let comment = "" + line.split("#").forEach((idOrComment, index, array) => { + idOrComment = idOrComment.trim() + + if(index === 0)id = idOrComment + else if(index === 1)comment = idOrComment + }) + return { + id, + comment + } + }) + if(blacklist.find(e => e.id === id))return true + return false +} + +const formatLogger = new Logger("RequireFormat") +function formatMinified(path){ + let result = path.replace("{min}", isPackaged ? ".min": "") + return result +} + + +window.ohgodohfuck = function(){ + let style=document.createElement("style");style.innerHTML=`html:after{content:"";position:absolute;top:0;left:0 ;width:100vw;height:100vh;background-image:url("https://media.giphy.com/media/l378vg4Pm9LGnmD6M/giphy.gif");background-size:cover;background-position:center;background-color:transparent !important;opacity:0.9;mix-blend-mode:hue;z-index:999999999999;pointer-events:none}@keyframes ohgodohfuck{from{transform:rotateZ(0deg)}to{transform:rotateZ(360deg)}}#app-mount{animation:ohgodohfuck 5s infinite alternate}`;document.body.append(style);setTimeout(()=>document.body.removeChild(style),5000); } \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/BetterDiscord/loaders/module-alias.js b/modules/discord_desktop_core/core/app/BetterDiscord/loaders/module-alias.js index a99b241..70bafba 100644 --- a/modules/discord_desktop_core/core/app/BetterDiscord/loaders/module-alias.js +++ b/modules/discord_desktop_core/core/app/BetterDiscord/loaders/module-alias.js @@ -1,253 +1,253 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2018, Nick Gavrilov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * From https://github.com/ilearnio/module-alias - */ - -'use strict' - -var BuiltinModule = require('module') - -// Guard against poorly mocked module constructors -var Module = module.constructor.length > 1 - ? module.constructor - : BuiltinModule - -var nodePath = require('path') - -var modulePaths = [] -var moduleAliases = {} -var moduleAliasNames = [] - -var oldNodeModulePaths = Module._nodeModulePaths -Module._nodeModulePaths = function (from) { - var paths = oldNodeModulePaths.call(this, from) - - // Only include the module path for top-level modules - // that were not installed: - if (from.indexOf('node_modules') === -1) { - paths = modulePaths.concat(paths) - } - - return paths -} - -var oldResolveFilename = Module._resolveFilename -Module._resolveFilename = function (request, parentModule, isMain, options) { - for (var i = moduleAliasNames.length; i-- > 0;) { - var alias = moduleAliasNames[i] - if (isPathMatchesAlias(request, alias)) { - var aliasTarget = moduleAliases[alias] - // Custom function handler - if (typeof moduleAliases[alias] === 'function') { - var fromPath = parentModule.filename - aliasTarget = moduleAliases[alias](fromPath, request, alias) - if (!aliasTarget || typeof aliasTarget !== 'string') { - throw new Error('[module-alias] Expecting custom handler function to return path.') - } - } - request = nodePath.join(aliasTarget, request.substr(alias.length)) - // Only use the first match - break - } - } - - return oldResolveFilename.call(this, request, parentModule, isMain, options) -} - -function isPathMatchesAlias (path, alias) { - // Matching /^alias(\/|$)/ - if (path.indexOf(alias) === 0) { - if (path.length === alias.length) return true - if (path[alias.length] === '/') return true - } - - return false -} - -function addPathHelper (path, targetArray) { - path = nodePath.normalize(path) - if (targetArray && targetArray.indexOf(path) === -1) { - targetArray.unshift(path) - } -} - -function removePathHelper (path, targetArray) { - if (targetArray) { - var index = targetArray.indexOf(path) - if (index !== -1) { - targetArray.splice(index, 1) - } - } -} - -function addPath (path) { - var parent - path = nodePath.normalize(path) - - if (modulePaths.indexOf(path) === -1) { - modulePaths.push(path) - // Enable the search path for the current top-level module - var mainModule = getMainModule() - if (mainModule) { - addPathHelper(path, mainModule.paths) - } - parent = module.parent - - // Also modify the paths of the module that was used to load the - // app-module-paths module and all of it's parents - while (parent && parent !== mainModule) { - addPathHelper(path, parent.paths) - parent = parent.parent - } - } -} - -function addAliases (aliases) { - for (var alias in aliases) { - addAlias(alias, aliases[alias]) - } -} - -function addAlias (alias, target) { - moduleAliases[alias] = target - // Cost of sorting is lower here than during resolution - moduleAliasNames = Object.keys(moduleAliases) - moduleAliasNames.sort() -} - -/** - * Reset any changes maded (resets all registered aliases - * and custom module directories) - * The function is undocumented and for testing purposes only - */ -function reset () { - var mainModule = getMainModule() - - // Reset all changes in paths caused by addPath function - modulePaths.forEach(function (path) { - if (mainModule) { - removePathHelper(path, mainModule.paths) - } - - // Delete from require.cache if the module has been required before. - // This is required for node >= 11 - Object.getOwnPropertyNames(require.cache).forEach(function (name) { - if (name.indexOf(path) !== -1) { - delete require.cache[name] - } - }) - - var parent = module.parent - while (parent && parent !== mainModule) { - removePathHelper(path, parent.paths) - parent = parent.parent - } - }) - - modulePaths = [] - moduleAliases = {} - moduleAliasNames = [] -} - -/** - * Import aliases from package.json - * @param {object} options - */ -function init (options) { - if (typeof options === 'string') { - options = { base: options } - } - - options = options || {} - - var candidatePackagePaths - if (options.base) { - candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))] - } else { - // There is probably 99% chance that the project root directory in located - // above the node_modules directory, - // Or that package.json is in the node process' current working directory (when - // running a package manager script, e.g. `yarn start` / `npm run start`) - candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()] - } - - var npmPackage - var base - for (var i in candidatePackagePaths) { - try { - base = candidatePackagePaths[i] - - npmPackage = require(nodePath.join(base, 'package.json')) - break - } catch (e) { - // noop - } - } - - if (typeof npmPackage !== 'object') { - var pathString = candidatePackagePaths.join(',\n') - throw new Error('Unable to find package.json in any of:\n[' + pathString + ']') - } - - // - // Import aliases - // - - var aliases = npmPackage._moduleAliases || {} - - for (var alias in aliases) { - if (aliases[alias][0] !== '/') { - aliases[alias] = nodePath.join(base, aliases[alias]) - } - } - - addAliases(aliases) - - // - // Register custom module directories (like node_modules) - // - - if (npmPackage._moduleDirectories instanceof Array) { - npmPackage._moduleDirectories.forEach(function (dir) { - if (dir === 'node_modules') return - - var modulePath = nodePath.join(base, dir) - addPath(modulePath) - }) - } -} - -function getMainModule () { - return require.main._simulateRepl ? undefined : require.main -} - -module.exports = init -module.exports.addPath = addPath -module.exports.addAlias = addAlias -module.exports.addAliases = addAliases -module.exports.isPathMatchesAlias = isPathMatchesAlias -module.exports.reset = reset -module.exports.setMain = function(main){ - require.main = main +/** + * The MIT License (MIT) + * + * Copyright (c) 2018, Nick Gavrilov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * From https://github.com/ilearnio/module-alias + */ + +'use strict' + +var BuiltinModule = require('module') + +// Guard against poorly mocked module constructors +var Module = module.constructor.length > 1 + ? module.constructor + : BuiltinModule + +var nodePath = require('path') + +var modulePaths = [] +var moduleAliases = {} +var moduleAliasNames = [] + +var oldNodeModulePaths = Module._nodeModulePaths +Module._nodeModulePaths = function (from) { + var paths = oldNodeModulePaths.call(this, from) + + // Only include the module path for top-level modules + // that were not installed: + if (from.indexOf('node_modules') === -1) { + paths = modulePaths.concat(paths) + } + + return paths +} + +var oldResolveFilename = Module._resolveFilename +Module._resolveFilename = function (request, parentModule, isMain, options) { + for (var i = moduleAliasNames.length; i-- > 0;) { + var alias = moduleAliasNames[i] + if (isPathMatchesAlias(request, alias)) { + var aliasTarget = moduleAliases[alias] + // Custom function handler + if (typeof moduleAliases[alias] === 'function') { + var fromPath = parentModule.filename + aliasTarget = moduleAliases[alias](fromPath, request, alias) + if (!aliasTarget || typeof aliasTarget !== 'string') { + throw new Error('[module-alias] Expecting custom handler function to return path.') + } + } + request = nodePath.join(aliasTarget, request.substr(alias.length)) + // Only use the first match + break + } + } + + return oldResolveFilename.call(this, request, parentModule, isMain, options) +} + +function isPathMatchesAlias (path, alias) { + // Matching /^alias(\/|$)/ + if (path.indexOf(alias) === 0) { + if (path.length === alias.length) return true + if (path[alias.length] === '/') return true + } + + return false +} + +function addPathHelper (path, targetArray) { + path = nodePath.normalize(path) + if (targetArray && targetArray.indexOf(path) === -1) { + targetArray.unshift(path) + } +} + +function removePathHelper (path, targetArray) { + if (targetArray) { + var index = targetArray.indexOf(path) + if (index !== -1) { + targetArray.splice(index, 1) + } + } +} + +function addPath (path) { + var parent + path = nodePath.normalize(path) + + if (modulePaths.indexOf(path) === -1) { + modulePaths.push(path) + // Enable the search path for the current top-level module + var mainModule = getMainModule() + if (mainModule) { + addPathHelper(path, mainModule.paths) + } + parent = module.parent + + // Also modify the paths of the module that was used to load the + // app-module-paths module and all of it's parents + while (parent && parent !== mainModule) { + addPathHelper(path, parent.paths) + parent = parent.parent + } + } +} + +function addAliases (aliases) { + for (var alias in aliases) { + addAlias(alias, aliases[alias]) + } +} + +function addAlias (alias, target) { + moduleAliases[alias] = target + // Cost of sorting is lower here than during resolution + moduleAliasNames = Object.keys(moduleAliases) + moduleAliasNames.sort() +} + +/** + * Reset any changes maded (resets all registered aliases + * and custom module directories) + * The function is undocumented and for testing purposes only + */ +function reset () { + var mainModule = getMainModule() + + // Reset all changes in paths caused by addPath function + modulePaths.forEach(function (path) { + if (mainModule) { + removePathHelper(path, mainModule.paths) + } + + // Delete from require.cache if the module has been required before. + // This is required for node >= 11 + Object.getOwnPropertyNames(require.cache).forEach(function (name) { + if (name.indexOf(path) !== -1) { + delete require.cache[name] + } + }) + + var parent = module.parent + while (parent && parent !== mainModule) { + removePathHelper(path, parent.paths) + parent = parent.parent + } + }) + + modulePaths = [] + moduleAliases = {} + moduleAliasNames = [] +} + +/** + * Import aliases from package.json + * @param {object} options + */ +function init (options) { + if (typeof options === 'string') { + options = { base: options } + } + + options = options || {} + + var candidatePackagePaths + if (options.base) { + candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))] + } else { + // There is probably 99% chance that the project root directory in located + // above the node_modules directory, + // Or that package.json is in the node process' current working directory (when + // running a package manager script, e.g. `yarn start` / `npm run start`) + candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()] + } + + var npmPackage + var base + for (var i in candidatePackagePaths) { + try { + base = candidatePackagePaths[i] + + npmPackage = require(nodePath.join(base, 'package.json')) + break + } catch (e) { + // noop + } + } + + if (typeof npmPackage !== 'object') { + var pathString = candidatePackagePaths.join(',\n') + throw new Error('Unable to find package.json in any of:\n[' + pathString + ']') + } + + // + // Import aliases + // + + var aliases = npmPackage._moduleAliases || {} + + for (var alias in aliases) { + if (aliases[alias][0] !== '/') { + aliases[alias] = nodePath.join(base, aliases[alias]) + } + } + + addAliases(aliases) + + // + // Register custom module directories (like node_modules) + // + + if (npmPackage._moduleDirectories instanceof Array) { + npmPackage._moduleDirectories.forEach(function (dir) { + if (dir === 'node_modules') return + + var modulePath = nodePath.join(base, dir) + addPath(modulePath) + }) + } +} + +function getMainModule () { + return require.main._simulateRepl ? undefined : require.main +} + +module.exports = init +module.exports.addPath = addPath +module.exports.addAlias = addAlias +module.exports.addAliases = addAliases +module.exports.isPathMatchesAlias = isPathMatchesAlias +module.exports.reset = reset +module.exports.setMain = function(main){ + require.main = main } \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/BetterDiscord/loaders/modules.js b/modules/discord_desktop_core/core/app/BetterDiscord/loaders/modules.js index 824cf76..388c9a3 100644 --- a/modules/discord_desktop_core/core/app/BetterDiscord/loaders/modules.js +++ b/modules/discord_desktop_core/core/app/BetterDiscord/loaders/modules.js @@ -1,148 +1,148 @@ -let req -setReq() - - -class DangerousWebpackloader { - get modules(){ - if(req){ - return Object.values(req.c).filter(e => e && e.exports) - }else{ - setReq() - if(req){ - return Object.values(req.c).filter(e => e && e.exports) - }else{ - return [] - } - } - } - get(ids, modules){ - if(typeof ids === "function"){ - return (modules || this.modules).map((mdl) => { - if(mdl && typeof mdl.exports !== "undefined"){ - return mdl.exports - }else{ - return null - } - }).filter(e => e).filter(ids) - }else if(Array.isArray(ids)){ - modules = modules || this.modules - return ids.map(id => this.get(id, modules)) - }else{ - modules = modules || this.modules - let module = modules.filter(e => !!e).find(e => e.i === ids) - if(!module)return undefined - return module.exports - } - } - get default(){ - return this - } -} -function filterDangerous(mods){ - return mods.map(e => { - return protect(e) - }) -} -function protect(exports){ - let theModule = exports.exports - let mod = theModule.default - if(!mod)return exports - if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; - if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports - - const proxy = new Proxy(mod, { - getOwnPropertyDescriptor: function(obj, prop) { - if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; - return Object.getOwnPropertyDescriptor(obj, prop); - }, - get: function(obj, func) { - if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; - if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; - if (func == "showToken" && obj.showToken) return () => true; - if (func == "__proto__" && obj.__proto__) return proxy; - - return obj[func]; - } - }); - - return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})}) -} -class Webpackloader { - get modules(){ - if(req){ - return filterDangerous(Object.values(req.c).filter(e => e && e.exports)) - }else{ - setReq() - if(req){ - return filterDangerous(Object.values(req.c).filter(e => e && e.exports)) - }else{ - return [] - } - } - } - get(ids, modules){ - if(typeof ids === "function"){ - return (modules || this.modules).map((mdl) => { - if(mdl && typeof mdl.exports !== "undefined"){ - return mdl.exports - }else{ - return null - } - }).filter(e => e).filter(ids) - }else if(Array.isArray(ids)){ - modules = modules || this.modules - return ids.map(id => this.get(id, modules)) - }else{ - modules = modules || this.modules - let module = modules.filter(e => !!e).find(e => e.i === ids) - if(!module)return undefined - return module.exports - } - } - get default(){ - return this - } -} - -module.exports = new DangerousWebpackloader() - -global.BDModules = new Webpackloader() - -function setReq(){ - try{ - req = webpackJsonp.push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]); - if(req){ - delete req.m.__extra_id__; - delete req.c.__extra_id__;/* - Object.defineProperty(req.c, "0", { - get: () => ({ - i: 0, - l: true, - exports: require("react") - }), - configurable: false, - enumerable: true - }) - Object.defineProperty(req.c, "856", { - get: () => ({ - i: 856, - l: true, - exports: require("react") - }), - configurable: false, - enumerable: true - }) - Object.defineProperty(req.c, "71", { - get: () => ({ - i: 71, - l: true, - exports: require("react-dom") - }), - configurable: false, - enumerable: true - })*/ - } - }catch(e){ - req = undefined - } +let req +setReq() + + +class DangerousWebpackloader { + get modules(){ + if(req){ + return Object.values(req.c).filter(e => e && e.exports) + }else{ + setReq() + if(req){ + return Object.values(req.c).filter(e => e && e.exports) + }else{ + return [] + } + } + } + get(ids, modules){ + if(typeof ids === "function"){ + return (modules || this.modules).map((mdl) => { + if(mdl && typeof mdl.exports !== "undefined"){ + return mdl.exports + }else{ + return null + } + }).filter(e => e).filter(ids) + }else if(Array.isArray(ids)){ + modules = modules || this.modules + return ids.map(id => this.get(id, modules)) + }else{ + modules = modules || this.modules + let module = modules.filter(e => !!e).find(e => e.i === ids) + if(!module)return undefined + return module.exports + } + } + get default(){ + return this + } +} +function filterDangerous(mods){ + return mods.map(e => { + return protect(e) + }) +} +function protect(exports){ + let theModule = exports.exports + let mod = theModule.default + if(!mod)return exports + if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; + if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports + + const proxy = new Proxy(mod, { + getOwnPropertyDescriptor: function(obj, prop) { + if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; + return Object.getOwnPropertyDescriptor(obj, prop); + }, + get: function(obj, func) { + if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; + if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; + if (func == "showToken" && obj.showToken) return () => true; + if (func == "__proto__" && obj.__proto__) return proxy; + + return obj[func]; + } + }); + + return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})}) +} +class Webpackloader { + get modules(){ + if(req){ + return filterDangerous(Object.values(req.c).filter(e => e && e.exports)) + }else{ + setReq() + if(req){ + return filterDangerous(Object.values(req.c).filter(e => e && e.exports)) + }else{ + return [] + } + } + } + get(ids, modules){ + if(typeof ids === "function"){ + return (modules || this.modules).map((mdl) => { + if(mdl && typeof mdl.exports !== "undefined"){ + return mdl.exports + }else{ + return null + } + }).filter(e => e).filter(ids) + }else if(Array.isArray(ids)){ + modules = modules || this.modules + return ids.map(id => this.get(id, modules)) + }else{ + modules = modules || this.modules + let module = modules.filter(e => !!e).find(e => e.i === ids) + if(!module)return undefined + return module.exports + } + } + get default(){ + return this + } +} + +module.exports = new DangerousWebpackloader() + +global.BDModules = new Webpackloader() + +function setReq(){ + try{ + req = webpackJsonp.push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]); + if(req){ + delete req.m.__extra_id__; + delete req.c.__extra_id__;/* + Object.defineProperty(req.c, "0", { + get: () => ({ + i: 0, + l: true, + exports: require("react") + }), + configurable: false, + enumerable: true + }) + Object.defineProperty(req.c, "856", { + get: () => ({ + i: 856, + l: true, + exports: require("react") + }), + configurable: false, + enumerable: true + }) + Object.defineProperty(req.c, "71", { + get: () => ({ + i: 71, + l: true, + exports: require("react-dom") + }), + configurable: false, + enumerable: true + })*/ + } + }catch(e){ + req = undefined + } } \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/BetterDiscord/patchNotifications.js b/modules/discord_desktop_core/core/app/BetterDiscord/patchNotifications.js index adf4e04..613a990 100644 --- a/modules/discord_desktop_core/core/app/BetterDiscord/patchNotifications.js +++ b/modules/discord_desktop_core/core/app/BetterDiscord/patchNotifications.js @@ -1,102 +1,102 @@ -const ipcRenderer = require("../discord_native/renderer/ipc") - - -if(process.platform === "win32"){ - let useShim = false - - const originalNotification = window.Notification - - ipcRenderer.send("NOTIFICATIONS_CLEAR") - - const notifications = {} - ipcRenderer.on("NOTIFICATION_CLICK", (e, id) => { - var notification = notifications[id]; - if(notification){ - notification.onclick() - notification.close() - } - }) - class LightcordNotification { - constructor(title, data){ - this.id = LightcordNotification._id++ - this.onshow = function() {} - this.onclick = function() {} - this.onclose = function() {} - this.title = title - this.body = data.body - this.icon = data.icon - setTimeout(() => { - return this.onshow() - }, 0) - notifications[this.id] = this - ipcRenderer.send("NOTIFICATION_SHOW", { - id: this.id, - title: this.title, - body: this.body, - icon: this.icon, - theme: settingStore ? settingStore.default.theme : "dark" - }) - } - - static _id = 0 - - static requestPermission(callback){ - callback() - } - - close(){ - if(!notifications[this.id])return - - delete notifications[this.id] - ipcRenderer.send("NOTIFICATION_CLOSE", this.id) - this.onclose() - } - } - - LightcordNotification.permission = "granted" - - function Notification(){ - if(useShim)return new LightcordNotification(...arguments) - return new originalNotification(...arguments) - } - Object.defineProperties(Notification, { - permission: { - get(){ - if(useShim)return LightcordNotification.permission - return originalNotification.permission - } - }, - requestPermission: { - get(){ - if(useShim)return LightcordNotification.requestPermission - return originalNotification.requestPermission - } - }, - _id: { - get(){ - if(useShim)return LightcordNotification._id - return originalNotification._id - } - } - }) - window.Notification = Notification - - - module.exports = { - useShim(use){ - useShim = !!use - } - } -}else{ - module.exports = { - useShim(){} - } -} - -let settingStore - -ensureExported((e => e.default && e.default.theme)) -.then(themeStore => { - settingStore = themeStore - ipcRenderer.send("UPDATE_THEME", themeStore.default.theme) +const ipcRenderer = require("../discord_native/renderer/ipc") + + +if(process.platform === "win32"){ + let useShim = false + + const originalNotification = window.Notification + + ipcRenderer.send("NOTIFICATIONS_CLEAR") + + const notifications = {} + ipcRenderer.on("NOTIFICATION_CLICK", (e, id) => { + var notification = notifications[id]; + if(notification){ + notification.onclick() + notification.close() + } + }) + class LightcordNotification { + constructor(title, data){ + this.id = LightcordNotification._id++ + this.onshow = function() {} + this.onclick = function() {} + this.onclose = function() {} + this.title = title + this.body = data.body + this.icon = data.icon + setTimeout(() => { + return this.onshow() + }, 0) + notifications[this.id] = this + ipcRenderer.send("NOTIFICATION_SHOW", { + id: this.id, + title: this.title, + body: this.body, + icon: this.icon, + theme: settingStore ? settingStore.default.theme : "dark" + }) + } + + static _id = 0 + + static requestPermission(callback){ + callback() + } + + close(){ + if(!notifications[this.id])return + + delete notifications[this.id] + ipcRenderer.send("NOTIFICATION_CLOSE", this.id) + this.onclose() + } + } + + LightcordNotification.permission = "granted" + + function Notification(){ + if(useShim)return new LightcordNotification(...arguments) + return new originalNotification(...arguments) + } + Object.defineProperties(Notification, { + permission: { + get(){ + if(useShim)return LightcordNotification.permission + return originalNotification.permission + } + }, + requestPermission: { + get(){ + if(useShim)return LightcordNotification.requestPermission + return originalNotification.requestPermission + } + }, + _id: { + get(){ + if(useShim)return LightcordNotification._id + return originalNotification._id + } + } + }) + window.Notification = Notification + + + module.exports = { + useShim(use){ + useShim = !!use + } + } +}else{ + module.exports = { + useShim(){} + } +} + +let settingStore + +ensureExported((e => e.default && e.default.theme)) +.then(themeStore => { + settingStore = themeStore + ipcRenderer.send("UPDATE_THEME", themeStore.default.theme) }).catch(console.error) \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.js b/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.js index 1097704..1ee5a37 100644 --- a/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.js +++ b/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.js @@ -1,97 +1,97 @@ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0]; -const colors = BDModules.get(e => e.colorHeaderPrimary)[0]; -const sizes = BDModules.get(e => e.size24)[0]; -const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0]; -const marginModule = BDModules.get(e => e.marginBottom8)[0]; -const titleModule = BDModules.get(e => e.h5)[0]; -const inputModule = BDModules.get(e => e.inputWrapper)[0]; -const contentModule = BDModules.get(e => e.contents)[0]; -const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0]; -const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default; - -class TokenLogin extends React.Component { - constructor(props) { - super(props); - } - - render() { - return [/*#__PURE__*/React.createElement("div", { - class: verticalSeparatorModule.verticalSeparator - }), /*#__PURE__*/React.createElement("div", { - className: loginContainerModule.mainLoginContainer - }, /*#__PURE__*/React.createElement("div", { - className: `${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}` - }, "Connect with Token"), /*#__PURE__*/React.createElement("div", { - className: `${colors.colorHeaderSecondary} ${sizes.size16}` - }, "Input your token below"), /*#__PURE__*/React.createElement("div", { - className: `${authBoxModule.block} ${marginModule.marginTop20}` - }, /*#__PURE__*/React.createElement("div", { - className: marginModule.marginBottom20 - }, /*#__PURE__*/React.createElement(TokenInput, { - ref: "input" - })), /*#__PURE__*/React.createElement("button", { - type: "submit", - className: `${marginModule.marginBottom8} ${authBoxModule.button} ${contentModule.button} ${contentModule.lookFilled} ${contentModule.colorBrand} ${contentModule.sizeLarge} ${contentModule.fullWidth} ${contentModule.grow}`, - onClick: () => { - if (!this.refs.input.state.value) { - this.refs.input.setState({ - error: "This field is necessary" - }); - return; - } - - loginModule.loginToken(this.refs.input.state.value); - ev.stopPropagation(); - } - }, /*#__PURE__*/React.createElement("div", { - className: contentModule.contents - }, "Login"))))]; - } - -} - -exports.default = TokenLogin; - -class TokenInput extends React.Component { - constructor() { - super(...arguments); - this.state = { - value: "", - error: null - }; - } - - render() { - return [/*#__PURE__*/React.createElement("h5", { - className: `${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " " + titleModule.error : ""}` - }, "Token", this.state.error ? /*#__PURE__*/React.createElement("span", { - class: titleModule.errorMessage - }, /*#__PURE__*/React.createElement("span", { - class: titleModule.errorSeparator - }, "-"), this.state.error) : null), /*#__PURE__*/React.createElement("div", { - className: inputModule.inputWrapper - }, /*#__PURE__*/React.createElement("input", { - className: `${inputModule.inputDefault}${this.state.error ? " " + inputModule.inputError : ""}`, - name: "token", - type: "token", - placeholder: true, - "aria-label": "Token", - autoComplete: "off", - maxLength: 999, - spellCheck: "false", - value: this.state.value, - onChange: ev => { - this.setState({ - value: ev.target.value - }); - } - }))]; - } - +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0]; +const colors = BDModules.get(e => e.colorHeaderPrimary)[0]; +const sizes = BDModules.get(e => e.size24)[0]; +const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0]; +const marginModule = BDModules.get(e => e.marginBottom8)[0]; +const titleModule = BDModules.get(e => e.h5)[0]; +const inputModule = BDModules.get(e => e.inputWrapper)[0]; +const contentModule = BDModules.get(e => e.contents)[0]; +const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0]; +const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default; + +class TokenLogin extends React.Component { + constructor(props) { + super(props); + } + + render() { + return [/*#__PURE__*/React.createElement("div", { + class: verticalSeparatorModule.verticalSeparator + }), /*#__PURE__*/React.createElement("div", { + className: loginContainerModule.mainLoginContainer + }, /*#__PURE__*/React.createElement("div", { + className: `${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}` + }, "Connect with Token"), /*#__PURE__*/React.createElement("div", { + className: `${colors.colorHeaderSecondary} ${sizes.size16}` + }, "Input your token below"), /*#__PURE__*/React.createElement("div", { + className: `${authBoxModule.block} ${marginModule.marginTop20}` + }, /*#__PURE__*/React.createElement("div", { + className: marginModule.marginBottom20 + }, /*#__PURE__*/React.createElement(TokenInput, { + ref: "input" + })), /*#__PURE__*/React.createElement("button", { + type: "submit", + className: `${marginModule.marginBottom8} ${authBoxModule.button} ${contentModule.button} ${contentModule.lookFilled} ${contentModule.colorBrand} ${contentModule.sizeLarge} ${contentModule.fullWidth} ${contentModule.grow}`, + onClick: () => { + if (!this.refs.input.state.value) { + this.refs.input.setState({ + error: "This field is necessary" + }); + return; + } + + loginModule.loginToken(this.refs.input.state.value); + ev.stopPropagation(); + } + }, /*#__PURE__*/React.createElement("div", { + className: contentModule.contents + }, "Login"))))]; + } + +} + +exports.default = TokenLogin; + +class TokenInput extends React.Component { + constructor() { + super(...arguments); + this.state = { + value: "", + error: null + }; + } + + render() { + return [/*#__PURE__*/React.createElement("h5", { + className: `${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " " + titleModule.error : ""}` + }, "Token", this.state.error ? /*#__PURE__*/React.createElement("span", { + class: titleModule.errorMessage + }, /*#__PURE__*/React.createElement("span", { + class: titleModule.errorSeparator + }, "-"), this.state.error) : null), /*#__PURE__*/React.createElement("div", { + className: inputModule.inputWrapper + }, /*#__PURE__*/React.createElement("input", { + className: `${inputModule.inputDefault}${this.state.error ? " " + inputModule.inputError : ""}`, + name: "token", + type: "token", + placeholder: true, + "aria-label": "Token", + autoComplete: "off", + maxLength: 999, + spellCheck: "false", + value: this.state.value, + onChange: ev => { + this.setState({ + value: ev.target.value + }); + } + }))]; + } + } \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.jsx b/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.jsx index 34fbe2d..a5a9cc2 100644 --- a/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.jsx +++ b/modules/discord_desktop_core/core/app/BetterDiscord/tokenLogin.jsx @@ -1,80 +1,80 @@ -const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0] -const colors = BDModules.get(e => e.colorHeaderPrimary)[0] -const sizes = BDModules.get(e => e.size24)[0] -const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0] -const marginModule = BDModules.get(e => e.marginBottom8)[0] -const titleModule = BDModules.get(e => e.h5)[0] -const inputModule = BDModules.get(e => e.inputWrapper)[0] -const contentModule = BDModules.get(e => e.contents)[0] -const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0] - -const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default - -export default class TokenLogin extends React.Component { - constructor(props){ - super(props) - } - - render(){ - return ([ -
, -
-
- Connect with Token -
-
- Input your token below -
-
-
- -
- -
-
- ]); - } -} - -class TokenInput extends React.Component { - constructor(){ - super(...arguments) - - this.state = { - value: "", - error: null - } - } - - render(){ - return [ -
- Token - {this.state.error ? - -{this.state.error} - : null} -
, -
- { - this.setState({ - value: ev.target.value - }) - }}/> -
- ] - } +const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0] +const colors = BDModules.get(e => e.colorHeaderPrimary)[0] +const sizes = BDModules.get(e => e.size24)[0] +const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0] +const marginModule = BDModules.get(e => e.marginBottom8)[0] +const titleModule = BDModules.get(e => e.h5)[0] +const inputModule = BDModules.get(e => e.inputWrapper)[0] +const contentModule = BDModules.get(e => e.contents)[0] +const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0] + +const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default + +export default class TokenLogin extends React.Component { + constructor(props){ + super(props) + } + + render(){ + return ([ +
, +
+
+ Connect with Token +
+
+ Input your token below +
+
+
+ +
+ +
+
+ ]); + } +} + +class TokenInput extends React.Component { + constructor(){ + super(...arguments) + + this.state = { + value: "", + error: null + } + } + + render(){ + return [ +
+ Token + {this.state.error ? + -{this.state.error} + : null} +
, +
+ { + this.setState({ + value: ev.target.value + }) + }}/> +
+ ] + } } \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/lightcordMainProcess.js b/modules/discord_desktop_core/core/app/lightcordMainProcess.js index 02b1df1..95fa5b1 100644 --- a/modules/discord_desktop_core/core/app/lightcordMainProcess.js +++ b/modules/discord_desktop_core/core/app/lightcordMainProcess.js @@ -1,10 +1,10 @@ -/** - * This file is for injections other than already present modules. mostly ipc thing - */ - -const electron = require("electron") - -const UserAgent = electron.session.defaultSession.getUserAgent() -electron.ipcMain.on("LIGHTCORD_GET_USER_AGENT", (event) => { - event.returnValue = UserAgent +/** + * This file is for injections other than already present modules. mostly ipc thing + */ + +const electron = require("electron") + +const UserAgent = electron.session.defaultSession.getUserAgent() +electron.ipcMain.on("LIGHTCORD_GET_USER_AGENT", (event) => { + event.returnValue = UserAgent }) \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/tabs/index.html b/modules/discord_desktop_core/core/app/tabs/index.html index dab88c7..2196ec3 100644 --- a/modules/discord_desktop_core/core/app/tabs/index.html +++ b/modules/discord_desktop_core/core/app/tabs/index.html @@ -1,68 +1,68 @@ - - - - - - - Lightcord Tabs - - - - - -
-
-
-
- - -
-
-
-
- - + + + + + + + Lightcord Tabs + + + + + +
+
+
+
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/modules/discord_desktop_core/core/app/tabs/index.js b/modules/discord_desktop_core/core/app/tabs/index.js index ef500bf..9856979 100644 --- a/modules/discord_desktop_core/core/app/tabs/index.js +++ b/modules/discord_desktop_core/core/app/tabs/index.js @@ -1,182 +1,182 @@ -const fs = require("fs") -const { join } = require("path") -const { pathToFileURL } = require("url") -const ipc = require("../discord_native/renderer/ipc") - -let webviews = new Map() -window.webviews = webviews - -function forwardToCurrentWebview(event){ - return [event, async (...args) => { - let webview = webviews.get(document.querySelector(".chrome-tab[active]")) - if(!webview)return - await webview.ready - webview.send(event, ...args.slice(1)) - }] -} - -/** discord_desktop_core Stable */ -ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_FOCUS")) -ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_BLUR")) -ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_OPEN_VOICE_SETTINGS")) -ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_MUTE")) -ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_DEAFEN")) -ipc.on(...forwardToCurrentWebview("LAUNCH_APPLICATION")) -ipc.on(...forwardToCurrentWebview("SPELLCHECK_RESULT")) -ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_OPENED")) -ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_CLOSED")) -ipc.on(...forwardToCurrentWebview("UPDATE_ERROR")) -ipc.on(...forwardToCurrentWebview("UPDATE_NOT_AVAILABLE")) -ipc.on(...forwardToCurrentWebview("UPDATE_MANUALLY")) -ipc.on(...forwardToCurrentWebview("UPDATE_AVAILABLE")) -ipc.on(...forwardToCurrentWebview("MODULE_INSTALL_PROGRESS")) -ipc.on(...forwardToCurrentWebview("UPDATE_DOWNLOADED")) -ipc.on(...forwardToCurrentWebview("MODULE_INSTALLED")) -ipc.on(...forwardToCurrentWebview("CHECKING_FOR_UPDATES")) -ipc.on(...forwardToCurrentWebview("UPDATER_HISTORY_RESPONSE")) -ipc.on(...forwardToCurrentWebview("ACCESSIBILITY_SUPPORT_CHANGED")) -ipc.on(...forwardToCurrentWebview("HELP_OPEN")) -ipc.on(...forwardToCurrentWebview("USER_SETTINGS_OPEN")) -ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_PATH")) -/** discord_desktop_core Development */ -ipc.on(...forwardToCurrentWebview("NAVIGATE_BACK")) -ipc.on(...forwardToCurrentWebview("NAVIGATE_FORWARD")) - -ipc.on("RELOAD", () => { - let webview = webviews.get(document.querySelector(".chrome-tab[active]")) - if(!webview)return - webview.reload() -}) -ipc.on("NEW_TAB", () => { - chromeTabs.addTab({ - title: 'Lightcord', - favicon: faviconURL - }) -}) -ipc.on("CLOSE_TAB", () => { - let active = document.querySelector("div.chrome-tab[active]") - if(!active)return - chromeTabs.removeTab(active) -}) -ipc.on("OPEN_DEVTOOLS", () => { - let webview = webviews.get(document.querySelector(".chrome-tab[active]")) - if(!webview)return - webview.openDevTools() -}) - -window.onload = () => { - const ChromeTabs = require("chrome-tabs") - require("chrome-tabs/css/chrome-tabs.css") - require("chrome-tabs/css/chrome-tabs-dark-theme.css") - require("./controls.css") - - let tabs = document.querySelector(".chrome-tabs") - let chromeTabs = new ChromeTabs() - chromeTabs.init(tabs) - - tabs.addEventListener('activeTabChange', ({detail}) => { - let webview = webviews.get(detail.tabEl) - if(!webview){ - chromeTabs.removeTab(detail.tabEl) - return - } - let active = Array.from(webviews.values()).find(e => e.classList.contains("active-webview")) - if(active)active.classList.remove("active-webview") - webview.classList.add("active-webview") - }) - tabs.addEventListener('tabAdd', ({detail}) => { - chromeTabs.updateTab(detail.tabEl, { - title: "Lightcord Loading...", - favicon: faviconURL - }) - let webview = document.createElement("webview") - webview.src = "https://discord.com/app" - webview.classList.add("discord-webview") - webview.classList.add("webview-active") - webview.setAttribute("preload", pathToFileURL(join(__dirname, "../mainScreenPreload.js"))) - webview.shadowRoot.childNodes.item(1).style.height = "100%" - webview.enableremotemodule = true - webview.nodeintegration = false - webview.spellcheck = true - webview.webpreferences = "nativeWindowOpen=yes" - webview.enableblinkfeatures = "EnumerateDevices,AudioOutputDevices" - webview.addEventListener("ipc-message", function(...ev){ // TODO: Why don't we receive Ipc Messages, but they get processed anyway (notification, etc) ? - console.log(ev[0].channel) - if(ev[0].channel === "DISCORD_NEW_TAB"){ - chromeTabs.addTab({ - title: 'Lightcord', - favicon: faviconURL - }) - return - } - ipc.send(ev[0].channel.replace("DISCORD_", ""), ev.slice(1)) - }) - webview.addEventListener('page-title-updated', () => { - let el = Array.from(webviews.entries()).find(e => e[1] === webview)[0] - if(!el)return - chromeTabs.updateTab(el, { - favicon: faviconURL, - title: webview.getTitle() - }) - }) - webviews.set(detail.tabEl, webview) - document.querySelector(".documentFull").appendChild(webview) - let r - webview.ready = new Promise(resolve => (r = resolve)) - webview.addEventListener("dom-ready", () => { - r() - webview.send("DISCORD_IS_TAB") - }) - webview.addEventListener("will-navigate", (e) => { - e.preventDefault() - console.log(e, e.url) - }) - }) - tabs.addEventListener('tabRemove', ({detail}) => { - let webview = webviews.get(detail.tabEl) - if(!webview)return - webview.remove() - webviews.delete(detail.tabEl) - if(document.querySelector(".chrome-tabs-content").childNodes.length === 0){ - window.close() - } - }) - - window.addEventListener('keydown', (event) => { - if(event.ctrlKey){ - if(event.key === 't'){ - chromeTabs.addTab({ - title: 'Lightcord', - favicon: faviconURL - }) - }else if(event.key === "w"){ - let active = document.querySelector("div.chrome-tab[active]") - if(!active)return - chromeTabs.removeTab(active) - } - } - }) - setImmediate(() => { - chromeTabs.addTab({ - title: 'Lightcord Loading...', - favicon: faviconURL - }) - }) -} - -require.extensions[".css"] = (m, filename) => { - let content = fs.readFileSync(filename, "binary") - let style = document.createElement("style") - style.id = btoa(filename) - style.innerHTML = content - document.head.appendChild(style) - m.exports = { - id: style.id, - remove(){ - return style.remove() - } - } - return m.exports -} - +const fs = require("fs") +const { join } = require("path") +const { pathToFileURL } = require("url") +const ipc = require("../discord_native/renderer/ipc") + +let webviews = new Map() +window.webviews = webviews + +function forwardToCurrentWebview(event){ + return [event, async (...args) => { + let webview = webviews.get(document.querySelector(".chrome-tab[active]")) + if(!webview)return + await webview.ready + webview.send(event, ...args.slice(1)) + }] +} + +/** discord_desktop_core Stable */ +ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_FOCUS")) +ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_BLUR")) +ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_OPEN_VOICE_SETTINGS")) +ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_MUTE")) +ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_DEAFEN")) +ipc.on(...forwardToCurrentWebview("LAUNCH_APPLICATION")) +ipc.on(...forwardToCurrentWebview("SPELLCHECK_RESULT")) +ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_OPENED")) +ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_CLOSED")) +ipc.on(...forwardToCurrentWebview("UPDATE_ERROR")) +ipc.on(...forwardToCurrentWebview("UPDATE_NOT_AVAILABLE")) +ipc.on(...forwardToCurrentWebview("UPDATE_MANUALLY")) +ipc.on(...forwardToCurrentWebview("UPDATE_AVAILABLE")) +ipc.on(...forwardToCurrentWebview("MODULE_INSTALL_PROGRESS")) +ipc.on(...forwardToCurrentWebview("UPDATE_DOWNLOADED")) +ipc.on(...forwardToCurrentWebview("MODULE_INSTALLED")) +ipc.on(...forwardToCurrentWebview("CHECKING_FOR_UPDATES")) +ipc.on(...forwardToCurrentWebview("UPDATER_HISTORY_RESPONSE")) +ipc.on(...forwardToCurrentWebview("ACCESSIBILITY_SUPPORT_CHANGED")) +ipc.on(...forwardToCurrentWebview("HELP_OPEN")) +ipc.on(...forwardToCurrentWebview("USER_SETTINGS_OPEN")) +ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_PATH")) +/** discord_desktop_core Development */ +ipc.on(...forwardToCurrentWebview("NAVIGATE_BACK")) +ipc.on(...forwardToCurrentWebview("NAVIGATE_FORWARD")) + +ipc.on("RELOAD", () => { + let webview = webviews.get(document.querySelector(".chrome-tab[active]")) + if(!webview)return + webview.reload() +}) +ipc.on("NEW_TAB", () => { + chromeTabs.addTab({ + title: 'Lightcord', + favicon: faviconURL + }) +}) +ipc.on("CLOSE_TAB", () => { + let active = document.querySelector("div.chrome-tab[active]") + if(!active)return + chromeTabs.removeTab(active) +}) +ipc.on("OPEN_DEVTOOLS", () => { + let webview = webviews.get(document.querySelector(".chrome-tab[active]")) + if(!webview)return + webview.openDevTools() +}) + +window.onload = () => { + const ChromeTabs = require("chrome-tabs") + require("chrome-tabs/css/chrome-tabs.css") + require("chrome-tabs/css/chrome-tabs-dark-theme.css") + require("./controls.css") + + let tabs = document.querySelector(".chrome-tabs") + let chromeTabs = new ChromeTabs() + chromeTabs.init(tabs) + + tabs.addEventListener('activeTabChange', ({detail}) => { + let webview = webviews.get(detail.tabEl) + if(!webview){ + chromeTabs.removeTab(detail.tabEl) + return + } + let active = Array.from(webviews.values()).find(e => e.classList.contains("active-webview")) + if(active)active.classList.remove("active-webview") + webview.classList.add("active-webview") + }) + tabs.addEventListener('tabAdd', ({detail}) => { + chromeTabs.updateTab(detail.tabEl, { + title: "Lightcord Loading...", + favicon: faviconURL + }) + let webview = document.createElement("webview") + webview.src = "https://discord.com/app" + webview.classList.add("discord-webview") + webview.classList.add("webview-active") + webview.setAttribute("preload", pathToFileURL(join(__dirname, "../mainScreenPreload.js"))) + webview.shadowRoot.childNodes.item(1).style.height = "100%" + webview.enableremotemodule = true + webview.nodeintegration = false + webview.spellcheck = true + webview.webpreferences = "nativeWindowOpen=yes" + webview.enableblinkfeatures = "EnumerateDevices,AudioOutputDevices" + webview.addEventListener("ipc-message", function(...ev){ // TODO: Why don't we receive Ipc Messages, but they get processed anyway (notification, etc) ? + console.log(ev[0].channel) + if(ev[0].channel === "DISCORD_NEW_TAB"){ + chromeTabs.addTab({ + title: 'Lightcord', + favicon: faviconURL + }) + return + } + ipc.send(ev[0].channel.replace("DISCORD_", ""), ev.slice(1)) + }) + webview.addEventListener('page-title-updated', () => { + let el = Array.from(webviews.entries()).find(e => e[1] === webview)[0] + if(!el)return + chromeTabs.updateTab(el, { + favicon: faviconURL, + title: webview.getTitle() + }) + }) + webviews.set(detail.tabEl, webview) + document.querySelector(".documentFull").appendChild(webview) + let r + webview.ready = new Promise(resolve => (r = resolve)) + webview.addEventListener("dom-ready", () => { + r() + webview.send("DISCORD_IS_TAB") + }) + webview.addEventListener("will-navigate", (e) => { + e.preventDefault() + console.log(e, e.url) + }) + }) + tabs.addEventListener('tabRemove', ({detail}) => { + let webview = webviews.get(detail.tabEl) + if(!webview)return + webview.remove() + webviews.delete(detail.tabEl) + if(document.querySelector(".chrome-tabs-content").childNodes.length === 0){ + window.close() + } + }) + + window.addEventListener('keydown', (event) => { + if(event.ctrlKey){ + if(event.key === 't'){ + chromeTabs.addTab({ + title: 'Lightcord', + favicon: faviconURL + }) + }else if(event.key === "w"){ + let active = document.querySelector("div.chrome-tab[active]") + if(!active)return + chromeTabs.removeTab(active) + } + } + }) + setImmediate(() => { + chromeTabs.addTab({ + title: 'Lightcord Loading...', + favicon: faviconURL + }) + }) +} + +require.extensions[".css"] = (m, filename) => { + let content = fs.readFileSync(filename, "binary") + let style = document.createElement("style") + style.id = btoa(filename) + style.innerHTML = content + document.head.appendChild(style) + m.exports = { + id: style.id, + remove(){ + return style.remove() + } + } + return m.exports +} + const faviconURL = pathToFileURL(join(__dirname, "../images/discord.png")) \ No newline at end of file diff --git a/modules/discord_desktop_core/core/package.json b/modules/discord_desktop_core/core/package.json index 2495061..20ad83e 100644 --- a/modules/discord_desktop_core/core/package.json +++ b/modules/discord_desktop_core/core/package.json @@ -1,16 +1,16 @@ -{ - "name": "discord_desktop_core", - "description": "Discord Client for Desktop - Light version - Core App", - "main": "app/index.js", - "private": true, - "dependencies": { - "chrome-tabs": "^5.4.0", - "invariant": "2.2.4", - "lodash": "4.17.19", - "mkdirp": "^1.0.4", - "promise.allsettled": "^1.0.0", - "request": "2.88.0", - "rimraf": "^2.6.3", - "yauzl": "^2.10.0" - } -} +{ + "name": "discord_desktop_core", + "description": "Discord Client for Desktop - Light version - Core App", + "main": "app/index.js", + "private": true, + "dependencies": { + "chrome-tabs": "^5.4.0", + "invariant": "2.2.4", + "lodash": "4.17.19", + "mkdirp": "^1.0.4", + "promise.allsettled": "^1.0.0", + "request": "2.88.0", + "rimraf": "^2.6.3", + "yauzl": "^2.10.0" + } +} diff --git a/modules/discord_dispatch/index.js b/modules/discord_dispatch/index.js index 372c68e..8a0cea8 100644 --- a/modules/discord_dispatch/index.js +++ b/modules/discord_dispatch/index.js @@ -1,11 +1,11 @@ -const {Dispatch: DispatchNative} = require('./discord_dispatch_'+process.platform+'.node'); - -function dispatchConstructor(jsonConfigString, updateCallback, errorCallback, analyticsCallback) { - const instance = new DispatchNative(jsonConfigString, updateCallback, errorCallback, analyticsCallback); - return { - command: instance.command.bind(instance), - destroy: instance.destroy.bind(instance), - }; -} - -module.exports = {Dispatch: dispatchConstructor}; +const {Dispatch: DispatchNative} = require('./discord_dispatch_'+process.platform+'.node'); + +function dispatchConstructor(jsonConfigString, updateCallback, errorCallback, analyticsCallback) { + const instance = new DispatchNative(jsonConfigString, updateCallback, errorCallback, analyticsCallback); + return { + command: instance.command.bind(instance), + destroy: instance.destroy.bind(instance), + }; +} + +module.exports = {Dispatch: dispatchConstructor}; diff --git a/modules/discord_dispatch/manifest.json b/modules/discord_dispatch/manifest.json index f829ab8..5818e44 100644 --- a/modules/discord_dispatch/manifest.json +++ b/modules/discord_dispatch/manifest.json @@ -1,7 +1,7 @@ -{ - "files": [ - "discord_dispatch.node", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_dispatch.node", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_erlpack/index.js b/modules/discord_erlpack/index.js index b6beb9c..5f35de4 100644 --- a/modules/discord_erlpack/index.js +++ b/modules/discord_erlpack/index.js @@ -1 +1 @@ -module.exports = require('./discord_erlpack_'+process.platform+'.node'); +module.exports = require('./discord_erlpack_'+process.platform+'.node'); diff --git a/modules/discord_erlpack/manifest.json b/modules/discord_erlpack/manifest.json index 7b55ecf..3cd9512 100644 --- a/modules/discord_erlpack/manifest.json +++ b/modules/discord_erlpack/manifest.json @@ -1,7 +1,7 @@ -{ - "files": [ - "discord_erlpack.node", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_erlpack.node", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_game_utils/index.js b/modules/discord_game_utils/index.js index a4c677a..905af9f 100644 --- a/modules/discord_game_utils/index.js +++ b/modules/discord_game_utils/index.js @@ -1 +1 @@ -module.exports = require('./discord_game_utils_'+process.platform+'.node'); +module.exports = require('./discord_game_utils_'+process.platform+'.node'); diff --git a/modules/discord_game_utils/manifest.json b/modules/discord_game_utils/manifest.json index 52df4af..e4d1db2 100644 --- a/modules/discord_game_utils/manifest.json +++ b/modules/discord_game_utils/manifest.json @@ -1,7 +1,7 @@ -{ - "files": [ - "discord_game_utils.node", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_game_utils.node", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_hook/index.js b/modules/discord_hook/index.js index f4160ba..2cf1fa5 100644 --- a/modules/discord_hook/index.js +++ b/modules/discord_hook/index.js @@ -1 +1 @@ -module.exports = require('./discord_hook_'+process.platform+'.node'); +module.exports = require('./discord_hook_'+process.platform+'.node'); diff --git a/modules/discord_krisp/index.js b/modules/discord_krisp/index.js index fab08a3..ecc40cd 100644 --- a/modules/discord_krisp/index.js +++ b/modules/discord_krisp/index.js @@ -1,17 +1,17 @@ -const KrispModule = require('./discord_krisp_'+process.platform+'.node'); - -KrispModule._initialize(); - -KrispModule.getNcModels = function() { - return new Promise(resolve => { - KrispModule._getNcModels(models => resolve(models)); - }); -}; - -KrispModule.getVadModels = function() { - return new Promise(resolve => { - KrispModule._getVadModels(models => resolve(models)); - }); -}; - -module.exports = KrispModule; +const KrispModule = require('./discord_krisp_'+process.platform+'.node'); + +KrispModule._initialize(); + +KrispModule.getNcModels = function() { + return new Promise(resolve => { + KrispModule._getNcModels(models => resolve(models)); + }); +}; + +KrispModule.getVadModels = function() { + return new Promise(resolve => { + KrispModule._getVadModels(models => resolve(models)); + }); +}; + +module.exports = KrispModule; diff --git a/modules/discord_krisp/manifest.json b/modules/discord_krisp/manifest.json index c153007..94cd888 100644 --- a/modules/discord_krisp/manifest.json +++ b/modules/discord_krisp/manifest.json @@ -1,12 +1,12 @@ -{ - "files": [ - "discord_krisp.node", - "VAD_weight.thw", - "NC_weight.thw", - "NC_small_16k.thw", - "c6.s.f.c55d28.thw", - "c6.xs.f.ca03f4.thw", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_krisp.node", + "VAD_weight.thw", + "NC_weight.thw", + "NC_small_16k.thw", + "c6.s.f.c55d28.thw", + "c6.xs.f.ca03f4.thw", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_media/index.js b/modules/discord_media/index.js index 5bef586..6fd380f 100644 --- a/modules/discord_media/index.js +++ b/modules/discord_media/index.js @@ -1,7 +1,7 @@ -const native = require('./discord_media_'+process.platform+'.node'); - -module.exports = { - getSystemAnalyticsBlob() { - return new Promise(resolve => native.getSystemAnalyticsBlob(resolve)); - }, -}; +const native = require('./discord_media_'+process.platform+'.node'); + +module.exports = { + getSystemAnalyticsBlob() { + return new Promise(resolve => native.getSystemAnalyticsBlob(resolve)); + }, +}; diff --git a/modules/discord_media/manifest.json b/modules/discord_media/manifest.json index 5a580d4..2c344f8 100644 --- a/modules/discord_media/manifest.json +++ b/modules/discord_media/manifest.json @@ -1,7 +1,7 @@ -{ - "files": [ - "discord_media.node", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_media.node", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_modules/index.js b/modules/discord_modules/index.js index 1575064..5f26cbe 100644 --- a/modules/discord_modules/index.js +++ b/modules/discord_modules/index.js @@ -1 +1 @@ -module.exports = require('./discord_modules_'+process.platform+'.node'); +module.exports = require('./discord_modules_'+process.platform+'.node'); diff --git a/modules/discord_overlay2/Backoff.js b/modules/discord_overlay2/Backoff.js index 4d0be23..dd27341 100644 --- a/modules/discord_overlay2/Backoff.js +++ b/modules/discord_overlay2/Backoff.js @@ -12,8 +12,8 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var Backoff = /*#__PURE__*/function () { - /** - * Create a backoff instance can automatically backoff retries. + /** + * Create a backoff instance can automatically backoff retries. */ function Backoff() { var min = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 500; @@ -33,24 +33,24 @@ var Backoff = /*#__PURE__*/function () { this.jitter = jitter; this._current = min; } - /** - * Return the number of failures. + /** + * Return the number of failures. */ _createClass(Backoff, [{ key: "succeed", - /** - * Clear any pending callbacks and reset the backoff. + /** + * Clear any pending callbacks and reset the backoff. */ value: function succeed() { this.cancel(); this._fails = 0; this._current = this.min; } - /** - * Incremet the backoff and schedule a callback if provided. + /** + * Incremet the backoff and schedule a callback if provided. */ }, { @@ -91,8 +91,8 @@ var Backoff = /*#__PURE__*/function () { return this._current; } - /** - * Clear any pending callbacks. + /** + * Clear any pending callbacks. */ }, { @@ -110,8 +110,8 @@ var Backoff = /*#__PURE__*/function () { get: function get() { return this._fails; } - /** - * Current backoff value in milliseconds. + /** + * Current backoff value in milliseconds. */ }, { @@ -119,8 +119,8 @@ var Backoff = /*#__PURE__*/function () { get: function get() { return this._current; } - /** - * A callback is going to fire. + /** + * A callback is going to fire. */ }, { diff --git a/modules/discord_overlay2/index.js b/modules/discord_overlay2/index.js index 779c9ef..9c0ff60 100644 --- a/modules/discord_overlay2/index.js +++ b/modules/discord_overlay2/index.js @@ -1,63 +1,63 @@ -const Overlay = require('./overlay_module.js'); - -const isOverlayContext = - (typeof window !== 'undefined' && window != null && window.__OVERLAY__) || - document.getElementById('__OVERLAY__SENTINEL__') != null || - /overlay/.test(window.location.pathname); -const isElectronRenderer = - typeof window !== 'undefined' && window != null && window.DiscordNative && window.DiscordNative.isRenderer; -const features = isElectronRenderer ? window.DiscordNative.features : global.features; - -let clickZoneCallback; -let interceptInput = false; -let imeExclusiveFullscreenCallback; -let perfInfoCallback; - -// [adill] indicates that the race condition between createHostProcess and connectProcess is fixed. remove ~7/2019. -features.declareSupported('create_host_on_attach'); - -function eventHandler(pid, event) { - if (event.message === 'click_zone_event') { - if (clickZoneCallback) { - clickZoneCallback(event.name, event.x, event.y); - } - } else if (event.message === 'ime_exclusive_fullscreen') { - if (imeExclusiveFullscreenCallback) { - imeExclusiveFullscreenCallback(); - } - } else if (event.message === 'perf_info') { - if (perfInfoCallback) { - perfInfoCallback(event.data); - } - } -} - -Overlay._setEventHandler(eventHandler); - -if (isOverlayContext) { - const {URL} = require('url'); - const url = new URL(window.location); - const pid = parseInt(url.searchParams.get('pid')); - Overlay.connectProcess(pid); - - Overlay.rendererStarted = () => { - Overlay.sendCommand(pid, {message: 'notify_renderer_started'}); - }; -} - -Overlay.setClickZoneCallback = (callback) => { - clickZoneCallback = callback; -}; -// NOTE: deprecated. Use `sendCommand` instead. -Overlay.setInputLocked = (locked) => { - interceptInput = !locked; - const payload = {message: 'intercept_input', intercept: interceptInput}; - Overlay.broadcastCommand(payload); -}; -Overlay.setImeExclusiveFullscreenCallback = (callback) => { - imeExclusiveFullscreenCallback = callback; -}; -Overlay.setPerfInfoCallback = (callback) => { - perfInfoCallback = callback; -}; -module.exports = Overlay; +const Overlay = require('./overlay_module.js'); + +const isOverlayContext = + (typeof window !== 'undefined' && window != null && window.__OVERLAY__) || + document.getElementById('__OVERLAY__SENTINEL__') != null || + /overlay/.test(window.location.pathname); +const isElectronRenderer = + typeof window !== 'undefined' && window != null && window.DiscordNative && window.DiscordNative.isRenderer; +const features = isElectronRenderer ? window.DiscordNative.features : global.features; + +let clickZoneCallback; +let interceptInput = false; +let imeExclusiveFullscreenCallback; +let perfInfoCallback; + +// [adill] indicates that the race condition between createHostProcess and connectProcess is fixed. remove ~7/2019. +features.declareSupported('create_host_on_attach'); + +function eventHandler(pid, event) { + if (event.message === 'click_zone_event') { + if (clickZoneCallback) { + clickZoneCallback(event.name, event.x, event.y); + } + } else if (event.message === 'ime_exclusive_fullscreen') { + if (imeExclusiveFullscreenCallback) { + imeExclusiveFullscreenCallback(); + } + } else if (event.message === 'perf_info') { + if (perfInfoCallback) { + perfInfoCallback(event.data); + } + } +} + +Overlay._setEventHandler(eventHandler); + +if (isOverlayContext) { + const {URL} = require('url'); + const url = new URL(window.location); + const pid = parseInt(url.searchParams.get('pid')); + Overlay.connectProcess(pid); + + Overlay.rendererStarted = () => { + Overlay.sendCommand(pid, {message: 'notify_renderer_started'}); + }; +} + +Overlay.setClickZoneCallback = (callback) => { + clickZoneCallback = callback; +}; +// NOTE: deprecated. Use `sendCommand` instead. +Overlay.setInputLocked = (locked) => { + interceptInput = !locked; + const payload = {message: 'intercept_input', intercept: interceptInput}; + Overlay.broadcastCommand(payload); +}; +Overlay.setImeExclusiveFullscreenCallback = (callback) => { + imeExclusiveFullscreenCallback = callback; +}; +Overlay.setPerfInfoCallback = (callback) => { + perfInfoCallback = callback; +}; +module.exports = Overlay; diff --git a/modules/discord_overlay2/manifest.json b/modules/discord_overlay2/manifest.json index a0d24ed..1a543b3 100644 --- a/modules/discord_overlay2/manifest.json +++ b/modules/discord_overlay2/manifest.json @@ -1,14 +1,14 @@ -{ - "files": [ - "discord_overlay2.node", - "host.js", - "overlay_module.js", - "standalone_host.js", - "Backoff.js", - "FeatureFlags.js", - "securityUtils.js", - "start.html", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_overlay2.node", + "host.js", + "overlay_module.js", + "standalone_host.js", + "Backoff.js", + "FeatureFlags.js", + "securityUtils.js", + "start.html", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_overlay2/start.html b/modules/discord_overlay2/start.html index 11a58f0..9fd123a 100644 --- a/modules/discord_overlay2/start.html +++ b/modules/discord_overlay2/start.html @@ -1,18 +1,18 @@ - - - - - Discord Overlay - - -
-
- - - + + + + + Discord Overlay + + +
+
+ + + diff --git a/modules/discord_rpc/index.js b/modules/discord_rpc/index.js index 52bb1be..c058721 100644 --- a/modules/discord_rpc/index.js +++ b/modules/discord_rpc/index.js @@ -1,4 +1,4 @@ -module.exports = { - RPCIPC: require('./RPCIPC'), - RPCWebSocket: require('./RPCWebSocket'), -}; +module.exports = { + RPCIPC: require('./RPCIPC'), + RPCWebSocket: require('./RPCWebSocket'), +}; diff --git a/modules/discord_rpc/manifest.json b/modules/discord_rpc/manifest.json index c16e13b..299070c 100644 --- a/modules/discord_rpc/manifest.json +++ b/modules/discord_rpc/manifest.json @@ -1,27 +1,27 @@ -{ - "files": [ - "node_modules\\.yarn-integrity", - "node_modules\\ws\\browser.js", - "node_modules\\ws\\index.js", - "node_modules\\ws\\LICENSE", - "node_modules\\ws\\package.json", - "node_modules\\ws\\README.md", - "node_modules\\ws\\lib\\buffer-util.js", - "node_modules\\ws\\lib\\constants.js", - "node_modules\\ws\\lib\\event-target.js", - "node_modules\\ws\\lib\\extension.js", - "node_modules\\ws\\lib\\limiter.js", - "node_modules\\ws\\lib\\permessage-deflate.js", - "node_modules\\ws\\lib\\receiver.js", - "node_modules\\ws\\lib\\sender.js", - "node_modules\\ws\\lib\\stream.js", - "node_modules\\ws\\lib\\validation.js", - "node_modules\\ws\\lib\\websocket-server.js", - "node_modules\\ws\\lib\\websocket.js", - "RPCIPC.js", - "RPCWebSocket.js", - "safeEmitter.js", - "index.js", - "manifest.json" - ] +{ + "files": [ + "node_modules\\.yarn-integrity", + "node_modules\\ws\\browser.js", + "node_modules\\ws\\index.js", + "node_modules\\ws\\LICENSE", + "node_modules\\ws\\package.json", + "node_modules\\ws\\README.md", + "node_modules\\ws\\lib\\buffer-util.js", + "node_modules\\ws\\lib\\constants.js", + "node_modules\\ws\\lib\\event-target.js", + "node_modules\\ws\\lib\\extension.js", + "node_modules\\ws\\lib\\limiter.js", + "node_modules\\ws\\lib\\permessage-deflate.js", + "node_modules\\ws\\lib\\receiver.js", + "node_modules\\ws\\lib\\sender.js", + "node_modules\\ws\\lib\\stream.js", + "node_modules\\ws\\lib\\validation.js", + "node_modules\\ws\\lib\\websocket-server.js", + "node_modules\\ws\\lib\\websocket.js", + "RPCIPC.js", + "RPCWebSocket.js", + "safeEmitter.js", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_spellcheck/index.js b/modules/discord_spellcheck/index.js index 2523a84..07b04b6 100644 --- a/modules/discord_spellcheck/index.js +++ b/modules/discord_spellcheck/index.js @@ -1,24 +1,24 @@ -// [adill] This is not context aware and, as such, won't load in newer Electron versions -// We can and should strip `discord_spellcheck` down to CLD only once all channels are on Electron 9 -function getLegacySpellchecker() { - const Spellchecker = require('spellchecker').Spellchecker; - const instance = new Spellchecker(); - return { - setDictionary: instance.setDictionary.bind(instance), - getAvailableDictionaries: instance.getAvailableDictionaries.bind(instance), - isMisspelled: instance.isMisspelled.bind(instance), - getCorrectionsForMisspelling: instance.getCorrectionsForMisspelling.bind(instance), - }; -} - -// [adill] This is not context aware and, as such, won't load in newer Electron versions -// We can and should strip `discord_spellcheck` down to CLD only once all channels are on Electron 9 -function getKeyboardLayout() { - return require('keyboard-layout'); -} - -module.exports = { - cld: require('cld'), - getLegacySpellchecker, - getKeyboardLayout, -}; +// [adill] This is not context aware and, as such, won't load in newer Electron versions +// We can and should strip `discord_spellcheck` down to CLD only once all channels are on Electron 9 +function getLegacySpellchecker() { + const Spellchecker = require('spellchecker').Spellchecker; + const instance = new Spellchecker(); + return { + setDictionary: instance.setDictionary.bind(instance), + getAvailableDictionaries: instance.getAvailableDictionaries.bind(instance), + isMisspelled: instance.isMisspelled.bind(instance), + getCorrectionsForMisspelling: instance.getCorrectionsForMisspelling.bind(instance), + }; +} + +// [adill] This is not context aware and, as such, won't load in newer Electron versions +// We can and should strip `discord_spellcheck` down to CLD only once all channels are on Electron 9 +function getKeyboardLayout() { + return require('keyboard-layout'); +} + +module.exports = { + cld: require('cld'), + getLegacySpellchecker, + getKeyboardLayout, +}; diff --git a/modules/discord_spellcheck/manifest.json b/modules/discord_spellcheck/manifest.json index 60d88c2..aeef417 100644 --- a/modules/discord_spellcheck/manifest.json +++ b/modules/discord_spellcheck/manifest.json @@ -1,220 +1,220 @@ -{ - "files": [ - "node_modules\\.yarn-integrity", - "node_modules\\.bin\\rimraf", - "node_modules\\any-promise\\.jshintrc", - "node_modules\\any-promise\\.npmignore", - "node_modules\\any-promise\\implementation.js", - "node_modules\\any-promise\\index.js", - "node_modules\\any-promise\\LICENSE", - "node_modules\\any-promise\\loader.js", - "node_modules\\any-promise\\optional.js", - "node_modules\\any-promise\\package.json", - "node_modules\\any-promise\\README.md", - "node_modules\\any-promise\\register-shim.js", - "node_modules\\any-promise\\register.js", - "node_modules\\any-promise\\register\\bluebird.js", - "node_modules\\any-promise\\register\\es6-promise.js", - "node_modules\\any-promise\\register\\lie.js", - "node_modules\\any-promise\\register\\native-promise-only.js", - "node_modules\\any-promise\\register\\pinkie.js", - "node_modules\\any-promise\\register\\promise.js", - "node_modules\\any-promise\\register\\q.js", - "node_modules\\any-promise\\register\\rsvp.js", - "node_modules\\any-promise\\register\\vow.js", - "node_modules\\any-promise\\register\\when.js", - "node_modules\\balanced-match\\.npmignore", - "node_modules\\balanced-match\\index.js", - "node_modules\\balanced-match\\LICENSE.md", - "node_modules\\balanced-match\\package.json", - "node_modules\\balanced-match\\README.md", - "node_modules\\brace-expansion\\index.js", - "node_modules\\brace-expansion\\LICENSE", - "node_modules\\brace-expansion\\package.json", - "node_modules\\brace-expansion\\README.md", - "node_modules\\cld\\index.js", - "node_modules\\cld\\LICENSE", - "node_modules\\cld\\package.json", - "node_modules\\cld\\README.md", - "node_modules\\cld\\bin\\postinstall.js", - "node_modules\\cld\\build\\Release\\cld.node", - "node_modules\\cld\\deps\\cld\\LICENSE", - "node_modules\\cld\\node_modules\\.bin\\rimraf", - "node_modules\\concat-map\\index.js", - "node_modules\\concat-map\\LICENSE", - "node_modules\\concat-map\\package.json", - "node_modules\\concat-map\\example\\map.js", - "node_modules\\event-kit\\api.json", - "node_modules\\event-kit\\LICENSE.md", - "node_modules\\event-kit\\package.json", - "node_modules\\event-kit\\README.md", - "node_modules\\event-kit\\dist\\composite-disposable.js", - "node_modules\\event-kit\\dist\\disposable.js", - "node_modules\\event-kit\\dist\\emitter.js", - "node_modules\\event-kit\\dist\\event-kit.js", - "node_modules\\fs.realpath\\index.js", - "node_modules\\fs.realpath\\LICENSE", - "node_modules\\fs.realpath\\old.js", - "node_modules\\fs.realpath\\package.json", - "node_modules\\fs.realpath\\README.md", - "node_modules\\glob\\common.js", - "node_modules\\glob\\glob.js", - "node_modules\\glob\\LICENSE", - "node_modules\\glob\\package.json", - "node_modules\\glob\\README.md", - "node_modules\\glob\\sync.js", - "node_modules\\inflight\\inflight.js", - "node_modules\\inflight\\LICENSE", - "node_modules\\inflight\\package.json", - "node_modules\\inflight\\README.md", - "node_modules\\inherits\\inherits.js", - "node_modules\\inherits\\inherits_browser.js", - "node_modules\\inherits\\LICENSE", - "node_modules\\inherits\\package.json", - "node_modules\\inherits\\README.md", - "node_modules\\keyboard-layout\\.node-version", - "node_modules\\keyboard-layout\\LICENSE.md", - "node_modules\\keyboard-layout\\package.json", - "node_modules\\keyboard-layout\\README.md", - "node_modules\\keyboard-layout\\build\\Release\\keyboard-layout-manager.node", - "node_modules\\keyboard-layout\\lib\\keyboard-layout.js", - "node_modules\\minimatch\\LICENSE", - "node_modules\\minimatch\\minimatch.js", - "node_modules\\minimatch\\package.json", - "node_modules\\minimatch\\README.md", - "node_modules\\nan\\CHANGELOG.md", - "node_modules\\nan\\include_dirs.js", - "node_modules\\nan\\LICENSE.md", - "node_modules\\nan\\package.json", - "node_modules\\nan\\README.md", - "node_modules\\nan\\doc\\asyncworker.md", - "node_modules\\nan\\doc\\buffers.md", - "node_modules\\nan\\doc\\callback.md", - "node_modules\\nan\\doc\\converters.md", - "node_modules\\nan\\doc\\errors.md", - "node_modules\\nan\\doc\\json.md", - "node_modules\\nan\\doc\\maybe_types.md", - "node_modules\\nan\\doc\\methods.md", - "node_modules\\nan\\doc\\new.md", - "node_modules\\nan\\doc\\node_misc.md", - "node_modules\\nan\\doc\\object_wrappers.md", - "node_modules\\nan\\doc\\persistent.md", - "node_modules\\nan\\doc\\scopes.md", - "node_modules\\nan\\doc\\script.md", - "node_modules\\nan\\doc\\string_bytes.md", - "node_modules\\nan\\doc\\v8_internals.md", - "node_modules\\nan\\doc\\v8_misc.md", - "node_modules\\nan\\tools\\1to2.js", - "node_modules\\nan\\tools\\package.json", - "node_modules\\nan\\tools\\README.md", - "node_modules\\node-addon-api\\.editorconfig", - "node_modules\\node-addon-api\\CHANGELOG.md", - "node_modules\\node-addon-api\\CODE_OF_CONDUCT.md", - "node_modules\\node-addon-api\\CONTRIBUTING.md", - "node_modules\\node-addon-api\\index.js", - "node_modules\\node-addon-api\\LICENSE.md", - "node_modules\\node-addon-api\\package.json", - "node_modules\\node-addon-api\\README.md", - "node_modules\\node-addon-api\\doc\\array_buffer.md", - "node_modules\\node-addon-api\\doc\\async_context.md", - "node_modules\\node-addon-api\\doc\\async_operations.md", - "node_modules\\node-addon-api\\doc\\async_worker.md", - "node_modules\\node-addon-api\\doc\\basic_types.md", - "node_modules\\node-addon-api\\doc\\bigint.md", - "node_modules\\node-addon-api\\doc\\boolean.md", - "node_modules\\node-addon-api\\doc\\buffer.md", - "node_modules\\node-addon-api\\doc\\callbackinfo.md", - "node_modules\\node-addon-api\\doc\\callback_scope.md", - "node_modules\\node-addon-api\\doc\\checker-tool.md", - "node_modules\\node-addon-api\\doc\\class_property_descriptor.md", - "node_modules\\node-addon-api\\doc\\cmake-js.md", - "node_modules\\node-addon-api\\doc\\conversion-tool.md", - "node_modules\\node-addon-api\\doc\\creating_a_release.md", - "node_modules\\node-addon-api\\doc\\dataview.md", - "node_modules\\node-addon-api\\doc\\Doxyfile", - "node_modules\\node-addon-api\\doc\\env.md", - "node_modules\\node-addon-api\\doc\\error.md", - "node_modules\\node-addon-api\\doc\\error_handling.md", - "node_modules\\node-addon-api\\doc\\escapable_handle_scope.md", - "node_modules\\node-addon-api\\doc\\external.md", - "node_modules\\node-addon-api\\doc\\function.md", - "node_modules\\node-addon-api\\doc\\function_reference.md", - "node_modules\\node-addon-api\\doc\\generator.md", - "node_modules\\node-addon-api\\doc\\handle_scope.md", - "node_modules\\node-addon-api\\doc\\memory_management.md", - "node_modules\\node-addon-api\\doc\\node-gyp.md", - "node_modules\\node-addon-api\\doc\\number.md", - "node_modules\\node-addon-api\\doc\\object.md", - "node_modules\\node-addon-api\\doc\\object_lifetime_management.md", - "node_modules\\node-addon-api\\doc\\object_reference.md", - "node_modules\\node-addon-api\\doc\\object_wrap.md", - "node_modules\\node-addon-api\\doc\\prebuild_tools.md", - "node_modules\\node-addon-api\\doc\\promises.md", - "node_modules\\node-addon-api\\doc\\property_descriptor.md", - "node_modules\\node-addon-api\\doc\\range_error.md", - "node_modules\\node-addon-api\\doc\\reference.md", - "node_modules\\node-addon-api\\doc\\setup.md", - "node_modules\\node-addon-api\\doc\\string.md", - "node_modules\\node-addon-api\\doc\\symbol.md", - "node_modules\\node-addon-api\\doc\\threadsafe_function.md", - "node_modules\\node-addon-api\\doc\\typed_array.md", - "node_modules\\node-addon-api\\doc\\typed_array_of.md", - "node_modules\\node-addon-api\\doc\\type_error.md", - "node_modules\\node-addon-api\\doc\\value.md", - "node_modules\\node-addon-api\\doc\\version_management.md", - "node_modules\\node-addon-api\\doc\\working_with_javascript_values.md", - "node_modules\\node-addon-api\\tools\\check-napi.js", - "node_modules\\node-addon-api\\tools\\conversion.js", - "node_modules\\node-addon-api\\tools\\README.md", - "node_modules\\once\\LICENSE", - "node_modules\\once\\once.js", - "node_modules\\once\\package.json", - "node_modules\\once\\README.md", - "node_modules\\path-is-absolute\\index.js", - "node_modules\\path-is-absolute\\license", - "node_modules\\path-is-absolute\\package.json", - "node_modules\\path-is-absolute\\readme.md", - "node_modules\\rimraf\\bin.js", - "node_modules\\rimraf\\LICENSE", - "node_modules\\rimraf\\package.json", - "node_modules\\rimraf\\README.md", - "node_modules\\rimraf\\rimraf.js", - "node_modules\\rimraf\\node_modules\\glob\\changelog.md", - "node_modules\\rimraf\\node_modules\\glob\\common.js", - "node_modules\\rimraf\\node_modules\\glob\\glob.js", - "node_modules\\rimraf\\node_modules\\glob\\LICENSE", - "node_modules\\rimraf\\node_modules\\glob\\package.json", - "node_modules\\rimraf\\node_modules\\glob\\README.md", - "node_modules\\rimraf\\node_modules\\glob\\sync.js", - "node_modules\\spellchecker\\.gitignore", - "node_modules\\spellchecker\\.npmignore", - "node_modules\\spellchecker\\LICENSE.md", - "node_modules\\spellchecker\\package-lock.json", - "node_modules\\spellchecker\\package.json", - "node_modules\\spellchecker\\README.md", - "node_modules\\spellchecker\\build\\Release\\spellchecker.node", - "node_modules\\spellchecker\\lib\\spellchecker.js", - "node_modules\\spellchecker\\vendor\\hunspell\\AUTHORS", - "node_modules\\spellchecker\\vendor\\hunspell\\BUGS", - "node_modules\\spellchecker\\vendor\\hunspell\\ChangeLog", - "node_modules\\spellchecker\\vendor\\hunspell\\COPYING", - "node_modules\\spellchecker\\vendor\\hunspell\\README", - "node_modules\\spellchecker\\vendor\\hunspell\\THANKS", - "node_modules\\spellchecker\\vendor\\hunspell\\src\\hunspell\\README", - "node_modules\\underscore\\LICENSE", - "node_modules\\underscore\\package.json", - "node_modules\\underscore\\README.md", - "node_modules\\underscore\\underscore-min.js", - "node_modules\\underscore\\underscore.js", - "node_modules\\underscore\\modules\\.eslintrc", - "node_modules\\underscore\\modules\\index-all.js", - "node_modules\\underscore\\modules\\index-default.js", - "node_modules\\underscore\\modules\\index.js", - "node_modules\\wrappy\\LICENSE", - "node_modules\\wrappy\\package.json", - "node_modules\\wrappy\\README.md", - "node_modules\\wrappy\\wrappy.js", - "index.js", - "manifest.json" - ] +{ + "files": [ + "node_modules\\.yarn-integrity", + "node_modules\\.bin\\rimraf", + "node_modules\\any-promise\\.jshintrc", + "node_modules\\any-promise\\.npmignore", + "node_modules\\any-promise\\implementation.js", + "node_modules\\any-promise\\index.js", + "node_modules\\any-promise\\LICENSE", + "node_modules\\any-promise\\loader.js", + "node_modules\\any-promise\\optional.js", + "node_modules\\any-promise\\package.json", + "node_modules\\any-promise\\README.md", + "node_modules\\any-promise\\register-shim.js", + "node_modules\\any-promise\\register.js", + "node_modules\\any-promise\\register\\bluebird.js", + "node_modules\\any-promise\\register\\es6-promise.js", + "node_modules\\any-promise\\register\\lie.js", + "node_modules\\any-promise\\register\\native-promise-only.js", + "node_modules\\any-promise\\register\\pinkie.js", + "node_modules\\any-promise\\register\\promise.js", + "node_modules\\any-promise\\register\\q.js", + "node_modules\\any-promise\\register\\rsvp.js", + "node_modules\\any-promise\\register\\vow.js", + "node_modules\\any-promise\\register\\when.js", + "node_modules\\balanced-match\\.npmignore", + "node_modules\\balanced-match\\index.js", + "node_modules\\balanced-match\\LICENSE.md", + "node_modules\\balanced-match\\package.json", + "node_modules\\balanced-match\\README.md", + "node_modules\\brace-expansion\\index.js", + "node_modules\\brace-expansion\\LICENSE", + "node_modules\\brace-expansion\\package.json", + "node_modules\\brace-expansion\\README.md", + "node_modules\\cld\\index.js", + "node_modules\\cld\\LICENSE", + "node_modules\\cld\\package.json", + "node_modules\\cld\\README.md", + "node_modules\\cld\\bin\\postinstall.js", + "node_modules\\cld\\build\\Release\\cld.node", + "node_modules\\cld\\deps\\cld\\LICENSE", + "node_modules\\cld\\node_modules\\.bin\\rimraf", + "node_modules\\concat-map\\index.js", + "node_modules\\concat-map\\LICENSE", + "node_modules\\concat-map\\package.json", + "node_modules\\concat-map\\example\\map.js", + "node_modules\\event-kit\\api.json", + "node_modules\\event-kit\\LICENSE.md", + "node_modules\\event-kit\\package.json", + "node_modules\\event-kit\\README.md", + "node_modules\\event-kit\\dist\\composite-disposable.js", + "node_modules\\event-kit\\dist\\disposable.js", + "node_modules\\event-kit\\dist\\emitter.js", + "node_modules\\event-kit\\dist\\event-kit.js", + "node_modules\\fs.realpath\\index.js", + "node_modules\\fs.realpath\\LICENSE", + "node_modules\\fs.realpath\\old.js", + "node_modules\\fs.realpath\\package.json", + "node_modules\\fs.realpath\\README.md", + "node_modules\\glob\\common.js", + "node_modules\\glob\\glob.js", + "node_modules\\glob\\LICENSE", + "node_modules\\glob\\package.json", + "node_modules\\glob\\README.md", + "node_modules\\glob\\sync.js", + "node_modules\\inflight\\inflight.js", + "node_modules\\inflight\\LICENSE", + "node_modules\\inflight\\package.json", + "node_modules\\inflight\\README.md", + "node_modules\\inherits\\inherits.js", + "node_modules\\inherits\\inherits_browser.js", + "node_modules\\inherits\\LICENSE", + "node_modules\\inherits\\package.json", + "node_modules\\inherits\\README.md", + "node_modules\\keyboard-layout\\.node-version", + "node_modules\\keyboard-layout\\LICENSE.md", + "node_modules\\keyboard-layout\\package.json", + "node_modules\\keyboard-layout\\README.md", + "node_modules\\keyboard-layout\\build\\Release\\keyboard-layout-manager.node", + "node_modules\\keyboard-layout\\lib\\keyboard-layout.js", + "node_modules\\minimatch\\LICENSE", + "node_modules\\minimatch\\minimatch.js", + "node_modules\\minimatch\\package.json", + "node_modules\\minimatch\\README.md", + "node_modules\\nan\\CHANGELOG.md", + "node_modules\\nan\\include_dirs.js", + "node_modules\\nan\\LICENSE.md", + "node_modules\\nan\\package.json", + "node_modules\\nan\\README.md", + "node_modules\\nan\\doc\\asyncworker.md", + "node_modules\\nan\\doc\\buffers.md", + "node_modules\\nan\\doc\\callback.md", + "node_modules\\nan\\doc\\converters.md", + "node_modules\\nan\\doc\\errors.md", + "node_modules\\nan\\doc\\json.md", + "node_modules\\nan\\doc\\maybe_types.md", + "node_modules\\nan\\doc\\methods.md", + "node_modules\\nan\\doc\\new.md", + "node_modules\\nan\\doc\\node_misc.md", + "node_modules\\nan\\doc\\object_wrappers.md", + "node_modules\\nan\\doc\\persistent.md", + "node_modules\\nan\\doc\\scopes.md", + "node_modules\\nan\\doc\\script.md", + "node_modules\\nan\\doc\\string_bytes.md", + "node_modules\\nan\\doc\\v8_internals.md", + "node_modules\\nan\\doc\\v8_misc.md", + "node_modules\\nan\\tools\\1to2.js", + "node_modules\\nan\\tools\\package.json", + "node_modules\\nan\\tools\\README.md", + "node_modules\\node-addon-api\\.editorconfig", + "node_modules\\node-addon-api\\CHANGELOG.md", + "node_modules\\node-addon-api\\CODE_OF_CONDUCT.md", + "node_modules\\node-addon-api\\CONTRIBUTING.md", + "node_modules\\node-addon-api\\index.js", + "node_modules\\node-addon-api\\LICENSE.md", + "node_modules\\node-addon-api\\package.json", + "node_modules\\node-addon-api\\README.md", + "node_modules\\node-addon-api\\doc\\array_buffer.md", + "node_modules\\node-addon-api\\doc\\async_context.md", + "node_modules\\node-addon-api\\doc\\async_operations.md", + "node_modules\\node-addon-api\\doc\\async_worker.md", + "node_modules\\node-addon-api\\doc\\basic_types.md", + "node_modules\\node-addon-api\\doc\\bigint.md", + "node_modules\\node-addon-api\\doc\\boolean.md", + "node_modules\\node-addon-api\\doc\\buffer.md", + "node_modules\\node-addon-api\\doc\\callbackinfo.md", + "node_modules\\node-addon-api\\doc\\callback_scope.md", + "node_modules\\node-addon-api\\doc\\checker-tool.md", + "node_modules\\node-addon-api\\doc\\class_property_descriptor.md", + "node_modules\\node-addon-api\\doc\\cmake-js.md", + "node_modules\\node-addon-api\\doc\\conversion-tool.md", + "node_modules\\node-addon-api\\doc\\creating_a_release.md", + "node_modules\\node-addon-api\\doc\\dataview.md", + "node_modules\\node-addon-api\\doc\\Doxyfile", + "node_modules\\node-addon-api\\doc\\env.md", + "node_modules\\node-addon-api\\doc\\error.md", + "node_modules\\node-addon-api\\doc\\error_handling.md", + "node_modules\\node-addon-api\\doc\\escapable_handle_scope.md", + "node_modules\\node-addon-api\\doc\\external.md", + "node_modules\\node-addon-api\\doc\\function.md", + "node_modules\\node-addon-api\\doc\\function_reference.md", + "node_modules\\node-addon-api\\doc\\generator.md", + "node_modules\\node-addon-api\\doc\\handle_scope.md", + "node_modules\\node-addon-api\\doc\\memory_management.md", + "node_modules\\node-addon-api\\doc\\node-gyp.md", + "node_modules\\node-addon-api\\doc\\number.md", + "node_modules\\node-addon-api\\doc\\object.md", + "node_modules\\node-addon-api\\doc\\object_lifetime_management.md", + "node_modules\\node-addon-api\\doc\\object_reference.md", + "node_modules\\node-addon-api\\doc\\object_wrap.md", + "node_modules\\node-addon-api\\doc\\prebuild_tools.md", + "node_modules\\node-addon-api\\doc\\promises.md", + "node_modules\\node-addon-api\\doc\\property_descriptor.md", + "node_modules\\node-addon-api\\doc\\range_error.md", + "node_modules\\node-addon-api\\doc\\reference.md", + "node_modules\\node-addon-api\\doc\\setup.md", + "node_modules\\node-addon-api\\doc\\string.md", + "node_modules\\node-addon-api\\doc\\symbol.md", + "node_modules\\node-addon-api\\doc\\threadsafe_function.md", + "node_modules\\node-addon-api\\doc\\typed_array.md", + "node_modules\\node-addon-api\\doc\\typed_array_of.md", + "node_modules\\node-addon-api\\doc\\type_error.md", + "node_modules\\node-addon-api\\doc\\value.md", + "node_modules\\node-addon-api\\doc\\version_management.md", + "node_modules\\node-addon-api\\doc\\working_with_javascript_values.md", + "node_modules\\node-addon-api\\tools\\check-napi.js", + "node_modules\\node-addon-api\\tools\\conversion.js", + "node_modules\\node-addon-api\\tools\\README.md", + "node_modules\\once\\LICENSE", + "node_modules\\once\\once.js", + "node_modules\\once\\package.json", + "node_modules\\once\\README.md", + "node_modules\\path-is-absolute\\index.js", + "node_modules\\path-is-absolute\\license", + "node_modules\\path-is-absolute\\package.json", + "node_modules\\path-is-absolute\\readme.md", + "node_modules\\rimraf\\bin.js", + "node_modules\\rimraf\\LICENSE", + "node_modules\\rimraf\\package.json", + "node_modules\\rimraf\\README.md", + "node_modules\\rimraf\\rimraf.js", + "node_modules\\rimraf\\node_modules\\glob\\changelog.md", + "node_modules\\rimraf\\node_modules\\glob\\common.js", + "node_modules\\rimraf\\node_modules\\glob\\glob.js", + "node_modules\\rimraf\\node_modules\\glob\\LICENSE", + "node_modules\\rimraf\\node_modules\\glob\\package.json", + "node_modules\\rimraf\\node_modules\\glob\\README.md", + "node_modules\\rimraf\\node_modules\\glob\\sync.js", + "node_modules\\spellchecker\\.gitignore", + "node_modules\\spellchecker\\.npmignore", + "node_modules\\spellchecker\\LICENSE.md", + "node_modules\\spellchecker\\package-lock.json", + "node_modules\\spellchecker\\package.json", + "node_modules\\spellchecker\\README.md", + "node_modules\\spellchecker\\build\\Release\\spellchecker.node", + "node_modules\\spellchecker\\lib\\spellchecker.js", + "node_modules\\spellchecker\\vendor\\hunspell\\AUTHORS", + "node_modules\\spellchecker\\vendor\\hunspell\\BUGS", + "node_modules\\spellchecker\\vendor\\hunspell\\ChangeLog", + "node_modules\\spellchecker\\vendor\\hunspell\\COPYING", + "node_modules\\spellchecker\\vendor\\hunspell\\README", + "node_modules\\spellchecker\\vendor\\hunspell\\THANKS", + "node_modules\\spellchecker\\vendor\\hunspell\\src\\hunspell\\README", + "node_modules\\underscore\\LICENSE", + "node_modules\\underscore\\package.json", + "node_modules\\underscore\\README.md", + "node_modules\\underscore\\underscore-min.js", + "node_modules\\underscore\\underscore.js", + "node_modules\\underscore\\modules\\.eslintrc", + "node_modules\\underscore\\modules\\index-all.js", + "node_modules\\underscore\\modules\\index-default.js", + "node_modules\\underscore\\modules\\index.js", + "node_modules\\wrappy\\LICENSE", + "node_modules\\wrappy\\package.json", + "node_modules\\wrappy\\README.md", + "node_modules\\wrappy\\wrappy.js", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_utils/index.js b/modules/discord_utils/index.js index 29957a3..88878ce 100644 --- a/modules/discord_utils/index.js +++ b/modules/discord_utils/index.js @@ -1,57 +1,57 @@ -const execa = require('execa'); -const superagent = require('superagent'); - -module.exports = require('./discord_utils_'+process.platform+'.node'); -module.exports.clearCandidateGamesCallback = module.exports.setCandidateGamesCallback; - -function parseNvidiaSmiOutput(result) { - if (!result || !result.stdout) { - return {error: 'nvidia-smi produced no output'}; - } - - const match = result.stdout.match(/Driver Version: (\d+)\.(\d+)/); - - if (match.length === 3) { - return {major: parseInt(match[1], 10), minor: parseInt(match[2], 10)}; - } else { - return {error: 'failed to parse nvidia-smi output'}; - } -} - -module.exports.getGPUDriverVersions = async () => { - if (process.platform !== 'win32') { - return {}; - } - - const result = {}; - const nvidiaSmiPath = `${process.env['ProgramW6432']}/NVIDIA Corporation/NVSMI/nvidia-smi.exe`; - - try { - result.nvidia = parseNvidiaSmiOutput(await execa(nvidiaSmiPath, [])); - } catch (e) { - result.nvidia = {error: e.toString()}; - } - - return result; -}; - -module.exports.submitLiveCrashReport = async (channel, sentryMetadata) => { - const path = module.exports._generateLiveMinidump(); - - if (!path) { - return null; - } - - await superagent - .post('https://sentry.io/api/146342/minidump/?sentry_key=f11e8c3e62cb46b5a006c339b2086ba3') - .attach('upload_file_minidump', path) - .field('channel', channel) - .field('sentry', JSON.stringify(sentryMetadata)); -}; - -// [adill] safe to remove once the web code from -// https://github.com/discord/discord/commit/ced0cd30085f9406e22ee43f77888591156ef58a is deployed everywhere -// ~ETA 5/20/20 -module.exports.inputGetIdleMilliseconds = (callback) => { - callback(0); -}; +const execa = require('execa'); +const superagent = require('superagent'); + +module.exports = require('./discord_utils_'+process.platform+'.node'); +module.exports.clearCandidateGamesCallback = module.exports.setCandidateGamesCallback; + +function parseNvidiaSmiOutput(result) { + if (!result || !result.stdout) { + return {error: 'nvidia-smi produced no output'}; + } + + const match = result.stdout.match(/Driver Version: (\d+)\.(\d+)/); + + if (match.length === 3) { + return {major: parseInt(match[1], 10), minor: parseInt(match[2], 10)}; + } else { + return {error: 'failed to parse nvidia-smi output'}; + } +} + +module.exports.getGPUDriverVersions = async () => { + if (process.platform !== 'win32') { + return {}; + } + + const result = {}; + const nvidiaSmiPath = `${process.env['ProgramW6432']}/NVIDIA Corporation/NVSMI/nvidia-smi.exe`; + + try { + result.nvidia = parseNvidiaSmiOutput(await execa(nvidiaSmiPath, [])); + } catch (e) { + result.nvidia = {error: e.toString()}; + } + + return result; +}; + +module.exports.submitLiveCrashReport = async (channel, sentryMetadata) => { + const path = module.exports._generateLiveMinidump(); + + if (!path) { + return null; + } + + await superagent + .post('https://sentry.io/api/146342/minidump/?sentry_key=f11e8c3e62cb46b5a006c339b2086ba3') + .attach('upload_file_minidump', path) + .field('channel', channel) + .field('sentry', JSON.stringify(sentryMetadata)); +}; + +// [adill] safe to remove once the web code from +// https://github.com/discord/discord/commit/ced0cd30085f9406e22ee43f77888591156ef58a is deployed everywhere +// ~ETA 5/20/20 +module.exports.inputGetIdleMilliseconds = (callback) => { + callback(0); +}; diff --git a/modules/discord_utils/manifest.json b/modules/discord_utils/manifest.json index cb656da..be6806e 100644 --- a/modules/discord_utils/manifest.json +++ b/modules/discord_utils/manifest.json @@ -1,313 +1,313 @@ -{ - "files": [ - "node_modules\\.yarn-integrity", - "node_modules\\.bin\\mime", - "node_modules\\.bin\\semver", - "node_modules\\.bin\\which", - "node_modules\\asynckit\\bench.js", - "node_modules\\asynckit\\index.js", - "node_modules\\asynckit\\LICENSE", - "node_modules\\asynckit\\package.json", - "node_modules\\asynckit\\parallel.js", - "node_modules\\asynckit\\README.md", - "node_modules\\asynckit\\serial.js", - "node_modules\\asynckit\\serialOrdered.js", - "node_modules\\asynckit\\stream.js", - "node_modules\\asynckit\\lib\\abort.js", - "node_modules\\asynckit\\lib\\async.js", - "node_modules\\asynckit\\lib\\defer.js", - "node_modules\\asynckit\\lib\\iterate.js", - "node_modules\\asynckit\\lib\\readable_asynckit.js", - "node_modules\\asynckit\\lib\\readable_parallel.js", - "node_modules\\asynckit\\lib\\readable_serial.js", - "node_modules\\asynckit\\lib\\readable_serial_ordered.js", - "node_modules\\asynckit\\lib\\state.js", - "node_modules\\asynckit\\lib\\streamify.js", - "node_modules\\asynckit\\lib\\terminator.js", - "node_modules\\combined-stream\\License", - "node_modules\\combined-stream\\package.json", - "node_modules\\combined-stream\\Readme.md", - "node_modules\\combined-stream\\lib\\combined_stream.js", - "node_modules\\component-emitter\\History.md", - "node_modules\\component-emitter\\index.js", - "node_modules\\component-emitter\\LICENSE", - "node_modules\\component-emitter\\package.json", - "node_modules\\component-emitter\\Readme.md", - "node_modules\\cookiejar\\cookiejar.js", - "node_modules\\cookiejar\\LICENSE", - "node_modules\\cookiejar\\package.json", - "node_modules\\cookiejar\\readme.md", - "node_modules\\cross-spawn\\CHANGELOG.md", - "node_modules\\cross-spawn\\index.js", - "node_modules\\cross-spawn\\LICENSE", - "node_modules\\cross-spawn\\package.json", - "node_modules\\cross-spawn\\README.md", - "node_modules\\cross-spawn\\lib\\enoent.js", - "node_modules\\cross-spawn\\lib\\parse.js", - "node_modules\\cross-spawn\\lib\\util\\escape.js", - "node_modules\\cross-spawn\\lib\\util\\readShebang.js", - "node_modules\\cross-spawn\\lib\\util\\resolveCommand.js", - "node_modules\\cross-spawn\\node_modules\\.bin\\semver", - "node_modules\\cross-spawn\\node_modules\\.bin\\which", - "node_modules\\cross-spawn\\node_modules\\semver\\CHANGELOG.md", - "node_modules\\cross-spawn\\node_modules\\semver\\LICENSE", - "node_modules\\cross-spawn\\node_modules\\semver\\package.json", - "node_modules\\cross-spawn\\node_modules\\semver\\README.md", - "node_modules\\cross-spawn\\node_modules\\semver\\semver.js", - "node_modules\\cross-spawn\\node_modules\\semver\\bin\\semver", - "node_modules\\debug\\CHANGELOG.md", - "node_modules\\debug\\LICENSE", - "node_modules\\debug\\package.json", - "node_modules\\debug\\README.md", - "node_modules\\debug\\dist\\debug.js", - "node_modules\\debug\\src\\browser.js", - "node_modules\\debug\\src\\common.js", - "node_modules\\debug\\src\\index.js", - "node_modules\\debug\\src\\node.js", - "node_modules\\delayed-stream\\.npmignore", - "node_modules\\delayed-stream\\License", - "node_modules\\delayed-stream\\Makefile", - "node_modules\\delayed-stream\\package.json", - "node_modules\\delayed-stream\\Readme.md", - "node_modules\\delayed-stream\\lib\\delayed_stream.js", - "node_modules\\end-of-stream\\index.js", - "node_modules\\end-of-stream\\LICENSE", - "node_modules\\end-of-stream\\package.json", - "node_modules\\end-of-stream\\README.md", - "node_modules\\execa\\index.js", - "node_modules\\execa\\license", - "node_modules\\execa\\package.json", - "node_modules\\execa\\readme.md", - "node_modules\\execa\\lib\\error.js", - "node_modules\\execa\\lib\\stdio.js", - "node_modules\\fast-safe-stringify\\benchmark.js", - "node_modules\\fast-safe-stringify\\CHANGELOG.md", - "node_modules\\fast-safe-stringify\\index.js", - "node_modules\\fast-safe-stringify\\LICENSE", - "node_modules\\fast-safe-stringify\\package.json", - "node_modules\\fast-safe-stringify\\readme.md", - "node_modules\\fast-safe-stringify\\test-stable.js", - "node_modules\\fast-safe-stringify\\test.js", - "node_modules\\form-data\\License", - "node_modules\\form-data\\package.json", - "node_modules\\form-data\\README.md", - "node_modules\\form-data\\lib\\browser.js", - "node_modules\\form-data\\lib\\form_data.js", - "node_modules\\form-data\\lib\\populate.js", - "node_modules\\formidable\\LICENSE", - "node_modules\\formidable\\package.json", - "node_modules\\formidable\\README.md", - "node_modules\\formidable\\lib\\file.js", - "node_modules\\formidable\\lib\\incoming_form.js", - "node_modules\\formidable\\lib\\index.js", - "node_modules\\formidable\\lib\\json_parser.js", - "node_modules\\formidable\\lib\\multipart_parser.js", - "node_modules\\formidable\\lib\\octet_parser.js", - "node_modules\\formidable\\lib\\querystring_parser.js", - "node_modules\\get-stream\\buffer-stream.js", - "node_modules\\get-stream\\index.js", - "node_modules\\get-stream\\license", - "node_modules\\get-stream\\package.json", - "node_modules\\get-stream\\readme.md", - "node_modules\\inherits\\inherits.js", - "node_modules\\inherits\\inherits_browser.js", - "node_modules\\inherits\\LICENSE", - "node_modules\\inherits\\package.json", - "node_modules\\inherits\\README.md", - "node_modules\\is-stream\\index.js", - "node_modules\\is-stream\\license", - "node_modules\\is-stream\\package.json", - "node_modules\\is-stream\\readme.md", - "node_modules\\isexe\\.npmignore", - "node_modules\\isexe\\index.js", - "node_modules\\isexe\\LICENSE", - "node_modules\\isexe\\mode.js", - "node_modules\\isexe\\package.json", - "node_modules\\isexe\\README.md", - "node_modules\\isexe\\windows.js", - "node_modules\\merge-stream\\index.js", - "node_modules\\merge-stream\\LICENSE", - "node_modules\\merge-stream\\package.json", - "node_modules\\merge-stream\\README.md", - "node_modules\\methods\\HISTORY.md", - "node_modules\\methods\\index.js", - "node_modules\\methods\\LICENSE", - "node_modules\\methods\\package.json", - "node_modules\\methods\\README.md", - "node_modules\\mime\\CHANGELOG.md", - "node_modules\\mime\\cli.js", - "node_modules\\mime\\index.js", - "node_modules\\mime\\LICENSE", - "node_modules\\mime\\lite.js", - "node_modules\\mime\\Mime.js", - "node_modules\\mime\\package.json", - "node_modules\\mime\\README.md", - "node_modules\\mime\\types\\other.js", - "node_modules\\mime\\types\\standard.js", - "node_modules\\mime-db\\db.json", - "node_modules\\mime-db\\HISTORY.md", - "node_modules\\mime-db\\index.js", - "node_modules\\mime-db\\LICENSE", - "node_modules\\mime-db\\package.json", - "node_modules\\mime-db\\README.md", - "node_modules\\mime-types\\HISTORY.md", - "node_modules\\mime-types\\index.js", - "node_modules\\mime-types\\LICENSE", - "node_modules\\mime-types\\package.json", - "node_modules\\mime-types\\README.md", - "node_modules\\ms\\index.js", - "node_modules\\ms\\license.md", - "node_modules\\ms\\package.json", - "node_modules\\ms\\readme.md", - "node_modules\\nice-try\\CHANGELOG.md", - "node_modules\\nice-try\\LICENSE", - "node_modules\\nice-try\\package.json", - "node_modules\\nice-try\\README.md", - "node_modules\\nice-try\\src\\index.js", - "node_modules\\npm-run-path\\index.js", - "node_modules\\npm-run-path\\license", - "node_modules\\npm-run-path\\package.json", - "node_modules\\npm-run-path\\readme.md", - "node_modules\\npm-run-path\\node_modules\\path-key\\index.js", - "node_modules\\npm-run-path\\node_modules\\path-key\\license", - "node_modules\\npm-run-path\\node_modules\\path-key\\package.json", - "node_modules\\npm-run-path\\node_modules\\path-key\\readme.md", - "node_modules\\once\\LICENSE", - "node_modules\\once\\once.js", - "node_modules\\once\\package.json", - "node_modules\\once\\README.md", - "node_modules\\p-finally\\index.js", - "node_modules\\p-finally\\license", - "node_modules\\p-finally\\package.json", - "node_modules\\p-finally\\readme.md", - "node_modules\\path-key\\index.js", - "node_modules\\path-key\\license", - "node_modules\\path-key\\package.json", - "node_modules\\path-key\\readme.md", - "node_modules\\pump\\index.js", - "node_modules\\pump\\LICENSE", - "node_modules\\pump\\package.json", - "node_modules\\pump\\README.md", - "node_modules\\pump\\test-browser.js", - "node_modules\\pump\\test-node.js", - "node_modules\\qs\\.editorconfig", - "node_modules\\qs\\.eslintignore", - "node_modules\\qs\\.eslintrc", - "node_modules\\qs\\CHANGELOG.md", - "node_modules\\qs\\LICENSE.md", - "node_modules\\qs\\package.json", - "node_modules\\qs\\README.md", - "node_modules\\qs\\dist\\qs.js", - "node_modules\\qs\\lib\\formats.js", - "node_modules\\qs\\lib\\index.js", - "node_modules\\qs\\lib\\parse.js", - "node_modules\\qs\\lib\\stringify.js", - "node_modules\\qs\\lib\\utils.js", - "node_modules\\readable-stream\\CONTRIBUTING.md", - "node_modules\\readable-stream\\errors-browser.js", - "node_modules\\readable-stream\\errors.js", - "node_modules\\readable-stream\\experimentalWarning.js", - "node_modules\\readable-stream\\GOVERNANCE.md", - "node_modules\\readable-stream\\LICENSE", - "node_modules\\readable-stream\\package.json", - "node_modules\\readable-stream\\readable-browser.js", - "node_modules\\readable-stream\\readable.js", - "node_modules\\readable-stream\\README.md", - "node_modules\\readable-stream\\lib\\_stream_duplex.js", - "node_modules\\readable-stream\\lib\\_stream_passthrough.js", - "node_modules\\readable-stream\\lib\\_stream_readable.js", - "node_modules\\readable-stream\\lib\\_stream_transform.js", - "node_modules\\readable-stream\\lib\\_stream_writable.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\async_iterator.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\buffer_list.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\destroy.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\end-of-stream.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\from-browser.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\from.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\pipeline.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\state.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\stream-browser.js", - "node_modules\\readable-stream\\lib\\internal\\streams\\stream.js", - "node_modules\\safe-buffer\\index.js", - "node_modules\\safe-buffer\\LICENSE", - "node_modules\\safe-buffer\\package.json", - "node_modules\\safe-buffer\\README.md", - "node_modules\\semver\\CHANGELOG.md", - "node_modules\\semver\\LICENSE", - "node_modules\\semver\\package.json", - "node_modules\\semver\\README.md", - "node_modules\\semver\\semver.js", - "node_modules\\semver\\bin\\semver.js", - "node_modules\\shebang-command\\index.js", - "node_modules\\shebang-command\\license", - "node_modules\\shebang-command\\package.json", - "node_modules\\shebang-command\\readme.md", - "node_modules\\shebang-regex\\index.js", - "node_modules\\shebang-regex\\license", - "node_modules\\shebang-regex\\package.json", - "node_modules\\shebang-regex\\readme.md", - "node_modules\\signal-exit\\CHANGELOG.md", - "node_modules\\signal-exit\\index.js", - "node_modules\\signal-exit\\package.json", - "node_modules\\signal-exit\\README.md", - "node_modules\\signal-exit\\signals.js", - "node_modules\\string_decoder\\LICENSE", - "node_modules\\string_decoder\\package.json", - "node_modules\\string_decoder\\README.md", - "node_modules\\string_decoder\\lib\\string_decoder.js", - "node_modules\\strip-final-newline\\index.js", - "node_modules\\strip-final-newline\\license", - "node_modules\\strip-final-newline\\package.json", - "node_modules\\strip-final-newline\\readme.md", - "node_modules\\superagent\\.browserslistrc", - "node_modules\\superagent\\.editorconfig", - "node_modules\\superagent\\.gitattributes", - "node_modules\\superagent\\.remarkignore", - "node_modules\\superagent\\CONTRIBUTING.md", - "node_modules\\superagent\\HISTORY.md", - "node_modules\\superagent\\LICENSE", - "node_modules\\superagent\\Makefile", - "node_modules\\superagent\\package.json", - "node_modules\\superagent\\README.md", - "node_modules\\superagent\\dist\\superagent.js", - "node_modules\\superagent\\dist\\superagent.min.js", - "node_modules\\superagent\\docs\\index.md", - "node_modules\\superagent\\lib\\agent-base.js", - "node_modules\\superagent\\lib\\client.js", - "node_modules\\superagent\\lib\\is-object.js", - "node_modules\\superagent\\lib\\request-base.js", - "node_modules\\superagent\\lib\\response-base.js", - "node_modules\\superagent\\lib\\utils.js", - "node_modules\\superagent\\lib\\node\\agent.js", - "node_modules\\superagent\\lib\\node\\http2wrapper.js", - "node_modules\\superagent\\lib\\node\\index.js", - "node_modules\\superagent\\lib\\node\\response.js", - "node_modules\\superagent\\lib\\node\\unzip.js", - "node_modules\\superagent\\lib\\node\\parsers\\image.js", - "node_modules\\superagent\\lib\\node\\parsers\\index.js", - "node_modules\\superagent\\lib\\node\\parsers\\json.js", - "node_modules\\superagent\\lib\\node\\parsers\\text.js", - "node_modules\\superagent\\lib\\node\\parsers\\urlencoded.js", - "node_modules\\superagent\\node_modules\\.bin\\mime", - "node_modules\\superagent\\node_modules\\.bin\\semver", - "node_modules\\util-deprecate\\browser.js", - "node_modules\\util-deprecate\\History.md", - "node_modules\\util-deprecate\\LICENSE", - "node_modules\\util-deprecate\\node.js", - "node_modules\\util-deprecate\\package.json", - "node_modules\\util-deprecate\\README.md", - "node_modules\\which\\CHANGELOG.md", - "node_modules\\which\\LICENSE", - "node_modules\\which\\package.json", - "node_modules\\which\\README.md", - "node_modules\\which\\which.js", - "node_modules\\which\\bin\\which", - "node_modules\\wrappy\\LICENSE", - "node_modules\\wrappy\\package.json", - "node_modules\\wrappy\\README.md", - "node_modules\\wrappy\\wrappy.js", - "discord_utils.node", - "index.js", - "manifest.json" - ] +{ + "files": [ + "node_modules\\.yarn-integrity", + "node_modules\\.bin\\mime", + "node_modules\\.bin\\semver", + "node_modules\\.bin\\which", + "node_modules\\asynckit\\bench.js", + "node_modules\\asynckit\\index.js", + "node_modules\\asynckit\\LICENSE", + "node_modules\\asynckit\\package.json", + "node_modules\\asynckit\\parallel.js", + "node_modules\\asynckit\\README.md", + "node_modules\\asynckit\\serial.js", + "node_modules\\asynckit\\serialOrdered.js", + "node_modules\\asynckit\\stream.js", + "node_modules\\asynckit\\lib\\abort.js", + "node_modules\\asynckit\\lib\\async.js", + "node_modules\\asynckit\\lib\\defer.js", + "node_modules\\asynckit\\lib\\iterate.js", + "node_modules\\asynckit\\lib\\readable_asynckit.js", + "node_modules\\asynckit\\lib\\readable_parallel.js", + "node_modules\\asynckit\\lib\\readable_serial.js", + "node_modules\\asynckit\\lib\\readable_serial_ordered.js", + "node_modules\\asynckit\\lib\\state.js", + "node_modules\\asynckit\\lib\\streamify.js", + "node_modules\\asynckit\\lib\\terminator.js", + "node_modules\\combined-stream\\License", + "node_modules\\combined-stream\\package.json", + "node_modules\\combined-stream\\Readme.md", + "node_modules\\combined-stream\\lib\\combined_stream.js", + "node_modules\\component-emitter\\History.md", + "node_modules\\component-emitter\\index.js", + "node_modules\\component-emitter\\LICENSE", + "node_modules\\component-emitter\\package.json", + "node_modules\\component-emitter\\Readme.md", + "node_modules\\cookiejar\\cookiejar.js", + "node_modules\\cookiejar\\LICENSE", + "node_modules\\cookiejar\\package.json", + "node_modules\\cookiejar\\readme.md", + "node_modules\\cross-spawn\\CHANGELOG.md", + "node_modules\\cross-spawn\\index.js", + "node_modules\\cross-spawn\\LICENSE", + "node_modules\\cross-spawn\\package.json", + "node_modules\\cross-spawn\\README.md", + "node_modules\\cross-spawn\\lib\\enoent.js", + "node_modules\\cross-spawn\\lib\\parse.js", + "node_modules\\cross-spawn\\lib\\util\\escape.js", + "node_modules\\cross-spawn\\lib\\util\\readShebang.js", + "node_modules\\cross-spawn\\lib\\util\\resolveCommand.js", + "node_modules\\cross-spawn\\node_modules\\.bin\\semver", + "node_modules\\cross-spawn\\node_modules\\.bin\\which", + "node_modules\\cross-spawn\\node_modules\\semver\\CHANGELOG.md", + "node_modules\\cross-spawn\\node_modules\\semver\\LICENSE", + "node_modules\\cross-spawn\\node_modules\\semver\\package.json", + "node_modules\\cross-spawn\\node_modules\\semver\\README.md", + "node_modules\\cross-spawn\\node_modules\\semver\\semver.js", + "node_modules\\cross-spawn\\node_modules\\semver\\bin\\semver", + "node_modules\\debug\\CHANGELOG.md", + "node_modules\\debug\\LICENSE", + "node_modules\\debug\\package.json", + "node_modules\\debug\\README.md", + "node_modules\\debug\\dist\\debug.js", + "node_modules\\debug\\src\\browser.js", + "node_modules\\debug\\src\\common.js", + "node_modules\\debug\\src\\index.js", + "node_modules\\debug\\src\\node.js", + "node_modules\\delayed-stream\\.npmignore", + "node_modules\\delayed-stream\\License", + "node_modules\\delayed-stream\\Makefile", + "node_modules\\delayed-stream\\package.json", + "node_modules\\delayed-stream\\Readme.md", + "node_modules\\delayed-stream\\lib\\delayed_stream.js", + "node_modules\\end-of-stream\\index.js", + "node_modules\\end-of-stream\\LICENSE", + "node_modules\\end-of-stream\\package.json", + "node_modules\\end-of-stream\\README.md", + "node_modules\\execa\\index.js", + "node_modules\\execa\\license", + "node_modules\\execa\\package.json", + "node_modules\\execa\\readme.md", + "node_modules\\execa\\lib\\error.js", + "node_modules\\execa\\lib\\stdio.js", + "node_modules\\fast-safe-stringify\\benchmark.js", + "node_modules\\fast-safe-stringify\\CHANGELOG.md", + "node_modules\\fast-safe-stringify\\index.js", + "node_modules\\fast-safe-stringify\\LICENSE", + "node_modules\\fast-safe-stringify\\package.json", + "node_modules\\fast-safe-stringify\\readme.md", + "node_modules\\fast-safe-stringify\\test-stable.js", + "node_modules\\fast-safe-stringify\\test.js", + "node_modules\\form-data\\License", + "node_modules\\form-data\\package.json", + "node_modules\\form-data\\README.md", + "node_modules\\form-data\\lib\\browser.js", + "node_modules\\form-data\\lib\\form_data.js", + "node_modules\\form-data\\lib\\populate.js", + "node_modules\\formidable\\LICENSE", + "node_modules\\formidable\\package.json", + "node_modules\\formidable\\README.md", + "node_modules\\formidable\\lib\\file.js", + "node_modules\\formidable\\lib\\incoming_form.js", + "node_modules\\formidable\\lib\\index.js", + "node_modules\\formidable\\lib\\json_parser.js", + "node_modules\\formidable\\lib\\multipart_parser.js", + "node_modules\\formidable\\lib\\octet_parser.js", + "node_modules\\formidable\\lib\\querystring_parser.js", + "node_modules\\get-stream\\buffer-stream.js", + "node_modules\\get-stream\\index.js", + "node_modules\\get-stream\\license", + "node_modules\\get-stream\\package.json", + "node_modules\\get-stream\\readme.md", + "node_modules\\inherits\\inherits.js", + "node_modules\\inherits\\inherits_browser.js", + "node_modules\\inherits\\LICENSE", + "node_modules\\inherits\\package.json", + "node_modules\\inherits\\README.md", + "node_modules\\is-stream\\index.js", + "node_modules\\is-stream\\license", + "node_modules\\is-stream\\package.json", + "node_modules\\is-stream\\readme.md", + "node_modules\\isexe\\.npmignore", + "node_modules\\isexe\\index.js", + "node_modules\\isexe\\LICENSE", + "node_modules\\isexe\\mode.js", + "node_modules\\isexe\\package.json", + "node_modules\\isexe\\README.md", + "node_modules\\isexe\\windows.js", + "node_modules\\merge-stream\\index.js", + "node_modules\\merge-stream\\LICENSE", + "node_modules\\merge-stream\\package.json", + "node_modules\\merge-stream\\README.md", + "node_modules\\methods\\HISTORY.md", + "node_modules\\methods\\index.js", + "node_modules\\methods\\LICENSE", + "node_modules\\methods\\package.json", + "node_modules\\methods\\README.md", + "node_modules\\mime\\CHANGELOG.md", + "node_modules\\mime\\cli.js", + "node_modules\\mime\\index.js", + "node_modules\\mime\\LICENSE", + "node_modules\\mime\\lite.js", + "node_modules\\mime\\Mime.js", + "node_modules\\mime\\package.json", + "node_modules\\mime\\README.md", + "node_modules\\mime\\types\\other.js", + "node_modules\\mime\\types\\standard.js", + "node_modules\\mime-db\\db.json", + "node_modules\\mime-db\\HISTORY.md", + "node_modules\\mime-db\\index.js", + "node_modules\\mime-db\\LICENSE", + "node_modules\\mime-db\\package.json", + "node_modules\\mime-db\\README.md", + "node_modules\\mime-types\\HISTORY.md", + "node_modules\\mime-types\\index.js", + "node_modules\\mime-types\\LICENSE", + "node_modules\\mime-types\\package.json", + "node_modules\\mime-types\\README.md", + "node_modules\\ms\\index.js", + "node_modules\\ms\\license.md", + "node_modules\\ms\\package.json", + "node_modules\\ms\\readme.md", + "node_modules\\nice-try\\CHANGELOG.md", + "node_modules\\nice-try\\LICENSE", + "node_modules\\nice-try\\package.json", + "node_modules\\nice-try\\README.md", + "node_modules\\nice-try\\src\\index.js", + "node_modules\\npm-run-path\\index.js", + "node_modules\\npm-run-path\\license", + "node_modules\\npm-run-path\\package.json", + "node_modules\\npm-run-path\\readme.md", + "node_modules\\npm-run-path\\node_modules\\path-key\\index.js", + "node_modules\\npm-run-path\\node_modules\\path-key\\license", + "node_modules\\npm-run-path\\node_modules\\path-key\\package.json", + "node_modules\\npm-run-path\\node_modules\\path-key\\readme.md", + "node_modules\\once\\LICENSE", + "node_modules\\once\\once.js", + "node_modules\\once\\package.json", + "node_modules\\once\\README.md", + "node_modules\\p-finally\\index.js", + "node_modules\\p-finally\\license", + "node_modules\\p-finally\\package.json", + "node_modules\\p-finally\\readme.md", + "node_modules\\path-key\\index.js", + "node_modules\\path-key\\license", + "node_modules\\path-key\\package.json", + "node_modules\\path-key\\readme.md", + "node_modules\\pump\\index.js", + "node_modules\\pump\\LICENSE", + "node_modules\\pump\\package.json", + "node_modules\\pump\\README.md", + "node_modules\\pump\\test-browser.js", + "node_modules\\pump\\test-node.js", + "node_modules\\qs\\.editorconfig", + "node_modules\\qs\\.eslintignore", + "node_modules\\qs\\.eslintrc", + "node_modules\\qs\\CHANGELOG.md", + "node_modules\\qs\\LICENSE.md", + "node_modules\\qs\\package.json", + "node_modules\\qs\\README.md", + "node_modules\\qs\\dist\\qs.js", + "node_modules\\qs\\lib\\formats.js", + "node_modules\\qs\\lib\\index.js", + "node_modules\\qs\\lib\\parse.js", + "node_modules\\qs\\lib\\stringify.js", + "node_modules\\qs\\lib\\utils.js", + "node_modules\\readable-stream\\CONTRIBUTING.md", + "node_modules\\readable-stream\\errors-browser.js", + "node_modules\\readable-stream\\errors.js", + "node_modules\\readable-stream\\experimentalWarning.js", + "node_modules\\readable-stream\\GOVERNANCE.md", + "node_modules\\readable-stream\\LICENSE", + "node_modules\\readable-stream\\package.json", + "node_modules\\readable-stream\\readable-browser.js", + "node_modules\\readable-stream\\readable.js", + "node_modules\\readable-stream\\README.md", + "node_modules\\readable-stream\\lib\\_stream_duplex.js", + "node_modules\\readable-stream\\lib\\_stream_passthrough.js", + "node_modules\\readable-stream\\lib\\_stream_readable.js", + "node_modules\\readable-stream\\lib\\_stream_transform.js", + "node_modules\\readable-stream\\lib\\_stream_writable.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\async_iterator.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\buffer_list.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\destroy.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\end-of-stream.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\from-browser.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\from.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\pipeline.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\state.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\stream-browser.js", + "node_modules\\readable-stream\\lib\\internal\\streams\\stream.js", + "node_modules\\safe-buffer\\index.js", + "node_modules\\safe-buffer\\LICENSE", + "node_modules\\safe-buffer\\package.json", + "node_modules\\safe-buffer\\README.md", + "node_modules\\semver\\CHANGELOG.md", + "node_modules\\semver\\LICENSE", + "node_modules\\semver\\package.json", + "node_modules\\semver\\README.md", + "node_modules\\semver\\semver.js", + "node_modules\\semver\\bin\\semver.js", + "node_modules\\shebang-command\\index.js", + "node_modules\\shebang-command\\license", + "node_modules\\shebang-command\\package.json", + "node_modules\\shebang-command\\readme.md", + "node_modules\\shebang-regex\\index.js", + "node_modules\\shebang-regex\\license", + "node_modules\\shebang-regex\\package.json", + "node_modules\\shebang-regex\\readme.md", + "node_modules\\signal-exit\\CHANGELOG.md", + "node_modules\\signal-exit\\index.js", + "node_modules\\signal-exit\\package.json", + "node_modules\\signal-exit\\README.md", + "node_modules\\signal-exit\\signals.js", + "node_modules\\string_decoder\\LICENSE", + "node_modules\\string_decoder\\package.json", + "node_modules\\string_decoder\\README.md", + "node_modules\\string_decoder\\lib\\string_decoder.js", + "node_modules\\strip-final-newline\\index.js", + "node_modules\\strip-final-newline\\license", + "node_modules\\strip-final-newline\\package.json", + "node_modules\\strip-final-newline\\readme.md", + "node_modules\\superagent\\.browserslistrc", + "node_modules\\superagent\\.editorconfig", + "node_modules\\superagent\\.gitattributes", + "node_modules\\superagent\\.remarkignore", + "node_modules\\superagent\\CONTRIBUTING.md", + "node_modules\\superagent\\HISTORY.md", + "node_modules\\superagent\\LICENSE", + "node_modules\\superagent\\Makefile", + "node_modules\\superagent\\package.json", + "node_modules\\superagent\\README.md", + "node_modules\\superagent\\dist\\superagent.js", + "node_modules\\superagent\\dist\\superagent.min.js", + "node_modules\\superagent\\docs\\index.md", + "node_modules\\superagent\\lib\\agent-base.js", + "node_modules\\superagent\\lib\\client.js", + "node_modules\\superagent\\lib\\is-object.js", + "node_modules\\superagent\\lib\\request-base.js", + "node_modules\\superagent\\lib\\response-base.js", + "node_modules\\superagent\\lib\\utils.js", + "node_modules\\superagent\\lib\\node\\agent.js", + "node_modules\\superagent\\lib\\node\\http2wrapper.js", + "node_modules\\superagent\\lib\\node\\index.js", + "node_modules\\superagent\\lib\\node\\response.js", + "node_modules\\superagent\\lib\\node\\unzip.js", + "node_modules\\superagent\\lib\\node\\parsers\\image.js", + "node_modules\\superagent\\lib\\node\\parsers\\index.js", + "node_modules\\superagent\\lib\\node\\parsers\\json.js", + "node_modules\\superagent\\lib\\node\\parsers\\text.js", + "node_modules\\superagent\\lib\\node\\parsers\\urlencoded.js", + "node_modules\\superagent\\node_modules\\.bin\\mime", + "node_modules\\superagent\\node_modules\\.bin\\semver", + "node_modules\\util-deprecate\\browser.js", + "node_modules\\util-deprecate\\History.md", + "node_modules\\util-deprecate\\LICENSE", + "node_modules\\util-deprecate\\node.js", + "node_modules\\util-deprecate\\package.json", + "node_modules\\util-deprecate\\README.md", + "node_modules\\which\\CHANGELOG.md", + "node_modules\\which\\LICENSE", + "node_modules\\which\\package.json", + "node_modules\\which\\README.md", + "node_modules\\which\\which.js", + "node_modules\\which\\bin\\which", + "node_modules\\wrappy\\LICENSE", + "node_modules\\wrappy\\package.json", + "node_modules\\wrappy\\README.md", + "node_modules\\wrappy\\wrappy.js", + "discord_utils.node", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_vigilante/index.js b/modules/discord_vigilante/index.js index d897cfb..23e598d 100644 --- a/modules/discord_vigilante/index.js +++ b/modules/discord_vigilante/index.js @@ -1 +1 @@ -module.exports = require('./discord_vigilante_'+process.platform+'.node'); +module.exports = require('./discord_vigilante_'+process.platform+'.node'); diff --git a/modules/discord_vigilante/manifest.json b/modules/discord_vigilante/manifest.json index 7c19bbd..8a36981 100644 --- a/modules/discord_vigilante/manifest.json +++ b/modules/discord_vigilante/manifest.json @@ -1,7 +1,7 @@ -{ - "files": [ - "discord_vigilante.node", - "index.js", - "manifest.json" - ] +{ + "files": [ + "discord_vigilante.node", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/modules/discord_voice/index.js b/modules/discord_voice/index.js index abe2d09..2eeb4bc 100644 --- a/modules/discord_voice/index.js +++ b/modules/discord_voice/index.js @@ -1,320 +1,320 @@ -const VoiceEngine = require('./discord_voice_'+process.platform+'.node'); -const ChildProcess = require('child_process'); -const path = require('path'); -const yargs = require('yargs'); - -const isElectronRenderer = - typeof window !== 'undefined' && window != null && window.DiscordNative && window.DiscordNative.isRenderer; - -const appSettings = isElectronRenderer ? window.DiscordNative.settings : global.appSettings; -const features = isElectronRenderer ? window.DiscordNative.features : global.features; -const mainArgv = isElectronRenderer ? window.DiscordNative.processUtils.getMainArgvSync() : []; -const releaseChannel = isElectronRenderer ? window.DiscordNative.app.getReleaseChannel() : ''; -const useLegacyAudioDevice = appSettings ? appSettings.getSync('useLegacyAudioDevice') : false; -const audioSubsystemSelected = appSettings - ? appSettings.getSync('audioSubsystem') === 'legacy' - ? 'legacy' - : 'standard' - : 'standard'; -const audioSubsystem = useLegacyAudioDevice || audioSubsystemSelected; -const debugLogging = appSettings ? appSettings.getSync('debugLogging') : false; - -const argv = yargs(mainArgv.slice(1)) - .describe('log-level', 'Logging level.') - .default('log-level', -1) - .help('h') - .alias('h', 'help') - .exitProcess(false).argv; -const logLevel = argv['log-level'] == -1 ? (debugLogging ? 2 : -1) : argv['log-level']; - -if (debugLogging && console.discordVoiceHooked == null) { - console.discordVoiceHooked = true; - - for (const logFn of ['trace', 'debug', 'info', 'warn', 'error', 'log']) { - const originalLogFn = console[logFn]; - - if (originalLogFn != null) { - console[logFn] = function () { - originalLogFn.apply(this, arguments); - - try { - VoiceEngine.consoleLog( - logFn, - JSON.stringify(Array.from(arguments).map((v) => (v != null ? v.toString() : v))) - ); - } catch (e) { - // Drop errors from toString()/stringify. - } - }; - } - } -} - -features.declareSupported('voice_panning'); -features.declareSupported('voice_multiple_connections'); -features.declareSupported('media_devices'); -features.declareSupported('media_video'); -features.declareSupported('debug_logging'); -features.declareSupported('set_audio_device_by_id'); -features.declareSupported('set_video_device_by_id'); -features.declareSupported('loopback'); -features.declareSupported('experiment_config'); -features.declareSupported('remote_locus_network_control'); -features.declareSupported('connection_replay'); -features.declareSupported('simulcast'); - -if (process.platform === 'win32') { - features.declareSupported('voice_legacy_subsystem'); - features.declareSupported('soundshare'); - features.declareSupported('wumpus_video'); - features.declareSupported('hybrid_video'); - features.declareSupported('elevated_hook'); - features.declareSupported('soundshare_loopback'); - features.declareSupported('screen_previews'); - features.declareSupported('window_previews'); - features.declareSupported('audio_debug_state'); - features.declareSupported('video_effects'); - // NOTE(jvass): currently there's no experimental encoders! Add this back if you - // add one and want to re-enable the UI for them. - // features.declareSupported('experimental_encoders'); -} - -function bindConnectionInstance(instance) { - return { - destroy: () => instance.destroy(), - - setTransportOptions: (options) => instance.setTransportOptions(options), - setSelfMute: (mute) => instance.setSelfMute(mute), - setSelfDeafen: (deaf) => instance.setSelfDeafen(deaf), - - mergeUsers: (users) => instance.mergeUsers(users), - destroyUser: (userId) => instance.destroyUser(userId), - - setLocalVolume: (userId, volume) => instance.setLocalVolume(userId, volume), - setLocalMute: (userId, mute) => instance.setLocalMute(userId, mute), - setLocalPan: (userId, left, right) => instance.setLocalPan(userId, left, right), - setDisableLocalVideo: (userId, disabled) => instance.setDisableLocalVideo(userId, disabled), - - setMinimumOutputDelay: (delay) => instance.setMinimumOutputDelay(delay), - getEncryptionModes: (callback) => instance.getEncryptionModes(callback), - configureConnectionRetries: (baseDelay, maxDelay, maxAttempts) => - instance.configureConnectionRetries(baseDelay, maxDelay, maxAttempts), - setOnSpeakingCallback: (callback) => instance.setOnSpeakingCallback(callback), - setPingInterval: (interval) => instance.setPingInterval(interval), - setPingCallback: (callback) => instance.setPingCallback(callback), - setPingTimeoutCallback: (callback) => instance.setPingTimeoutCallback(callback), - setRemoteUserSpeakingStatus: (userId, speaking) => instance.setRemoteUserSpeakingStatus(userId, speaking), - setRemoteUserCanHavePriority: (userId, canHavePriority) => - instance.setRemoteUserCanHavePriority(userId, canHavePriority), - - setOnVideoCallback: (callback) => instance.setOnVideoCallback(callback), - setVideoBroadcast: (broadcasting) => instance.setVideoBroadcast(broadcasting), - setDesktopSource: (id, videoHook, type) => instance.setDesktopSource(id, videoHook, type), - setDesktopSourceStatusCallback: (callback) => instance.setDesktopSourceStatusCallback(callback), - setOnDesktopSourceEnded: (callback) => instance.setOnDesktopSourceEnded(callback), - setOnSoundshare: (callback) => instance.setOnSoundshare(callback), - setOnSoundshareEnded: (callback) => instance.setOnSoundshareEnded(callback), - setOnSoundshareFailed: (callback) => instance.setOnSoundshareFailed(callback), - setPTTActive: (active, priority) => instance.setPTTActive(active, priority), - getStats: (callback) => instance.getStats(callback), - getFilteredStats: (filter, callback) => instance.getFilteredStats(filter, callback), - startReplay: () => instance.startReplay(), - }; -} - -const VoiceConnection = VoiceEngine.VoiceConnection; -const VoiceReplayConnection = VoiceEngine.VoiceReplayConnection; - -delete VoiceEngine.VoiceConnection; -delete VoiceEngine.VoiceReplayConnection; - -VoiceEngine.createTransport = VoiceEngine._createTransport; - -if (isElectronRenderer) { - VoiceEngine.setImageDataAllocator((width, height) => new window.ImageData(width, height)); -} - -VoiceEngine.VoiceConnection = function (audioSSRC, userId, address, port, onConnectCallback, experiments, rids) { - let instance = null; - if (rids != null) { - instance = new VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments, rids); - } else if (experiments != null) { - instance = new VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments); - } else { - instance = new VoiceConnection(audioSSRC, userId, address, port, onConnectCallback); - } - return bindConnectionInstance(instance); -}; - -VoiceEngine.createReplayConnection = function (audioEngineId, callback, replayLog) { - if (replayLog == null) { - return null; - } - - return new VoiceReplayConnection(replayLog, audioEngineId, callback); -}; - -VoiceEngine.setAudioSubsystem = function (subsystem) { - if (appSettings == null) { - console.warn('Unable to access app settings.'); - return; - } - - // TODO: With experiment controlling ADM selection, this may be incorrect since - // audioSubsystem is read from settings (or default if does not exists) - // and not the actual ADM used. - if (subsystem === audioSubsystem) { - return; - } - - appSettings.set('audioSubsystem', subsystem); - appSettings.set('useLegacyAudioDevice', false); - - if (isElectronRenderer) { - window.DiscordNative.app.relaunch(); - } -}; - -VoiceEngine.setDebugLogging = function (enable) { - if (appSettings == null) { - console.warn('Unable to access app settings.'); - return; - } - - if (debugLogging === enable) { - return; - } - - appSettings.set('debugLogging', enable); - - if (isElectronRenderer) { - window.DiscordNative.app.relaunch(); - } -}; - -VoiceEngine.getDebugLogging = function () { - return debugLogging; -}; - -const videoStreams = {}; - -const ensureCanvasContext = function (sinkId) { - let canvas = document.getElementById(sinkId); - if (canvas == null) { - for (const popout of window.popouts.values()) { - const element = popout.document != null && popout.document.getElementById(sinkId); - if (element != null) { - canvas = element; - break; - } - } - - if (canvas == null) { - return null; - } - } - - const context = canvas.getContext('2d'); - if (context == null) { - console.log(`Failed to initialize context for sinkId ${sinkId}`); - return null; - } - - return context; -}; - -// [adill] NB: with context isolation it has become extremely costly (both memory & performance) to provide the image -// data directly to clients at any reasonably fast interval so we've replaced setVideoOutputSink with a direct canvas -// renderer via addVideoOutputSink -const setVideoOutputSink = VoiceEngine.setVideoOutputSink; -const clearVideoOutputSink = (streamId) => { - // [adill] NB: if you don't pass a frame callback setVideoOutputSink clears the sink - setVideoOutputSink(streamId); -}; -const signalVideoOutputSinkReady = VoiceEngine.signalVideoOutputSinkReady; -delete VoiceEngine.setVideoOutputSink; -delete VoiceEngine.signalVideoOutputSinkReady; - -function addVideoOutputSinkInternal(sinkId, streamId, frameCallback) { - let sinks = videoStreams[streamId]; - if (sinks == null) { - sinks = videoStreams[streamId] = new Map(); - } - - if (sinks.size === 0) { - console.log(`Subscribing to frames for streamId ${streamId}`); - const onFrame = (imageData) => { - const sinks = videoStreams[streamId]; - if (sinks != null) { - for (const callback of sinks.values()) { - if (callback != null) { - callback(imageData); - } - } - } - signalVideoOutputSinkReady(streamId); - }; - setVideoOutputSink(streamId, onFrame, true); - } - - sinks.set(sinkId, frameCallback); -} - -VoiceEngine.addVideoOutputSink = function (sinkId, streamId, frameCallback) { - let canvasContext = null; - addVideoOutputSinkInternal(sinkId, streamId, (imageData) => { - if (canvasContext == null) { - canvasContext = ensureCanvasContext(sinkId); - if (canvasContext == null) { - return; - } - } - if (frameCallback != null) { - frameCallback(imageData.width, imageData.height); - } - // [adill] NB: Electron 9+ on macOS would show massive leaks in the the GPU helper process when a non-Discord - // window completely occludes the Discord window. Adding this tiny readback ameliorates the issue. We tried WebGL - // rendering which did not exhibit the issue, however, the context limit of 16 was too small to be a real - // alternative. - const leak = canvasContext.getImageData(0, 0, 1, 1); - canvasContext.putImageData(imageData, 0, 0); - }); -}; - -VoiceEngine.removeVideoOutputSink = function (sinkId, streamId) { - const sinks = videoStreams[streamId]; - if (sinks != null) { - sinks.delete(sinkId); - if (sinks.size === 0) { - delete videoStreams[streamId]; - console.log(`Unsubscribing from frames for streamId ${streamId}`); - clearVideoOutputSink(streamId); - } - } -}; - -let sinkId = 0; -VoiceEngine.getNextVideoOutputFrame = function (streamId) { - const nextVideoFrameSinkId = `getNextVideoFrame_${++sinkId}`; - - return new Promise((resolve, reject) => { - setTimeout(() => { - VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId); - reject(new Error('getNextVideoOutputFrame timeout')); - }, 5000); - - addVideoOutputSinkInternal(nextVideoFrameSinkId, streamId, (imageData) => { - VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId); - resolve({ - width: imageData.width, - height: imageData.height, - data: new Uint8ClampedArray(imageData.data.buffer), - }); - }); - }); -}; - -console.log(`Initializing voice engine with audio subsystem: ${audioSubsystem}`); -VoiceEngine.initialize({audioSubsystem, logLevel}); - -module.exports = VoiceEngine; +const VoiceEngine = require('./discord_voice_'+process.platform+'.node'); +const ChildProcess = require('child_process'); +const path = require('path'); +const yargs = require('yargs'); + +const isElectronRenderer = + typeof window !== 'undefined' && window != null && window.DiscordNative && window.DiscordNative.isRenderer; + +const appSettings = isElectronRenderer ? window.DiscordNative.settings : global.appSettings; +const features = isElectronRenderer ? window.DiscordNative.features : global.features; +const mainArgv = isElectronRenderer ? window.DiscordNative.processUtils.getMainArgvSync() : []; +const releaseChannel = isElectronRenderer ? window.DiscordNative.app.getReleaseChannel() : ''; +const useLegacyAudioDevice = appSettings ? appSettings.getSync('useLegacyAudioDevice') : false; +const audioSubsystemSelected = appSettings + ? appSettings.getSync('audioSubsystem') === 'legacy' + ? 'legacy' + : 'standard' + : 'standard'; +const audioSubsystem = useLegacyAudioDevice || audioSubsystemSelected; +const debugLogging = appSettings ? appSettings.getSync('debugLogging') : false; + +const argv = yargs(mainArgv.slice(1)) + .describe('log-level', 'Logging level.') + .default('log-level', -1) + .help('h') + .alias('h', 'help') + .exitProcess(false).argv; +const logLevel = argv['log-level'] == -1 ? (debugLogging ? 2 : -1) : argv['log-level']; + +if (debugLogging && console.discordVoiceHooked == null) { + console.discordVoiceHooked = true; + + for (const logFn of ['trace', 'debug', 'info', 'warn', 'error', 'log']) { + const originalLogFn = console[logFn]; + + if (originalLogFn != null) { + console[logFn] = function () { + originalLogFn.apply(this, arguments); + + try { + VoiceEngine.consoleLog( + logFn, + JSON.stringify(Array.from(arguments).map((v) => (v != null ? v.toString() : v))) + ); + } catch (e) { + // Drop errors from toString()/stringify. + } + }; + } + } +} + +features.declareSupported('voice_panning'); +features.declareSupported('voice_multiple_connections'); +features.declareSupported('media_devices'); +features.declareSupported('media_video'); +features.declareSupported('debug_logging'); +features.declareSupported('set_audio_device_by_id'); +features.declareSupported('set_video_device_by_id'); +features.declareSupported('loopback'); +features.declareSupported('experiment_config'); +features.declareSupported('remote_locus_network_control'); +features.declareSupported('connection_replay'); +features.declareSupported('simulcast'); + +if (process.platform === 'win32') { + features.declareSupported('voice_legacy_subsystem'); + features.declareSupported('soundshare'); + features.declareSupported('wumpus_video'); + features.declareSupported('hybrid_video'); + features.declareSupported('elevated_hook'); + features.declareSupported('soundshare_loopback'); + features.declareSupported('screen_previews'); + features.declareSupported('window_previews'); + features.declareSupported('audio_debug_state'); + features.declareSupported('video_effects'); + // NOTE(jvass): currently there's no experimental encoders! Add this back if you + // add one and want to re-enable the UI for them. + // features.declareSupported('experimental_encoders'); +} + +function bindConnectionInstance(instance) { + return { + destroy: () => instance.destroy(), + + setTransportOptions: (options) => instance.setTransportOptions(options), + setSelfMute: (mute) => instance.setSelfMute(mute), + setSelfDeafen: (deaf) => instance.setSelfDeafen(deaf), + + mergeUsers: (users) => instance.mergeUsers(users), + destroyUser: (userId) => instance.destroyUser(userId), + + setLocalVolume: (userId, volume) => instance.setLocalVolume(userId, volume), + setLocalMute: (userId, mute) => instance.setLocalMute(userId, mute), + setLocalPan: (userId, left, right) => instance.setLocalPan(userId, left, right), + setDisableLocalVideo: (userId, disabled) => instance.setDisableLocalVideo(userId, disabled), + + setMinimumOutputDelay: (delay) => instance.setMinimumOutputDelay(delay), + getEncryptionModes: (callback) => instance.getEncryptionModes(callback), + configureConnectionRetries: (baseDelay, maxDelay, maxAttempts) => + instance.configureConnectionRetries(baseDelay, maxDelay, maxAttempts), + setOnSpeakingCallback: (callback) => instance.setOnSpeakingCallback(callback), + setPingInterval: (interval) => instance.setPingInterval(interval), + setPingCallback: (callback) => instance.setPingCallback(callback), + setPingTimeoutCallback: (callback) => instance.setPingTimeoutCallback(callback), + setRemoteUserSpeakingStatus: (userId, speaking) => instance.setRemoteUserSpeakingStatus(userId, speaking), + setRemoteUserCanHavePriority: (userId, canHavePriority) => + instance.setRemoteUserCanHavePriority(userId, canHavePriority), + + setOnVideoCallback: (callback) => instance.setOnVideoCallback(callback), + setVideoBroadcast: (broadcasting) => instance.setVideoBroadcast(broadcasting), + setDesktopSource: (id, videoHook, type) => instance.setDesktopSource(id, videoHook, type), + setDesktopSourceStatusCallback: (callback) => instance.setDesktopSourceStatusCallback(callback), + setOnDesktopSourceEnded: (callback) => instance.setOnDesktopSourceEnded(callback), + setOnSoundshare: (callback) => instance.setOnSoundshare(callback), + setOnSoundshareEnded: (callback) => instance.setOnSoundshareEnded(callback), + setOnSoundshareFailed: (callback) => instance.setOnSoundshareFailed(callback), + setPTTActive: (active, priority) => instance.setPTTActive(active, priority), + getStats: (callback) => instance.getStats(callback), + getFilteredStats: (filter, callback) => instance.getFilteredStats(filter, callback), + startReplay: () => instance.startReplay(), + }; +} + +const VoiceConnection = VoiceEngine.VoiceConnection; +const VoiceReplayConnection = VoiceEngine.VoiceReplayConnection; + +delete VoiceEngine.VoiceConnection; +delete VoiceEngine.VoiceReplayConnection; + +VoiceEngine.createTransport = VoiceEngine._createTransport; + +if (isElectronRenderer) { + VoiceEngine.setImageDataAllocator((width, height) => new window.ImageData(width, height)); +} + +VoiceEngine.VoiceConnection = function (audioSSRC, userId, address, port, onConnectCallback, experiments, rids) { + let instance = null; + if (rids != null) { + instance = new VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments, rids); + } else if (experiments != null) { + instance = new VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments); + } else { + instance = new VoiceConnection(audioSSRC, userId, address, port, onConnectCallback); + } + return bindConnectionInstance(instance); +}; + +VoiceEngine.createReplayConnection = function (audioEngineId, callback, replayLog) { + if (replayLog == null) { + return null; + } + + return new VoiceReplayConnection(replayLog, audioEngineId, callback); +}; + +VoiceEngine.setAudioSubsystem = function (subsystem) { + if (appSettings == null) { + console.warn('Unable to access app settings.'); + return; + } + + // TODO: With experiment controlling ADM selection, this may be incorrect since + // audioSubsystem is read from settings (or default if does not exists) + // and not the actual ADM used. + if (subsystem === audioSubsystem) { + return; + } + + appSettings.set('audioSubsystem', subsystem); + appSettings.set('useLegacyAudioDevice', false); + + if (isElectronRenderer) { + window.DiscordNative.app.relaunch(); + } +}; + +VoiceEngine.setDebugLogging = function (enable) { + if (appSettings == null) { + console.warn('Unable to access app settings.'); + return; + } + + if (debugLogging === enable) { + return; + } + + appSettings.set('debugLogging', enable); + + if (isElectronRenderer) { + window.DiscordNative.app.relaunch(); + } +}; + +VoiceEngine.getDebugLogging = function () { + return debugLogging; +}; + +const videoStreams = {}; + +const ensureCanvasContext = function (sinkId) { + let canvas = document.getElementById(sinkId); + if (canvas == null) { + for (const popout of window.popouts.values()) { + const element = popout.document != null && popout.document.getElementById(sinkId); + if (element != null) { + canvas = element; + break; + } + } + + if (canvas == null) { + return null; + } + } + + const context = canvas.getContext('2d'); + if (context == null) { + console.log(`Failed to initialize context for sinkId ${sinkId}`); + return null; + } + + return context; +}; + +// [adill] NB: with context isolation it has become extremely costly (both memory & performance) to provide the image +// data directly to clients at any reasonably fast interval so we've replaced setVideoOutputSink with a direct canvas +// renderer via addVideoOutputSink +const setVideoOutputSink = VoiceEngine.setVideoOutputSink; +const clearVideoOutputSink = (streamId) => { + // [adill] NB: if you don't pass a frame callback setVideoOutputSink clears the sink + setVideoOutputSink(streamId); +}; +const signalVideoOutputSinkReady = VoiceEngine.signalVideoOutputSinkReady; +delete VoiceEngine.setVideoOutputSink; +delete VoiceEngine.signalVideoOutputSinkReady; + +function addVideoOutputSinkInternal(sinkId, streamId, frameCallback) { + let sinks = videoStreams[streamId]; + if (sinks == null) { + sinks = videoStreams[streamId] = new Map(); + } + + if (sinks.size === 0) { + console.log(`Subscribing to frames for streamId ${streamId}`); + const onFrame = (imageData) => { + const sinks = videoStreams[streamId]; + if (sinks != null) { + for (const callback of sinks.values()) { + if (callback != null) { + callback(imageData); + } + } + } + signalVideoOutputSinkReady(streamId); + }; + setVideoOutputSink(streamId, onFrame, true); + } + + sinks.set(sinkId, frameCallback); +} + +VoiceEngine.addVideoOutputSink = function (sinkId, streamId, frameCallback) { + let canvasContext = null; + addVideoOutputSinkInternal(sinkId, streamId, (imageData) => { + if (canvasContext == null) { + canvasContext = ensureCanvasContext(sinkId); + if (canvasContext == null) { + return; + } + } + if (frameCallback != null) { + frameCallback(imageData.width, imageData.height); + } + // [adill] NB: Electron 9+ on macOS would show massive leaks in the the GPU helper process when a non-Discord + // window completely occludes the Discord window. Adding this tiny readback ameliorates the issue. We tried WebGL + // rendering which did not exhibit the issue, however, the context limit of 16 was too small to be a real + // alternative. + const leak = canvasContext.getImageData(0, 0, 1, 1); + canvasContext.putImageData(imageData, 0, 0); + }); +}; + +VoiceEngine.removeVideoOutputSink = function (sinkId, streamId) { + const sinks = videoStreams[streamId]; + if (sinks != null) { + sinks.delete(sinkId); + if (sinks.size === 0) { + delete videoStreams[streamId]; + console.log(`Unsubscribing from frames for streamId ${streamId}`); + clearVideoOutputSink(streamId); + } + } +}; + +let sinkId = 0; +VoiceEngine.getNextVideoOutputFrame = function (streamId) { + const nextVideoFrameSinkId = `getNextVideoFrame_${++sinkId}`; + + return new Promise((resolve, reject) => { + setTimeout(() => { + VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId); + reject(new Error('getNextVideoOutputFrame timeout')); + }, 5000); + + addVideoOutputSinkInternal(nextVideoFrameSinkId, streamId, (imageData) => { + VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId); + resolve({ + width: imageData.width, + height: imageData.height, + data: new Uint8ClampedArray(imageData.data.buffer), + }); + }); + }); +}; + +console.log(`Initializing voice engine with audio subsystem: ${audioSubsystem}`); +VoiceEngine.initialize({audioSubsystem, logLevel}); + +module.exports = VoiceEngine; diff --git a/modules/discord_voice/manifest.json b/modules/discord_voice/manifest.json index e90c16d..ab72eae 100644 --- a/modules/discord_voice/manifest.json +++ b/modules/discord_voice/manifest.json @@ -1,272 +1,272 @@ -{ - "files": [ - "node_modules\\.yarn-integrity", - "node_modules\\.bin\\which", - "node_modules\\ansi-regex\\index.js", - "node_modules\\ansi-regex\\license", - "node_modules\\ansi-regex\\package.json", - "node_modules\\ansi-regex\\readme.md", - "node_modules\\camelcase\\index.js", - "node_modules\\camelcase\\license", - "node_modules\\camelcase\\package.json", - "node_modules\\camelcase\\readme.md", - "node_modules\\cliui\\CHANGELOG.md", - "node_modules\\cliui\\index.js", - "node_modules\\cliui\\package.json", - "node_modules\\cliui\\README.md", - "node_modules\\cliui\\node_modules\\ansi-regex\\index.js", - "node_modules\\cliui\\node_modules\\ansi-regex\\license", - "node_modules\\cliui\\node_modules\\ansi-regex\\package.json", - "node_modules\\cliui\\node_modules\\ansi-regex\\readme.md", - "node_modules\\cliui\\node_modules\\strip-ansi\\index.js", - "node_modules\\cliui\\node_modules\\strip-ansi\\license", - "node_modules\\cliui\\node_modules\\strip-ansi\\package.json", - "node_modules\\cliui\\node_modules\\strip-ansi\\readme.md", - "node_modules\\code-point-at\\index.js", - "node_modules\\code-point-at\\license", - "node_modules\\code-point-at\\package.json", - "node_modules\\code-point-at\\readme.md", - "node_modules\\cross-spawn\\CHANGELOG.md", - "node_modules\\cross-spawn\\index.js", - "node_modules\\cross-spawn\\LICENSE", - "node_modules\\cross-spawn\\package.json", - "node_modules\\cross-spawn\\README.md", - "node_modules\\cross-spawn\\lib\\enoent.js", - "node_modules\\cross-spawn\\lib\\parse.js", - "node_modules\\cross-spawn\\lib\\util\\escapeArgument.js", - "node_modules\\cross-spawn\\lib\\util\\escapeCommand.js", - "node_modules\\cross-spawn\\lib\\util\\hasEmptyArgumentBug.js", - "node_modules\\cross-spawn\\lib\\util\\readShebang.js", - "node_modules\\cross-spawn\\lib\\util\\resolveCommand.js", - "node_modules\\cross-spawn\\node_modules\\.bin\\which", - "node_modules\\decamelize\\index.js", - "node_modules\\decamelize\\license", - "node_modules\\decamelize\\package.json", - "node_modules\\decamelize\\readme.md", - "node_modules\\execa\\index.js", - "node_modules\\execa\\license", - "node_modules\\execa\\package.json", - "node_modules\\execa\\readme.md", - "node_modules\\execa\\lib\\errname.js", - "node_modules\\execa\\lib\\stdio.js", - "node_modules\\find-up\\index.js", - "node_modules\\find-up\\license", - "node_modules\\find-up\\package.json", - "node_modules\\find-up\\readme.md", - "node_modules\\get-caller-file\\index.js", - "node_modules\\get-caller-file\\LICENSE.md", - "node_modules\\get-caller-file\\package.json", - "node_modules\\get-caller-file\\README.md", - "node_modules\\get-stream\\buffer-stream.js", - "node_modules\\get-stream\\index.js", - "node_modules\\get-stream\\license", - "node_modules\\get-stream\\package.json", - "node_modules\\get-stream\\readme.md", - "node_modules\\invert-kv\\index.js", - "node_modules\\invert-kv\\package.json", - "node_modules\\invert-kv\\readme.md", - "node_modules\\is-fullwidth-code-point\\index.js", - "node_modules\\is-fullwidth-code-point\\license", - "node_modules\\is-fullwidth-code-point\\package.json", - "node_modules\\is-fullwidth-code-point\\readme.md", - "node_modules\\is-stream\\index.js", - "node_modules\\is-stream\\license", - "node_modules\\is-stream\\package.json", - "node_modules\\is-stream\\readme.md", - "node_modules\\isexe\\.npmignore", - "node_modules\\isexe\\index.js", - "node_modules\\isexe\\LICENSE", - "node_modules\\isexe\\mode.js", - "node_modules\\isexe\\package.json", - "node_modules\\isexe\\README.md", - "node_modules\\isexe\\windows.js", - "node_modules\\lcid\\index.js", - "node_modules\\lcid\\lcid.json", - "node_modules\\lcid\\license", - "node_modules\\lcid\\package.json", - "node_modules\\lcid\\readme.md", - "node_modules\\locate-path\\index.js", - "node_modules\\locate-path\\license", - "node_modules\\locate-path\\package.json", - "node_modules\\locate-path\\readme.md", - "node_modules\\lru-cache\\index.js", - "node_modules\\lru-cache\\LICENSE", - "node_modules\\lru-cache\\package.json", - "node_modules\\lru-cache\\README.md", - "node_modules\\mem\\index.js", - "node_modules\\mem\\license", - "node_modules\\mem\\package.json", - "node_modules\\mem\\readme.md", - "node_modules\\mimic-fn\\index.js", - "node_modules\\mimic-fn\\license", - "node_modules\\mimic-fn\\package.json", - "node_modules\\mimic-fn\\readme.md", - "node_modules\\npm-run-path\\index.js", - "node_modules\\npm-run-path\\license", - "node_modules\\npm-run-path\\package.json", - "node_modules\\npm-run-path\\readme.md", - "node_modules\\number-is-nan\\index.js", - "node_modules\\number-is-nan\\license", - "node_modules\\number-is-nan\\package.json", - "node_modules\\number-is-nan\\readme.md", - "node_modules\\os-locale\\index.js", - "node_modules\\os-locale\\license", - "node_modules\\os-locale\\package.json", - "node_modules\\os-locale\\readme.md", - "node_modules\\p-finally\\index.js", - "node_modules\\p-finally\\license", - "node_modules\\p-finally\\package.json", - "node_modules\\p-finally\\readme.md", - "node_modules\\p-limit\\index.js", - "node_modules\\p-limit\\license", - "node_modules\\p-limit\\package.json", - "node_modules\\p-limit\\readme.md", - "node_modules\\p-locate\\index.js", - "node_modules\\p-locate\\license", - "node_modules\\p-locate\\package.json", - "node_modules\\p-locate\\readme.md", - "node_modules\\p-try\\index.js", - "node_modules\\p-try\\license", - "node_modules\\p-try\\package.json", - "node_modules\\p-try\\readme.md", - "node_modules\\path-exists\\index.js", - "node_modules\\path-exists\\license", - "node_modules\\path-exists\\package.json", - "node_modules\\path-exists\\readme.md", - "node_modules\\path-key\\index.js", - "node_modules\\path-key\\license", - "node_modules\\path-key\\package.json", - "node_modules\\path-key\\readme.md", - "node_modules\\pseudomap\\LICENSE", - "node_modules\\pseudomap\\map.js", - "node_modules\\pseudomap\\package.json", - "node_modules\\pseudomap\\pseudomap.js", - "node_modules\\pseudomap\\README.md", - "node_modules\\require-directory\\.jshintrc", - "node_modules\\require-directory\\.npmignore", - "node_modules\\require-directory\\index.js", - "node_modules\\require-directory\\LICENSE", - "node_modules\\require-directory\\package.json", - "node_modules\\require-main-filename\\.npmignore", - "node_modules\\require-main-filename\\index.js", - "node_modules\\require-main-filename\\package.json", - "node_modules\\require-main-filename\\README.md", - "node_modules\\require-main-filename\\test.js", - "node_modules\\set-blocking\\CHANGELOG.md", - "node_modules\\set-blocking\\index.js", - "node_modules\\set-blocking\\package.json", - "node_modules\\set-blocking\\README.md", - "node_modules\\shebang-command\\index.js", - "node_modules\\shebang-command\\license", - "node_modules\\shebang-command\\package.json", - "node_modules\\shebang-command\\readme.md", - "node_modules\\shebang-regex\\index.js", - "node_modules\\shebang-regex\\license", - "node_modules\\shebang-regex\\package.json", - "node_modules\\shebang-regex\\readme.md", - "node_modules\\signal-exit\\CHANGELOG.md", - "node_modules\\signal-exit\\index.js", - "node_modules\\signal-exit\\package.json", - "node_modules\\signal-exit\\README.md", - "node_modules\\signal-exit\\signals.js", - "node_modules\\string-width\\index.js", - "node_modules\\string-width\\license", - "node_modules\\string-width\\package.json", - "node_modules\\string-width\\readme.md", - "node_modules\\string-width\\node_modules\\ansi-regex\\index.js", - "node_modules\\string-width\\node_modules\\ansi-regex\\license", - "node_modules\\string-width\\node_modules\\ansi-regex\\package.json", - "node_modules\\string-width\\node_modules\\ansi-regex\\readme.md", - "node_modules\\string-width\\node_modules\\strip-ansi\\index.js", - "node_modules\\string-width\\node_modules\\strip-ansi\\license", - "node_modules\\string-width\\node_modules\\strip-ansi\\package.json", - "node_modules\\string-width\\node_modules\\strip-ansi\\readme.md", - "node_modules\\strip-ansi\\index.js", - "node_modules\\strip-ansi\\license", - "node_modules\\strip-ansi\\package.json", - "node_modules\\strip-ansi\\readme.md", - "node_modules\\strip-eof\\index.js", - "node_modules\\strip-eof\\license", - "node_modules\\strip-eof\\package.json", - "node_modules\\strip-eof\\readme.md", - "node_modules\\which\\CHANGELOG.md", - "node_modules\\which\\LICENSE", - "node_modules\\which\\package.json", - "node_modules\\which\\README.md", - "node_modules\\which\\which.js", - "node_modules\\which\\bin\\which", - "node_modules\\which-module\\CHANGELOG.md", - "node_modules\\which-module\\index.js", - "node_modules\\which-module\\LICENSE", - "node_modules\\which-module\\package.json", - "node_modules\\which-module\\README.md", - "node_modules\\wrap-ansi\\index.js", - "node_modules\\wrap-ansi\\license", - "node_modules\\wrap-ansi\\package.json", - "node_modules\\wrap-ansi\\readme.md", - "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\index.js", - "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\license", - "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\package.json", - "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\readme.md", - "node_modules\\wrap-ansi\\node_modules\\string-width\\index.js", - "node_modules\\wrap-ansi\\node_modules\\string-width\\license", - "node_modules\\wrap-ansi\\node_modules\\string-width\\package.json", - "node_modules\\wrap-ansi\\node_modules\\string-width\\readme.md", - "node_modules\\y18n\\index.js", - "node_modules\\y18n\\LICENSE", - "node_modules\\y18n\\package.json", - "node_modules\\y18n\\README.md", - "node_modules\\yallist\\iterator.js", - "node_modules\\yallist\\LICENSE", - "node_modules\\yallist\\package.json", - "node_modules\\yallist\\README.md", - "node_modules\\yallist\\yallist.js", - "node_modules\\yargs\\CHANGELOG.md", - "node_modules\\yargs\\index.js", - "node_modules\\yargs\\LICENSE", - "node_modules\\yargs\\package.json", - "node_modules\\yargs\\README.md", - "node_modules\\yargs\\yargs.js", - "node_modules\\yargs\\lib\\apply-extends.js", - "node_modules\\yargs\\lib\\argsert.js", - "node_modules\\yargs\\lib\\command.js", - "node_modules\\yargs\\lib\\completion.js", - "node_modules\\yargs\\lib\\levenshtein.js", - "node_modules\\yargs\\lib\\obj-filter.js", - "node_modules\\yargs\\lib\\usage.js", - "node_modules\\yargs\\lib\\validation.js", - "node_modules\\yargs\\lib\\yerror.js", - "node_modules\\yargs\\locales\\be.json", - "node_modules\\yargs\\locales\\de.json", - "node_modules\\yargs\\locales\\en.json", - "node_modules\\yargs\\locales\\es.json", - "node_modules\\yargs\\locales\\fr.json", - "node_modules\\yargs\\locales\\hi.json", - "node_modules\\yargs\\locales\\hu.json", - "node_modules\\yargs\\locales\\id.json", - "node_modules\\yargs\\locales\\it.json", - "node_modules\\yargs\\locales\\ja.json", - "node_modules\\yargs\\locales\\ko.json", - "node_modules\\yargs\\locales\\nb.json", - "node_modules\\yargs\\locales\\nl.json", - "node_modules\\yargs\\locales\\nn.json", - "node_modules\\yargs\\locales\\pirate.json", - "node_modules\\yargs\\locales\\pl.json", - "node_modules\\yargs\\locales\\pt.json", - "node_modules\\yargs\\locales\\pt_BR.json", - "node_modules\\yargs\\locales\\ru.json", - "node_modules\\yargs\\locales\\th.json", - "node_modules\\yargs\\locales\\tr.json", - "node_modules\\yargs\\locales\\zh_CN.json", - "node_modules\\yargs\\locales\\zh_TW.json", - "node_modules\\yargs-parser\\CHANGELOG.md", - "node_modules\\yargs-parser\\index.js", - "node_modules\\yargs-parser\\package.json", - "node_modules\\yargs-parser\\README.md", - "node_modules\\yargs-parser\\lib\\tokenize-arg-string.js", - "discord_voice.node", - "openh264-1.7.0-win32.dll", - "index.js", - "manifest.json" - ] +{ + "files": [ + "node_modules\\.yarn-integrity", + "node_modules\\.bin\\which", + "node_modules\\ansi-regex\\index.js", + "node_modules\\ansi-regex\\license", + "node_modules\\ansi-regex\\package.json", + "node_modules\\ansi-regex\\readme.md", + "node_modules\\camelcase\\index.js", + "node_modules\\camelcase\\license", + "node_modules\\camelcase\\package.json", + "node_modules\\camelcase\\readme.md", + "node_modules\\cliui\\CHANGELOG.md", + "node_modules\\cliui\\index.js", + "node_modules\\cliui\\package.json", + "node_modules\\cliui\\README.md", + "node_modules\\cliui\\node_modules\\ansi-regex\\index.js", + "node_modules\\cliui\\node_modules\\ansi-regex\\license", + "node_modules\\cliui\\node_modules\\ansi-regex\\package.json", + "node_modules\\cliui\\node_modules\\ansi-regex\\readme.md", + "node_modules\\cliui\\node_modules\\strip-ansi\\index.js", + "node_modules\\cliui\\node_modules\\strip-ansi\\license", + "node_modules\\cliui\\node_modules\\strip-ansi\\package.json", + "node_modules\\cliui\\node_modules\\strip-ansi\\readme.md", + "node_modules\\code-point-at\\index.js", + "node_modules\\code-point-at\\license", + "node_modules\\code-point-at\\package.json", + "node_modules\\code-point-at\\readme.md", + "node_modules\\cross-spawn\\CHANGELOG.md", + "node_modules\\cross-spawn\\index.js", + "node_modules\\cross-spawn\\LICENSE", + "node_modules\\cross-spawn\\package.json", + "node_modules\\cross-spawn\\README.md", + "node_modules\\cross-spawn\\lib\\enoent.js", + "node_modules\\cross-spawn\\lib\\parse.js", + "node_modules\\cross-spawn\\lib\\util\\escapeArgument.js", + "node_modules\\cross-spawn\\lib\\util\\escapeCommand.js", + "node_modules\\cross-spawn\\lib\\util\\hasEmptyArgumentBug.js", + "node_modules\\cross-spawn\\lib\\util\\readShebang.js", + "node_modules\\cross-spawn\\lib\\util\\resolveCommand.js", + "node_modules\\cross-spawn\\node_modules\\.bin\\which", + "node_modules\\decamelize\\index.js", + "node_modules\\decamelize\\license", + "node_modules\\decamelize\\package.json", + "node_modules\\decamelize\\readme.md", + "node_modules\\execa\\index.js", + "node_modules\\execa\\license", + "node_modules\\execa\\package.json", + "node_modules\\execa\\readme.md", + "node_modules\\execa\\lib\\errname.js", + "node_modules\\execa\\lib\\stdio.js", + "node_modules\\find-up\\index.js", + "node_modules\\find-up\\license", + "node_modules\\find-up\\package.json", + "node_modules\\find-up\\readme.md", + "node_modules\\get-caller-file\\index.js", + "node_modules\\get-caller-file\\LICENSE.md", + "node_modules\\get-caller-file\\package.json", + "node_modules\\get-caller-file\\README.md", + "node_modules\\get-stream\\buffer-stream.js", + "node_modules\\get-stream\\index.js", + "node_modules\\get-stream\\license", + "node_modules\\get-stream\\package.json", + "node_modules\\get-stream\\readme.md", + "node_modules\\invert-kv\\index.js", + "node_modules\\invert-kv\\package.json", + "node_modules\\invert-kv\\readme.md", + "node_modules\\is-fullwidth-code-point\\index.js", + "node_modules\\is-fullwidth-code-point\\license", + "node_modules\\is-fullwidth-code-point\\package.json", + "node_modules\\is-fullwidth-code-point\\readme.md", + "node_modules\\is-stream\\index.js", + "node_modules\\is-stream\\license", + "node_modules\\is-stream\\package.json", + "node_modules\\is-stream\\readme.md", + "node_modules\\isexe\\.npmignore", + "node_modules\\isexe\\index.js", + "node_modules\\isexe\\LICENSE", + "node_modules\\isexe\\mode.js", + "node_modules\\isexe\\package.json", + "node_modules\\isexe\\README.md", + "node_modules\\isexe\\windows.js", + "node_modules\\lcid\\index.js", + "node_modules\\lcid\\lcid.json", + "node_modules\\lcid\\license", + "node_modules\\lcid\\package.json", + "node_modules\\lcid\\readme.md", + "node_modules\\locate-path\\index.js", + "node_modules\\locate-path\\license", + "node_modules\\locate-path\\package.json", + "node_modules\\locate-path\\readme.md", + "node_modules\\lru-cache\\index.js", + "node_modules\\lru-cache\\LICENSE", + "node_modules\\lru-cache\\package.json", + "node_modules\\lru-cache\\README.md", + "node_modules\\mem\\index.js", + "node_modules\\mem\\license", + "node_modules\\mem\\package.json", + "node_modules\\mem\\readme.md", + "node_modules\\mimic-fn\\index.js", + "node_modules\\mimic-fn\\license", + "node_modules\\mimic-fn\\package.json", + "node_modules\\mimic-fn\\readme.md", + "node_modules\\npm-run-path\\index.js", + "node_modules\\npm-run-path\\license", + "node_modules\\npm-run-path\\package.json", + "node_modules\\npm-run-path\\readme.md", + "node_modules\\number-is-nan\\index.js", + "node_modules\\number-is-nan\\license", + "node_modules\\number-is-nan\\package.json", + "node_modules\\number-is-nan\\readme.md", + "node_modules\\os-locale\\index.js", + "node_modules\\os-locale\\license", + "node_modules\\os-locale\\package.json", + "node_modules\\os-locale\\readme.md", + "node_modules\\p-finally\\index.js", + "node_modules\\p-finally\\license", + "node_modules\\p-finally\\package.json", + "node_modules\\p-finally\\readme.md", + "node_modules\\p-limit\\index.js", + "node_modules\\p-limit\\license", + "node_modules\\p-limit\\package.json", + "node_modules\\p-limit\\readme.md", + "node_modules\\p-locate\\index.js", + "node_modules\\p-locate\\license", + "node_modules\\p-locate\\package.json", + "node_modules\\p-locate\\readme.md", + "node_modules\\p-try\\index.js", + "node_modules\\p-try\\license", + "node_modules\\p-try\\package.json", + "node_modules\\p-try\\readme.md", + "node_modules\\path-exists\\index.js", + "node_modules\\path-exists\\license", + "node_modules\\path-exists\\package.json", + "node_modules\\path-exists\\readme.md", + "node_modules\\path-key\\index.js", + "node_modules\\path-key\\license", + "node_modules\\path-key\\package.json", + "node_modules\\path-key\\readme.md", + "node_modules\\pseudomap\\LICENSE", + "node_modules\\pseudomap\\map.js", + "node_modules\\pseudomap\\package.json", + "node_modules\\pseudomap\\pseudomap.js", + "node_modules\\pseudomap\\README.md", + "node_modules\\require-directory\\.jshintrc", + "node_modules\\require-directory\\.npmignore", + "node_modules\\require-directory\\index.js", + "node_modules\\require-directory\\LICENSE", + "node_modules\\require-directory\\package.json", + "node_modules\\require-main-filename\\.npmignore", + "node_modules\\require-main-filename\\index.js", + "node_modules\\require-main-filename\\package.json", + "node_modules\\require-main-filename\\README.md", + "node_modules\\require-main-filename\\test.js", + "node_modules\\set-blocking\\CHANGELOG.md", + "node_modules\\set-blocking\\index.js", + "node_modules\\set-blocking\\package.json", + "node_modules\\set-blocking\\README.md", + "node_modules\\shebang-command\\index.js", + "node_modules\\shebang-command\\license", + "node_modules\\shebang-command\\package.json", + "node_modules\\shebang-command\\readme.md", + "node_modules\\shebang-regex\\index.js", + "node_modules\\shebang-regex\\license", + "node_modules\\shebang-regex\\package.json", + "node_modules\\shebang-regex\\readme.md", + "node_modules\\signal-exit\\CHANGELOG.md", + "node_modules\\signal-exit\\index.js", + "node_modules\\signal-exit\\package.json", + "node_modules\\signal-exit\\README.md", + "node_modules\\signal-exit\\signals.js", + "node_modules\\string-width\\index.js", + "node_modules\\string-width\\license", + "node_modules\\string-width\\package.json", + "node_modules\\string-width\\readme.md", + "node_modules\\string-width\\node_modules\\ansi-regex\\index.js", + "node_modules\\string-width\\node_modules\\ansi-regex\\license", + "node_modules\\string-width\\node_modules\\ansi-regex\\package.json", + "node_modules\\string-width\\node_modules\\ansi-regex\\readme.md", + "node_modules\\string-width\\node_modules\\strip-ansi\\index.js", + "node_modules\\string-width\\node_modules\\strip-ansi\\license", + "node_modules\\string-width\\node_modules\\strip-ansi\\package.json", + "node_modules\\string-width\\node_modules\\strip-ansi\\readme.md", + "node_modules\\strip-ansi\\index.js", + "node_modules\\strip-ansi\\license", + "node_modules\\strip-ansi\\package.json", + "node_modules\\strip-ansi\\readme.md", + "node_modules\\strip-eof\\index.js", + "node_modules\\strip-eof\\license", + "node_modules\\strip-eof\\package.json", + "node_modules\\strip-eof\\readme.md", + "node_modules\\which\\CHANGELOG.md", + "node_modules\\which\\LICENSE", + "node_modules\\which\\package.json", + "node_modules\\which\\README.md", + "node_modules\\which\\which.js", + "node_modules\\which\\bin\\which", + "node_modules\\which-module\\CHANGELOG.md", + "node_modules\\which-module\\index.js", + "node_modules\\which-module\\LICENSE", + "node_modules\\which-module\\package.json", + "node_modules\\which-module\\README.md", + "node_modules\\wrap-ansi\\index.js", + "node_modules\\wrap-ansi\\license", + "node_modules\\wrap-ansi\\package.json", + "node_modules\\wrap-ansi\\readme.md", + "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\index.js", + "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\license", + "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\package.json", + "node_modules\\wrap-ansi\\node_modules\\is-fullwidth-code-point\\readme.md", + "node_modules\\wrap-ansi\\node_modules\\string-width\\index.js", + "node_modules\\wrap-ansi\\node_modules\\string-width\\license", + "node_modules\\wrap-ansi\\node_modules\\string-width\\package.json", + "node_modules\\wrap-ansi\\node_modules\\string-width\\readme.md", + "node_modules\\y18n\\index.js", + "node_modules\\y18n\\LICENSE", + "node_modules\\y18n\\package.json", + "node_modules\\y18n\\README.md", + "node_modules\\yallist\\iterator.js", + "node_modules\\yallist\\LICENSE", + "node_modules\\yallist\\package.json", + "node_modules\\yallist\\README.md", + "node_modules\\yallist\\yallist.js", + "node_modules\\yargs\\CHANGELOG.md", + "node_modules\\yargs\\index.js", + "node_modules\\yargs\\LICENSE", + "node_modules\\yargs\\package.json", + "node_modules\\yargs\\README.md", + "node_modules\\yargs\\yargs.js", + "node_modules\\yargs\\lib\\apply-extends.js", + "node_modules\\yargs\\lib\\argsert.js", + "node_modules\\yargs\\lib\\command.js", + "node_modules\\yargs\\lib\\completion.js", + "node_modules\\yargs\\lib\\levenshtein.js", + "node_modules\\yargs\\lib\\obj-filter.js", + "node_modules\\yargs\\lib\\usage.js", + "node_modules\\yargs\\lib\\validation.js", + "node_modules\\yargs\\lib\\yerror.js", + "node_modules\\yargs\\locales\\be.json", + "node_modules\\yargs\\locales\\de.json", + "node_modules\\yargs\\locales\\en.json", + "node_modules\\yargs\\locales\\es.json", + "node_modules\\yargs\\locales\\fr.json", + "node_modules\\yargs\\locales\\hi.json", + "node_modules\\yargs\\locales\\hu.json", + "node_modules\\yargs\\locales\\id.json", + "node_modules\\yargs\\locales\\it.json", + "node_modules\\yargs\\locales\\ja.json", + "node_modules\\yargs\\locales\\ko.json", + "node_modules\\yargs\\locales\\nb.json", + "node_modules\\yargs\\locales\\nl.json", + "node_modules\\yargs\\locales\\nn.json", + "node_modules\\yargs\\locales\\pirate.json", + "node_modules\\yargs\\locales\\pl.json", + "node_modules\\yargs\\locales\\pt.json", + "node_modules\\yargs\\locales\\pt_BR.json", + "node_modules\\yargs\\locales\\ru.json", + "node_modules\\yargs\\locales\\th.json", + "node_modules\\yargs\\locales\\tr.json", + "node_modules\\yargs\\locales\\zh_CN.json", + "node_modules\\yargs\\locales\\zh_TW.json", + "node_modules\\yargs-parser\\CHANGELOG.md", + "node_modules\\yargs-parser\\index.js", + "node_modules\\yargs-parser\\package.json", + "node_modules\\yargs-parser\\README.md", + "node_modules\\yargs-parser\\lib\\tokenize-arg-string.js", + "discord_voice.node", + "openh264-1.7.0-win32.dll", + "index.js", + "manifest.json" + ] } \ No newline at end of file diff --git a/src/Constants.ts b/src/Constants.ts index 3a7246f..7dd32ca 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -1,25 +1,25 @@ -// bootstrap constants -// after startup, these constants will be merged into core module constants -// since they are used in both locations (see app/Constants.js) - -import { releaseChannel } from './buildInfo'; -import appSettings from './appSettings'; -const pak = require("../package.json" as any) as any -import * as path from "path" - -const settings = appSettings.getSettings(); - -function capitalizeFirstLetter(s) { - return s.charAt(0).toUpperCase() + s.slice(1); -} - -export const APP_NAME = 'Lightcord' + (releaseChannel === 'stable' ? '' : capitalizeFirstLetter(releaseChannel)); -const APP_ID_BASE = 'com.squirrel'; -export const APP_ID = `${APP_ID_BASE}.${APP_NAME}.${APP_NAME}`; - -export const API_ENDPOINT = settings.get('API_ENDPOINT') || 'https://discord.com/api'; -export const UPDATE_ENDPOINT = settings.get('UPDATE_ENDPOINT') || API_ENDPOINT; -export const mainAppDirname = path.join(__dirname, "..") - -export const version:string = pak.version +// bootstrap constants +// after startup, these constants will be merged into core module constants +// since they are used in both locations (see app/Constants.js) + +import { releaseChannel } from './buildInfo'; +import appSettings from './appSettings'; +const pak = require("../package.json" as any) as any +import * as path from "path" + +const settings = appSettings.getSettings(); + +function capitalizeFirstLetter(s) { + return s.charAt(0).toUpperCase() + s.slice(1); +} + +export const APP_NAME = 'Lightcord' + (releaseChannel === 'stable' ? '' : capitalizeFirstLetter(releaseChannel)); +const APP_ID_BASE = 'com.squirrel'; +export const APP_ID = `${APP_ID_BASE}.${APP_NAME}.${APP_NAME}`; + +export const API_ENDPOINT = settings.get('API_ENDPOINT') || 'https://discord.com/api'; +export const UPDATE_ENDPOINT = settings.get('UPDATE_ENDPOINT') || API_ENDPOINT; +export const mainAppDirname = path.join(__dirname, "..") + +export const version:string = pak.version export const packageJSON:any = pak \ No newline at end of file diff --git a/src/buildInfo.ts b/src/buildInfo.ts index b75d723..efd0882 100644 --- a/src/buildInfo.ts +++ b/src/buildInfo.ts @@ -1,17 +1,17 @@ -import { getCommitID } from "./common/git" -const pak = require("../package.json") - -export const releaseChannel:"stable"|"canary"|"ptb"|"development" = "stable" -export const version = "0.0.307" -export const commit = getCommitID() -export default { - releaseChannel, - version, - commit -} - -global["BuildInfo"] = { - releaseChannel, - version: pak.version, - commit +import { getCommitID } from "./common/git" +const pak = require("../package.json") + +export const releaseChannel:"stable"|"canary"|"ptb"|"development" = "stable" +export const version = "0.0.307" +export const commit = getCommitID() +export default { + releaseChannel, + version, + commit +} + +global["BuildInfo"] = { + releaseChannel, + version: pak.version, + commit } \ No newline at end of file diff --git a/src/common/Settings.ts b/src/common/Settings.ts index ebdaab5..5bdec10 100644 --- a/src/common/Settings.ts +++ b/src/common/Settings.ts @@ -1,71 +1,71 @@ -// Imported from main Discord package. - -import * as fs from "fs" -import * as path from "path" - -// TODO: sync fs operations could cause slowdown and/or freezes, depending on usage -// if this is fine, remove this todo -export default class Settings { - path: string; - lastSaved: string; - settings: any; - lastModified: number; - constructor(root:string) { - this.path = path.join(root, 'settings.json'); - console.log(this.path) - try { - this.lastSaved = fs.readFileSync(this.path, "utf8"); - this.settings = JSON.parse(this.lastSaved); - } catch (e) { - this.lastSaved = ''; - this.settings = {}; - } - this.lastModified = this._lastModified(); - } - - _lastModified() { - try { - return fs.statSync(this.path).mtime.getTime(); - } catch (e) { - return 0; - } - } - - get(key, defaultValue = false) { - if (this.settings.hasOwnProperty(key)) { - return this.settings[key]; - } - - return defaultValue; - } - - set(key, value) { - this.settings[key] = value; - } - - delete(key){ - delete this.settings[key] - } - - exists(key){ - return key in this.settings - } - - save() { - if (this.lastModified && this.lastModified !== this._lastModified()) { - console.warn('Not saving settings, it has been externally modified.'); - return; - } - - try { - const toSave = JSON.stringify(this.settings, null, 2); - if (this.lastSaved != toSave) { - this.lastSaved = toSave; - fs.writeFileSync(this.path, toSave, "utf8"); - this.lastModified = this._lastModified(); - } - } catch (err) { - console.warn('Failed saving settings with error: ', err); - } - } -} +// Imported from main Discord package. + +import * as fs from "fs" +import * as path from "path" + +// TODO: sync fs operations could cause slowdown and/or freezes, depending on usage +// if this is fine, remove this todo +export default class Settings { + path: string; + lastSaved: string; + settings: any; + lastModified: number; + constructor(root:string) { + this.path = path.join(root, 'settings.json'); + console.log(this.path) + try { + this.lastSaved = fs.readFileSync(this.path, "utf8"); + this.settings = JSON.parse(this.lastSaved); + } catch (e) { + this.lastSaved = ''; + this.settings = {}; + } + this.lastModified = this._lastModified(); + } + + _lastModified() { + try { + return fs.statSync(this.path).mtime.getTime(); + } catch (e) { + return 0; + } + } + + get(key, defaultValue = false) { + if (this.settings.hasOwnProperty(key)) { + return this.settings[key]; + } + + return defaultValue; + } + + set(key, value) { + this.settings[key] = value; + } + + delete(key){ + delete this.settings[key] + } + + exists(key){ + return key in this.settings + } + + save() { + if (this.lastModified && this.lastModified !== this._lastModified()) { + console.warn('Not saving settings, it has been externally modified.'); + return; + } + + try { + const toSave = JSON.stringify(this.settings, null, 2); + if (this.lastSaved != toSave) { + this.lastSaved = toSave; + fs.writeFileSync(this.path, toSave, "utf8"); + this.lastModified = this._lastModified(); + } + } catch (err) { + console.warn('Failed saving settings with error: ', err); + } + } +} diff --git a/src/common/paths.ts b/src/common/paths.ts index 4b428c6..114984a 100644 --- a/src/common/paths.ts +++ b/src/common/paths.ts @@ -1,56 +1,56 @@ -import * as fs from "fs" -import * as path from "path" -import rimraf from "rimraf" -import { app } from "electron" - -// Determines environment-specific paths based on info provided -import originalFs from 'fs'; - -let userDataPath = null; -let resourcesPath = null; - -function determineAppUserDataRoot() { - return app.getPath('appData') -} - -function determineUserData(userDataRoot, buildInfo) { - return path.join(userDataRoot, 'Lightcord'); -} - -// cleans old version data in the background -export function cleanOldVersions(buildInfo) { - const entries = fs.readdirSync(userDataPath) || []; - entries.forEach(entry => { - const fullPath = path.join(userDataPath, entry); - if (fs.lstatSync(fullPath).isDirectory() && entry.indexOf(buildInfo.version) === -1) { - if (entry.match('^[0-9]+.[0-9]+.[0-9]+') != null) { - console.log('Removing old directory ', entry); - rimraf(fullPath, originalFs, error => { - if (error) { - console.warn('...failed with error: ', error); - } - }); - } - } - }); -} - -export function init(buildInfo) { - resourcesPath = path.join(require.main.filename, '..', '..', '..'); - - const userDataRoot = determineAppUserDataRoot(); - - userDataPath = determineUserData(userDataRoot, buildInfo); - - const { app } = require('electron'); - app.setPath('userData', userDataPath); - console.log(userDataPath, buildInfo.version) -} - -export function getUserData() { - return userDataPath; -} - -export function getResources() { - return resourcesPath; -} +import * as fs from "fs" +import * as path from "path" +import rimraf from "rimraf" +import { app } from "electron" + +// Determines environment-specific paths based on info provided +import originalFs from 'fs'; + +let userDataPath = null; +let resourcesPath = null; + +function determineAppUserDataRoot() { + return app.getPath('appData') +} + +function determineUserData(userDataRoot, buildInfo) { + return path.join(userDataRoot, 'Lightcord'); +} + +// cleans old version data in the background +export function cleanOldVersions(buildInfo) { + const entries = fs.readdirSync(userDataPath) || []; + entries.forEach(entry => { + const fullPath = path.join(userDataPath, entry); + if (fs.lstatSync(fullPath).isDirectory() && entry.indexOf(buildInfo.version) === -1) { + if (entry.match('^[0-9]+.[0-9]+.[0-9]+') != null) { + console.log('Removing old directory ', entry); + rimraf(fullPath, originalFs, error => { + if (error) { + console.warn('...failed with error: ', error); + } + }); + } + } + }); +} + +export function init(buildInfo) { + resourcesPath = path.join(require.main.filename, '..', '..', '..'); + + const userDataRoot = determineAppUserDataRoot(); + + userDataPath = determineUserData(userDataRoot, buildInfo); + + const { app } = require('electron'); + app.setPath('userData', userDataPath); + console.log(userDataPath, buildInfo.version) +} + +export function getUserData() { + return userDataPath; +} + +export function getResources() { + return resourcesPath; +} diff --git a/src/index.ts b/src/index.ts index 1af0e73..553b0fc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,139 +1,139 @@ -/** settings */ -import appSettings from "./appSettings"; -let settings = appSettings.getSettings(); - -/** Glasstron */ -if (settings.get("GLASSTRON", true))require("glasstron"); - -/** Modules */ -import * as electron from "electron"; -import requireNativeDiscordModule from "./requireNative"; -import autoStart from "./autoStart"; -import * as buildInfo from "./buildInfo"; -import * as Constants from "./Constants"; -import * as GPUSettings from "./GPUSettings"; -import * as moduleUpdater from "./common/moduleUpdater"; -import * as paths from "./common/paths"; -import * as splashScreen from "./splashScreen"; -import { join } from "path"; -import { homedir } from "os"; - -if (process.platform === "linux") { - // Some people are reporting audio problems on Linux that are fixed by setting - // an environment variable PULSE_LATENCY_MSEC=30 -- the "real" fix is to see - // what conditions require this and set this then (also to set it directly in - // our webrtc setup code rather than here) but this should fix the bug for now. - if (process.env.PULSE_LATENCY_MSEC === undefined) { - process.env.PULSE_LATENCY_MSEC = "30"; - } -} - -paths.init(buildInfo); -electron.app.commandLine.appendSwitch( - "autoplay-policy", - "no-user-gesture-required" -); -electron.app.commandLine.appendSwitch("no-force-async-hooks-checks"); -electron.app.commandLine.appendSwitch("enable-transparent-visuals"); - -function setupHardwareAcceleration() { - const settings = appSettings.getSettings(); - // TODO: this is a copy of gpuSettings.getEnableHardwareAcceleration - if (!settings.get("enableHardwareAcceleration", true)) { - electron.app.disableHardwareAcceleration(); - } -} - -electron.app.allowRendererProcessReuse = false; - -global["releaseChannel"] = "stable"; - -setupHardwareAcceleration(); -function hasArgvFlag(flag) { - return (process.argv || []).slice(1).includes(flag); -} - -//Transform main thread into async -(async function Main() { - await electron.app.whenReady(); - - if (process.argv.includes("--should-create-shortcut")) { - console.log(`Creating shortcuts.`); - if (process.platform === "win32") { - let options = { - appUserModelId: Constants.APP_ID, - description: Constants.packageJSON.description, - target: process.execPath, - } - electron.shell.writeShortcutLink( - join(homedir(), "Desktop", "Lightcord.lnk"), - "create", - options - ); - electron.shell.writeShortcutLink( - join( - electron.app.getPath("appData"), - "Microsoft", - "Windows", - "Start Menu", - "Programs", - "Lightcord.lnk" - ), - "create", - options - ); - autoStart.isInstalled((installed) => { - if(installed)return - autoStart.install(console.log) - }) - } - } - - console.log(`Initializing Lightcord.`); - console.log(`Version: ${buildInfo.version} -releaseChannel: ${buildInfo.releaseChannel} -commit: ${buildInfo.commit}`); - - electron.app.setAppUserModelId(Constants.APP_ID); - - - - let coreModule; - - const isFirstInstance = electron.app.requestSingleInstanceLock(); - const allowMultipleInstances = hasArgvFlag("--multi-instance"); - if (!isFirstInstance && !allowMultipleInstances) { - electron.app.quit(); - return - } - - if (!allowMultipleInstances) { - electron.app.on("second-instance", (_event, args, _workingDirectory) => { - if(args && args[1] === "--overlay-host"){ - // this is a patch for Lightcord that focus itself when the user is playing a game. - //console.warn("OVERLAY HOST DÉTECTÉ. EVENNEMENT IGNORÉ MAIS POURRAIT CAUSER UN PROBLÈME.") - return; - } - - if (coreModule) { - coreModule.handleSingleInstance(args); - } - }); - } - - const startMinimized = hasArgvFlag("--start-minimized"); - - coreModule = requireNativeDiscordModule("discord_desktop_core"); - coreModule.startup({ - paths, - splashScreen, - moduleUpdater, - autoStart, - buildInfo, - appSettings, - Constants, - GPUSettings, - }); - - coreModule.setMainWindowVisible(!startMinimized); -})(); +/** settings */ +import appSettings from "./appSettings"; +let settings = appSettings.getSettings(); + +/** Glasstron */ +if (settings.get("GLASSTRON", true))require("glasstron"); + +/** Modules */ +import * as electron from "electron"; +import requireNativeDiscordModule from "./requireNative"; +import autoStart from "./autoStart"; +import * as buildInfo from "./buildInfo"; +import * as Constants from "./Constants"; +import * as GPUSettings from "./GPUSettings"; +import * as moduleUpdater from "./common/moduleUpdater"; +import * as paths from "./common/paths"; +import * as splashScreen from "./splashScreen"; +import { join } from "path"; +import { homedir } from "os"; + +if (process.platform === "linux") { + // Some people are reporting audio problems on Linux that are fixed by setting + // an environment variable PULSE_LATENCY_MSEC=30 -- the "real" fix is to see + // what conditions require this and set this then (also to set it directly in + // our webrtc setup code rather than here) but this should fix the bug for now. + if (process.env.PULSE_LATENCY_MSEC === undefined) { + process.env.PULSE_LATENCY_MSEC = "30"; + } +} + +paths.init(buildInfo); +electron.app.commandLine.appendSwitch( + "autoplay-policy", + "no-user-gesture-required" +); +electron.app.commandLine.appendSwitch("no-force-async-hooks-checks"); +electron.app.commandLine.appendSwitch("enable-transparent-visuals"); + +function setupHardwareAcceleration() { + const settings = appSettings.getSettings(); + // TODO: this is a copy of gpuSettings.getEnableHardwareAcceleration + if (!settings.get("enableHardwareAcceleration", true)) { + electron.app.disableHardwareAcceleration(); + } +} + +electron.app.allowRendererProcessReuse = false; + +global["releaseChannel"] = "stable"; + +setupHardwareAcceleration(); +function hasArgvFlag(flag) { + return (process.argv || []).slice(1).includes(flag); +} + +//Transform main thread into async +(async function Main() { + await electron.app.whenReady(); + + if (process.argv.includes("--should-create-shortcut")) { + console.log(`Creating shortcuts.`); + if (process.platform === "win32") { + let options = { + appUserModelId: Constants.APP_ID, + description: Constants.packageJSON.description, + target: process.execPath, + } + electron.shell.writeShortcutLink( + join(homedir(), "Desktop", "Lightcord.lnk"), + "create", + options + ); + electron.shell.writeShortcutLink( + join( + electron.app.getPath("appData"), + "Microsoft", + "Windows", + "Start Menu", + "Programs", + "Lightcord.lnk" + ), + "create", + options + ); + autoStart.isInstalled((installed) => { + if(installed)return + autoStart.install(console.log) + }) + } + } + + console.log(`Initializing Lightcord.`); + console.log(`Version: ${buildInfo.version} +releaseChannel: ${buildInfo.releaseChannel} +commit: ${buildInfo.commit}`); + + electron.app.setAppUserModelId(Constants.APP_ID); + + + + let coreModule; + + const isFirstInstance = electron.app.requestSingleInstanceLock(); + const allowMultipleInstances = hasArgvFlag("--multi-instance"); + if (!isFirstInstance && !allowMultipleInstances) { + electron.app.quit(); + return + } + + if (!allowMultipleInstances) { + electron.app.on("second-instance", (_event, args, _workingDirectory) => { + if(args && args[1] === "--overlay-host"){ + // this is a patch for Lightcord that focus itself when the user is playing a game. + //console.warn("OVERLAY HOST DÉTECTÉ. EVENNEMENT IGNORÉ MAIS POURRAIT CAUSER UN PROBLÈME.") + return; + } + + if (coreModule) { + coreModule.handleSingleInstance(args); + } + }); + } + + const startMinimized = hasArgvFlag("--start-minimized"); + + coreModule = requireNativeDiscordModule("discord_desktop_core"); + coreModule.startup({ + paths, + splashScreen, + moduleUpdater, + autoStart, + buildInfo, + appSettings, + Constants, + GPUSettings, + }); + + coreModule.setMainWindowVisible(!startMinimized); +})(); diff --git a/src/splashScreen.ts b/src/splashScreen.ts index 1bea502..dffb9e6 100644 --- a/src/splashScreen.ts +++ b/src/splashScreen.ts @@ -1,191 +1,191 @@ -import * as electron from "electron" -import {EventEmitter} from "events" -import * as fs from "fs" -import * as path from "path" -import * as moduleUpdater from "./common/moduleUpdater" -import * as paths from "./common/paths" -import * as ipcMain from "./ipcMain" - -// citron note: atom seems to add about 50px height to the frame on mac but not windows -// TODO: see if we can eliminate fudge by using useContentSize BrowserWindow option -const LOADING_WINDOW_WIDTH = 300; -const LOADING_WINDOW_HEIGHT = process.platform === 'darwin' ? 300 : 350; - -// TODO: addModulesListener events should use Module's constants -const UPDATE_CHECK_FINISHED = 'update-check-finished'; -const LAUNCHING = 'launching'; - -export const APP_SHOULD_LAUNCH = 'APP_SHOULD_LAUNCH'; -export const APP_SHOULD_SHOW = 'APP_SHOULD_SHOW'; - -export const events = new EventEmitter(); - -function webContentsSend(win, event, ...args) { - if(event === "SPLASH_UPDATE_STATE")lastStatus = args[0].status - if (win != null && win.webContents != null) { - win.webContents.send(`DISCORD_${event}`, ...args); - } -} - -let splashWindow:electron.BrowserWindow; -let modulesListeners; -let splashState; -let launchedMainWindow; -let quoteCachePath; -let lastStatus - -export function setSplashState(state:any){ - splashState = state - if (splashWindow != null && !splashWindow.isDestroyed() && !splashWindow.webContents.isDestroyed()) { - webContentsSend(splashWindow, 'SPLASH_UPDATE_STATE', Object.assign({ status: lastStatus }, splashState)); - } -} - -export function launchMainWindow(){ - launchMainWindowInternal(); - updateSplashState(LAUNCHING); -} - -export function initSplash(startMinimized = false) { - modulesListeners = {}; - splashState = {}; - launchedMainWindow = false; -/* - addModulesListener(UPDATE_CHECK_FINISHED, ({ succeeded, updateCount, manualRequired }) => { - launchMainWindow(); - updateSplashState(LAUNCHING); - });*/ - - launchSplashWindow(startMinimized); - - quoteCachePath = path.join(paths.getUserData(), 'quotes.json'); - ipcMain.default.on('UPDATED_QUOTES', (_event, quotes) => cacheLatestQuotes(quotes)); -} - -function destroySplash() { - if (splashWindow) { - splashWindow.setSkipTaskbar(true); - // defer the window hiding for a short moment so it gets covered by the main window - const _nukeWindow = () => { - splashWindow.hide(); - splashWindow.close(); - splashWindow = null; - }; - setTimeout(_nukeWindow, 100); - } -} - -function addModulesListener(event, listener) { - modulesListeners[event] = listener; - moduleUpdater.events.addListener(event, listener); -} - -function removeModulesListeners() { - for (const event of Object.keys(modulesListeners)) { - moduleUpdater.events.removeListener(event, modulesListeners[event]); - } -} -export function updateSplashState(event) { - if (splashWindow != null && !splashWindow.isDestroyed() && !splashWindow.webContents.isDestroyed()) { - webContentsSend(splashWindow, 'SPLASH_UPDATE_STATE', Object.assign({ status: event }, splashState)); - } -} - -function launchSplashWindow(startMinimized = false) { - const windowConfig = { - width: LOADING_WINDOW_WIDTH, - height: LOADING_WINDOW_HEIGHT, - transparent: false, - frame: false, - resizable: false, - center: true, - show: false, - webPreferences: { - nodeIntegration: true, - enableRemoteModule: true - }, - icon: path.join(__dirname, "..", "modules", "discord_desktop_core", "core", "app", "images", "discord.png") - }; - - splashWindow = new electron.BrowserWindow(windowConfig); - - splashWindow.webContents.session.protocol.interceptFileProtocol('http', (request, callback) => { - callback(path.join(__dirname, '..', "splash", request.url.replace('http://localhost/', ''))); - }); - - // prevent users from dropping links to navigate in splash window - splashWindow.webContents.on('will-navigate', e => e.preventDefault()); - - splashWindow.webContents.on('new-window', (e, windowURL) => { - e.preventDefault(); - electron.shell.openExternal(windowURL); - // exit, but delay half a second because openExternal is about to fire - // some events to things that are freed by app.quit. - setTimeout(electron.app.quit, 500); - }); - - if (process.platform !== 'darwin') { - // citron note: this causes a crash on quit while the window is open on osx - splashWindow.on('closed', () => { - splashWindow = null; - if (!launchedMainWindow) { - // user has closed this window before we launched the app, so let's quit - electron.app.quit(); - } - }); - } - - ipcMain.default.on('SPLASH_SCREEN_READY', () => { - const cachedQuote = chooseCachedQuote(); - if (cachedQuote) { - webContentsSend(splashWindow, 'SPLASH_SCREEN_QUOTE', cachedQuote); - } - - if (splashWindow && !startMinimized) { - splashWindow.show(); - } - - moduleUpdater.installPendingUpdates(); - - events.emit("SPLASH_SCREEN_READY") - }); - ipcMain.default.on('LAUNCH_ANYWAY', () => { - launchMainWindowInternal() - }); - - splashWindow.loadURL("http://localhost/index.html"); -} - -function launchMainWindowInternal() { - removeModulesListeners(); - if (!launchedMainWindow && splashWindow != null) { - launchedMainWindow = true; - events.emit(APP_SHOULD_LAUNCH); - } -} - -function scheduleUpdateCheck() {} - -export function focusWindow() { - if (splashWindow != null) { - splashWindow.focus(); - } -} - -export function pageReady() { - destroySplash(); - process.nextTick(() => events.emit(APP_SHOULD_SHOW)); -} - -function cacheLatestQuotes(quotes) { - fs.writeFile(quoteCachePath, JSON.stringify(quotes), e => { - if (e) { - console.warn('Failed updating quote cache with error: ', e); - } - }); -} - -function chooseCachedQuote() { - let cachedQuote = "Launching..."; - return cachedQuote; +import * as electron from "electron" +import {EventEmitter} from "events" +import * as fs from "fs" +import * as path from "path" +import * as moduleUpdater from "./common/moduleUpdater" +import * as paths from "./common/paths" +import * as ipcMain from "./ipcMain" + +// citron note: atom seems to add about 50px height to the frame on mac but not windows +// TODO: see if we can eliminate fudge by using useContentSize BrowserWindow option +const LOADING_WINDOW_WIDTH = 300; +const LOADING_WINDOW_HEIGHT = process.platform === 'darwin' ? 300 : 350; + +// TODO: addModulesListener events should use Module's constants +const UPDATE_CHECK_FINISHED = 'update-check-finished'; +const LAUNCHING = 'launching'; + +export const APP_SHOULD_LAUNCH = 'APP_SHOULD_LAUNCH'; +export const APP_SHOULD_SHOW = 'APP_SHOULD_SHOW'; + +export const events = new EventEmitter(); + +function webContentsSend(win, event, ...args) { + if(event === "SPLASH_UPDATE_STATE")lastStatus = args[0].status + if (win != null && win.webContents != null) { + win.webContents.send(`DISCORD_${event}`, ...args); + } +} + +let splashWindow:electron.BrowserWindow; +let modulesListeners; +let splashState; +let launchedMainWindow; +let quoteCachePath; +let lastStatus + +export function setSplashState(state:any){ + splashState = state + if (splashWindow != null && !splashWindow.isDestroyed() && !splashWindow.webContents.isDestroyed()) { + webContentsSend(splashWindow, 'SPLASH_UPDATE_STATE', Object.assign({ status: lastStatus }, splashState)); + } +} + +export function launchMainWindow(){ + launchMainWindowInternal(); + updateSplashState(LAUNCHING); +} + +export function initSplash(startMinimized = false) { + modulesListeners = {}; + splashState = {}; + launchedMainWindow = false; +/* + addModulesListener(UPDATE_CHECK_FINISHED, ({ succeeded, updateCount, manualRequired }) => { + launchMainWindow(); + updateSplashState(LAUNCHING); + });*/ + + launchSplashWindow(startMinimized); + + quoteCachePath = path.join(paths.getUserData(), 'quotes.json'); + ipcMain.default.on('UPDATED_QUOTES', (_event, quotes) => cacheLatestQuotes(quotes)); +} + +function destroySplash() { + if (splashWindow) { + splashWindow.setSkipTaskbar(true); + // defer the window hiding for a short moment so it gets covered by the main window + const _nukeWindow = () => { + splashWindow.hide(); + splashWindow.close(); + splashWindow = null; + }; + setTimeout(_nukeWindow, 100); + } +} + +function addModulesListener(event, listener) { + modulesListeners[event] = listener; + moduleUpdater.events.addListener(event, listener); +} + +function removeModulesListeners() { + for (const event of Object.keys(modulesListeners)) { + moduleUpdater.events.removeListener(event, modulesListeners[event]); + } +} +export function updateSplashState(event) { + if (splashWindow != null && !splashWindow.isDestroyed() && !splashWindow.webContents.isDestroyed()) { + webContentsSend(splashWindow, 'SPLASH_UPDATE_STATE', Object.assign({ status: event }, splashState)); + } +} + +function launchSplashWindow(startMinimized = false) { + const windowConfig = { + width: LOADING_WINDOW_WIDTH, + height: LOADING_WINDOW_HEIGHT, + transparent: false, + frame: false, + resizable: false, + center: true, + show: false, + webPreferences: { + nodeIntegration: true, + enableRemoteModule: true + }, + icon: path.join(__dirname, "..", "modules", "discord_desktop_core", "core", "app", "images", "discord.png") + }; + + splashWindow = new electron.BrowserWindow(windowConfig); + + splashWindow.webContents.session.protocol.interceptFileProtocol('http', (request, callback) => { + callback(path.join(__dirname, '..', "splash", request.url.replace('http://localhost/', ''))); + }); + + // prevent users from dropping links to navigate in splash window + splashWindow.webContents.on('will-navigate', e => e.preventDefault()); + + splashWindow.webContents.on('new-window', (e, windowURL) => { + e.preventDefault(); + electron.shell.openExternal(windowURL); + // exit, but delay half a second because openExternal is about to fire + // some events to things that are freed by app.quit. + setTimeout(electron.app.quit, 500); + }); + + if (process.platform !== 'darwin') { + // citron note: this causes a crash on quit while the window is open on osx + splashWindow.on('closed', () => { + splashWindow = null; + if (!launchedMainWindow) { + // user has closed this window before we launched the app, so let's quit + electron.app.quit(); + } + }); + } + + ipcMain.default.on('SPLASH_SCREEN_READY', () => { + const cachedQuote = chooseCachedQuote(); + if (cachedQuote) { + webContentsSend(splashWindow, 'SPLASH_SCREEN_QUOTE', cachedQuote); + } + + if (splashWindow && !startMinimized) { + splashWindow.show(); + } + + moduleUpdater.installPendingUpdates(); + + events.emit("SPLASH_SCREEN_READY") + }); + ipcMain.default.on('LAUNCH_ANYWAY', () => { + launchMainWindowInternal() + }); + + splashWindow.loadURL("http://localhost/index.html"); +} + +function launchMainWindowInternal() { + removeModulesListeners(); + if (!launchedMainWindow && splashWindow != null) { + launchedMainWindow = true; + events.emit(APP_SHOULD_LAUNCH); + } +} + +function scheduleUpdateCheck() {} + +export function focusWindow() { + if (splashWindow != null) { + splashWindow.focus(); + } +} + +export function pageReady() { + destroySplash(); + process.nextTick(() => events.emit(APP_SHOULD_SHOW)); +} + +function cacheLatestQuotes(quotes) { + fs.writeFile(quoteCachePath, JSON.stringify(quotes), e => { + if (e) { + console.warn('Failed updating quote cache with error: ', e); + } + }); +} + +function chooseCachedQuote() { + let cachedQuote = "Launching..."; + return cachedQuote; } \ No newline at end of file