feat(coreplugins): add badges

This commit is contained in:
pylixonly 2024-10-09 22:15:39 +08:00
parent ed52f332da
commit e3df56dc58
5 changed files with 105 additions and 2 deletions

View file

@ -0,0 +1,60 @@
import { addJSXCallback } from "@lib/api/jsx";
import { after } from "@lib/api/patcher";
import { findByName } from "@metro";
import { FC, useEffect, useState } from "react";
import { ImageSourcePropType, ViewStyle } from "react-native";
import { defineCorePlugin } from "..";
interface BunnyBadge {
label: string;
url: string;
}
const useBadgesModule = findByName("useBadges", false);
export default defineCorePlugin({
manifest: {
id: "bunny.badges",
name: "Badges",
version: "1.0.0",
description: "Adds badges to user's profile",
authors: [{ name: "pylixonly" }]
},
start() {
const propHolder = {} as Record<string, any>;
const badgeCache = {} as Record<string, BunnyBadge[]>;
addJSXCallback("RenderedBadge", (_, ret) => {
if (ret.props.id.match(/bunny-\d+-\d+/)) {
Object.assign(ret.props, propHolder[ret.props.id]);
}
});
after("default", useBadgesModule, ([user], r) => {
const [badges, setBadges] = useState<BunnyBadge[]>(user ? badgeCache[user.userId] ??= [] : []);
useEffect(() => {
if (user) {
fetch(`https://raw.githubusercontent.com/pyoncord/badges/refs/heads/main/${user.userId}.json`)
.then(r => r.json())
.then(badges => setBadges(badgeCache[user.userId] = badges));
}
}, [user]);
badges.forEach((badges, i) => {
propHolder[`bunny-${user.userId}-${i}`] = {
source: { uri: badges.url },
id: `bunny-${i}`,
label: badges.label
};
r.push({
id: `bunny-${user.userId}-${i}`,
description: badges.label,
icon: "2ba85e8026a8614b640c2837bcdfe21b",
});
});
});
}
});

View file

@ -7,7 +7,8 @@ interface CorePlugin {
// Called from @lib/plugins
export const getCorePlugins = (): Record<string, CorePlugin> => ({
"bunny.quickinstall": require("./quickinstall")
"bunny.quickinstall": require("./quickinstall"),
"bunny.badges": require("./badges")
});
/**

View file

@ -10,7 +10,7 @@ export default defineCorePlugin({
name: "QuickInstall",
version: "1.0.0",
description: "Quickly install Vendetta plugins and themes",
authors: ["pyoncord"]
authors: [{ name: "Vendetta Team" }]
},
start() {
patches = [patchForumPost(), patchUrl()];

View file

@ -6,6 +6,7 @@ import { VdPluginManager } from "@core/vendetta/plugins";
import { patchCommands } from "@lib/api/commands";
import { patchLogHook } from "@lib/api/debug";
import { injectFluxInterceptor } from "@lib/api/flux";
import { patchJSX } from "@lib/api/jsx";
import { removeFile, writeFile } from "@lib/api/native/fs";
import { isPyonLoader, isThemeSupported } from "@lib/api/native/loader";
import { FileManager } from "@lib/api/native/modules";
@ -42,6 +43,7 @@ export default async () => {
patchLogHook(),
patchCommands(),
patchChatBackground(),
patchJSX(),
initVendettaObject(),
initFetchI18nStrings(),
initSettings(),

40
src/lib/api/jsx/index.ts Normal file
View file

@ -0,0 +1,40 @@
import { after } from "@lib/api/patcher";
import { findByPropsLazy } from "@metro";
type Callback = (Component: any, ret: JSX.Element) => void;
const callbacks = new Map<string, Callback[]>();
const jsxRuntime = findByPropsLazy("jsx", "jsxs");
export function addJSXCallback(Component: string, callback: Callback) {
if (!callbacks.has(Component)) callbacks.set(Component, []);
callbacks.get(Component)!.push(callback);
}
export function removeJSXCallback(Component: string, callback: Callback) {
if (!callbacks.has(Component)) return;
const cbs = callbacks.get(Component)!;
cbs.splice(cbs.indexOf(callback), 1);
if (cbs.length === 0) callbacks.delete(Component);
}
/**
* @internal
*/
export function patchJSX() {
// Only a simple name check for now
const callback = ([Component, props]: any[], ret: any) => {
if (typeof Component === "function" && callbacks.has(Component.name)) {
const cbs = callbacks.get(Component.name)!;
for (const cb of cbs) ret = cb(Component, ret);
return ret;
}
};
const patches = [
after("jsx", jsxRuntime, callback),
after("jsxs", jsxRuntime, callback)
];
return () => patches.forEach(unpatch => unpatch());
}