Fix multiple bugs, change to sync loading
- Removes copy selector developer option - Add a crash check in the injector - Switch from throwing over the wall to using <script> elements - Revert to synchronous loading due to above - Don't load on login screen - Plugin logging includes version numbers - Fix blankslate links - Fix changelog overflow
This commit is contained in:
parent
1312bca78f
commit
f3f15ec72c
|
@ -22,6 +22,8 @@ electron.app.once("ready", async () => {
|
|||
await ReactDevTools.install();
|
||||
});
|
||||
|
||||
let hasCrashed = false;
|
||||
|
||||
export default class BetterDiscord {
|
||||
static getWindowPrefs() {
|
||||
if (!fs.existsSync(buildInfoFile)) return {};
|
||||
|
@ -89,12 +91,24 @@ export default class BetterDiscord {
|
|||
|
||||
// When DOM is available, pass the renderer over the wall
|
||||
browserWindow.webContents.on("dom-ready", () => {
|
||||
this.injectRenderer(browserWindow);
|
||||
if (!hasCrashed) return this.injectRenderer(browserWindow);
|
||||
|
||||
// If a previous crash was detected, show a message explaining why BD isn't there
|
||||
electron.dialog.showMessageBox({
|
||||
title: "BetterDiscord Crashed",
|
||||
type: "warning",
|
||||
message: "BetterDiscord seems to have crashed your Discord client.",
|
||||
detail: "BetterDiscord has automatically disabled itself temporarily. Try removing all your plugins then restarting Discord."
|
||||
});
|
||||
});
|
||||
|
||||
// This is used to alert renderer code to onSwitch events
|
||||
browserWindow.webContents.on("did-navigate-in-page", () => {
|
||||
browserWindow.webContents.send(IPCEvents.NAVIGATE);
|
||||
});
|
||||
|
||||
browserWindow.webContents.on("render-process-gone", (event, details) => {
|
||||
hasCrashed = true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -21,7 +21,12 @@ if (preload) {
|
|||
process.electronBinding("command_line").appendSwitch("preload", preload);
|
||||
|
||||
// Run original preload
|
||||
try {require(preload);}
|
||||
try {
|
||||
const originalKill = process.kill;
|
||||
process.kill = function() {};
|
||||
require(preload);
|
||||
process.kill = originalKill;
|
||||
}
|
||||
catch (e) {
|
||||
// TODO bail out
|
||||
}
|
||||
|
|
|
@ -17,6 +17,5 @@ export {default as EmoteModule} from "./emotes/emotes";
|
|||
export {default as EmoteMenu} from "./emotes/emotemenu";
|
||||
// export {default as EmoteAutocaps} from "./emotes/emoteautocaps";
|
||||
|
||||
export {default as CopySelector} from "./developer/copyselector";
|
||||
export {default as Debugger} from "./developer/debugger";
|
||||
export {default as ReactDevTools} from "./developer/reactdevtools";
|
|
@ -1,79 +0,0 @@
|
|||
import Builtin from "../../structs/builtin";
|
||||
import {DOM, DiscordModules, Strings} from "modules";
|
||||
|
||||
export default new class DeveloperMode extends Builtin {
|
||||
get name() {return "DeveloperMode";}
|
||||
get category() {return "developer";}
|
||||
get id() {return "developerMode";}
|
||||
get selectorModeID() {return "copySelector";}
|
||||
get selectorMode() {return this.get(this.selectorModeID);}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.copySelectorListener = this.copySelectorListener.bind(this);
|
||||
}
|
||||
|
||||
enabled() {
|
||||
document.addEventListener("contextmenu", this.copySelectorListener);
|
||||
}
|
||||
|
||||
disabled() {
|
||||
document.removeEventListener("contextmenu", this.copySelectorListener);
|
||||
}
|
||||
|
||||
copySelectorListener(ctxEvent) {
|
||||
ctxEvent.stopPropagation();
|
||||
const selector = this.getSelector(ctxEvent.target);
|
||||
function attach() {
|
||||
let cm = DOM.query(".contextMenu-HLZMGh");
|
||||
if (!cm) {
|
||||
const container = DOM.query("#app-mount");
|
||||
const cmWrap = DOM.createElement(`<div class="layer-v9HyYc da-layer">`);
|
||||
cm = DOM.createElement(`<div class="contextMenu-HLZMGh da-contextMenu bd-context-menu"></div>`);
|
||||
cmWrap.append(cm);
|
||||
container.append(cmWrap);
|
||||
cmWrap.style.top = ctxEvent.clientY + "px";
|
||||
cmWrap.style.left = ctxEvent.clientX + "px";
|
||||
cmWrap.style.zIndex = "1002";
|
||||
const removeCM = function(removeEvent) {
|
||||
if (removeEvent.keyCode && removeEvent.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 cmg = DOM.createElement(`<div class="itemGroup-1tL0uz da-itemGroup">`);
|
||||
const cmi = DOM.createElement(`<div class="item-1Yvehc itemBase-tz5SeC da-item da-itemBase clickable-11uBi- da-clickable">`);
|
||||
cmi.append(DOM.createElement(`<div class="label-JWQiNe da-label">${Strings.Developer.copySelector}</div>`));
|
||||
cmi.addEventListener("click", () => {
|
||||
DiscordModules.ElectronModule.copy(selector);
|
||||
cm.style.display = "none";
|
||||
});
|
||||
cmg.append(cmi);
|
||||
cm.append(cmg);
|
||||
}
|
||||
|
||||
setImmediate(attach);
|
||||
}
|
||||
|
||||
getSelector(element) {
|
||||
if (element.id) return `#${element.id}`;
|
||||
const rules = this.getRules(element);
|
||||
const latestRule = rules[rules.length - 1];
|
||||
if (latestRule) return latestRule.selectorText;
|
||||
else if (element.classList.length) return `.${Array.from(element.classList).join(".")}`;
|
||||
return `.${Array.from(element.parentElement.classList).join(".")}`;
|
||||
}
|
||||
|
||||
getRules(element, css = element.ownerDocument.styleSheets) {
|
||||
const sheets = [...css].filter(s => !s.href || !s.href.includes("BetterDiscordApp"));
|
||||
const rules = sheets.map(s => [...(s.cssRules || [])]).flat();
|
||||
const elementRules = rules.filter(r => r && r.selectorText && element.matches(r.selectorText) && r.style.length && r.selectorText.split(", ").length < 8 && !r.selectorText.split(", ").includes("*"));
|
||||
return elementRules;
|
||||
}
|
||||
};
|
|
@ -52,7 +52,6 @@ export default [
|
|||
shown: false,
|
||||
settings: [
|
||||
{type: "switch", id: "debuggerHotkey", value: false},
|
||||
{type: "switch", id: "copySelector", value: false},
|
||||
{type: "switch", id: "reactDevTools", value: false}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -106,10 +106,6 @@ export default {
|
|||
name: "Debugger Hotkey",
|
||||
note: "Allows activating debugger when pressing F8"
|
||||
},
|
||||
copySelector: {
|
||||
name: "Copy Selector",
|
||||
note: "Adds a \"Copy Selector\" option to context menus when developer mode is active"
|
||||
},
|
||||
reactDevTools: {
|
||||
name: "React Developer Tools",
|
||||
note: "Injects your local installation of React Developer Tools into Discord"
|
||||
|
@ -227,9 +223,6 @@ export default {
|
|||
settings: "Editor Settings",
|
||||
editorTitle: "Custom CSS Editor"
|
||||
},
|
||||
Developer: {
|
||||
copySelector: "Copy Selector"
|
||||
},
|
||||
Emotes: {
|
||||
loading: "Loading emotes in the background do not reload.",
|
||||
loaded: "All emotes successfully loaded.",
|
||||
|
|
|
@ -48,14 +48,14 @@ export default class AddonManager {
|
|||
this.windows = new Set();
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
initialize() {
|
||||
this.originalRequire = Module._extensions[this.moduleExtension];
|
||||
Module._extensions[this.moduleExtension] = this.getAddonRequire();
|
||||
Settings.on(this.collection, this.category, this.id, (enabled) => {
|
||||
if (enabled) this.watchAddons();
|
||||
else this.unwatchAddons();
|
||||
});
|
||||
return await this.loadAllAddons();
|
||||
return this.loadAllAddons();
|
||||
}
|
||||
|
||||
// Subclasses should overload this and modify the addon object as needed to fully load it
|
||||
|
@ -195,11 +195,10 @@ export default class AddonManager {
|
|||
}
|
||||
|
||||
// Subclasses should use the return (if not AddonError) and push to this.addonList
|
||||
async loadAddon(filename, shouldToast = false) {
|
||||
loadAddon(filename, shouldToast = false) {
|
||||
if (typeof(filename) === "undefined") return;
|
||||
try {
|
||||
const addon = __non_webpack_require__(path.resolve(this.addonFolder, filename));
|
||||
await Promise.resolve(addon);
|
||||
}
|
||||
catch (error) {
|
||||
return new AddonError(filename, filename, Strings.Addons.compileError, {message: error.message, stack: error.stack});
|
||||
|
@ -234,11 +233,11 @@ export default class AddonManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
async reloadAddon(idOrFileOrAddon, shouldToast = true) {
|
||||
reloadAddon(idOrFileOrAddon, shouldToast = true) {
|
||||
const addon = typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
|
||||
const didUnload = this.unloadAddon(addon, shouldToast, true);
|
||||
if (addon && !didUnload) return didUnload;
|
||||
return await this.loadAddon(addon ? addon.filename : idOrFileOrAddon, shouldToast);
|
||||
return this.loadAddon(addon ? addon.filename : idOrFileOrAddon, shouldToast);
|
||||
}
|
||||
|
||||
isLoaded(idOrFile) {
|
||||
|
@ -293,7 +292,7 @@ export default class AddonManager {
|
|||
for (const name of results.removed) this.unloadAddon(name);
|
||||
}
|
||||
|
||||
async loadAllAddons() {
|
||||
loadAllAddons() {
|
||||
this.loadState();
|
||||
const errors = [];
|
||||
const files = fs.readdirSync(this.addonFolder);
|
||||
|
@ -321,7 +320,7 @@ export default class AddonManager {
|
|||
// Rename the file and let it go on
|
||||
fs.renameSync(absolutePath, path.resolve(this.addonFolder, newFilename));
|
||||
}
|
||||
const addon = await this.loadAddon(filename, false);
|
||||
const addon = this.loadAddon(filename, false);
|
||||
if (addon instanceof AddonError) errors.push(addon);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,18 @@ export default new class Core {
|
|||
// (() => {
|
||||
// const fs = require("fs");
|
||||
// fs.appendFileSync("Z:\\debug.log", "\n\n\n");
|
||||
// window.ocl = console.log;
|
||||
// console.log = (...args) => {
|
||||
|
||||
// const toFile = orig => (...args) => {
|
||||
// fs.appendFileSync("Z:\\debug.log", JSON.stringify(args) + "\n");
|
||||
// window.ocl(...args);
|
||||
// orig(...args);
|
||||
// };
|
||||
|
||||
// window.ocl = console.log;
|
||||
// window.oce = console.error;
|
||||
// window.ocx = console.exception;
|
||||
// console.log = toFile(window.ocl);
|
||||
// console.error = toFile(window.oce);
|
||||
// console.exception = toFile(window.ocx);
|
||||
// })();
|
||||
|
||||
Config.appPath = process.env.DISCORD_APP_PATH;
|
||||
|
@ -106,7 +113,7 @@ export default new class Core {
|
|||
const guild = GuildClasses.listItem.split(" ")[0];
|
||||
const blob = GuildClasses.blobContainer.split(" ")[0];
|
||||
if (document.querySelectorAll(`.${wrapper} .${guild} .${blob}`).length > 0) return resolve(Config.deferLoaded = true);
|
||||
else if (timesChecked >= 50) return resolve(Config.deferLoaded = true);
|
||||
// else if (timesChecked >= 50) return resolve(Config.deferLoaded = true);
|
||||
setTimeout(checkForGuilds, 100);
|
||||
};
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ export default new class DataStore {
|
|||
general: {publicServers: oldSettings["bda-gs-1"], voiceDisconnect: oldSettings["bda-dc-0"], classNormalizer: oldSettings["fork-ps-4"], showToasts: oldSettings["fork-ps-2"]},
|
||||
appearance: {twentyFourHour: oldSettings["bda-gs-6"], minimalMode: oldSettings["bda-gs-2"], coloredText: oldSettings["bda-gs-7"]},
|
||||
addons: {addonErrors: oldSettings["fork-ps-1"], autoReload: oldSettings["fork-ps-5"]},
|
||||
developer: {debuggerHotkey: oldSettings["bda-gs-8"], copySelector: oldSettings["fork-dm-1"], reactDevTools: oldSettings.reactDevTools}
|
||||
developer: {debuggerHotkey: oldSettings["bda-gs-8"], reactDevTools: oldSettings.reactDevTools}
|
||||
};
|
||||
|
||||
const newEmotes = {
|
||||
|
|
|
@ -38,8 +38,8 @@ export default new class PluginManager extends AddonManager {
|
|||
});
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
const errors = await super.initialize();
|
||||
initialize() {
|
||||
const errors = super.initialize();
|
||||
this.setupFunctions();
|
||||
Settings.registerPanel("plugins", Strings.Panels.plugins, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.plugins, this.addonList, this.state, {
|
||||
type: this.prefix,
|
||||
|
@ -66,13 +66,13 @@ export default new class PluginManager extends AddonManager {
|
|||
unloadPlugin(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);}
|
||||
loadPlugin(filename) {return this.loadAddon(filename);}
|
||||
|
||||
async loadAddon(filename) {
|
||||
const error = await super.loadAddon(filename);
|
||||
loadAddon(filename) {
|
||||
const error = super.loadAddon(filename);
|
||||
if (error) Modals.showAddonErrors({plugins: [error]});
|
||||
}
|
||||
|
||||
async reloadPlugin(idOrFileOrAddon) {
|
||||
const error = await this.reloadAddon(idOrFileOrAddon);
|
||||
reloadPlugin(idOrFileOrAddon) {
|
||||
const error = this.reloadAddon(idOrFileOrAddon);
|
||||
if (error) Modals.showAddonErrors({plugins: [error]});
|
||||
return typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
|
||||
}
|
||||
|
@ -107,21 +107,19 @@ export default new class PluginManager extends AddonManager {
|
|||
window.__filename = path.basename(module.filename);
|
||||
window.__dirname = this.addonFolder;
|
||||
const wrapped = `(${vm.compileFunction(fileContent, ["exports", "require", "module", "__filename", "__dirname"]).toString()})`;
|
||||
// console.log(module);
|
||||
module.exports = new Promise(resolve => {
|
||||
IPC.runScript(`${wrapped}(window.module.exports, window.require, window.module, window.__filename, window.__dirname)`).then(() => {
|
||||
// console.log(window.module);
|
||||
meta.exports = module.exports;
|
||||
module.exports = meta;
|
||||
delete window.module;
|
||||
delete window.__filename;
|
||||
delete window.__dirname;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
// module._compile(fileContent, module.filename);
|
||||
// meta.exports = module.exports;
|
||||
// module.exports = meta;
|
||||
const final = `${wrapped}(window.module.exports, window.require, window.module, window.__filename, window.__dirname)`;
|
||||
|
||||
const container = document.createElement("script");
|
||||
container.innerHTML = final;
|
||||
container.id = `${meta.id}-script-container`;
|
||||
// container.src = `data:text/javascript;${btoa(final)}`;
|
||||
document.head.append(container);
|
||||
|
||||
meta.exports = module.exports;
|
||||
module.exports = meta;
|
||||
delete window.module;
|
||||
delete window.__filename;
|
||||
delete window.__dirname;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -139,7 +137,7 @@ export default new class PluginManager extends AddonManager {
|
|||
catch (err) {
|
||||
this.state[addon.id] = false;
|
||||
Toasts.error(Strings.Addons.couldNotStart.format({name: addon.name, version: addon.version}));
|
||||
Logger.stacktrace(this.name, addon.name + " could not be started.", err);
|
||||
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});
|
||||
}
|
||||
this.emit("started", addon.id);
|
||||
|
@ -156,7 +154,7 @@ export default new class PluginManager extends AddonManager {
|
|||
catch (err) {
|
||||
this.state[addon.id] = false;
|
||||
Toasts.error(Strings.Addons.couldNotStop.format({name: addon.name, version: addon.version}));
|
||||
Logger.stacktrace(this.name, addon.name + " could not be stopped.", err);
|
||||
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});
|
||||
}
|
||||
this.emit("stopped", addon.id);
|
||||
|
@ -185,7 +183,7 @@ export default new class PluginManager extends AddonManager {
|
|||
if (!this.state[this.addonList[i].id]) continue;
|
||||
if (typeof(plugin.onSwitch) === "function") {
|
||||
try {plugin.onSwitch();}
|
||||
catch (err) {Logger.stacktrace(this.name, "Unable to fire onSwitch for " + this.addonList[i].name + ".", err);}
|
||||
catch (err) {Logger.stacktrace(this.name, `Unable to fire onSwitch for ${this.addonList[i].name} v${this.addonList[i].version}`, err);}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +194,7 @@ export default new class PluginManager extends AddonManager {
|
|||
if (!this.state[this.addonList[i].id]) continue;
|
||||
if (typeof plugin.observer === "function") {
|
||||
try {plugin.observer(mutation);}
|
||||
catch (err) {Logger.stacktrace(this.name, "Unable to fire observer for " + this.addonList[i].name + ".", err);}
|
||||
catch (err) {Logger.stacktrace(this.name, `Unable to fire observer for ${this.addonList[i].name} v${this.addonList[i].version}`, err);}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ export default new class ThemeManager extends AddonManager {
|
|||
get prefix() {return "theme";}
|
||||
get language() {return "css";}
|
||||
|
||||
async initialize() {
|
||||
const errors = await super.initialize();
|
||||
initialize() {
|
||||
const errors = super.initialize();
|
||||
Settings.registerPanel("themes", Strings.Panels.themes, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.themes, this.addonList, this.state, {
|
||||
type: this.prefix,
|
||||
folder: this.addonFolder,
|
||||
|
@ -45,10 +45,10 @@ export default new class ThemeManager extends AddonManager {
|
|||
|
||||
unloadTheme(idOrFileOrAddon) {return this.unloadAddon(idOrFileOrAddon);}
|
||||
loadTheme(filename) {return this.loadAddon(filename);}
|
||||
async reloadTheme(idOrFileOrAddon) {return await this.reloadAddon(idOrFileOrAddon);}
|
||||
reloadTheme(idOrFileOrAddon) {return this.reloadAddon(idOrFileOrAddon);}
|
||||
|
||||
async loadAddon(filename) {
|
||||
const error = await super.loadAddon(filename);
|
||||
loadAddon(filename) {
|
||||
const error = super.loadAddon(filename);
|
||||
if (error) Modals.showAddonErrors({themes: [error]});
|
||||
}
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ export default class Modals {
|
|||
};
|
||||
|
||||
const ModalActions = this.ModalActions;
|
||||
const OriginalModalClasses = WebpackModules.getByProps("hideOnFullscreen");
|
||||
const OriginalModalClasses = WebpackModules.getByProps("hideOnFullscreen", "root");
|
||||
const originalRoot = OriginalModalClasses.root;
|
||||
if (originalRoot) OriginalModalClasses.root = `${originalRoot} bd-changelog-modal`;
|
||||
const key = ModalActions.openModal(props => {
|
||||
|
|
|
@ -108,7 +108,7 @@ export default class AddonList extends React.Component {
|
|||
}
|
||||
|
||||
get emptyImage() {
|
||||
const message = Strings.Addons.blankSlateMessage.format({link: `https://betterdiscordlibrary.com/${this.props.type}`, type: this.props.type}).toString();
|
||||
const message = Strings.Addons.blankSlateMessage.format({link: `https://betterdiscordlibrary.com/${this.props.type}s`, type: this.props.type}).toString();
|
||||
return <EmptyImage title={Strings.Addons.blankSlateHeader.format({type: this.props.type})} message={message}>
|
||||
<button className="bd-button" onClick={this.openFolder}>{Strings.Addons.openFolder.format({type: this.props.type})}</button>
|
||||
</EmptyImage>;
|
||||
|
|
Loading…
Reference in New Issue