diff --git a/src/plugins/showBadgesInChat/README.md b/src/plugins/showBadgesInChat/README.md new file mode 100644 index 0000000000..86daa1e2e3 --- /dev/null +++ b/src/plugins/showBadgesInChat/README.md @@ -0,0 +1,24 @@ +# ShowBadgesInChat + +Shows profile badges in chat. The badges include all built-in Discord badges. It also proudly displays your Vencord donor/contributor badges. + +--- + +The settings editor lets you: + +- Drag to reorder +- Click to enable/disable a specific badge type +- Hover over a badge for the tool-tip about what badges it represents + +https://github.com/Vendicated/Vencord/assets/119569726/c7543516-f691-42c5-baa2-68dbda96d46a + +![](https://github.com/Vendicated/Vencord/assets/119569726/ca736d0d-c09a-4737-9cfa-4fc88cfc91ae) + +--- + +All badges can fit easily, for example here's Vencord's creator: Vee (vending.machine): +![](https://github.com/Vendicated/Vencord/assets/119569726/fc768079-5027-482b-ac52-a652812d482d) + +--- + +_Discord currently has a small bug where an author who isn't the current user will return their premiumType as undefined. There is nothing we can do about this until Discord fixes this bug_ diff --git a/src/plugins/showBadgesInChat/index.tsx b/src/plugins/showBadgesInChat/index.tsx new file mode 100644 index 0000000000..1a708637cf --- /dev/null +++ b/src/plugins/showBadgesInChat/index.tsx @@ -0,0 +1,129 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { addDecoration, removeDecoration } from "@api/MessageDecorations"; +import { Devs } from "@utils/constants"; +import { isPluginDev } from "@utils/misc"; +import definePlugin from "@utils/types"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import badges from "plugins/_api/badges"; +const roleIconClassName = findByPropsLazy("roleIcon", "separator").roleIcon; +const RoleIconComponent = findComponentByCodeLazy("#{intl::ROLE_ICON_ALT_TEXT}"); +import "./styles.css"; + +import { User } from "discord-types/general"; +import { JSX } from "react"; + +import settings from "./settings"; + +const discordBadges: readonly [number, string, string][] = Object.freeze([ + [0, "Discord Staff", "5e74e9b61934fc1f67c65515d1f7e60d"], + [1, "Partnered Server Owner", "3f9748e53446a137a052f3454e2de41e"], + [2, "HypeSquad Events", "bf01d1073931f921909045f3a39fd264"], + [6, "HypeSquad Bravery", "8a88d63823d8a71cd5e390baa45efa02"], + [7, "HypeSquad Brilliance", "011940fd013da3f7fb926e4a1cd2e618"], + [8, "HypeSquad Balance", "3aa41de486fa12454c3761e8e223442e"], + [3, "Discord Bug Hunter", "2717692c7dca7289b35297368a940dd0"], + [14, "Discord Bug Hunter", "848f79194d4be5ff5f81505cbd0ce1e6"], + [22, "Active Developer", "6bdc42827a38498929a4920da12695d9"], + [17, "Early Verified Bot Developer", "6df5892e0f35b051f8b61eace34f4967"], + [9, "Early Supporter", "7060786766c9c840eb3019e725d2b358"], + [18, "Moderator Programs Alumni", "fee1624003e2fee35cb398e125dc479b"] +]); + +function CheckBadge({ badge, author }: { badge: string; author: User; }): JSX.Element | null { + + switch (badge) { + case "VencordDonor": + return ( + + {badges.getDonorBadges(author.id)?.map(badge => ( + + + ))} + + ); + case "VencordContributer": + return isPluginDev(author.id) ? ( + + + + ) : null; + case "DiscordProfile": + const chatBadges = discordBadges + .filter(badge => (author.flags || author.publicFlags) & (1 << badge[0])) + + .map(badge => ( + + + )); + return chatBadges.length > 0 ? ( + + {chatBadges} + + ) : null; + case "DiscordNitro": + return (author?.premiumType ?? 0) > 0 ? ( + + + + ) : null; + default: + return null; + } +} + +function ChatBadges({ author }: { author: User; }) { + + return ( + + {settings.store.showVencordDonor && } + {settings.store.showVencordContributor && } + {settings.store.showDiscordProfile && } + {settings.store.showDiscordNitro && } + + ); +} + +export default definePlugin({ + name: "ShowBadgesInChat", + authors: [Devs.Inbestigator, Devs.KrystalSkull], + description: "Shows the message author's badges beside their name in chat.", + dependencies: ["MessageDecorationsAPI"], + settings, + start: () => { + addDecoration("vc-show-badges-in-chat", props => props.message?.author ? : null); + + }, + stop: () => { + removeDecoration("vc-show-badges-in-chat"); + } +}); diff --git a/src/plugins/showBadgesInChat/settings.tsx b/src/plugins/showBadgesInChat/settings.tsx new file mode 100644 index 0000000000..a1446b6c5f --- /dev/null +++ b/src/plugins/showBadgesInChat/settings.tsx @@ -0,0 +1,163 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { OptionType } from "@utils/types"; +import { Text, useEffect, UserStore, useState } from "@webpack/common"; + +const settings = definePluginSettings({ + showVencordDonor: { + type: OptionType.BOOLEAN, + description: "Enable to show Vencord donor badges in chat.", + hidden: true, + default: true + }, + VencordDonorPosition: { + type: OptionType.NUMBER, + description: "The position of the Vencord Donor badges.", + hidden: true, + default: 0 + }, + showVencordContributor: { + type: OptionType.BOOLEAN, + description: "Enable to show Vencord contributor badges in chat.", + hidden: true, + default: true + }, + VencordContributorPosition: { + type: OptionType.NUMBER, + description: "The position of the Vencord Contributor badge.", + hidden: true, + default: 1 + }, + showDiscordProfile: { + type: OptionType.BOOLEAN, + description: "Enable to show Discord profile badges in chat.", + hidden: true, + default: true + }, + DiscordProfilePosition: { + type: OptionType.NUMBER, + description: "The position of the Discord profile badges.", + hidden: true, + default: 2 + }, + showDiscordNitro: { + type: OptionType.BOOLEAN, + description: "Enable to show Discord Nitro badges in chat.", + hidden: true, + default: true + }, + DiscordNitroPosition: { + type: OptionType.NUMBER, + description: "The position of the Discord Nitro badge.", + hidden: true, + default: 3 + }, + badgeSettings: { + type: OptionType.COMPONENT, + description: "Setup badge layout and visibility", + component: () => + } +}); + +export default settings; + +const BadgeSettings = () => { + const [images, setImages] = useState([ + { src: "https://cdn.discordapp.com/emojis/1026533070955872337.png", shown: settings.store.showVencordDonor, title: "Vencord donor badges", key: "VencordDonor", position: settings.store.VencordDonorPosition }, + { src: "https://vencord.dev/assets/favicon.png", shown: settings.store.showVencordContributor, title: "Vencord contributor badge", key: "VencordContributer", position: settings.store.VencordContributorPosition }, + { src: "https://cdn.discordapp.com/badge-icons/bf01d1073931f921909045f3a39fd264.png", shown: settings.store.showDiscordProfile, title: "Discord profile badges (HypeSquad, Discord Staff, Active Developer, etc.)", key: "DiscordProfile", position: settings.store.DiscordProfilePosition }, + { src: "https://cdn.discordapp.com/badge-icons/2ba85e8026a8614b640c2837bcdfe21b.png", shown: settings.store.showDiscordNitro, title: "Nitro badge", key: "DiscordNitro", position: settings.store.DiscordNitroPosition } + ]); + + useEffect(() => { + images.forEach(image => { + switch (image.key) { + case "VencordDonor": + settings.store.VencordDonorPosition = image.position; + settings.store.showVencordDonor = image.shown; + break; + case "VencordContributer": + settings.store.VencordContributorPosition = image.position; + settings.store.showVencordContributor = image.shown; + break; + case "DiscordProfile": + settings.store.DiscordProfilePosition = image.position; + settings.store.showDiscordProfile = image.shown; + break; + case "DiscordNitro": + settings.store.DiscordNitroPosition = image.position; + settings.store.showDiscordNitro = image.shown; + break; + default: + break; + } + }); + }, [images]); + + const handleDragStart = (e: any, index: number) => { + if (!images[index].shown) { + e.preventDefault(); + } else { + e.dataTransfer.setData("index", index); + } + }; + + const handleDragOver = e => { + e.preventDefault(); + }; + + const handleDrop = (e: any, dropIndex: number) => { + const dragIndex = e.dataTransfer.getData("index"); + const newImages = [...images]; + const draggedImage = newImages[dragIndex]; + + newImages.splice(dragIndex, 1); + newImages.splice(dropIndex, 0, draggedImage); + + newImages.forEach((image, index) => { + image.position = index; + }); + + setImages(newImages); + }; + + const toggleDisable = (index: number) => { + const newImages = [...images]; + newImages[index].shown = !newImages[index].shown; + setImages(newImages); + }; + + return ( + <> + Drag the badges to reorder them, you can click to enable/disable a specific badge type. +
+ + {(UserStore.getCurrentUser() as any).globalName} + {images + .sort((a, b) => a.position - b.position) + .map((image, index) => ( +
handleDragOver(e)} + onDrop={e => handleDrop(e, index)} + onClick={() => toggleDisable(index)} + > + handleDragStart(e, index)} + title={image.title} + /> +
+ )) + } +
+ + ); +}; diff --git a/src/plugins/showBadgesInChat/styles.css b/src/plugins/showBadgesInChat/styles.css new file mode 100644 index 0000000000..6507e9c5a1 --- /dev/null +++ b/src/plugins/showBadgesInChat/styles.css @@ -0,0 +1,41 @@ +.vc-sbic-badge-settings { + display: flex; + gap: 5px; + flex-direction: row; +} + +.vc-sbic-image-container { + position: relative; + transition: 0.15s; +} + +.vc-sbic-image-container img { + width: 25px; + height: 25px; + cursor: pointer; +} + +.vc-sbic-disabled { + opacity: 0.5; + scale: 0.95; +} + +.vc-sbic-settings-avatar { + width: 50px; + height: 50px; + border-radius: 50%; + margin-right: 12px; +} + +.vc-sbic-settings-username { + font-size: 22px; + color: white; + margin-right: 5px; +} + +.vc-sbic-badge-row { + display: inline-flex; + margin-left: 2; + vertical-align: top; + height: 1rem; +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e758259125..3fa187e46d 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -579,6 +579,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "jamesbt365", id: 158567567487795200n, }, + KrystalSkull: { + name: "KrystalSkullOfficial", + id: 929208515883569182n + }, } satisfies Record); // iife so #__PURE__ works correctly