ServerDrawer: make GuildsBar's hide detector persistent, not one-shot

This commit is contained in:
everestmcarthur 2026-08-02 11:41:02 -04:00
parent 62192d5035
commit 97642009b3
2 changed files with 20 additions and 7 deletions

View file

@ -20,6 +20,7 @@ interface PropsTransform {
interface TypeDetector { interface TypeDetector {
predicate: (type: any) => boolean; predicate: (type: any) => boolean;
onDetected: (type: any) => void; onDetected: (type: any) => void;
persistent: boolean;
} }
const intercepts = new Map<React.ComponentType<any>, Intercept>(); const intercepts = new Map<React.ComponentType<any>, Intercept>();
@ -65,9 +66,9 @@ export function registerPropsTransform(predicate: (props: any) => boolean, trans
} }
/** /**
* Purely observational, one-shot: the first time an element is created whose `type` matches * Purely observational: the first time an element is created whose `type` matches `predicate`,
* `predicate`, calls `onDetected(type)` with the real, live type reference and then stops * calls `onDetected(type)` with the real, live type reference - it never alters what actually
* watching - it never alters what actually renders. * renders (pair with registerIntercept/registerPropsIntercept in the callback for that).
* *
* This exists for components that can't be found reliably by searching Metro's module registry * This exists for components that can't be found reliably by searching Metro's module registry
* after the fact (findByTypeName/rawFindByTypeName), because that races against whenever the * after the fact (findByTypeName/rawFindByTypeName), because that races against whenever the
@ -77,9 +78,20 @@ export function registerPropsTransform(predicate: (props: any) => boolean, trans
* creation instead sidesteps the race entirely: by the time ANYTHING calls createElement/jsx with * creation instead sidesteps the race entirely: by the time ANYTHING calls createElement/jsx with
* this component as `type`, that reference already exists and is already the real one about to be * this component as `type`, that reference already exists and is already the real one about to be
* used - there's no way to observe the call any earlier than this. * used - there's no way to observe the call any earlier than this.
*
* `persistent` (default false, one-shot): keeps watching and firing `onDetected` again for every
* later match too, instead of stopping after the first. Needed when the component's own type
* reference isn't a stable session-long singleton - confirmed live for GuildsBar, whose reference
* changes on some navigation paths (e.g. switching servers via this plugin's own drawer), which a
* one-shot detector plus registerIntercept's identity-keyed map can't follow: the intercept stays
* registered for the old, now-abandoned reference while the new one renders unmodified.
*/ */
export function registerTypeDetector(predicate: (type: any) => boolean, onDetected: (type: any) => void) { export function registerTypeDetector(
typeDetectors.push({ predicate, onDetected }); predicate: (type: any) => boolean,
onDetected: (type: any) => void,
options?: { persistent?: boolean },
) {
typeDetectors.push({ predicate, onDetected, persistent: options?.persistent ?? false });
} }
function runTypeDetectors(type: any) { function runTypeDetectors(type: any) {
@ -98,6 +110,7 @@ function runTypeDetectors(type: any) {
} catch { } catch {
// Same - a detector's own handler throwing shouldn't take anything else down. // Same - a detector's own handler throwing shouldn't take anything else down.
} }
if (detector.persistent) remaining.push(detector);
} else { } else {
remaining.push(detector); remaining.push(detector);
} }

View file

@ -51,8 +51,8 @@ function isGuildsBar(type: any): boolean {
export function patchHideGuildsBar(cleanups: (() => void)[]): boolean { export function patchHideGuildsBar(cleanups: (() => void)[]): boolean {
registerTypeDetector(isGuildsBar, (realGuildsBar) => { registerTypeDetector(isGuildsBar, (realGuildsBar) => {
registerIntercept(realGuildsBar, Nothing); registerIntercept(realGuildsBar, Nothing);
console.log(TAG, "PATCH: found the real GuildsBar reference, now rendering nothing"); console.log(TAG, "PATCH: found a real GuildsBar reference, now rendering nothing");
}); }, { persistent: true });
cleanups.push(() => { cleanups.push(() => {
// No per-call unregister needed - the whole detector/intercept state gets cleared when // No per-call unregister needed - the whole detector/intercept state gets cleared when
// createElementIntercept's own patch unwinds, part of the same cleanup pass this plugin // createElementIntercept's own patch unwinds, part of the same cleanup pass this plugin