From 1cf2787f3ae4a1f62bfb46779b697518d0147234 Mon Sep 17 00:00:00 2001 From: cocobo1 Date: Wed, 18 Mar 2026 22:12:52 +0200 Subject: [PATCH] fix: make badges work again --- src/core/plugins/badges/index.tsx | 148 +++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 43 deletions(-) diff --git a/src/core/plugins/badges/index.tsx b/src/core/plugins/badges/index.tsx index 79baa15..7cc5ea6 100644 --- a/src/core/plugins/badges/index.tsx +++ b/src/core/plugins/badges/index.tsx @@ -1,15 +1,43 @@ import { after } from "@lib/api/patcher"; import { onJsxCreate } from "@lib/api/react/jsx"; -import { findByName } from "@metro"; +import { findByName, findByNameLazy } from "@metro"; import { useEffect, useState } from "react"; import { defineCorePlugin } from ".."; +import { FluxDispatcher } from "@metro/common"; -interface BunnyBadge { +interface Badge { label: string; url: string; } -const useBadgesModule = findByName("useBadges", false); +interface CustomBadge { + label: string; + url: string; +} + +interface UserBadgeData { + roles?: string[]; + custom?: CustomBadge[]; +} + +interface BadgeData { + [userId: string]: UserBadgeData; +} + +interface RoleData { + label: string; + url: string; +} + +interface RolesData { + [roleName: string]: RoleData; +} + +const useBadgesModule = findByNameLazy("useBadges", false); + +const badgesCache = new Map(); +const badgeProps = new Map>(); +const pendingRequests = new Set(); export default defineCorePlugin({ manifest: { @@ -26,12 +54,9 @@ export default defineCorePlugin({ }, start() { - let allBadges: { [x: string]: any; } | null = null; - const badgeProps = {} as Record; - onJsxCreate("ProfileBadge", (component, ret) => { - if (ret.props.id?.startsWith("bunny-")) { - const cachedProps = badgeProps[ret.props.id]; + if (ret.props.id?.startsWith("rain-")) { + const cachedProps = badgeProps.get(ret.props.id); if (cachedProps) { ret.props.source = cachedProps.source; ret.props.label = cachedProps.label; @@ -39,54 +64,91 @@ export default defineCorePlugin({ } } }); - + onJsxCreate("RenderedBadge", (component, ret) => { - if (ret.props.id?.startsWith("bunny-")) { - const cachedProps = badgeProps[ret.props.id]; + if (ret.props.id?.startsWith("rain-")) { + const cachedProps = badgeProps.get(ret.props.id); if (cachedProps) { Object.assign(ret.props, cachedProps); } } }); - - after("default", useBadgesModule, ([user], result) => { - const [badges, setBadges] = useState([]); - - useEffect(() => { - if (!user) return; - - if (!allBadges) { - fetch("https://codeberg.org/raincord/badges/raw/branch/main/badges.json") - .then(r => r.json()) - .then(data => { - allBadges = data; - //@ts-expect-error - setBadges(allBadges[user.userId] || []); - }) - } else { - //user has no badges, but maybe they should get some by contributing - setBadges(allBadges[user.userId] || []); + + const fetchAndProcessBadges = async (userId: string) => { + if (pendingRequests.has(userId)) return; + pendingRequests.add(userId); + + try { + const [badgesRes, rolesRes] = await Promise.all([ + fetch("https://codeberg.org/raincord/badges/raw/branch/main/badges.json"), + fetch("https://codeberg.org/raincord/badges/raw/branch/main/assets/roles/roles.json"), + ]); + + const badgesData: BadgeData = await badgesRes.json(); + const rolesData: RolesData = await rolesRes.json(); + + const userBadgeData = badgesData[userId] || { roles: [], custom: [] }; + + const allBadges: Badge[] = []; + + // process role badges + if (userBadgeData.roles) { + userBadgeData.roles.forEach(roleName => { + const roleData = rolesData[roleName]; + if (roleData) { + allBadges.push({ + label: roleData.label, + url: roleData.url, + }); + } + }); } - }, [user?.userId]); - - if (user && badges.length > 0) { - badges.forEach((badge, i) => { - const badgeId = `bunny-${user.userId}-${i}`; - - badgeProps[badgeId] = { + + // process custom badges + if (userBadgeData.custom) { + allBadges.push(...userBadgeData.custom); + } + + badgesCache.set(userId, allBadges); + + allBadges.forEach((badge, i) => { + const badgeId = `rain-${userId}-${i}`; + badgeProps.set(badgeId, { id: badgeId, source: { uri: badge.url }, label: badge.label, - userId: user.userId, - }; - - result.push({ - id: badgeId, - description: badge.label, - icon: " _", + userId, }); }); + + FluxDispatcher.dispatch({ type: "USER_UPDATE", user: { id: userId } }); + } finally { + pendingRequests.delete(userId); } + }; + + after("default", useBadgesModule, ([user], result) => { + if (!user) return; + + const userId = user.userId; + const cached = badgesCache.get(userId); + + if (!cached) { + if (!pendingRequests.has(userId)) { + fetchAndProcessBadges(userId); + } + return; + } + + cached.forEach((badge, i) => { + const badgeId = `rain-${userId}-${i}`; + + result.unshift({ + id: badgeId, + description: badge.label, + icon: " _", + }); + }); }); } }); \ No newline at end of file