ServerDrawer: fully hide GuildsBar and reclaim its space, not just replace its content

This commit is contained in:
Ally Founder 2026-08-11 11:56:36 -04:00
parent 8bbfd6d7a5
commit 47da6d615c

View file

@ -1,94 +1,78 @@
import React from "react";
import { Pressable, Image, View } from "react-native";
import { find, findByName, findByProps } from "@vendetta/metro";
import { getAssetIDByName } from "@vendetta/ui/assets";
import { registerIntercept } from "./createElementIntercept";
import { View } from "react-native";
import { registerPropsTransform, registerTypeDetector, registerIntercept } from "./createElementIntercept";
const TAG = "[ServerDrawer]";
const SD_NOTHING_TEST_ID = "ServerDrawerNothing";
const ChatIcon = getAssetIDByName("ChatIcon");
const Haptic = findByProps("triggerHapticFeedback", "HapticFeedbackTypes");
const ChannelActions = findByProps("selectPrivateChannel");
const SelectedChannelStore = findByName("SelectedChannelStore");
const FluxStores = findByProps("useStateFromStores");
const useStateFromStores = FluxStores?.useStateFromStores;
const GuildStore = findByProps("getGuildId");
const colors = findByProps("colors", "unsafe_rawColors")?.colors;
const Routes = findByProps("ME");
const ME = Routes?.ME ?? "/channels/@me";
function Nothing() {
return React.createElement(View, {
style: {
display: "none",
width: 0,
minWidth: 0,
maxWidth: 0,
flexGrow: 0,
flexShrink: 0,
flexBasis: 0,
margin: 0,
padding: 0,
borderWidth: 0,
overflow: "hidden",
},
});
}
function openDms() {
Haptic?.triggerHapticFeedback(Haptic.HapticFeedbackTypes.SOFT);
if (ChannelActions?.selectPrivateChannel) {
const lastChannelId = SelectedChannelStore?.getLastSelectedChannelId?.();
ChannelActions.selectPrivateChannel(lastChannelId);
// The parent creates GuildsBar via its outer React.memo wrapper object, which has no own
// .name/.displayName at all - those only exist on the memo's inner function, at
// type.type.name/type.type.displayName.
function isGuildsBar(type: any): boolean {
return type?.name === "GuildsBar" || type?.displayName === "GuildsBar" ||
type?.type?.name === "GuildsBar" || type?.type?.displayName === "GuildsBar";
}
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;
}
}
function useIsInDms(): boolean {
try {
return GuildStore ? useStateFromStores([GuildStore], () => {
const guildId = GuildStore?.getGuildId?.();
return guildId == null || guildId === ME;
}) : false;
} catch { return false; }
}
function DmsGuildIcon() {
const selected = useIsInDms();
const iconColor = selected
? (colors?.WHITE ?? "#fff")
: (colors?.INTERACTIVE_NORMAL ?? "#80848e");
return (
<Pressable onPress={openDms} style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
{selected ? (
<View style={{
position: "absolute", left: 0, top: "25%",
width: 4, height: "50%", borderRadius: 2,
backgroundColor: colors?.WHITE ?? "#fff",
}} />
) : null}
<Image
source={ChatIcon}
style={{ width: 28, height: 28, tintColor: iconColor }}
/>
</Pressable>
);
}
function findGuildsBar(): any {
const byName = findByName("GuildsBar");
if (byName) return { default: byName };
let mod = find((m) => {
try { return m?.default?.type?.name === "GuildsBar"; } catch { return false; }
});
if (mod?.default) return mod;
mod = find((m) => {
try { return m?.default?.displayName === "GuildsBar"; } catch { return false; }
});
if (mod?.default) return mod;
return null;
for (const child of rest) if (inspect(child)) return true;
return false;
}
export function patchHideGuildsBar(cleanups: (() => void)[]): boolean {
const mod = findGuildsBar();
if (!mod?.default) {
console.log(TAG, "WARN: GuildsBar not found");
return false;
}
const orig = mod.default;
// Rendering GuildsBar as nothing alone still leaves its space reserved - the immediate parent
// carries an explicit width in its own style, so display: none on the child doesn't reclaim
// it. This zeroes the parent wrapper too once it spots the hidden marker as a child.
registerPropsTransform(
(props: any, _type: any, rest: any[]) =>
hasChildWithTestID(props?.children, rest, SD_NOTHING_TEST_ID),
(props: any) => ({
...props,
style: [
props?.style,
{
display: "none",
width: 0,
minWidth: 0,
maxWidth: 0,
flexGrow: 0,
flexShrink: 0,
flexBasis: 0,
margin: 0,
padding: 0,
borderWidth: 0,
overflow: "hidden",
},
],
}),
);
registerIntercept(orig, DmsGuildIcon);
mod.default = function DmsGuildsBar() {
return React.createElement(DmsGuildIcon);
};
mod.default.displayName = "GuildsBar";
cleanups.push(() => { mod.default = orig; });
registerTypeDetector("ServerDrawer.HideGuildsBar", isGuildsBar, (realGuildsBar) => {
registerIntercept(realGuildsBar, Nothing, { testID: SD_NOTHING_TEST_ID }, { collapseAncestors: 6 });
console.log(TAG, "PATCH: found a real GuildsBar reference, now rendering nothing");
}, { persistent: true });
console.log(TAG, "PATCH: watching for the real GuildsBar to appear");
return true;
}