diff --git a/renderer/src/modules/addonmanager.js b/renderer/src/modules/addonmanager.js index 358b6551..4c945c82 100644 --- a/renderer/src/modules/addonmanager.js +++ b/renderer/src/modules/addonmanager.js @@ -201,7 +201,7 @@ export default class AddonManager { const addon = __non_webpack_require__(path.resolve(this.addonFolder, filename)); } catch (error) { - return new AddonError(filename, filename, Strings.Addons.compileError, {message: error.message, stack: error.stack}); + return new AddonError(filename, filename, Strings.Addons.compileError, {message: error.message, stack: error.stack}, this.prefix); } const addon = __non_webpack_require__(path.resolve(this.addonFolder, filename)); @@ -209,7 +209,7 @@ export default class AddonManager { // await Promise.resolve(addon); // addon = __non_webpack_require__(path.resolve(this.addonFolder, filename)); // console.log(addon); - if (this.addonList.find(c => c.id == addon.id)) return new AddonError(addon.name, filename, Strings.Addons.alreadyExists.format({type: this.prefix, name: addon.name})); + if (this.addonList.find(c => c.id == addon.id)) return new AddonError(addon.name, filename, Strings.Addons.alreadyExists.format({type: this.prefix, name: addon.name}), this.prefix); const error = this.initializeAddon(addon); if (error) return error; diff --git a/renderer/src/modules/core.js b/renderer/src/modules/core.js index 3b801b15..d1e58d59 100644 --- a/renderer/src/modules/core.js +++ b/renderer/src/modules/core.js @@ -153,7 +153,7 @@ export default new class Core { catch (err) { Logger.stacktrace("Updater", "Failed to update", err); Modals.showConfirmationModal("Update Failed", "BetterDiscord failed to update. Please download the latest version of the installer from GitHub (https://github.com/BetterDiscord/Installer/releases/latest) and reinstall.", { - cancelText: "" + cancelText: null }); } } diff --git a/renderer/src/modules/pluginmanager.js b/renderer/src/modules/pluginmanager.js index f0584649..cc895a47 100644 --- a/renderer/src/modules/pluginmanager.js +++ b/renderer/src/modules/pluginmanager.js @@ -66,9 +66,10 @@ export default new class PluginManager extends AddonManager { unloadPlugin(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);} loadPlugin(filename) {return this.loadAddon(filename);} - loadAddon(filename) { + loadAddon(filename, shouldCTE = true) { const error = super.loadAddon(filename); - if (error) Modals.showAddonErrors({plugins: [error]}); + if (error && shouldCTE) Modals.showAddonErrors({plugins: [error]}); + return error; } reloadPlugin(idOrFileOrAddon) { @@ -79,7 +80,7 @@ export default new class PluginManager extends AddonManager { /* Overrides */ initializeAddon(addon) { - if (!addon.exports) return new AddonError(addon.name, addon.filename, "Plugin had no exports", {message: "Plugin had no exports or no name property.", stack: ""}); + if (!addon.exports) return new AddonError(addon.name, addon.filename, "Plugin had no exports", {message: "Plugin had no exports or no name property.", stack: ""}, this.prefix); try { const PluginClass = addon.exports; const thePlugin = new PluginClass(); @@ -93,10 +94,10 @@ export default new class PluginManager extends AddonManager { } catch (error) { this.state[addon.id] = false; - return new AddonError(addon.name, addon.filename, "load() could not be fired.", {message: error.message, stack: error.stack}); + return new AddonError(addon.name, addon.filename, "load() could not be fired.", {message: error.message, stack: error.stack}, this.prefix); } } - catch (error) {return new AddonError(addon.name, addon.filename, "Could not be constructed.", {message: error.message, stack: error.stack});} + catch (error) {return new AddonError(addon.name, addon.filename, "Could not be constructed.", {message: error.message, stack: error.stack}, this.prefix);} } getFileModification(module, fileContent, meta) { @@ -138,7 +139,7 @@ export default new class PluginManager extends AddonManager { this.state[addon.id] = false; Toasts.error(Strings.Addons.couldNotStart.format({name: addon.name, version: addon.version})); Logger.stacktrace(this.name, `${addon.name} v${addon.version} could not be started.`, err); - return new AddonError(addon.name, addon.filename, Strings.Addons.enabled.format({method: "start()"}), {message: err.message, stack: err.stack}); + return new AddonError(addon.name, addon.filename, Strings.Addons.enabled.format({method: "start()"}), {message: err.message, stack: err.stack}, this.prefix); } this.emit("started", addon.id); Toasts.show(Strings.Addons.enabled.format({name: addon.name, version: addon.version})); @@ -155,7 +156,7 @@ export default new class PluginManager extends AddonManager { this.state[addon.id] = false; Toasts.error(Strings.Addons.couldNotStop.format({name: addon.name, version: addon.version})); Logger.stacktrace(this.name, `${addon.name} v${addon.version} could not be started.`, err); - return new AddonError(addon.name, addon.filename, Strings.Addons.enabled.format({method: "stop()"}), {message: err.message, stack: err.stack}); + return new AddonError(addon.name, addon.filename, Strings.Addons.enabled.format({method: "stop()"}), {message: err.message, stack: err.stack}, this.prefix); } this.emit("stopped", addon.id); Toasts.show(Strings.Addons.disabled.format({name: addon.name, version: addon.version})); diff --git a/renderer/src/modules/thememanager.js b/renderer/src/modules/thememanager.js index f9a6de1e..32dd1e2b 100644 --- a/renderer/src/modules/thememanager.js +++ b/renderer/src/modules/thememanager.js @@ -47,9 +47,10 @@ export default new class ThemeManager extends AddonManager { loadTheme(filename) {return this.loadAddon(filename);} reloadTheme(idOrFileOrAddon) {return this.reloadAddon(idOrFileOrAddon);} - loadAddon(filename) { + loadAddon(filename, shouldCTE = true) { const error = super.loadAddon(filename); - if (error) Modals.showAddonErrors({themes: [error]}); + if (error && shouldCTE) Modals.showAddonErrors({themes: [error]}); + return error; } /* Overrides */ diff --git a/renderer/src/structs/addonerror.js b/renderer/src/structs/addonerror.js index 65b01670..a3b8a89f 100644 --- a/renderer/src/structs/addonerror.js +++ b/renderer/src/structs/addonerror.js @@ -1,8 +1,9 @@ export default class AddonError extends Error { - constructor(name, filename, message, error) { + constructor(name, filename, message, error, type) { super(message); this.name = name; this.file = filename; this.error = error; + this.type = type; } } \ No newline at end of file diff --git a/renderer/src/structs/builtin.js b/renderer/src/structs/builtin.js index 432d41fc..f3c6358c 100644 --- a/renderer/src/structs/builtin.js +++ b/renderer/src/structs/builtin.js @@ -90,6 +90,10 @@ export default class BuiltinModule { return Patcher.before(this.name, object, func, callback); } + instead(object, func, callback) { + return Patcher.instead(this.name, object, func, callback); + } + after(object, func, callback) { return Patcher.after(this.name, object, func, callback); } diff --git a/renderer/src/styles/ui/addonerror.css b/renderer/src/styles/ui/addonerror.css new file mode 100644 index 00000000..a970aba7 --- /dev/null +++ b/renderer/src/styles/ui/addonerror.css @@ -0,0 +1,44 @@ +.be-modal .tab-bar.TOP .tab-bar-item:nth-child(1) { + margin-left: 20px; +} + +.bd-addon-error { + margin: 10px; + border-radius: 4px; + overflow: hidden; +} + +.bd-addon-error-header { + display: flex; + padding: 10px; + background: rgba(0, 0, 0, 0.2); + align-items: center; +} + +.bd-addon-error-message { + font-weight: 700; +} + +.bd-addon-error-body { + padding: 10px; + background: var(--background-mobile-secondary); +} + +.bd-addon-error-header svg { + margin-right: 7px; +} + +.bd-addon-error-stack-header svg { + float: right; + transform: rotate(90deg); + transition: 0.4s; + color: #fff; +} + +.bd-addon-error-stack.opened svg { + transform: rotate(0deg); +} + +.bd-addon-error-stack-header { + color: #b9bbbe; +} diff --git a/renderer/src/styles/ui/modals.css b/renderer/src/styles/ui/modals.css index 2381f50a..af1d9f78 100644 --- a/renderer/src/styles/ui/modals.css +++ b/renderer/src/styles/ui/modals.css @@ -32,6 +32,14 @@ transform: translateZ(0); } +.bd-modal { + border-radius: 3px; + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; +} + .bd-modal-wrapper.closing .bd-backdrop { animation: bd-backdrop-closing 200ms linear; animation-fill-mode: forwards; @@ -66,7 +74,7 @@ transform: scale(1); } -.bd-modal-wrapper .bd-modal-inner { +/* .bd-modal .bd-modal-inner { display: flex; contain: layout; flex-direction: column; @@ -79,6 +87,14 @@ min-height: 200px; width: 440px; user-select: text; +} */ + +.bd-modal .bd-modal-inner { + display: flex; + flex-direction: column; + flex: 1; + max-height: 660px; + overflow-y: auto; } .bd-modal-wrapper .bd-content-modal .bd-modal-inner { @@ -86,7 +102,7 @@ width: 700px; } -.bd-modal-wrapper .header { +.bd-modal .header { background-color: #35393e; box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.2); padding: 12px 20px; @@ -97,7 +113,7 @@ line-height: 19px; } -.bd-modal-wrapper .bd-modal-body { +.bd-modal .bd-modal-body { background-color: #36393f; color: #fff; overflow: hidden; @@ -108,22 +124,22 @@ position: relative; } -.bd-modal-wrapper .scroller { +.bd-modal .scroller { padding: 10px; overflow-y: auto; } -.bd-modal-wrapper .bd-content-modal .bd-modal-body { +.bd-modal .bd-content-modal .bd-modal-body { padding: 0; } -.bd-modal-wrapper .footer { +.bd-modal .footer { display: flex; justify-content: flex-end; padding: 10px 20px; } -.bd-modal-wrapper .footer button { +.bd-modal .footer button { min-height: 32px; min-width: 60px; align-items: center; @@ -136,7 +152,7 @@ user-select: none; } -.bd-modal-wrapper .tab-bar-container { +.bd-modal .tab-bar-container { align-items: center; border-bottom: 0; background: rgba(0, 0, 0, 0.2); @@ -147,7 +163,7 @@ margin-bottom: 15px; } -.bd-modal-wrapper .tab-bar.TOP { +.bd-modal .tab-bar.TOP { margin: 0; border: 0; display: flex; @@ -156,30 +172,30 @@ align-items: center; } -.bd-modal-wrapper .tab-bar-container .tab-bar-item { - margin: 0 15px; - padding: 15px 0; - color: #fff; - opacity: 0.5; - transition: opacity 200ms ease; - border-bottom: 2px solid transparent; -} - -.bd-modal-wrapper .tab-bar-container .tab-bar-item:hover { - border-color: #fff; +.bd-modal .tab-bar-container .tab-bar-item { + margin: 10px; + padding: 7px 10px; + border-radius: 5px; + opacity: 0.7; cursor: pointer; } -.bd-modal-wrapper .tab-bar-container .tab-bar-item.selected { +.bd-modal .tab-bar-item:not(.selected):hover { + background: var(--background-primary); +} + +.bd-modal .tab-bar.TOP .tab-bar-item:nth-child(1) { + margin-left: 20px; +} + +.bd-modal .tab-bar-container .tab-bar-item.selected { opacity: 1; - border-color: #fff; + background: #36393f; + border-radius: 5px; + font-weight: 600; } -.bd-modal-wrapper .tab-bar.TOP .tab-bar-item + .tab-bar-item { - margin: 0; -} - -.bd-modal-wrapper .table-header { +.bd-modal .table-header { display: flex; justify-content: space-between; color: #fff; @@ -190,23 +206,23 @@ font-size: 14px; } -.bd-modal-wrapper .table-column { +.bd-modal .table-column { width: 25%; word-wrap: break-word; } -.bd-modal-wrapper .table-column.column-error { +.bd-modal .table-column.column-error { width: 50%; } -.bd-modal-wrapper .errors { +.bd-modal .errors { display: flex; flex-direction: column; font-size: 14px; padding: 0 5px; } -.bd-modal-wrapper .error { +.bd-modal .error { display: flex; color: #fff; border-bottom: 1px solid rgba(255, 255, 255, 0.25); @@ -214,12 +230,12 @@ align-items: center; } -.bd-modal-wrapper .error-link { +.bd-modal .error-link { color: #3e82e5; font-weight: 500; } -.bd-modal-wrapper .bd-content-modal .scroller { +.bd-modal .bd-content-modal .scroller { padding-top: 0; } diff --git a/renderer/src/ui/addonerrormodal.jsx b/renderer/src/ui/addonerrormodal.jsx new file mode 100644 index 00000000..7a703dc2 --- /dev/null +++ b/renderer/src/ui/addonerrormodal.jsx @@ -0,0 +1,122 @@ +import {React, Strings, WebpackModules} from "modules"; +import DownArrow from "./icons/downarrow"; +import Extension from "./icons/extension"; +import ThemeIcon from "./icons/theme"; + +const Parser = Object(WebpackModules.getByProps("defaultRules", "parse")).defaultRules; + +const joinClassNames = (...classNames) => classNames.filter(e => e).join(" "); + +class Collapse extends React.Component { + constructor(props) { + super(props); + this.state = {opened: false}; + } + + toggle() { + if (!this.props.error.stack) return; + this.setState({opened: !this.state.opened}); + } + + render() { + const title = this.props.error.error ? this.props.error.message : this.props.error.message; + const stack = this.props.error.error && this.props.error.error.stack; + + return