diff --git a/src/api/Badges.ts b/src/api/Badges.ts index 096f5206f..43db6c70c 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,23 +78,44 @@ 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); const equicordDonorBadges = BadgeAPIPlugin.getEquicordDonorBadges(args.userId); - if (donorBadges) badges.unshift(...donorBadges); - if (equicordDonorBadges) badges.unshift(...equicordDonorBadges); + if (donorBadges) { + badges.unshift( + ...donorBadges.map(badge => ({ + ...args, + ...badge, + })) + ); + } + + if (equicordDonorBadges) { + badges.unshift( + ...equicordDonorBadges.map(badge => ({ + ...args, + ...badge, + })) + ); + } return badges; } diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index 3e09fc21c..f4f407b7d 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -24,10 +24,10 @@ import { openContributorModal } from "@components/settings/tabs"; import { isEquicordDonor } from "@components/settings/tabs/vencord"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; -import { shouldShowContributorBadge, shouldShowEquicordContributorBadge } from "@utils/misc"; +import { copyWithToast, shouldShowContributorBadge, shouldShowEquicordContributorBadge } from "@utils/misc"; import definePlugin from "@utils/types"; import { User } from "@vencord/discord-types"; -import { Toasts, UserStore } from "@webpack/common"; +import { ContextMenuApi, Menu, Toasts, UserStore } from "@webpack/common"; import { EquicordDonorModal, VencordDonorModal } from "./modals"; @@ -85,9 +85,34 @@ async function loadAllBadges(noCache = false) { let intervalId: any; +function BadgeContextMenu({ badge }: { badge: ProfileBadge & BadgeUserArgs; }) { + 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: [ @@ -109,10 +134,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),$&" } ] }, @@ -176,6 +201,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, @@ -187,10 +225,13 @@ 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() { return VencordDonorModal(); }, - })); + } satisfies ProfileBadge)); }, getEquicordDonorBadges(userId: string) { @@ -204,9 +245,12 @@ 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() { return EquicordDonorModal(); }, - })); + } satisfies ProfileBadge)); } }); diff --git a/src/plugins/alwaysAnimate/index.ts b/src/plugins/alwaysAnimate/index.ts index e7d94d55b..e37d67c00 100644 --- a/src/plugins/alwaysAnimate/index.ts +++ b/src/plugins/alwaysAnimate/index.ts @@ -68,6 +68,14 @@ export default definePlugin({ return m; } } + }, + { + // Nameplates + find: ".MINI_PREVIEW,[", + replacement: { + match: /animate:\i,loop:/, + replace: "animate:true,loop:true,_loop:" + } } ] }); diff --git a/src/plugins/noTypingAnimation/index.ts b/src/plugins/noTypingAnimation/index.ts index d4aab9004..74bcdd50e 100644 --- a/src/plugins/noTypingAnimation/index.ts +++ b/src/plugins/noTypingAnimation/index.ts @@ -11,11 +11,13 @@ export default definePlugin({ name: "NoTypingAnimation", authors: [Devs.AutumnVN], description: "Disables the CPU-intensive typing dots animation", - patches: [{ - find: "dotCycle", - replacement: { - match: /document.hasFocus\(\)/, - replace: "false" + patches: [ + { + find: "dotCycle", + replacement: { + match: /focused:(\i)/g, + replace: (_, focused) => `_focused:${focused}=false` + } } - }] + ] }); diff --git a/src/plugins/translate/index.tsx b/src/plugins/translate/index.tsx index 363897d1b..1bcc8b3fa 100644 --- a/src/plugins/translate/index.tsx +++ b/src/plugins/translate/index.tsx @@ -21,6 +21,7 @@ import "./styles.css"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; +import { Message } from "@vencord/discord-types"; import { ChannelStore, Menu } from "@webpack/common"; import { settings } from "./settings"; @@ -28,8 +29,9 @@ import { setShouldShowTranslateEnabledTooltip, TranslateChatBarIcon, TranslateIc import { handleTranslate, TranslationAccessory } from "./TranslationAccessory"; import { translate } from "./utils"; -const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) => { - if (!message.content) return; +const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => { + const content = getMessageContent(message); + if (!content) return; const group = findGroupChildrenByChildId("copy-text", children); if (!group) return; @@ -40,13 +42,23 @@ const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) => label="Translate" icon={TranslateIcon} action={async () => { - const trans = await translate("received", message.content); + const trans = await translate("received", content); handleTranslate(message.id, trans); }} /> )); }; + +function getMessageContent(message: Message) { + // Message snapshots is an array, which allows for nested snapshots, which Discord does not do yet. + // no point collecting content or rewriting this to render in a certain way that makes sense + // for something currently impossible. + return message.content + || message.messageSnapshots?.[0]?.message.content + || message.embeds?.find(embed => embed.type === "auto_moderation_message")?.rawDescription || ""; +} + let tooltipTimeout: any; export default definePlugin({ @@ -64,8 +76,9 @@ export default definePlugin({ renderChatBarButton: TranslateChatBarIcon, - renderMessagePopoverButton(message) { - if (!message.content) return null; + renderMessagePopoverButton(message: Message) { + const content = getMessageContent(message); + if (!content) return null; return { label: "Translate", @@ -73,7 +86,7 @@ export default definePlugin({ message, channel: ChannelStore.getChannel(message.channel_id), onClick: async () => { - const trans = await translate("received", message.content); + const trans = await translate("received", content); handleTranslate(message.id, trans); } };