ServerDrawer: add a fiber capture point guaranteed to actually mount
This commit is contained in:
parent
094f18d736
commit
5ad56932fc
3 changed files with 62 additions and 63 deletions
|
|
@ -3,6 +3,7 @@ import { View, Text, Pressable, Animated, Dimensions, StyleSheet, BackHandler }
|
||||||
import { lazy } from "../lib/lazy";
|
import { lazy } from "../lib/lazy";
|
||||||
import { rawFind, rawFindByFunctionProps } from "../lib/rawFind";
|
import { rawFind, rawFindByFunctionProps } from "../lib/rawFind";
|
||||||
import { getFlux, getHaptic, getColorModule } from "../lib/commonModules";
|
import { getFlux, getHaptic, getColorModule } from "../lib/commonModules";
|
||||||
|
import { captureFiberRef } from "../lib/fiberCapture";
|
||||||
import { GuildNode } from "../utils/theme";
|
import { GuildNode } from "../utils/theme";
|
||||||
import GuildItem from "./GuildItem";
|
import GuildItem from "./GuildItem";
|
||||||
import FolderItem from "./FolderItem";
|
import FolderItem from "./FolderItem";
|
||||||
|
|
@ -122,6 +123,11 @@ export default function ServerDrawerSheet({ gestureContext }: { gestureContext:
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={st.alignTop}>
|
<View style={st.alignTop}>
|
||||||
|
{/* Invisible - see lib/fiberCapture.ts. This is the reliable capture point: it's
|
||||||
|
guaranteed to mount whenever the drawer itself is visibly rendering, independent of
|
||||||
|
whether hideGuildsBar.tsx's own capture point (which depends on that separate
|
||||||
|
intercept actually firing) works or not. */}
|
||||||
|
<View ref={captureFiberRef} style={{ width: 1, height: 1 }} />
|
||||||
<View
|
<View
|
||||||
style={[st.grid, { paddingHorizontal: padX, gap: GAP }]}
|
style={[st.grid, { paddingHorizontal: padX, gap: GAP }]}
|
||||||
onLayout={onLayout}
|
onLayout={onLayout}
|
||||||
|
|
|
||||||
51
plugins/server-drawer/src/lib/fiberCapture.ts
Normal file
51
plugins/server-drawer/src/lib/fiberCapture.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* Reads React's internal fiber pointer directly off a native view instance and walks up to the
|
||||||
|
* root, storing both under window.__serverDrawerFiberRoot/__serverDrawerFiberSelf for Eval scripts
|
||||||
|
* to search afterward. Same technique Key Inspector's own Settings-screen capture uses, but kept
|
||||||
|
* under separate globals - Key Inspector's capture only reaches whatever tree its own Settings
|
||||||
|
* screen renders in (very likely a separate native modal/root from MainTabs' guild tab content),
|
||||||
|
* so a capture point actually inside this plugin's own rendered output is what's needed to reach
|
||||||
|
* anything under the Guilds tab (confirmed live: a Settings-screen capture couldn't find
|
||||||
|
* QuestDockWithQuestContext at all, even navigating to the Guilds tab first).
|
||||||
|
*/
|
||||||
|
export function captureFiberRef(instance: any) {
|
||||||
|
if (!instance) return;
|
||||||
|
try {
|
||||||
|
let fiber: any = null;
|
||||||
|
|
||||||
|
if (instance._internalFiberInstanceHandleDEV) {
|
||||||
|
fiber = instance._internalFiberInstanceHandleDEV;
|
||||||
|
} else if (instance._internalInstanceHandle) {
|
||||||
|
fiber = instance._internalInstanceHandle;
|
||||||
|
} else if (instance.__internalInstanceHandle) {
|
||||||
|
fiber = instance.__internalInstanceHandle;
|
||||||
|
} else {
|
||||||
|
const fiberKey = Object.keys(instance).find(
|
||||||
|
(k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$")
|
||||||
|
);
|
||||||
|
if (fiberKey) fiber = instance[fiberKey];
|
||||||
|
}
|
||||||
|
if (!fiber) return;
|
||||||
|
|
||||||
|
if (fiber.return === undefined && fiber.child === undefined) {
|
||||||
|
const nested = fiber.stateNode ?? fiber.fiber ?? fiber._debugOwner;
|
||||||
|
if (nested && (nested.return !== undefined || nested.child !== undefined)) {
|
||||||
|
fiber = nested;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let root = fiber;
|
||||||
|
let guard = 0;
|
||||||
|
while (root.return && guard < 5000) {
|
||||||
|
root = root.return;
|
||||||
|
guard++;
|
||||||
|
}
|
||||||
|
|
||||||
|
(window as any).__serverDrawerFiberRoot = root;
|
||||||
|
(window as any).__serverDrawerFiberSelf = fiber;
|
||||||
|
} catch {
|
||||||
|
// Best-effort diagnostic capture - a failure here shouldn't affect the caller's own render.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,72 +1,14 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { registerTypeDetector, registerIntercept } from "../lib/createElementIntercept";
|
import { registerTypeDetector, registerIntercept } from "../lib/createElementIntercept";
|
||||||
|
import { captureFiberRef } from "../lib/fiberCapture";
|
||||||
|
|
||||||
const TAG = "[ServerDrawer]";
|
const TAG = "[ServerDrawer]";
|
||||||
|
|
||||||
// Key Inspector's own fiber capture only fires from a View rendered inside ITS OWN Settings
|
// Whether this capture point actually mounts depends entirely on whether the intercept below
|
||||||
// screen - and Settings very likely renders as a separate native modal/root from MainTabs' guild
|
// fires (unverified independently of it - see lib/fiberCapture.ts for the primary, confirmed
|
||||||
// tab content, so a fiber captured there can never reach anything under the Guilds tab (Quest
|
// capture point inside ServerDrawerSheet.tsx itself, which is guaranteed to mount whenever the
|
||||||
// Dock included) via .return, no matter what screen was visited beforehand. Confirmed live: two
|
// drawer is visibly showing). Kept here too as a second chance, harmless either way.
|
||||||
// independent searches for Quest Dock components both came up empty even while a real quest was
|
|
||||||
// visibly rendering on the Guilds screen, and a fiber-tree walk from a Settings-screen capture
|
|
||||||
// couldn't find QuestDockWithQuestContext at all.
|
|
||||||
//
|
|
||||||
// GuildsBar's hidden slot is a reliable alternative capture point: it's confirmed to actually
|
|
||||||
// mount on the Guilds screen (that's this whole file's job), and it's a sibling of the Quest Dock
|
|
||||||
// in the same tree (both live under MainTabsNavigatorPanel), so walking up from here and back down
|
|
||||||
// reaches Quest Dock correctly. Stored under the same window.__keyInspectorFiberRoot/
|
|
||||||
// __keyInspectorFiberSelf globals Key Inspector's Eval scripts already expect, so no existing
|
|
||||||
// script needs to change - this just gives them a capture point that's actually reachable.
|
|
||||||
function captureFiberRef(instance: any) {
|
|
||||||
if (!instance) return;
|
|
||||||
try {
|
|
||||||
let fiber: any = null;
|
|
||||||
|
|
||||||
if (instance._internalFiberInstanceHandleDEV) {
|
|
||||||
fiber = instance._internalFiberInstanceHandleDEV;
|
|
||||||
} else if (instance._internalInstanceHandle) {
|
|
||||||
fiber = instance._internalInstanceHandle;
|
|
||||||
} else if (instance.__internalInstanceHandle) {
|
|
||||||
fiber = instance.__internalInstanceHandle;
|
|
||||||
} else {
|
|
||||||
const fiberKey = Object.keys(instance).find(
|
|
||||||
(k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$")
|
|
||||||
);
|
|
||||||
if (fiberKey) fiber = instance[fiberKey];
|
|
||||||
}
|
|
||||||
if (!fiber) return;
|
|
||||||
|
|
||||||
if (fiber.return === undefined && fiber.child === undefined) {
|
|
||||||
const nested = fiber.stateNode ?? fiber.fiber ?? fiber._debugOwner;
|
|
||||||
if (nested && (nested.return !== undefined || nested.child !== undefined)) {
|
|
||||||
fiber = nested;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let root = fiber;
|
|
||||||
let guard = 0;
|
|
||||||
while (root.return && guard < 5000) {
|
|
||||||
root = root.return;
|
|
||||||
guard++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dedicated globals, not window.__keyInspectorFiberRoot/__keyInspectorFiberSelf: Key
|
|
||||||
// Inspector's own Settings screen has its own capture View using those same names, and its
|
|
||||||
// ref re-fires every time that screen mounts - including just scrolling to the Eval console
|
|
||||||
// to paste the next script. Sharing the names meant navigating back to Settings silently
|
|
||||||
// overwrote this capture with one rooted in Settings' own (likely separate-modal) tree,
|
|
||||||
// which is exactly the failure this capture point exists to avoid. Confirmed live: a text
|
|
||||||
// dump meant to read GuildsBar's slot came back with Key Inspector's own UI strings instead.
|
|
||||||
(window as any).__serverDrawerFiberRoot = root;
|
|
||||||
(window as any).__serverDrawerFiberSelf = fiber;
|
|
||||||
} catch {
|
|
||||||
// Best-effort diagnostic capture - a failure here shouldn't affect GuildsBar staying hidden.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Nothing() {
|
function Nothing() {
|
||||||
return React.createElement(View, {
|
return React.createElement(View, {
|
||||||
ref: captureFiberRef,
|
ref: captureFiberRef,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue