diff --git a/plugins/custom-user-tags/src/index.ts b/plugins/custom-user-tags/src/index.ts
index 12bd60e..ddbd7c6 100644
--- a/plugins/custom-user-tags/src/index.ts
+++ b/plugins/custom-user-tags/src/index.ts
@@ -4,6 +4,7 @@ import { applyPatches } from "@shared/lib/patcher";
import patchChat from "./patches/chat";
import patchName from "./patches/name";
import patchDetails from "./patches/details";
+import patchProfile from "./patches/profile";
import patchTag from "./patches/tag";
import Settings from "./ui/Settings";
@@ -17,7 +18,8 @@ export default {
tag: patchTag,
chat: patchChat,
name: patchName,
- details: patchDetails
+ details: patchDetails,
+ profile: patchProfile
});
},
onUnload: () => unpatchAll(),
diff --git a/plugins/custom-user-tags/src/patches/profile.tsx b/plugins/custom-user-tags/src/patches/profile.tsx
new file mode 100644
index 0000000..e0d3b99
--- /dev/null
+++ b/plugins/custom-user-tags/src/patches/profile.tsx
@@ -0,0 +1,70 @@
+import { find, findByProps } from "@vendetta/metro";
+import { after } from "@vendetta/patcher";
+import { findInReactTree } from "@vendetta/utils";
+import resolveTag from "../lib/resolveTag";
+
+// Same top-level profile component PronounDB already confirmed works, and the same one Staff Tags
+// now patches for its own profile-screen tag - see that plugin's patches/profile.tsx for the full
+// writeup on why this is a separate component from UserRow (which details.tsx already handles).
+const UserProfile =
+ find((m: any) => m?.type?.name === "UserProfileContent") ??
+ find((m: any) => m?.type?.name === "UserProfile");
+
+const TagModule = findByProps("getBotLabel");
+
+/**
+ * Not independently confirmed against a live render yet - the exact prop shape passed to this
+ * component isn't verified on-device, so this pulls from whichever of several plausible field names
+ * is actually present. Every step is guarded, so a wrong assumption just means no tag appears here.
+ */
+export default function patchProfile(): () => void {
+ if (!UserProfile) return () => {};
+
+ return after("type", UserProfile, (args: any[], res: any) => {
+ try {
+ const props = args?.[0] ?? {};
+ const userId: string | undefined = props.userId ?? props.user?.id ?? props.displayProfile?.userId;
+ if (!userId) return;
+
+ const tag = resolveTag(userId);
+ if (!tag) return;
+
+ const primaryInfo = findInReactTree(res, (c) => c?.type?.name === "PrimaryInfo" || c?.type?.displayName === "PrimaryInfo");
+ if (!primaryInfo) return;
+
+ const existingTag = findInReactTree(primaryInfo, (c) => c?.type?.Types);
+ if (existingTag && existingTag.props?.type !== 0) return;
+
+ if (existingTag) {
+ Object.assign(existingTag.props, {
+ type: 0,
+ text: tag.text,
+ textColor: tag.textColor,
+ backgroundColor: tag.backgroundColor,
+ verified: false
+ });
+ return;
+ }
+
+ const nameRow = findInReactTree(
+ primaryInfo,
+ (c) =>
+ Array.isArray(c?.props?.children) &&
+ c.props.children.some((ch: any) => typeof ch === "string" || typeof ch?.props?.children === "string")
+ );
+ if (!Array.isArray(nameRow?.props?.children) || !TagModule) return;
+
+ nameRow.props.children.push(
+
+ );
+ } catch {
+ // Skip - a broken profile tag beats a crashed profile screen.
+ }
+ });
+}
diff --git a/plugins/staff-tags/src/index.ts b/plugins/staff-tags/src/index.ts
index 432b328..1473ccb 100644
--- a/plugins/staff-tags/src/index.ts
+++ b/plugins/staff-tags/src/index.ts
@@ -4,6 +4,7 @@ import { applyPatches } from "@shared/lib/patcher";
import patchChat from "./patches/chat";
import patchDetails from "./patches/details";
import patchName from "./patches/name";
+import patchProfile from "./patches/profile";
import patchTag from "./patches/tag";
import Settings from "./ui/pages/Settings";
@@ -18,7 +19,8 @@ export default {
tag: patchTag,
chat: patchChat,
name: patchName,
- details: patchDetails
+ details: patchDetails,
+ profile: patchProfile
});
},
onUnload: () => unpatchAll(),
diff --git a/plugins/staff-tags/src/patches/profile.tsx b/plugins/staff-tags/src/patches/profile.tsx
new file mode 100644
index 0000000..b54c87e
--- /dev/null
+++ b/plugins/staff-tags/src/patches/profile.tsx
@@ -0,0 +1,82 @@
+import { find, findByProps, findByStoreName } from "@vendetta/metro";
+import { after } from "@vendetta/patcher";
+import { findInReactTree } from "@vendetta/utils";
+import getTag from "../lib/getTag";
+
+// Same top-level profile component PronounDB already confirmed works on current Discord (the
+// findByName-based lookup this repo used to use for it stopped matching after a rename, but this
+// predicate-based find still succeeds). The full profile screen doesn't render through UserRow the
+// way member list rows and the old "profile popout" did - this is a separate component entirely,
+// which is why tags weren't showing up here even though details.tsx's own patch is working fine.
+const UserProfile =
+ find((m: any) => m?.type?.name === "UserProfileContent") ??
+ find((m: any) => m?.type?.name === "UserProfile");
+
+const TagModule = findByProps("getBotLabel");
+const GuildStore = findByStoreName("GuildStore");
+const UserStore = findByStoreName("UserStore");
+
+/**
+ * Not independently confirmed against a live render the way most of this repo's fixes are - the
+ * exact prop shape passed to this component's render (userId vs. a full user object, whether
+ * guildId is even present) isn't verified on-device yet, so this pulls from whichever of several
+ * plausible field names is actually present rather than assuming one. Every step is guarded, so a
+ * wrong assumption just means no tag appears here, not a crash.
+ */
+export default function patchProfile(): () => void {
+ if (!UserProfile) return () => {};
+
+ return after("type", UserProfile, (args: any[], res: any) => {
+ try {
+ const props = args?.[0] ?? {};
+ const userId: string | undefined = props.userId ?? props.user?.id ?? props.displayProfile?.userId;
+ if (!userId) return;
+
+ const user = props.user ?? UserStore?.getUser?.(userId);
+ if (!user) return;
+
+ const guildId: string | undefined = props.guildId;
+ const guild = guildId ? GuildStore?.getGuild(guildId) : undefined;
+
+ const tag = getTag(guild, undefined, user);
+ if (!tag) return;
+
+ const primaryInfo = findInReactTree(res, (c) => c?.type?.name === "PrimaryInfo" || c?.type?.displayName === "PrimaryInfo");
+ if (!primaryInfo) return;
+
+ const existingTag = findInReactTree(primaryInfo, (c) => c?.type?.Types);
+ if (existingTag && existingTag.props?.type !== 0) return;
+
+ if (existingTag) {
+ Object.assign(existingTag.props, {
+ type: 0,
+ text: tag.text,
+ textColor: tag.textColor,
+ backgroundColor: tag.backgroundColor,
+ verified: false
+ });
+ return;
+ }
+
+ const nameRow = findInReactTree(
+ primaryInfo,
+ (c) =>
+ Array.isArray(c?.props?.children) &&
+ c.props.children.some((ch: any) => typeof ch === "string" || typeof ch?.props?.children === "string")
+ );
+ if (!Array.isArray(nameRow?.props?.children) || !TagModule) return;
+
+ nameRow.props.children.push(
+
+ );
+ } catch {
+ // Skip - a broken profile tag beats a crashed profile screen.
+ }
+ });
+}