From 81edc1407071c6c0328f40a1ee487ea0388b9a7e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 12 Nov 2022 15:09:02 +0100 Subject: [PATCH] fix PronounDB crash with new profile in dms, force start dependencies --- src/components/ErrorBoundary.tsx | 7 +++++ src/plugins/consoleShortcuts.ts | 1 + src/plugins/index.ts | 29 +++++++++++++++++-- .../components/PronounsChatComponent.tsx | 16 ++++++---- .../components/PronounsProfileWrapper.tsx | 20 +++++++++++-- src/plugins/viewIcons.tsx | 20 ++++++------- src/utils/types.ts | 3 +- src/webpack/common.tsx | 8 +++-- 8 files changed, 80 insertions(+), 24 deletions(-) diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index 4c2725d..870371e 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -22,8 +22,13 @@ import { Margins, React } from "../webpack/common"; import { ErrorCard } from "./ErrorCard"; interface Props { + /** Render nothing if an error occurs */ + noop?: boolean; + /** Fallback component to render if an error occurs */ fallback?: React.ComponentType>; + /** called when an error occurs */ onError?(error: Error, errorInfo: React.ErrorInfo): void; + /** Custom error message */ message?: string; } @@ -67,6 +72,8 @@ const ErrorBoundary = LazyComponent(() => { render() { if (this.state.error === NO_ERROR) return this.props.children; + if (this.props.noop) return null; + if (this.props.fallback) return Vencord.Webpack.extract(Vencord.Webpack.findModuleId(code)!), findByProps: Vencord.Webpack.findByProps, find: Vencord.Webpack.find, Plugins: Vencord.Plugins, diff --git a/src/plugins/index.ts b/src/plugins/index.ts index d3842aa..268e200 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -30,11 +30,36 @@ export const PMLogger = logger; export const plugins = Plugins; export const patches = [] as Patch[]; +const settings = Settings.plugins; + export function isPluginEnabled(p: string) { - return (Settings.plugins[p]?.enabled || Plugins[p]?.required) ?? false; + return ( + Plugins[p]?.required || + Plugins[p]?.isDependency || + settings[p]?.enabled + ) ?? false; } -for (const p of Object.values(Plugins)) +const pluginsValues = Object.values(Plugins); + +// First roundtrip to mark and force enable dependencies +for (const p of pluginsValues) { + p.dependencies?.forEach(d => { + const dep = Plugins[d]; + if (dep) { + settings[d].enabled = true; + dep.isDependency = true; + } + else { + const error = new Error(`Plugin ${p.name} has unresolved dependency ${d}`); + if (IS_DEV) + throw error; + logger.warn(error); + } + }); +} + +for (const p of pluginsValues) if (p.patches && isPluginEnabled(p.name)) { for (const patch of p.patches) { patch.plugin = p.name; diff --git a/src/plugins/pronoundb/components/PronounsChatComponent.tsx b/src/plugins/pronoundb/components/PronounsChatComponent.tsx index 78cee48..57033cc 100644 --- a/src/plugins/pronoundb/components/PronounsChatComponent.tsx +++ b/src/plugins/pronoundb/components/PronounsChatComponent.tsx @@ -27,12 +27,18 @@ import { PronounMapping } from "../types"; const styles: Record = lazyWebpack(filters.byProps("timestampInline")); -export default function PronounsChatComponent({ message }: { message: Message; }) { +export default function PronounsChatComponentWrapper({ message }: { message: Message; }) { // Don't bother fetching bot or system users - if (message.author.bot || message.author.system) return null; + if (message.author.bot || message.author.system) + return null; // Respect showSelf options - if (!Settings.plugins.PronounDB.showSelf && message.author.id === UserStore.getCurrentUser().id) return null; + if (!Settings.plugins.PronounDB.showSelf && message.author.id === UserStore.getCurrentUser().id) + return null; + return ; +} + +function PronounsChatComponent({ message }: { message: Message; }) { const [result, , isPending] = useAwaiter( () => fetchPronouns(message.author.id), null, @@ -47,6 +53,6 @@ export default function PronounsChatComponent({ message }: { message: Message; } >• {formatPronouns(result)} ); } - // Otherwise, return null so nothing else is rendered - else return null; + + return null; } diff --git a/src/plugins/pronoundb/components/PronounsProfileWrapper.tsx b/src/plugins/pronoundb/components/PronounsProfileWrapper.tsx index 139fb8a..b39f4ed 100644 --- a/src/plugins/pronoundb/components/PronounsProfileWrapper.tsx +++ b/src/plugins/pronoundb/components/PronounsProfileWrapper.tsx @@ -30,8 +30,22 @@ export default function PronounsProfileWrapper(PronounsComponent: React.ElementT if (!Settings.plugins.PronounDB.showSelf && user.id === UserStore.getCurrentUser().id) return null; + return ; +} + +function ProfilePronouns( + { userId, Component, leProps }: { + userId: string; + Component: React.ElementType; + leProps: UserProfilePronounsProps; + } +) { const [result, , isPending] = useAwaiter( - () => fetchPronouns(user.id), + () => fetchPronouns(userId), null, e => console.error("Fetching pronouns failed: ", e) ); @@ -39,8 +53,8 @@ export default function PronounsProfileWrapper(PronounsComponent: React.ElementT // If the promise completed, the result was not "unspecified", and there is a mapping for the code, then render if (!isPending && result && result !== "unspecified" && PronounMapping[result]) { // First child is the header, second is a div with the actual text - props.currentPronouns ||= formatPronouns(result); - return ; + leProps.currentPronouns ||= formatPronouns(result); + return ; } return null; diff --git a/src/plugins/viewIcons.tsx b/src/plugins/viewIcons.tsx index afffdff..671b197 100644 --- a/src/plugins/viewIcons.tsx +++ b/src/plugins/viewIcons.tsx @@ -21,7 +21,7 @@ import type { Guild } from "discord-types/general"; import { Devs } from "../utils/constants"; import { LazyComponent, lazyWebpack } from "../utils/misc"; import { ModalRoot, ModalSize, openModal } from "../utils/modal"; -import definePlugin from "../utils/types"; +import { PluginDef } from "../utils/types"; import { filters, find } from "../webpack"; import { Menu } from "../webpack/common"; @@ -31,12 +31,12 @@ const MaskedLink = LazyComponent(() => find(m => m.type?.toString().includes("MA const GuildBannerStore = lazyWebpack(filters.byProps("getGuildBannerURL")); const OPEN_URL = "Vencord.Plugins.plugins.ViewIcons.openImage("; -export default definePlugin({ - name: "ViewIcons", - authors: [Devs.Ven], - description: "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon.", +export default new class ViewIcons implements PluginDef { + name = "ViewIcons"; + authors = [Devs.Ven]; + description = "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon."; - dependencies: ["MenuItemDeobfuscatorApi"], + dependencies = ["MenuItemDeobfuscatorApi"]; openImage(url: string) { const u = new URL(url); @@ -53,9 +53,9 @@ export default definePlugin({ /> )); - }, + } - patches: [ + patches = [ { find: "onAddFriend:", replacement: { @@ -84,7 +84,7 @@ export default definePlugin({ } ] } - ], + ]; buildGuildContextMenuEntries(guild: Guild) { return ( @@ -108,4 +108,4 @@ export default definePlugin({ ); } -}); +}; diff --git a/src/utils/types.ts b/src/utils/types.ts index dd0a9c5..30c603f 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -49,9 +49,10 @@ export interface PluginAuthor { export interface Plugin extends PluginDef { patches?: Patch[]; started: boolean; + isDependency?: boolean; } -interface PluginDef { +export interface PluginDef { name: string; description: string; authors: PluginAuthor[]; diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx index 8c77506..497dfe4 100644 --- a/src/webpack/common.tsx +++ b/src/webpack/common.tsx @@ -228,9 +228,11 @@ export const Menu = proxyLazy(() => { if (!hasDeobfuscator) { for (const m of menuItems) - map[m] = () => { - throw new Error(`Your plugin needs to depend on MenuItemDeobfuscatorApi to use ${m}`); - }; + Object.defineProperty(map, m, { + get() { + throw new Error("MenuItemDeobfuscator must be enabled to use this."); + } + }); } return map;