diff --git a/src/components/ExpandableHeader.tsx b/src/components/ExpandableHeader.tsx index 84b06586..473dffaa 100644 --- a/src/components/ExpandableHeader.tsx +++ b/src/components/ExpandableHeader.tsx @@ -31,10 +31,20 @@ export interface ExpandableHeaderProps { headerText: string; children: React.ReactNode; buttons?: React.ReactNode[]; + forceOpen?: boolean; } -export function ExpandableHeader({ children, onMoreClick, buttons, moreTooltipText, defaultState = false, onDropDownClick, headerText }: ExpandableHeaderProps) { - const [showContent, setShowContent] = useState(defaultState); +export function ExpandableHeader({ + children, + onMoreClick, + buttons, + moreTooltipText, + onDropDownClick, + headerText, + defaultState = false, + forceOpen = false, +}: ExpandableHeaderProps) { + const [showContent, setShowContent] = useState(defaultState || forceOpen); return ( <> @@ -90,6 +100,7 @@ export function ExpandableHeader({ children, onMoreClick, buttons, moreTooltipTe setShowContent(v => !v); onDropDownClick?.(showContent); }} + disabled={forceOpen} > ); } + +export function SafetyIcon(props: IconProps) { + return ( + + + + + ); +} diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index 869a6a1e..49770bbe 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -43,7 +43,7 @@ const Classes = proxyLazyWebpack(() => )) ) as Record<"roles" | "rolePill" | "rolePillBorder" | "desaturateUserColors" | "flex" | "alignCenter" | "justifyCenter" | "svg" | "background" | "dot" | "dotBorderColor" | "roleCircle" | "dotBorderBase" | "flex" | "alignCenter" | "justifyCenter" | "wrap" | "root" | "role" | "roleRemoveButton" | "roleDot" | "roleFlowerStar" | "roleRemoveIcon" | "roleRemoveIconFocused" | "roleVerifiedIcon" | "roleName" | "roleNameOverflow" | "actionButton" | "overflowButton" | "addButton" | "addButtonIcon" | "overflowRolesPopout" | "overflowRolesPopoutArrowWrapper" | "overflowRolesPopoutArrow" | "popoutBottom" | "popoutTop" | "overflowRolesPopoutHeader" | "overflowRolesPopoutHeaderIcon" | "overflowRolesPopoutHeaderText" | "roleIcon", string>; -function UserPermissionsComponent({ guild, guildMember, showBorder }: { guild: Guild; guildMember: GuildMember; showBorder: boolean; }) { +function UserPermissionsComponent({ guild, guildMember, showBorder, forceOpen = false }: { guild: Guild; guildMember: GuildMember; showBorder: boolean; forceOpen?: boolean; }) { const stns = settings.use(["permissionsSortOrder"]); const [rolePermissions, userPermissions] = useMemo(() => { @@ -95,6 +95,7 @@ function UserPermissionsComponent({ guild, guildMember, showBorder }: { guild: G return ( diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx index b27a3c2f..6401d945 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -20,15 +20,22 @@ import "./styles.css"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; +import ErrorBoundary from "@components/ErrorBoundary"; +import { SafetyIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; +import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { ChannelStore, GuildMemberStore, GuildStore, Menu, PermissionsBits, UserStore } from "@webpack/common"; +import { findByPropsLazy } from "@webpack"; +import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common"; import type { Guild, GuildMember } from "discord-types/general"; import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "./components/RolesAndUsersPermissions"; import UserPermissions from "./components/UserPermissions"; import { getSortedRoles, sortPermissionOverwrites } from "./utils"; +const PopoutClasses = findByPropsLazy("container", "scroller", "list"); +const RoleButtonClasses = findByPropsLazy("button", "buttonInner", "icon", "text"); + export const enum PermissionsSortOrder { HighestRole, LowestRole @@ -168,10 +175,45 @@ export default definePlugin({ match: /showBorder:(.{0,60})}\),(?<=guild:(\i),guildMember:(\i),.+?)/, replace: (m, showBoder, guild, guildMember) => `${m}$self.UserPermissions(${guild},${guildMember},${showBoder}),` } + }, + { + find: ".VIEW_ALL_ROLES,", + replacement: { + match: /children:"\+"\.concat\(\i\.length-\i\.length\).{0,20}\}\),/, + replace: "$&$self.ViewPermissionsButton(arguments[0])," + } } ], - UserPermissions: (guild: Guild, guildMember: GuildMember | undefined, showBoder: boolean) => !!guildMember && , + UserPermissions: (guild: Guild, guildMember: GuildMember | undefined, showBorder: boolean) => + !!guildMember && , + + ViewPermissionsButton: ErrorBoundary.wrap(({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) => ( + ( + + + + )} + > + {popoutProps => ( + + + + )} + + ), { noop: true }), contextMenus: { "user-context": makeContextMenuPatch("roles", MenuItemParentType.User), diff --git a/src/plugins/permissionsViewer/styles.css b/src/plugins/permissionsViewer/styles.css index 1c60098f..0ef961e5 100644 --- a/src/plugins/permissionsViewer/styles.css +++ b/src/plugins/permissionsViewer/styles.css @@ -149,3 +149,21 @@ .vc-permviewer-perms-perms-item .vc-info-icon:hover { color: var(--interactive-active); } + +/* copy pasted from discord cause impossible to webpack find */ +.vc-permviewer-role-button { + border-radius: var(--radius-xs); + background: var(--bg-mod-faint); + color: var(--interactive-normal); + border: 1px solid var(--border-faint); + /* stylelint-disable-next-line value-no-vendor-prefix */ + width: -moz-fit-content; + width: fit-content; + height: 24px; + padding: 4px +} + +.custom-profile-theme .vc-permviewer-role-button { + background: rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-6)); + border-color: var(--profile-body-border-color) +} diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index 46f843ce..58c279f6 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -33,6 +33,7 @@ export let Card: t.Card; export let Button: t.Button; export let Switch: t.Switch; export let Tooltip: t.Tooltip; +export let TooltipContainer: t.TooltipContainer; export let TextInput: t.TextInput; export let TextArea: t.TextArea; export let Text: t.Text; @@ -66,6 +67,7 @@ waitFor(["FormItem", "Button"], m => { Button, FormSwitch: Switch, Tooltip, + TooltipContainer, TextInput, TextArea, Text, diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index c5126437..5dcc9519 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -101,6 +101,28 @@ export type Tooltip = ComponentType<{ export type TooltipPositions = Record<"BOTTOM" | "CENTER" | "LEFT" | "RIGHT" | "TOP" | "WINDOW_CENTER", string>; +export type TooltipContainer = ComponentType>; + export type Card = ComponentType & { editable?: boolean; outline?: boolean; @@ -110,6 +132,26 @@ export type Card = ComponentType & { Types: Record<"BRAND" | "CUSTOM" | "DANGER" | "PRIMARY" | "SUCCESS" | "WARNING", string>; }; +export type ComboboxPopout = ComponentType; + placeholder: string; + children(query: string): ReactNode[]; + + onChange(value: any): void; + itemToString?: (item: any) => string; + onClose?(): void; + + className?: string; + listClassName?: string; + + + autoFocus?: boolean; + multiSelect?: boolean; + maxVisibleItems?: number; + showScrollbar?: boolean; + +}>>; + export type Button = ComponentType, "size"> & { /** Button.Looks.FILLED */ look?: string; @@ -375,7 +417,7 @@ export type Popout = ComponentType<{ Animation: typeof PopoutAnimation; }; -export type Dialog = ComponentType>; +export type Dialog = ComponentType; type Resolve = (data: { theme: "light" | "dark", saturation: number; }) => { hex(): string;