BetterRoleContext: Add 'View Role Members'

This commit is contained in:
Vendicated 2026-05-04 16:57:37 +02:00
parent 89c90f71d0
commit b08fafb00b
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
2 changed files with 84 additions and 11 deletions

View file

@ -331,6 +331,15 @@ declare enum PopoutAnimation {
type PopoutPosition = "top" | "bottom" | "left" | "right" | "center" | "window_center";
export interface PopoutProps {
position: PopoutPosition;
nudge: number;
isPositioned: boolean;
setPopoutRef(ref: any): void;
closePopout(): void;
updatePosition(): void;
}
export type Popout = ComponentType<{
children(
thing: {
@ -347,14 +356,8 @@ export type Popout = ComponentType<{
): ReactNode;
shouldShow?: boolean;
targetElementRef: RefObject<any>;
renderPopout(args: {
closePopout(): void;
isPositioned: boolean;
nudge: number;
position: PopoutPosition;
setPopoutRef(ref: any): void;
updatePosition(): void;
}): ReactNode;
renderPopout(props: PopoutProps): ReactNode;
preload?(): Promise<any>;
onRequestOpen?(): void;
onRequestClose?(): void;

View file

@ -9,12 +9,17 @@ import { getUserSettingLazy } from "@api/UserSettings";
import { ImageIcon } from "@components/Icons";
import { copyToClipboard } from "@utils/clipboard";
import { Devs } from "@utils/constants";
import { getCurrentGuild, openImageModal } from "@utils/discord";
import { getCurrentChannel, getCurrentGuild, openImageModal } from "@utils/discord";
import { classes } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { GuildRoleStore, Menu, PermissionStore } from "@webpack/common";
import { PopoutProps } from "@vencord/discord-types";
import { findByCodeLazy, findByPropsLazy, findCssClassesLazy } from "@webpack";
import { GuildRoleStore, Menu, PermissionStore, Popout, useRef } from "@webpack/common";
import { ComponentType } from "react";
const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild");
const MenuItemClasses = findCssClassesLazy("item", "labelContainer", "colorDefault", "label", "iconContainer");
const loadRoleMembers = findByCodeLazy(".GUILD_ROLE_MEMBER_IDS(", "requestMembersById");
const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!;
@ -40,6 +45,14 @@ function AppearanceIcon() {
);
}
function RoleMembersIcon() {
return (
<svg width="18" height="18" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM11.53 11A9.53 9.53 0 0 0 2 20.53c0 .81.66 1.47 1.47 1.47h.22c.24 0 .44-.17.5-.4.29-1.12.84-2.17 1.32-2.91.14-.21.43-.1.4.15l-.26 2.61c-.02.3.2.55.5.55h11.7a.5.5 0 0 0 .5-.55l-.27-2.6c-.02-.26.27-.37.41-.16.48.74 1.03 1.8 1.32 2.9.06.24.26.41.5.41h.22c.81 0 1.47-.66 1.47-1.47A9.53 9.53 0 0 0 12.47 11h-.94Z" />
</svg>
);
}
const settings = definePluginSettings({
roleIconFileFormat: {
type: OptionType.SELECT,
@ -62,6 +75,16 @@ const settings = definePluginSettings({
}
});
interface RoleMemberPopoutProps {
popoutProps: PopoutProps;
guildId: string;
channelId: string;
roleId: string;
}
type RoleMemberPopout = ComponentType<RoleMemberPopoutProps>;
let RoleMemberPopout: RoleMemberPopout = () => null;
export default definePlugin({
name: "BetterRoleContext",
description: "Adds options to copy role color / edit role / view role icon when right clicking roles in the user profile",
@ -71,13 +94,27 @@ export default definePlugin({
settings,
patches: [{
find: ".ROLE_MENTION)",
replacement: {
match: /function (\i)(?=.+?renderPopout:.{0,20}\1,\{guildId:\i,channelId:\i)/,
replace: "$self.RoleMembers=$1;$&"
}
}],
start() {
// DeveloperMode needs to be enabled for the context menu to be shown
DeveloperMode.updateSetting(true);
},
set RoleMembers(component: RoleMemberPopout) {
RoleMemberPopout = component;
},
contextMenus: {
"dev-context"(children, { id }: { id: string; }) {
const popoutRef = useRef(null);
const guild = getCurrentGuild();
if (!guild) return;
@ -126,6 +163,39 @@ export default definePlugin({
);
}
children.push(
<Menu.MenuItem id="vc-view-role-members" label="View Role Members" render={() => (
<Popout
position="right"
align="center"
targetElementRef={popoutRef}
preload={() => loadRoleMembers(guild.id, id)}
renderPopout={popoutProps => (
<RoleMemberPopout
popoutProps={popoutProps}
guildId={guild.id}
channelId={getCurrentChannel()!.id}
roleId={id}
/>
)}
>
{popoutProps => (
<div
className={classes(MenuItemClasses.item, MenuItemClasses.labelContainer, MenuItemClasses.colorDefault)}
ref={popoutRef}
role="menuitem"
{...popoutProps}
>
<div className={MenuItemClasses.label}>View Role Members</div>
<div className={MenuItemClasses.iconContainer}>
<RoleMembersIcon />
</div>
</div>
)}
</Popout>
)} />
);
}
}
});