From 45688286884f298cb0eb7c1feb0a4ad4753c61d8 Mon Sep 17 00:00:00 2001 From: everestmcarthur <280397636+everestmcarthur@users.noreply.github.com> Date: Sun, 2 Aug 2026 09:43:05 -0400 Subject: [PATCH] ServerDrawer: revert contentPatch.tsx drawer-swap to the confirmed-working version --- .../src/patches/contentPatch.tsx | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/plugins/server-drawer/src/patches/contentPatch.tsx b/plugins/server-drawer/src/patches/contentPatch.tsx index 227fd9f..fbaec3d 100644 --- a/plugins/server-drawer/src/patches/contentPatch.tsx +++ b/plugins/server-drawer/src/patches/contentPatch.tsx @@ -1,10 +1,14 @@ import { React } from "@vendetta/metro/common"; import { rawFind } from "../lib/rawFind"; import ServerDrawerSheet from "../components/ServerDrawerSheet"; -import { registerIntercept, registerTypeDetector } from "../lib/createElementIntercept"; +import { registerIntercept } from "../lib/createElementIntercept"; const TAG = "[ServerDrawer]"; +// rawFind, not @vendetta/metro's find - both patchExpanded and patchEmpty are retried by index.ts's +// fast-retry loop, and Revenge's own find() permanently caches a "not found" result per call site +// and never rescans (see rawFind.ts), which would make the retry pointless after the first +// failed attempt. let cachedGestureContext: any = null; function getGestureContext(): any { if (!cachedGestureContext) { @@ -13,50 +17,47 @@ function getGestureContext(): any { return cachedGestureContext; } -// Same fix as hideGuildsBar.tsx: memo-wrapped components are passed to createElement/jsx as the -// outer wrapper object, which has no own .name/.displayName - only its nested .type does. Missing -// that nested check meant this never matched anything, which is why Discord's real -// QuestDockEnrolledHeader (and friends) kept running unpatched against the fake quest data. -function isNamed(name: string) { - return (type: any) => type?.name === name || type?.displayName === name || - type?.type?.name === name || type?.type?.displayName === name; -} +export function patchExpanded( + cleanups: (() => void)[] +): boolean { + const mod = rawFind((m) => m?.type?.displayName === "QuestDockContentExpanded" || m?.type?.name === "QuestDockContentExpanded"); + if (!mod?.type) { + console.log(TAG, "WARN: QuestDockContentExpanded not found (will retry)"); + return false; + } + const orig = mod.type; -function Nothing() { - return null; -} + registerIntercept(orig, ServerDrawerSheet, { gestureContext: getGestureContext() }); -/** - * Reset after live verification (Key Inspector's fiber capture + Eval, same methodology that found - * GuildsBar's bug) showed this file's original approach shares the exact same flaw: rawFind matching - * a component by `.type.displayName`/`.type.name` across window.modules can - and here, does - match - * a different, unrelated module than the one actually mounted. The visible symptom: Discord's real - * QuestDockEnrolledHeader (and friends) kept running unpatched, then crashed reading properties off - * the fake quest data useMobileQuestDock.ts hands out, because our replacement was silently mutating - * the wrong object the whole time. - * - * registerTypeDetector sidesteps this the same way it does for GuildsBar: it inspects the `type` - * argument passed directly to createElement/jsx at the moment something actually creates an element - * with it, which is necessarily the real, live reference - there's no module-registry ambiguity - * possible. registerIntercept then swaps that exact reference (matched by identity) for the - * replacement on every future creation. - */ -export function patchExpanded(cleanups: (() => void)[]): boolean { - registerTypeDetector(isNamed("QuestDockContentExpanded"), (real) => { - registerIntercept(real, ServerDrawerSheetWrapper); - console.log(TAG, "PATCH: found the real QuestDockContentExpanded, now rendering the drawer"); - }); + mod.type = function ServerDrawerPatch() { + return ; + }; + cleanups.push(() => { mod.type = orig; }); + console.log(TAG, "PATCH: QuestDockContentExpanded replaced"); return true; } -function ServerDrawerSheetWrapper() { - return ; -} +export function patchEmpty( + name: string, + cleanups: (() => void)[] +): boolean { + const mod = rawFind((m) => m?.type?.displayName === name || m?.type?.name === name); + if (!mod?.type) { + console.log(TAG, `WARN: ${name} not found`); + return false; + } + const orig = mod.type; + const originalComponent = orig; -export function patchEmpty(name: string, cleanups: (() => void)[]): boolean { - registerTypeDetector(isNamed(name), (real) => { - registerIntercept(real, Nothing); - console.log(TAG, `PATCH: found the real ${name}, now rendering nothing`); - }); + mod.type = function EmptyPatch() { + return null; + }; + + if (originalComponent) { + Object.defineProperties(mod.type, Object.getOwnPropertyDescriptors(originalComponent)); + mod.type.displayName = name; + } + + cleanups.push(() => { mod.type = orig; }); return true; }