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;
}