Fix various bugs
- Show public servers on startup - Update plugin tab on load/unload - Finish switching searchGetters to searchExports - Add disabled class to addon cards - Fix/rebuild changelog modal
This commit is contained in:
parent
a9220996b5
commit
af1a26c333
|
@ -1,5 +1,5 @@
|
|||
import Builtin from "../../structs/builtin";
|
||||
import {DiscordModules, WebpackModules, Strings, DOMManager, React} from "modules";
|
||||
import {DiscordModules, WebpackModules, Strings, DOMManager, React, ReactDOM} from "modules";
|
||||
import PublicServersMenu from "../../ui/publicservers/menu";
|
||||
import Globe from "../../ui/icons/globe";
|
||||
|
||||
|
@ -48,16 +48,17 @@ export default new class PublicServers extends Builtin {
|
|||
if (!PrivateChannelList || !PrivateChannelList.Z) return this.warn("Could not find PrivateChannelList", PrivateChannelList);
|
||||
const PrivateChannelButton = WebpackModules.getModule(m => m?.prototype?.render?.toString().includes("linkButton"), {searchExports: true});
|
||||
if (!PrivateChannelButton) return this.warn("Could not find PrivateChannelButton", PrivateChannelButton);
|
||||
|
||||
this.after(PrivateChannelList, "Z", (_, __, returnValue) => {
|
||||
const destination = returnValue?.props?.children?.props?.children;
|
||||
if (!destination || !Array.isArray(destination)) return;
|
||||
if (destination.find(b => b?.props?.children?.props?.id === "public-server-button")) return;
|
||||
if (destination.find(b => b?.props?.children?.props?.id === "public-servers-button")) return; // If it exists, don't try to add again
|
||||
|
||||
destination.push(
|
||||
React.createElement(ErrorBoundary, null,
|
||||
React.createElement(PrivateChannelButton,
|
||||
{
|
||||
id: "public-server-button",
|
||||
id: "public-servers-button",
|
||||
onClick: () => this.openPublicServers(),
|
||||
text: "Public Servers",
|
||||
icon: () => React.createElement(Globe, {color: "currentColor"})
|
||||
|
@ -66,6 +67,42 @@ export default new class PublicServers extends Builtin {
|
|||
)
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* On being first enabled, we have no way of forceUpdating the list,
|
||||
* so clone and modify an existing button and add it to the end
|
||||
* of the button list.
|
||||
*/
|
||||
const header = document.querySelector(`[class*="privateChannelsHeaderContainer-"]`);
|
||||
if (!header) return; // No known element
|
||||
const oldButton = header.previousElementSibling;
|
||||
if (!oldButton.className.includes("channel-")) return; // Not what we expected to be there
|
||||
|
||||
// Clone existing button and set click handler
|
||||
const newButton = oldButton.cloneNode(true);
|
||||
newButton.addEventListener("click", (event) => {
|
||||
event.stopImmediatePropagation();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.openPublicServers();
|
||||
});
|
||||
|
||||
// Remove existing route and id
|
||||
const aSlot = newButton.querySelector("a");
|
||||
aSlot.href = "";
|
||||
aSlot.dataset.listItemId = "public-servers";
|
||||
|
||||
// Render our icon in the avatar slot
|
||||
const avatarSlot = newButton.querySelector(`[class*="avatar-"]`);
|
||||
avatarSlot.replaceChildren();
|
||||
ReactDOM.render(React.createElement(Globe, {color: "currentColor"}), avatarSlot);
|
||||
|
||||
// Replace the existing name
|
||||
const nameSlot = newButton.querySelector(`[class*="name-"]`);
|
||||
nameSlot.textContent = "Public Servers";
|
||||
|
||||
// Insert before the header, end of the list
|
||||
header.parentNode.insertBefore(newButton, header);
|
||||
}
|
||||
|
||||
disabled() {
|
||||
|
|
|
@ -206,6 +206,7 @@ export default class AddonManager {
|
|||
if (partialAddon) {
|
||||
partialAddon.partial = true;
|
||||
this.state[partialAddon.id] = false;
|
||||
this.emit("loaded", partialAddon.id);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
@ -215,6 +216,7 @@ export default class AddonManager {
|
|||
if (error) {
|
||||
this.state[addon.id] = false;
|
||||
addon.partial = true;
|
||||
this.emit("loaded", addon.id);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ const MenuComponents = (() => {
|
|||
};
|
||||
|
||||
let ContextMenuIndex = null;
|
||||
const ContextMenuModule = WebpackModules.getModule((m, _, id) => Object.values(m).some(v => v?.FLEXIBLE) && (ContextMenuIndex = id), {searchGetters: false});
|
||||
const ContextMenuModule = WebpackModules.getModule((m, _, id) => Object.values(m).some(v => v?.FLEXIBLE) && (ContextMenuIndex = id), {searchExports: false});
|
||||
const rawMatches = WebpackModules.require.m[ContextMenuIndex].toString().matchAll(/if\(\w+\.type===\w+\.(\w+)\).+?type:"(.+?)"/g);
|
||||
|
||||
out.Menu = Object.values(ContextMenuModule).find(v => v.toString().includes(".isUsingKeyboardNavigation"));
|
||||
|
@ -30,7 +30,7 @@ const MenuComponents = (() => {
|
|||
const ContextMenuActions = (() => {
|
||||
const out = {};
|
||||
|
||||
const ActionsModule = WebpackModules.getModule(m => Object.values(m).some(m => typeof m === "function" && m.toString().includes("CONTEXT_MENU_CLOSE")), {searchGetters: false});
|
||||
const ActionsModule = WebpackModules.getModule(m => Object.values(m).some(m => typeof m === "function" && m.toString().includes("CONTEXT_MENU_CLOSE")), {searchExports: false});
|
||||
|
||||
for (const key of Object.keys(ActionsModule)) {
|
||||
if (ActionsModule[key].toString().includes("CONTEXT_MENU_CLOSE")) {
|
||||
|
@ -50,7 +50,7 @@ class MenuPatcher {
|
|||
|
||||
static initialize() {
|
||||
const {module, key} = (() => {
|
||||
const module = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes("CONTEXT_MENU_CLOSE")), {searchGetters: false});
|
||||
const module = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes("CONTEXT_MENU_CLOSE")), {searchExports: false});
|
||||
const key = Object.keys(module).find(key => module[key].length === 3);
|
||||
|
||||
return {module, key};
|
||||
|
|
|
@ -74,7 +74,7 @@ const Webpack = {
|
|||
getModule(filter, options = {}) {
|
||||
if (("first" in options) && typeof(options.first) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.first", options.first, "boolean expected.");
|
||||
if (("defaultExport" in options) && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected.");
|
||||
if (("searchGetters" in options) && typeof(options.searchGetters) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.searchGetters", options.searchGetters, "boolean expected.");
|
||||
if (("searchExports" in options) && typeof(options.searchExports) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.searchExports", options.searchExports, "boolean expected.");
|
||||
return WebpackModules.getModule(filter, options);
|
||||
},
|
||||
|
||||
|
@ -103,7 +103,7 @@ const Webpack = {
|
|||
waitForModule(filter, options = {}) {
|
||||
if (("defaultExport" in options) && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~waitForModule", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected.");
|
||||
if (("signal" in options) && !(options.signal instanceof AbortSignal)) return Logger.error("BdApi.Webpack~waitForModule", "Unsupported type used for options.signal", options.signal, "AbortSignal expected.");
|
||||
if (("searchGetters" in options) && typeof(options.searchGetters) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.searchGetters", options.searchGetters, "boolean expected.");
|
||||
if (("searchExports" in options) && typeof(options.searchExports) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.searchExports", options.searchExports, "boolean expected.");
|
||||
return WebpackModules.getLazy(filter, options);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Config} from "data";
|
||||
import Logger from "common/logger";
|
||||
import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager, DiscordModules} from "modules";
|
||||
import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager, DiscordModules, DiscordClasses} from "modules";
|
||||
import FormattableString from "../structs/string";
|
||||
import AddonErrorModal from "./addonerrormodal";
|
||||
import ErrorBoundary from "./errorboundary";
|
||||
|
@ -212,26 +212,16 @@ export default class Modals {
|
|||
}))));
|
||||
}
|
||||
|
||||
static showChangelogModal(changelog) {
|
||||
const md = [changelog.description];
|
||||
for (const type of changelog.changes) {
|
||||
md.push(`**${type.title}**`);
|
||||
for (const entry of type.items) {
|
||||
md.push(` - ${entry}`);
|
||||
}
|
||||
}
|
||||
Modals.showConfirmationModal(`BetterDiscord v${Config.version}`, md, {cancelText: ""});
|
||||
}
|
||||
|
||||
static BROKEN_showChangelogModal(options = {}) {
|
||||
const ModalStack = WebpackModules.getByProps("push", "update", "pop", "popWithKey");
|
||||
static showChangelogModal(options = {}) {
|
||||
const OriginalModalClasses = WebpackModules.getByProps("hideOnFullscreen", "root");
|
||||
const ChangelogModalClasses = WebpackModules.getModule(m => m.modal && m.maxModalWidth);
|
||||
const ChangelogClasses = WebpackModules.getByProps("fixed", "improved");
|
||||
const TextElement = WebpackModules.getByDisplayName("LegacyText");
|
||||
const FlexChild = WebpackModules.getByProps("Child");
|
||||
const Titles = WebpackModules.getByProps("Tags", "default");
|
||||
const Changelog = WebpackModules.getModule(m => m.defaultProps && m.defaultProps.selectable == false);
|
||||
const TextElement = this.TextElement;
|
||||
const FlexChild = this.FlexElements;
|
||||
const Titles = this.FormTitle;
|
||||
const MarkdownParser = WebpackModules.getByProps("defaultRules", "parse");
|
||||
if (!Changelog || !ModalStack || !ChangelogClasses || !TextElement || !FlexChild || !Titles || !MarkdownParser) return Logger.warn("Modals", "showChangelogModal missing modules");
|
||||
|
||||
if (!OriginalModalClasses || !ChangelogModalClasses || !ChangelogClasses || !TextElement || !FlexChild || !Titles || !MarkdownParser) return Logger.warn("Modals", "showChangelogModal missing modules");
|
||||
|
||||
const {image = "https://i.imgur.com/wuh5yMK.png", description = "", changes = [], title = "BetterDiscord", subtitle = `v${Config.version}`, footer} = options;
|
||||
const ce = React.createElement;
|
||||
|
@ -247,47 +237,38 @@ export default class Modals {
|
|||
changelogItems.push(list);
|
||||
}
|
||||
const renderHeader = function() {
|
||||
return ce(FlexChild.Child, {grow: 1, shrink: 1},
|
||||
ce(Titles.default, {tag: Titles.Tags.H4}, title),
|
||||
ce(TextElement, {size: TextElement.Sizes.SMALL, color: TextElement.Colors.STANDARD, className: ChangelogClasses.date}, subtitle)
|
||||
return ce(FlexChild, {className: OriginalModalClasses.header, grow: 0, shrink: 0, direction: FlexChild.Direction.VERTICAL},
|
||||
ce(Titles, {tag: Titles.Tags.H1, size: TextElement.Sizes.SIZE_20}, title),
|
||||
ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.STANDARD, className: ChangelogClasses.date}, subtitle)
|
||||
);
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
const Anchor = WebpackModules.getModule(m => m.displayName == "Anchor");
|
||||
const AnchorClasses = WebpackModules.getByProps("anchorUnderlineOnHover") || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};
|
||||
const joinSupportServer = (click) => {
|
||||
click.preventDefault();
|
||||
click.stopPropagation();
|
||||
ModalStack.pop();
|
||||
DiscordModules.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");
|
||||
};
|
||||
const supportLink = Anchor ? ce(Anchor, {onClick: joinSupportServer}, "Join our Discord Server.") : ce("a", {className: `${AnchorClasses.anchor} ${AnchorClasses.anchorUnderlineOnHover}`, onClick: joinSupportServer}, "Join our Discord Server.");
|
||||
const defaultFooter = ce(TextElement, {size: TextElement.Sizes.SMALL, color: TextElement.Colors.STANDARD}, "Need support? ", supportLink);
|
||||
return ce(FlexChild.Child, {grow: 1, shrink: 1}, footer ? footer : defaultFooter);
|
||||
const supportLink = ce("a", {className: `${AnchorClasses.anchor} ${AnchorClasses.anchorUnderlineOnHover}`, onClick: joinSupportServer}, "Join our Discord Server.");
|
||||
const defaultFooter = ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.STANDARD}, "Need support? ", supportLink);
|
||||
return ce(FlexChild, {className: OriginalModalClasses.footer + " " + OriginalModalClasses.footerSeparator},
|
||||
ce(FlexChild.Child, {grow: 1, shrink: 1}, footer ? footer : defaultFooter)
|
||||
);
|
||||
};
|
||||
|
||||
const ModalActions = this.ModalActions;
|
||||
const OriginalModalClasses = WebpackModules.getByProps("hideOnFullscreen", "root");
|
||||
const originalRoot = OriginalModalClasses.root;
|
||||
if (originalRoot) OriginalModalClasses.root = `${originalRoot} bd-changelog-modal`;
|
||||
const key = ModalActions.openModal(props => {
|
||||
return React.createElement(ErrorBoundary, null, React.createElement(Changelog, Object.assign({
|
||||
className: `bd-changelog ${ChangelogClasses.container}`,
|
||||
const body = ce("div", {
|
||||
className: `${OriginalModalClasses.content} ${ChangelogClasses.container} ${ChangelogModalClasses.content} ${DiscordClasses.Scrollers.thin}`
|
||||
}, changelogItems);
|
||||
|
||||
const key = this.ModalActions.openModal(props => {
|
||||
return React.createElement(ErrorBoundary, null, React.createElement(this.ModalRoot, Object.assign({
|
||||
className: `bd-changelog-modal ${OriginalModalClasses.root} ${OriginalModalClasses.small} ${ChangelogModalClasses.modal}`,
|
||||
selectable: true,
|
||||
onScroll: _ => _,
|
||||
onClose: _ => _,
|
||||
renderHeader: renderHeader,
|
||||
renderFooter: renderFooter,
|
||||
}, props), changelogItems));
|
||||
}, props), renderHeader(), body, renderFooter()));
|
||||
});
|
||||
|
||||
const closeModal = ModalActions.closeModal;
|
||||
ModalActions.closeModal = function(k) {
|
||||
Reflect.apply(closeModal, this, arguments);
|
||||
setTimeout(() => {if (originalRoot && k === key) OriginalModalClasses.root = originalRoot;}, 1000);
|
||||
ModalActions.closeModal = closeModal;
|
||||
};
|
||||
return key;
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ export default class AddonCard extends React.Component {
|
|||
const description = this.getString(addon.description);
|
||||
const version = this.getString(addon.version);
|
||||
|
||||
return <div id={`${addon.id}-card`} className="bd-addon-card settings-closed">
|
||||
return <div id={`${addon.id}-card`} className={"bd-addon-card" + (this.props.disabled ? " bd-addon-card-disabled" : "")}>
|
||||
<div className="bd-addon-header">
|
||||
{this.props.type === "plugin" ? <ExtIcon size="18px" className="bd-icon" /> : <ThemeIcon size="18px" className="bd-icon" />}
|
||||
<div className="bd-title">{this.buildTitle(name, version, {name: author, id: this.props.addon.authorId, link: this.props.addon.authorLink})}</div>
|
||||
|
|
Loading…
Reference in New Issue