From 02e9bf6b19c621df3f356320e88ff1db82f1b94e Mon Sep 17 00:00:00 2001 From: creations Date: Wed, 1 Oct 2025 10:39:10 -0400 Subject: [PATCH] move globalbadges api (#429) * move globalbadges api * Update API URL for badge images * bleh * bleh * bleh: the reckoning * bleh: the final remorse --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> --- .../globalBadges/badgeComponent.tsx | 73 ++++++ .../globalBadges/badgeModal.tsx | 68 ++++++ src/equicordplugins/globalBadges/index.tsx | 208 +----------------- src/equicordplugins/globalBadges/settings.ts | 77 +++++++ src/equicordplugins/globalBadges/types.ts | 16 ++ src/equicordplugins/globalBadges/utils.ts | 58 +++++ 6 files changed, 301 insertions(+), 199 deletions(-) create mode 100644 src/equicordplugins/globalBadges/badgeComponent.tsx create mode 100644 src/equicordplugins/globalBadges/badgeModal.tsx create mode 100644 src/equicordplugins/globalBadges/settings.ts create mode 100644 src/equicordplugins/globalBadges/types.ts create mode 100644 src/equicordplugins/globalBadges/utils.ts diff --git a/src/equicordplugins/globalBadges/badgeComponent.tsx b/src/equicordplugins/globalBadges/badgeComponent.tsx new file mode 100644 index 000000000..b7c4e5f7f --- /dev/null +++ b/src/equicordplugins/globalBadges/badgeComponent.tsx @@ -0,0 +1,73 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { React, Tooltip, UserStore } from "@webpack/common"; +import { JSX } from "react"; + +import { BadgeModalComponent, openBadgeModal } from "./badgeModal"; +import { settings } from "./settings"; +import { BadgeCache } from "./types"; +import { fetchBadges, serviceMap } from "./utils"; + +export let badgeImages; + +export const BadgeComponent = ({ name, img }: { name: string, img: string; }) => { + return ( + + {(tooltipProps: any) => ( + + )} + + ); +}; + +export const GlobalBadges = ({ userId }: { userId: string; }) => { + const [badges, setBadges] = React.useState({}); + React.useEffect(() => setBadges(fetchBadges(userId) ?? {}), [userId]); + + if (!badges) return null; + const globalBadges: JSX.Element[] = []; + const badgeModal: JSX.Element[] = []; + + Object.keys(badges).forEach(mod => { + if (!badges[mod] || !Array.isArray(badges[mod]) || badges[mod].length === 0) return; + + badges[mod].forEach(badge => { + if (!badge || !badge.tooltip || !badge.badge) return; + + const modDisplay = serviceMap[mod.toLowerCase()] || mod; + const prefix = settings.store.showPrefix ? `(${modDisplay})` : ""; + const suffix = settings.store.showSuffix ? `(${modDisplay})` : ""; + const displayName = `${prefix} ${badge.tooltip} ${suffix}`; + + if (mod.toLowerCase() === "badgevault") { + badge.custom = true; + } + + globalBadges.push(); + badgeModal.push(); + }); + }); + badgeImages = badgeModal; + + return ( +
openBadgeModal(UserStore.getUser(userId))} + > + {globalBadges} +
+ ); +}; diff --git a/src/equicordplugins/globalBadges/badgeModal.tsx b/src/equicordplugins/globalBadges/badgeModal.tsx new file mode 100644 index 000000000..e2682ffde --- /dev/null +++ b/src/equicordplugins/globalBadges/badgeModal.tsx @@ -0,0 +1,68 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { ModalContent, ModalRoot, openModal } from "@utils/modal"; +import { User } from "@vencord/discord-types"; +import { Forms, React, Tooltip } from "@webpack/common"; + +import { badgeImages } from "./badgeComponent"; +import { cl } from "./utils"; + +export const BadgeModalComponent = ({ name, img }: { name: string, img: string; }) => { + return ( + + {(tooltipProps: any) => ( + + )} + + ); +}; + +export function BadgeModal({ user }: { user: User; }) { + return ( + <> +
+ + {user.username} +
+ {badgeImages.length ? ( + + {user.username} has {badgeImages.length} global badges. + + ) : ( + + {user.username} has no global badges. + + )} + {!!badgeImages.length && ( +
+ {badgeImages} +
+ )} + + ); +} + +export function openBadgeModal(user: User) { + openModal(modalprops => + + + + + + + + ); +} diff --git a/src/equicordplugins/globalBadges/index.tsx b/src/equicordplugins/globalBadges/index.tsx index 9be95b77c..68bdb475b 100644 --- a/src/equicordplugins/globalBadges/index.tsx +++ b/src/equicordplugins/globalBadges/index.tsx @@ -1,126 +1,17 @@ /* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ import { addProfileBadge, BadgePosition, ProfileBadge, removeProfileBadge } from "@api/Badges"; -import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; -import ErrorBoundary from "@components/ErrorBoundary"; import { Devs, EquicordDevs } from "@utils/constants"; -import { ModalContent, ModalRoot, openModal } from "@utils/modal"; -import definePlugin, { OptionType } from "@utils/types"; -import { User } from "@vencord/discord-types"; -import { Forms, React, Tooltip, UserStore } from "@webpack/common"; -import { JSX } from "react"; +import definePlugin from "@utils/types"; +import { React } from "@webpack/common"; -type CustomBadge = string | { - name: string; - badge: string; - custom?: boolean; -}; - -interface BadgeCache { - badges: { [mod: string]: CustomBadge[]; }; - expires: number; -} - -let badgeImages; - -// const API_URL = "https://clientmodbadges-api.herokuapp.com/"; -const API_URL = "https://globalbadges.equicord.org/"; -const cache = new Map(); -const EXPIRES = 1000 * 60 * 15; - -const fetchBadges = (id: string): BadgeCache["badges"] | undefined => { - const cachedValue = cache.get(id); - if (!cache.has(id) || (cachedValue && cachedValue.expires < Date.now())) { - fetch(`${API_URL}users/${id}`) - .then(res => res.json() as Promise) - .then(body => { - cache.set(id, { badges: body, expires: Date.now() + EXPIRES }); - return body; - }) - .catch(() => null); - } else if (cachedValue) { - return cachedValue.badges; - } -}; - -const BadgeComponent = ({ name, img }: { name: string, img: string; }) => { - return ( - - {(tooltipProps: any) => ( - - )} - - ); -}; - -const GlobalBadges = ({ userId }: { userId: string; }) => { - const [badges, setBadges] = React.useState({}); - React.useEffect(() => setBadges(fetchBadges(userId) ?? {}), [userId]); - - if (!badges) return null; - const globalBadges: JSX.Element[] = []; - const badgeModal: JSX.Element[] = []; - - Object.keys(badges).forEach(mod => { - if (mod.toLowerCase() === "vencord") return; - if (mod.toLowerCase() === "equicord") return; - if (mod.toLowerCase() === "badgevault" && !settings.store.showCustom) return; - badges[mod].forEach(badge => { - if (typeof badge === "string") { - const fullNames = { "hunter": "Bug Hunter", "early": "Early User" }; - badge = { - name: fullNames[badge as string] ? fullNames[badge as string] : badge, - badge: `${API_URL}badges/${mod}/${(badge as string).replace(mod, "").trim().split(" ")[0]}` - }; - } else if (typeof badge === "object") badge.custom = true; - const cleanName = badge.name.replace(mod, "").trim(); - const prefix = settings.store.showPrefix ? mod : ""; - if (!badge.custom) badge.name = `${prefix} ${cleanName.charAt(0).toUpperCase() + cleanName.slice(1)}`; - if (badge.custom) { - if (cleanName.toLowerCase().includes(mod)) return; - else if (prefix) badge.name = `${cleanName} (${prefix})`.replaceAll(" ()", ""); - } - globalBadges.push(); - badgeModal.push(); - }); - }); - badgeImages = badgeModal; - - return ( -
openBadgeModal(UserStore.getUser(userId))} - > - {globalBadges} -
- ); -}; +import { GlobalBadges } from "./badgeComponent"; +import { settings } from "./settings"; +import { fetchBadges } from "./utils"; const Badge: ProfileBadge = { component: b => , @@ -129,21 +20,6 @@ const Badge: ProfileBadge = { key: "GlobalBadges" }; -const settings = definePluginSettings({ - showPrefix: { - type: OptionType.BOOLEAN, - description: "Shows the Mod as Prefix", - default: true, - restartNeeded: false - }, - showCustom: { - type: OptionType.BOOLEAN, - description: "Show Custom Badges", - default: true, - restartNeeded: false - } -}); - export default definePlugin({ name: "GlobalBadges", description: "Adds global badges from other client mods", @@ -152,69 +28,3 @@ export default definePlugin({ start: () => addProfileBadge(Badge), stop: () => removeProfileBadge(Badge), }); - -/* -Badge duping fix for modal lines below -L39 the value for everything below -L81 for not reusing globalbadges const -L100 for the size of the badges -L103 actual dupe fix -L109 is when clicking the badge open the modal -Everything below is related to the badge modal -*/ -const cl = classNameFactory("vc-author-modal-"); - -const BadgeModalComponent = ({ name, img }: { name: string, img: string; }) => { - return ( - - {(tooltipProps: any) => ( - - )} - - ); -}; - -function BadgeModal({ user }: { user: User; }) { - return ( - <> -
- - {user.username} -
- {badgeImages.length ? ( - - {user.username} has {badgeImages.length} global badges. - - ) : ( - - {user.username} has no global badges. - - )} - {!!badgeImages.length && ( -
- {badgeImages} -
- )} - - ); -} - -function openBadgeModal(user: User) { - openModal(modalprops => - - - - - - - - ); -} diff --git a/src/equicordplugins/globalBadges/settings.ts b/src/equicordplugins/globalBadges/settings.ts new file mode 100644 index 000000000..a8c37f5e3 --- /dev/null +++ b/src/equicordplugins/globalBadges/settings.ts @@ -0,0 +1,77 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { OptionType } from "@utils/types"; + +export const settings = definePluginSettings({ + showPrefix: { + type: OptionType.BOOLEAN, + description: "Shows the Mod as Prefix", + default: true, + restartNeeded: false + }, + showSuffix: { + type: OptionType.BOOLEAN, + description: "Shows the Mod as Suffix", + default: false, + restartNeeded: false + }, + showCustom: { + type: OptionType.BOOLEAN, + description: "Show Custom Badges", + default: true, + restartNeeded: false + }, + showNekocord: { + type: OptionType.BOOLEAN, + description: "Show Nekocord Badges", + default: true, + restartNeeded: false + }, + showReviewDB: { + type: OptionType.BOOLEAN, + description: "Show ReviewDB Badges", + default: true, + restartNeeded: false + }, + showAero: { + type: OptionType.BOOLEAN, + description: "Show Aero Badges", + default: true, + restartNeeded: false + }, + showAliucord: { + type: OptionType.BOOLEAN, + description: "Show Aliucord Badges", + default: true, + restartNeeded: false + }, + showRa1ncord: { + type: OptionType.BOOLEAN, + description: "Show Ra1ncord Badges", + default: true, + restartNeeded: false + }, + showVelocity: { + type: OptionType.BOOLEAN, + description: "Show Velocity Badges", + default: true, + restartNeeded: false + }, + showEnmity: { + type: OptionType.BOOLEAN, + description: "Show Enmity Badges", + default: true, + restartNeeded: false + }, + showReplugged: { + type: OptionType.BOOLEAN, + description: "Show Replugged Badges", + default: true, + restartNeeded: false + } +}); diff --git a/src/equicordplugins/globalBadges/types.ts b/src/equicordplugins/globalBadges/types.ts new file mode 100644 index 000000000..fc06b9265 --- /dev/null +++ b/src/equicordplugins/globalBadges/types.ts @@ -0,0 +1,16 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export type CustomBadge = { + tooltip: string; + badge: string; + custom?: boolean; +}; + +export interface BadgeCache { + badges: { [mod: string]: CustomBadge[]; }; + expires: number; +} diff --git a/src/equicordplugins/globalBadges/utils.ts b/src/equicordplugins/globalBadges/utils.ts new file mode 100644 index 000000000..6c715b5d9 --- /dev/null +++ b/src/equicordplugins/globalBadges/utils.ts @@ -0,0 +1,58 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { classNameFactory } from "@api/Styles"; + +import { settings } from "./settings"; +import { BadgeCache } from "./types"; + +const API_URL = "https://badges.equicord.org/"; +const cache = new Map(); +const EXPIRES = 1000 * 60 * 15; + +export const cl = classNameFactory("vc-author-modal-"); +export const serviceMap = { + "nekocord": "Nekocord", + "reviewdb": "ReviewDB", + "aero": "Aero", + "aliucord": "Aliucord", + "ra1ncord": "Ra1ncord", + "velocity": "Velocity", + "enmity": "Enmity", + "replugged": "Replugged", + "badgevault": "BadgeVault" +}; + +export const fetchBadges = (id: string): BadgeCache["badges"] | undefined => { + const cachedValue = cache.get(id); + if (!cache.has(id) || (cachedValue && cachedValue.expires < Date.now())) { + const services: string[] = []; + if (settings.store.showNekocord) services.push("nekocord"); + if (settings.store.showReviewDB) services.push("reviewdb"); + if (settings.store.showAero) services.push("aero"); + if (settings.store.showAliucord) services.push("aliucord"); + if (settings.store.showRa1ncord) services.push("ra1ncord"); + if (settings.store.showVelocity) services.push("velocity"); + if (settings.store.showEnmity) services.push("enmity"); + if (settings.store.showReplugged) services.push("replugged"); + if (settings.store.showCustom) services.push("badgevault"); + + if (services.length === 0) { + cache.set(id, { badges: {}, expires: Date.now() + EXPIRES }); + return {}; + } + + fetch(`${API_URL}${id}?seperated=true&services=${services.join(",")}`) + .then(res => res.json() as Promise<{ status: number; badges: BadgeCache["badges"]; }>) + .then(body => { + cache.set(id, { badges: body.badges, expires: Date.now() + EXPIRES }); + return body.badges; + }) + .catch(() => null); + } else if (cachedValue) { + return cachedValue.badges; + } +};