From 7b0faabf5d74391282dc65d30d6cd1b196455890 Mon Sep 17 00:00:00 2001 From: _Lighty_ Date: Tue, 19 May 2020 17:18:40 +0200 Subject: [PATCH] XL v1.3.20 --- Plugins/1XenoLib.plugin.js | 141 ++++++++++++------------------------- 1 file changed, 44 insertions(+), 97 deletions(-) diff --git a/Plugins/1XenoLib.plugin.js b/Plugins/1XenoLib.plugin.js index 69c2d9d..cbe72b6 100644 --- a/Plugins/1XenoLib.plugin.js +++ b/Plugins/1XenoLib.plugin.js @@ -41,7 +41,7 @@ var XenoLib = (() => { twitter_username: '' } ], - version: '1.3.19', + version: '1.3.20', description: 'Simple library to complement plugins with shared code without lowering performance.', github: 'https://github.com/1Lighty', github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js' @@ -50,7 +50,7 @@ var XenoLib = (() => { { title: 'Boring changes', type: 'fixed', - items: ['Fix up startup warnings and errors.', 'Fixed notifications closing sometimes if you hover them near the end of the timeout.', 'Future proofing.'] + items: ['Fix up a couple of errors related to recent context menu changes.', 'Deprecated global context menu patching.'] } ], defaultConfig: [ @@ -95,7 +95,7 @@ var XenoLib = (() => { /* Build */ const buildPlugin = ([Plugin, Api]) => { - const { ContextMenu, EmulatedTooltip, Toasts, Settings, Popouts, Modals, Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, Patcher, PluginUpdater, PluginUtilities, DiscordClassModules, Structs } = Api; + const { ContextMenu, EmulatedTooltip, Toasts, Settings, Popouts, Modals, Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, PluginUpdater, PluginUtilities, DiscordClassModules, Structs } = Api; const { React, ModalStack, ContextMenuActions, ContextMenuItem, ContextMenuItemsGroup, ReactDOM, ChannelStore, GuildStore, UserStore, DiscordConstants, Dispatcher, GuildMemberStore, GuildActions, PrivateChannelActions, LayerManager, InviteActions, FlexChild, Titles, Changelog: ChangelogModal } = DiscordModules; let CancelledAsync = false; @@ -181,6 +181,25 @@ var XenoLib = (() => { XenoLib.getClass.__warns = {}; XenoLib.getSingleClass.__warns = {}; + XenoLib.createSmartPatcher = patcher => { + const createPatcher = patcher => { + return (moduleToPatch, functionName, callback, options = {}) => { + const origDef = moduleToPatch[functionName]; + patcher(moduleToPatch, functionName, callback, options); + if (origDef.isBDFDBpatched && moduleToPatch.BDFDBpatch && typeof moduleToPatch.BDFDBpatch[functionName].originalMethod === 'function') { + patcher(moduleToPatch.BDFDBpatch[functionName], 'originalMethod', callback, options); + } + }; + }; + return Object.assign({}, patcher, { + before: createPatcher(patcher.before), + instead: createPatcher(patcher.instead), + after: createPatcher(patcher.after) + }); + }; + + const Patcher = XenoLib.createSmartPatcher(Api.Patcher); + const LibrarySettings = XenoLib.loadData(config.info.name, 'settings', DefaultLibrarySettings); PluginUtilities.addStyle( @@ -380,112 +399,40 @@ var XenoLib = (() => { } }; - XenoLib.__contextPatches = []; - try { - if (global.XenoLib) if (global.XenoLib.__contextPatches && global.XenoLib.__contextPatches.length) XenoLib.__contextPatches.push(...global.XenoLib.__contextPatches); - const ContextMenuClassname = XenoLib.getSingleClass('subMenuContext contextMenu'); /* I AM *SPEED* */ - const getContextMenuChild = val => { - if (!val) return; - const isValid = obj => obj.type === 'div' && obj.props && typeof obj.props.className === 'string' && obj.props.className.indexOf(ContextMenuClassname) !== -1 && Array.isArray(Utilities.getNestedProp(obj, 'props.children.props.children')); - if (isValid(val)) return val.props.children; - const children = Utilities.getNestedProp(val, 'props.children'); - if (!children) return; - if (Array.isArray(children)) { - for (let i = 0; i < children.length; i++) { - const ret = getContextMenuChild(children[i]); - if (ret) return ret.props.children; - } - } else if (isValid(children)) return children.props.children; - }; - const handleContextMenu = (_this, ret, noRender) => { - const menuGroups = getContextMenuChild(ret) || ret; - if (!menuGroups) return /* Logger.warn('Failed to get context menu groups!', _this, ret) */; - /* emulate a react class component */ - if (noRender) { - let [value, set] = React.useState(false); - let [state, setState] = React.useState({}); - _this.forceUpdate = () => set(!value); - _this.state = state; - _this.setState = setState; - } - if (!_this.state) _this.state = {}; - XenoLib.__contextPatches.forEach(e => { - try { - e(_this, menuGroups); - } catch (e) { - Logger.stacktrace('Error with patched context menu', e); - } - }); - }; - const getModule = regex => { - try { - const modules = WebpackModules.getAllModules(); - for (const index in modules) { - if (!modules.hasOwnProperty(index)) continue; - const module = modules[index]; - if (!module.exports || !module.exports.__esModule || !module.exports.default) continue; - /* if BDFDB was inited before us, patch the already patched function */ - if (module.exports.default.toString().search(regex) !== -1 || (module.exports.default.isBDFDBpatched && module.exports.default.__originalMethod.toString().search(regex) !== -1)) return module; - } - } catch (e) { - Logger.stacktrace(`Failed to getModule by regex ${regex}`, e); - return null; - } - }; - const renderContextMenus = ['NativeContextMenu', 'DeveloperContextMenu']; - const hookContextMenus = [getModule(/case \w.ContextMenuTypes.CHANNEL_LIST_TEXT/), getModule(/case \w.ContextMenuTypes.GUILD_CHANNEL_LIST/), getModule(/case \w.ContextMenuTypes.USER_CHANNEL_MEMBERS/), getModule(/case \w\.ContextMenuTypes\.MESSAGE_MAIN/)]; - for (const type of renderContextMenus) { - const module = WebpackModules.getByDisplayName(type); - if (!module) { - Logger.warn(`Failed to find ContextMenu type`, type); - continue; - } - Patcher.after(module.prototype, 'render', (_this, _, ret) => handleContextMenu(_this, ret)); - } - for (const menu of hookContextMenus) { - if (!menu) continue; - const origDef = menu.exports.default; - Patcher.after(menu.exports, 'default', (_, [props], ret) => handleContextMenu({ props }, ret, true)); - if (origDef.isBDFDBpatched && menu.exports.BDFDBpatch && typeof menu.exports.BDFDBpatch.default.originalMethod === 'function') { - Patcher.after(menu.exports.BDFDBpatch.default, 'originalMethod', (_, [props], ret) => handleContextMenu({ props }, ret, true)); - } - } - const GroupDMContextMenu = WebpackModules.getByDisplayName('FluxContainer(GroupDMContextMenu)'); - if (GroupDMContextMenu) { - try { - const type = new GroupDMContextMenu({}).render().type; - Patcher.after(type.prototype, 'render', (_this, _, ret) => handleContextMenu(_this, ret)); - } catch (e) { - Logger.stacktrace('Failed patching GroupDMContextMenu', e); - } - } - } catch (e) { - Logger.stacktrace('Failed to patch context menus', e); - } - XenoLib.patchContext = callback => { - XenoLib.__contextPatches.push(callback); - }; + const deprecateFunction = (name, advice, ret = undefined) => () => (Logger.warn(`XenoLib.${name} is deprecated! ${advice}`), ret); + + XenoLib.patchContext = deprecateFunction('patchContext', 'Do manual patching of context menus instead.'); + + const CTXMenu = WebpackModules.getByProps('default', 'MenuStyle'); class ContextMenuWrapper extends React.PureComponent { + constructor(props) { + super(props); + this.handleOnClose = this.handleOnClose.bind(this); + } + handleOnClose() { + ContextMenuActions.closeContextMenu(); + if (this.props.target instanceof HTMLElement) this.props.target.focus(); + } render() { - return React.createElement('div', { className: DiscordClasses.ContextMenu.contextMenu }, this.props.menu); + return React.createElement(CTXMenu.default, { onClose: this.handleOnClose, id: 'xenolib-context' }, this.props.menu); } } - XenoLib.createSharedContext = (element, type, menuCreation) => { + XenoLib.createSharedContext = (element, menuCreation) => { if (element.__XenoLib_ContextMenus) { element.__XenoLib_ContextMenus.push(menuCreation); } else { element.__XenoLib_ContextMenus = [menuCreation]; const oOnContextMenu = element.props.onContextMenu; - element.props.onContextMenu = e => (typeof oOnContextMenu === 'function' && oOnContextMenu(e), ContextMenuActions.openContextMenu(e, _ => React.createElement(ContextMenuWrapper, { menu: element.__XenoLib_ContextMenus.map(m => React.createElement(XenoLib.ReactComponents.ErrorBoundary, { label: 'shared context menu' }, m())), type }))); + element.props.onContextMenu = e => (typeof oOnContextMenu === 'function' && oOnContextMenu(e), ContextMenuActions.openContextMenu(e, _ => React.createElement(XenoLib.ReactComponents.ErrorBoundary, { label: 'CTX Menu' }, React.createElement(ContextMenuWrapper, { menu: element.__XenoLib_ContextMenus.map(m => m()), ..._ })))); } }; - const ContextMenuSubMenuItem = WebpackModules.getByDisplayName('FluxContainer(SubMenuItem)'); - XenoLib.unpatchContext = callback => XenoLib.__contextPatches.splice(XenoLib.__contextPatches.indexOf(callback), 1); - XenoLib.createContextMenuItem = (label, action, options = {}) => (!ContextMenuItem ? null : React.createElement(ContextMenuItem, { label, action: () => (!options.noClose && ContextMenuActions.closeContextMenu(), action()), ...options })); - XenoLib.createContextMenuSubMenu = (label, items, options = {}) => (!ContextMenuSubMenuItem ? null : React.createElement(ContextMenuSubMenuItem, { label, render: items, ...options })); - XenoLib.createContextMenuGroup = (children, options) => (!ContextMenuItemsGroup ? null : React.createElement(ContextMenuItemsGroup, { children, ...options })); + const contextMenuItems = WebpackModules.find(m => m.MenuRadioItem && !m.default); + XenoLib.unpatchContext = deprecateFunction('unpatchContext', 'Manual patching needs manual unpatching'); + XenoLib.createContextMenuItem = (label, action, id, options = {}) => (!contextMenuItems ? null : React.createElement(contextMenuItems.MenuItem, { label, id, action: () => (!options.noClose && ContextMenuActions.closeContextMenu(), action()), ...options })); + XenoLib.createContextMenuSubMenu = (label, children, id, options = {}) => (!contextMenuItems ? null : React.createElement(contextMenuItems.MenuItem, { label, children, id, ...options })); + XenoLib.createContextMenuGroup = (children, options) => (!contextMenuItems ? null : React.createElement(contextMenuItems.MenuGroup, { children, ...options })); try { XenoLib.ReactComponents.ButtonOptions = WebpackModules.getByProps('ButtonLink'); @@ -542,7 +489,7 @@ var XenoLib = (() => { footerProps.children = []; if (websiteLink) footerProps.children.push(websiteLink); if (sourceLink) footerProps.children.push(websiteLink ? ' | ' : null, sourceLink); - footerProps.children.push(websiteLink || sourceLink ? ' | ' : null, React.createElement('a', { className: 'bda-link', onClick: e => ContextMenuActions.openContextMenu(e, e => React.createElement('div', { className: DiscordClasses.ContextMenu.contextMenu }, XenoLib.createContextMenuGroup([XenoLib.createContextMenuItem('Paypal', () => window.open('https://paypal.me/lighty13')), XenoLib.createContextMenuItem('Ko-fi', () => window.open('https://ko-fi.com/lighty_')), XenoLib.createContextMenuItem('Patreon', () => window.open('https://www.patreon.com/lightyp'))]))) }, 'Donate')); + footerProps.children.push(websiteLink || sourceLink ? ' | ' : null, React.createElement('a', { className: 'bda-link', onClick: e => ContextMenuActions.openContextMenu(e, e => React.createElement(XenoLib.ReactComponents.ErrorBoundary, { label: 'Donate button CTX menu' }, React.createElement(ContextMenuWrapper, { menu: XenoLib.createContextMenuGroup([XenoLib.createContextMenuItem('Paypal', () => window.open('https://paypal.me/lighty13'), 'paypal'), XenoLib.createContextMenuItem('Ko-fi', () => window.open('https://ko-fi.com/lighty_'), 'kofi'), XenoLib.createContextMenuItem('Patreon', () => window.open('https://www.patreon.com/lightyp'), 'patreon')]), ...e }))) }, 'Donate')); footerProps.children.push(' | ', supportServerLink || React.createElement('a', { className: 'bda-link', onClick: () => (LayerManager.popLayer(), InviteActions.acceptInviteAndTransitionToInviteChannel('NYvWdN5')) }, 'Support Server')); footerProps.children.push(' | ', React.createElement('a', { className: 'bda-link', onClick: () => (_this.props.addon.plugin.showChangelog ? _this.props.addon.plugin.showChangelog() : Modals.showChangelogModal(_this.props.addon.plugin.getName() + ' Changelog', _this.props.addon.plugin.getVersion(), _this.props.addon.plugin.getChanges())) }, 'Changelog')); footerProps = null;