add context menu options to vencord badges

This commit is contained in:
Vendicated 2025-08-31 05:18:40 +02:00
parent 8807564053
commit 17b90beee1
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
2 changed files with 75 additions and 18 deletions

View file

@ -34,7 +34,9 @@ export interface ProfileBadge {
image?: string;
link?: string;
/** Action to perform when you click the badge */
onClick?(event: React.MouseEvent<HTMLButtonElement, 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;
}

View file

@ -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 (
<Menu.Menu
navId="vc-badge-context"
onClose={ContextMenuApi.closeContextMenu}
aria-label="Badge Options"
>
{badge.description && (
<Menu.MenuItem
id="vc-badge-copy-name"
label="Copy Badge Name"
action={() => copyWithToast(badge.description!)}
/>
)}
{badge.image && (
<Menu.MenuItem
id="vc-badge-copy-link"
label="Copy Badge Image Link"
action={() => copyWithToast(badge.image!)}
/>
)}
</Menu.Menu>
);
}
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<string, (e: React.MouseEvent) => 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, () => <BadgeContextMenu badge={badge} />);
},
onClick() {
const modalKey = openModal(props => (
<ErrorBoundary noop onError={() => {
@ -203,6 +245,6 @@ export default definePlugin({
</ErrorBoundary>
));
},
}));
} satisfies ProfileBadge));
}
});