GlobalBadges & UserPFP: Update Modals & Settings

This commit is contained in:
thororen1234 2025-11-25 11:09:28 -05:00
parent 832ac62f8e
commit 078914a40f
No known key found for this signature in database
9 changed files with 148 additions and 95 deletions

View file

@ -26,8 +26,6 @@ import { PluginCard } from "./PluginCard";
const cl = classNameFactory("vc-author-modal-");
type PluginFilter = "all" | "equicord" | "vencord" | "modified" | "user";
export function openContributorModal(user: User) {
openModal(modalProps =>
<ModalRoot {...modalProps}>

View file

@ -7,12 +7,17 @@
import { React, Tooltip, UserStore } from "@webpack/common";
import { JSX } from "react";
import { BadgeModalComponent, openBadgeModal } from "./badgeModal";
import { openBadgeModal } from "./badgeModal";
import { settings } from "./settings";
import { BadgeCache } from "./types";
import { cl, fetchBadges, serviceMap } from "./utils";
import { cl, fetchBadges } from "./utils";
export let badgeImages;
interface BadgeModalItem {
name: string;
rawName: string;
}
export let badgeData;
export const BadgeComponent = ({ name, img }: { name: string, img: string; }) => {
return (
@ -38,7 +43,7 @@ export const GlobalBadges = ({ userId }: { userId: string; }) => {
if (!badges) return null;
const globalBadges: JSX.Element[] = [];
const badgeModal: JSX.Element[] = [];
const badgeModal: BadgeModalItem[] = [];
Object.keys(badges).forEach(mod => {
if (!badges[mod] || !Array.isArray(badges[mod]) || badges[mod].length === 0) return;
@ -46,20 +51,23 @@ export const GlobalBadges = ({ userId }: { userId: string; }) => {
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 prefix = settings.store.showPrefix ? `(${mod})` : "";
const suffix = settings.store.showSuffix ? `(${mod})` : "";
const displayName = `${prefix} ${badge.tooltip} ${suffix}`;
if (mod.toLowerCase() === "badgevault") {
if (mod === "BadgeVault") {
badge.custom = true;
}
globalBadges.push(<BadgeComponent name={displayName} img={badge.badge} />);
badgeModal.push(<BadgeModalComponent name={displayName} img={badge.badge} />);
badgeModal.push({
name: displayName,
rawName: badge.tooltip,
...badge
});
});
});
badgeImages = badgeModal;
badgeData = badgeModal;
return (
<div

View file

@ -4,65 +4,78 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Button } from "@components/Button";
import ErrorBoundary from "@components/ErrorBoundary";
import { HeadingTertiary } from "@components/Heading";
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
import { Paragraph } from "@components/Paragraph";
import { openInviteModal } from "@utils/discord";
import { ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
import { User } from "@vencord/discord-types";
import { React, Tooltip } from "@webpack/common";
import { badgeImages } from "./badgeComponent";
import { cl } from "./utils";
import { badgeData } from "./badgeComponent";
import { cl, INVITE_LINK } from "./utils";
export const BadgeModalComponent = ({ name, img }: { name: string, img: string; }) => {
return (
<Tooltip text={name} >
{(tooltipProps: any) => (
<img
className={cl("modal-badges")}
{...tooltipProps}
src={img}
/>
)}
</Tooltip>
<>
<Tooltip text={name} >
{(tooltipProps: any) => (
<img
className={cl("modal-badge")}
{...tooltipProps}
src={img}
/>
)}
</Tooltip>
</>
);
};
export function BadgeModal({ user }: { user: User; }) {
return (
<>
<div className={cl("header")}>
<img
className={cl("avatar")}
src={user.getAvatarURL(void 0, 512, true)}
alt=""
/>
{badgeImages.length ? (
<HeadingTertiary className={cl("name")}>
{user.username} has {badgeImages.length} global badges
</HeadingTertiary>
) : (
<HeadingTertiary className={cl("name")}>
{user.username} has no global badges
</HeadingTertiary>
)}
</div>
{!!badgeImages.length && (
<div className={cl("badges")}>
{badgeImages}
</div>
)}
</>
);
}
export function openBadgeModal(user: User) {
openModal(modalprops =>
const badgeDataElements = badgeData.map(badge => (
<BadgeModalComponent key={badge.rawName} name={badge.name} img={badge.badge} />
));
openModal(modalprops => (
<ModalRoot {...modalprops}>
<ErrorBoundary>
<ModalContent className={cl("root")}>
<BadgeModal user={user} />
<ModalHeader>
<img
className={cl("modal-avatar")}
src={user.getAvatarURL(undefined, 512, true)}
alt=""
/>
<HeadingTertiary className={cl("modal-name")}>
{user.username}
</HeadingTertiary>
</ModalHeader>
<ModalContent className={cl("modal-description")}>
{badgeDataElements.length ? (
<>
<Paragraph className={cl("modal-text")}>
{badgeDataElements.length} global badge
{badgeDataElements.length > 1 ? "s" : ""}.
</Paragraph>
<div className={cl("modal-badges")}>
{badgeDataElements}
</div>
</>
) : (
<Paragraph>
No global badges.
</Paragraph>
)}
</ModalContent>
<ModalFooter>
<Button
variant="link"
onClick={() => openInviteModal(INVITE_LINK)}
>
Join GlobalBadges Server
</Button>
</ModalFooter>
</ErrorBoundary>
</ModalRoot>
);
));
}

View file

@ -7,13 +7,15 @@
import "./styles.css";
import { addProfileBadge, BadgePosition, ProfileBadge, removeProfileBadge } from "@api/Badges";
import { Button } from "@components/Button";
import { Devs, EquicordDevs } from "@utils/constants";
import { openInviteModal } from "@utils/discord";
import definePlugin from "@utils/types";
import { React } from "@webpack/common";
import { GlobalBadges } from "./badgeComponent";
import { settings } from "./settings";
import { fetchBadges } from "./utils";
import { cl, fetchBadges, INVITE_LINK } from "./utils";
const Badge: ProfileBadge = {
component: b => <GlobalBadges {...b} />,
@ -27,6 +29,17 @@ export default definePlugin({
description: "Adds global badges from other client mods",
authors: [Devs.HypedDomi, EquicordDevs.Wolfie],
settings,
settingsAboutComponent: () => (
<>
<Button
className={cl("settings-button")}
variant="link"
onClick={() => openInviteModal(INVITE_LINK)}
>
Join GlobalBadges Server
</Button>
</>
),
start: () => addProfileBadge(Badge),
stop: () => removeProfileBadge(Badge),
});

View file

@ -12,13 +12,23 @@ export const settings = definePluginSettings({
type: OptionType.BOOLEAN,
description: "Shows the Mod as Prefix",
default: true,
restartNeeded: false
restartNeeded: false,
onChange: (e => {
if (e && settings.store.showSuffix) {
settings.store.showSuffix = false;
}
})
},
showSuffix: {
type: OptionType.BOOLEAN,
description: "Shows the Mod as Suffix",
default: false,
restartNeeded: false
restartNeeded: false,
onChange: (e => {
if (e && settings.store.showPrefix) {
settings.store.showPrefix = false;
}
})
},
showCustom: {
type: OptionType.BOOLEAN,

View file

@ -4,13 +4,7 @@
flex-wrap: nowrap;
}
.vc-global-badges-header {
display: flex;
align-items: center;
margin-bottom: 1em;
}
.vc-global-badges-name {
.vc-global-badges-modal-name {
text-transform: none;
flex-grow: 0;
background: var(--background-base-lowest);
@ -22,7 +16,7 @@
text-wrap: nowrap;
}
.vc-global-badges-name::before {
.vc-global-badges-modal-name::before {
content: "";
display: block;
position: absolute;
@ -36,19 +30,27 @@
border-bottom-left-radius: 9999px;
}
.vc-global-badges-avatar {
.vc-global-badges-modal-avatar {
height: 48px;
width: 48px;
border-radius: 50%;
}
.vc-global-badges-description {
.vc-global-badges-modal-description {
font-size: 16px;
margin-left: 6px;
}
.vc-global-badges-modal-badges {
.vc-global-badges-modal-text {
margin-left: 8px;
}
.vc-global-badges-modal-badge {
width: 50px;
height: 50px;
margin: 2px;
}
.vc-global-badges-settings-button {
width: 100%;
height: 50px;
}

View file

@ -13,17 +13,7 @@ const cache = new Map<string, BadgeCache>();
const EXPIRES = 1000 * 60 * 15;
export const cl = classNameFactory("vc-global-badges-");
export const serviceMap = {
"nekocord": "Nekocord",
"reviewdb": "ReviewDB",
"aero": "Aero",
"aliucord": "Aliucord",
"ra1ncord": "Ra1ncord",
"velocity": "Velocity",
"enmity": "Enmity",
"replugged": "Replugged",
"badgevault": "BadgeVault"
};
export const INVITE_LINK = "https://discord.gg/kwHCJPxp8t";
export const fetchBadges = (id: string): BadgeCache["badges"] | undefined => {
const cachedValue = cache.get(id);
@ -44,7 +34,7 @@ export const fetchBadges = (id: string): BadgeCache["badges"] | undefined => {
return {};
}
fetch(`${settings.store.apiUrl}${id}?seperated=true&services=${services.join(",")}`)
fetch(`${settings.store.apiUrl}${id}?seperated=true&capitalize=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 });

View file

@ -4,12 +4,20 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./styles.css";
import { definePluginSettings } from "@api/Settings";
import { Link } from "@components/Link";
import { classNameFactory } from "@api/Styles";
import { Button } from "@components/Button";
import { Devs, EquicordDevs } from "@utils/constants";
import { openInviteModal } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { User } from "@vencord/discord-types";
const cl = classNameFactory("vc-userpfp-");
const DONO_URL = "https://ko-fi.com/coolesding";
const INVITE_LINK = "https://discord.gg/userpfp-1129784704267210844";
let data = { avatars: {} as Record<string, string> };
const settings = definePluginSettings({
preferNitro: {
@ -20,32 +28,39 @@ const settings = definePluginSettings({
{ label: "Nitro", value: true, default: true },
],
},
dbSource: {
databaseSource: {
description: "URL to load database from",
type: OptionType.STRING,
default: "https://userpfp.github.io/UserPFP/source/data.json",
onChange: (e => {
if (!e) return settings.store.dbSource = "https://userpfp.github.io/UserPFP/source/data.json";
if (!e) return settings.store.databaseSource = "https://userpfp.github.io/UserPFP/source/data.json";
return e;
})
}
});
export default definePlugin({
data,
name: "UserPFP",
description: "Allows you to use an animated avatar without Nitro",
authors: [EquicordDevs.nexpid, Devs.thororen],
settings,
settingsAboutComponent: () => (
<>
<Link href="https://userpfp.github.io/UserPFP/#how-to-request-a-profile-picture-pfp">
<b>Submit your own PFP here!</b>
</Link>
<Button
variant="link"
className={cl("settings-button")}
onClick={() => openInviteModal(INVITE_LINK)}
>
Join UserPFP Server
</Button>
<br />
<Link href="https://ko-fi.com/coolesding">
<b>Support UserPFP here!</b>
</Link>
<Button
variant="link"
className={cl("settings-button")}
onClick={() => VencordNative.native.openExternal(DONO_URL)}
>
Support UserPFP here
</Button>
</>
),
patches: [
@ -72,9 +87,9 @@ export default definePlugin({
return res.toString();
},
async start() {
await fetch(settings.store.dbSource)
await fetch(settings.store.databaseSource)
.then(async res => {
if (res.ok) this.data = data = await res.json();
if (res.ok) data = await res.json();
})
.catch(() => null);
}

View file

@ -0,0 +1,4 @@
.vc-userpfp-settings-button {
width: 100%;
height: 50px;
}