From 17b90beee1af377becbf92764cf954910037f140 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sun, 31 Aug 2025 05:18:40 +0200 Subject: [PATCH] add context menu options to vencord badges --- src/api/Badges.ts | 39 +++++++++++++++------- src/plugins/_api/badges/index.tsx | 54 +++++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/src/api/Badges.ts b/src/api/Badges.ts index f79ca099..1345b26d 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -34,7 +34,9 @@ export interface ProfileBadge { image?: string; link?: string; /** Action to perform when you click the badge */ - onClick?(event: React.MouseEvent, props: BadgeUserArgs): void; + onClick?(event: React.MouseEvent, props: ProfileBadge & BadgeUserArgs): void; + /** Action to perform when you right click the badge */ + onContextMenu?(event: React.MouseEvent, props: BadgeUserArgs & BadgeUserArgs): void; /** Should the user display this badge? */ shouldShow?(userInfo: BadgeUserArgs): boolean; /** Optional props (e.g. style) for the badge, ignored for component badges */ @@ -76,21 +78,34 @@ export function removeProfileBadge(badge: ProfileBadge) { export function _getBadges(args: BadgeUserArgs) { const badges = [] as ProfileBadge[]; for (const badge of Badges) { - if (!badge.shouldShow || badge.shouldShow(args)) { - const b = badge.getBadges - ? badge.getBadges(args).map(b => { - b.component &&= ErrorBoundary.wrap(b.component, { noop: true }); - return b; - }) - : [{ ...badge, ...args }]; + if (badge.shouldShow && !badge.shouldShow(args)) { + continue; + } - badge.position === BadgePosition.START - ? badges.unshift(...b) - : badges.push(...b); + const b = badge.getBadges + ? badge.getBadges(args).map(badge => ({ + ...args, + ...badge, + component: badge.component && ErrorBoundary.wrap(badge.component, { noop: true }) + })) + : [{ ...args, ...badge }]; + + if (badge.position === BadgePosition.START) { + badges.unshift(...b); + } else { + badges.push(...b); } } + const donorBadges = BadgeAPIPlugin.getDonorBadges(args.userId); - if (donorBadges) badges.unshift(...donorBadges); + if (donorBadges) { + badges.unshift( + ...donorBadges.map(badge => ({ + ...args, + ...badge, + })) + ); + } return badges; } diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index 433c3080..4118435a 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -27,11 +27,11 @@ import { openContributorModal } from "@components/settings/tabs"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { shouldShowContributorBadge } from "@utils/misc"; +import { copyWithToast, shouldShowContributorBadge } from "@utils/misc"; import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; import { User } from "@vencord/discord-types"; -import { Forms, Toasts, UserStore } from "@webpack/common"; +import { ContextMenuApi, Forms, Menu, Toasts, UserStore } from "@webpack/common"; const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/emojis/1092089799109775453.png?size=64"; @@ -56,9 +56,35 @@ async function loadBadges(noCache = false) { let intervalId: any; +function BadgeContextMenu({ badge }: { badge: ProfileBadge & BadgeUserArgs; }) { + console.log(badge); + return ( + + {badge.description && ( + copyWithToast(badge.description!)} + /> + )} + {badge.image && ( + copyWithToast(badge.image!)} + /> + )} + + ); +} + export default definePlugin({ name: "BadgeAPI", - description: "API to add badges to users.", + description: "API to add badges to users", authors: [Devs.Megu, Devs.Ven, Devs.TheSun], required: true, patches: [ @@ -80,10 +106,10 @@ export default definePlugin({ match: /(?<="aria-label":(\i)\.description,.{0,200})children:/, replace: "children:$1.component?$self.renderBadgeComponent({...$1}) :" }, - // conditionally override their onClick with badge.onClick if it exists + // handle onClick and onContextMenu { match: /href:(\i)\.link/, - replace: "...($1.onClick&&{onClick:vcE=>$1.onClick(vcE,$1)}),$&" + replace: "...$self.getBadgeMouseEventHandlers($1),$&" } ] } @@ -137,6 +163,19 @@ export default definePlugin({ }, { noop: true }), + getBadgeMouseEventHandlers(badge: ProfileBadge & BadgeUserArgs) { + const handlers = {} as Record void>; + + if (!badge) return handlers; // sanity check + + const { onClick, onContextMenu } = badge; + + if (onClick) handlers.onClick = e => onClick(e, badge); + if (onContextMenu) handlers.onContextMenu = e => onContextMenu(e, badge); + + return handlers; + }, + getDonorBadges(userId: string) { return DonorBadges[userId]?.map(badge => ({ image: badge.badge, @@ -148,6 +187,9 @@ export default definePlugin({ transform: "scale(0.9)" // The image is a bit too big compared to default badges } }, + onContextMenu(event, badge) { + ContextMenuApi.openContextMenu(event, () => ); + }, onClick() { const modalKey = openModal(props => ( { @@ -203,6 +245,6 @@ export default definePlugin({ )); }, - })); + } satisfies ProfileBadge)); } });