ServerDrawer: hard-enforce rail hide and quest dock transparency

This commit is contained in:
Rosie Val 2026-08-07 01:27:45 -04:00
parent 69053d8b33
commit b5224eb0a1
3 changed files with 42 additions and 9 deletions

View file

@ -216,7 +216,7 @@ function resolveReplacement(type: any, props: any, rest: any[]): { type: any; pr
if (effectiveProps) {
for (const { predicate, transform } of propsTransforms) {
try {
if (predicate(effectiveProps, type)) {
if (predicate(effectiveProps, type, rest)) {
effectiveProps = transform(effectiveProps);
}
} catch {
@ -228,7 +228,7 @@ function resolveReplacement(type: any, props: any, rest: any[]): { type: any; pr
if (effectiveProps) {
for (const { predicate, replacement } of propsIntercepts) {
try {
if (predicate(effectiveProps, type)) {
if (predicate(effectiveProps, type, rest)) {
return replacement ? { type: replacement, props: effectiveProps } : null;
}
} catch {
@ -333,7 +333,7 @@ export function patchCreateElement(cleanups: (() => void)[]) {
// This is synchronous because the race we're fixing is specifically the first QuestDock render:
// an async scan completes too late and the element is created through an unpatched runtime.
const initialRuntimes = rawFindAll<any>(
(m: any) => typeof m?.jsx === "function" && typeof m?.jsxs === "function",
(m: any) => typeof m?.jsx === "function" || typeof m?.jsxs === "function" || typeof m?.jsxDEV === "function",
);
for (const runtime of initialRuntimes) {
patchRuntime(runtime);
@ -344,7 +344,7 @@ export function patchCreateElement(cleanups: (() => void)[]) {
scanning = true;
rawFindAllAsync<any>(
(m: any) => typeof m?.jsx === "function" && typeof m?.jsxs === "function",
(m: any) => typeof m?.jsx === "function" || typeof m?.jsxs === "function" || typeof m?.jsxDEV === "function",
).then((runtimes) => {
for (const runtime of runtimes) {
patchRuntime(runtime);

View file

@ -37,6 +37,18 @@ function isQuestDock(type: any): boolean {
return QUEST_DOCK_NAMES.some((name) => isNamed(name)(type));
}
const SD_DRAWER_TEST_ID = "ServerDrawer";
function hasChildWithTestID(children: any, rest: any[], testID: string): boolean {
const inspect = (child: any) => child != null && typeof child === "object" && child.props?.testID === testID;
if (children != null) {
if (Array.isArray(children)) { if (children.some(inspect)) return true; }
else if (inspect(children)) return true;
}
for (const child of rest) if (inspect(child)) return true;
return false;
}
function ServerDrawerSheetWrapper() {
return <ServerDrawerSheet gestureContext={getGestureContext()} />;
}
@ -63,7 +75,9 @@ export function patchExpanded(cleanups: (() => void)[]): boolean {
// Make the quest dock's wrapper(s) transparent - the ServerDrawerSheet content still renders on
// top, but the panel/drawer background that used to be behind the quest menu is removed.
registerPropsTransform(
(_props: any, type: any) => questDockTypes.has(type),
(props: any, type: any, rest: any[]) =>
questDockTypes.has(type) ||
hasChildWithTestID(props?.children, rest, SD_DRAWER_TEST_ID),
(props: any) => ({ ...props, style: [props?.style, { backgroundColor: "transparent" }] }),
);
@ -73,7 +87,7 @@ export function patchExpanded(cleanups: (() => void)[]): boolean {
}, { persistent: true });
registerTypeDetector("ServerDrawer.Expanded", isNamed("QuestDockContentExpanded"), (real) => {
registerIntercept(real, ServerDrawerSheetWrapper);
registerIntercept(real, ServerDrawerSheetWrapper, { testID: SD_DRAWER_TEST_ID });
console.log(TAG, "PATCH: QuestDockContentExpanded replaced (type detector)");
}, { persistent: true });
@ -84,7 +98,7 @@ export function patchExpanded(cleanups: (() => void)[]): boolean {
}
const orig = mod.type;
registerIntercept(orig, ServerDrawerSheetWrapper);
registerIntercept(orig, ServerDrawerSheetWrapper, { testID: SD_DRAWER_TEST_ID });
mod.type = ServerDrawerSheetWrapper;
cleanups.push(() => { mod.type = orig; });
console.log(TAG, "PATCH: QuestDockContentExpanded replaced (module mutation)");

View file

@ -1,9 +1,10 @@
import React from "react";
import { View } from "react-native";
import { registerTypeDetector, registerIntercept } from "../lib/createElementIntercept";
import { registerPropsTransform, registerTypeDetector, registerIntercept } from "../lib/createElementIntercept";
import { captureFiberRef } from "../lib/fiberCapture";
const TAG = "[ServerDrawer]";
const SD_NOTHING_TEST_ID = "ServerDrawerNothing";
// Whether this capture point actually mounts depends entirely on whether the intercept below
// fires (unverified independently of it - see lib/fiberCapture.ts for the primary, confirmed
@ -64,12 +65,30 @@ function isGuildsBar(type: any): boolean {
* so every future creation of it - which happens on every re-render of GuildsBar's parent, i.e.
* whenever the rail would normally repaint - renders nothing instead.
*/
function hasChildWithTestID(children: any, rest: any[], testID: string): boolean {
const inspect = (child: any) => child != null && typeof child === "object" && child.props?.testID === testID;
if (children != null) {
if (Array.isArray(children)) { if (children.some(inspect)) return true; }
else if (inspect(children)) return true;
}
for (const child of rest) if (inspect(child)) return true;
return false;
}
export function patchHideGuildsBar(cleanups: (() => void)[]): boolean {
// Hard-enforce the GuildsBar parent's visibility by detecting the hidden Nothing child and
// applying display: none to the rail wrapper itself.
registerPropsTransform(
(props: any, _type: any, rest: any[]) =>
hasChildWithTestID(props?.children, rest, SD_NOTHING_TEST_ID),
(props: any) => ({ ...props, style: [props?.style, { display: "none" }] }),
);
// Use a stable string key so duplicate registrations from retry loops are deduped correctly.
registerTypeDetector("ServerDrawer.HideGuildsBar", isGuildsBar, (realGuildsBar) => {
// Ask createElementIntercept to collapse a few ancestor levels as well so wrapper
// containers around the rail don't leave a leftover gap.
registerIntercept(realGuildsBar, Nothing, undefined, { collapseAncestors: 6 });
registerIntercept(realGuildsBar, Nothing, { testID: SD_NOTHING_TEST_ID }, { collapseAncestors: 6 });
console.log(TAG, "PATCH: found a real GuildsBar reference, now rendering nothing");
}, { persistent: true });
cleanups.push(() => {