From 64b38348d43040aba9823003b28acbb906e8a7d7 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 14 May 2023 21:33:04 -0300 Subject: feat(plugins): Permissions Viewer (#477) Co-authored-by: V --- .../components/RolesAndUsersPermissions.tsx | 225 +++++++++++++++++++++ .../components/UserPermissions.tsx | 175 ++++++++++++++++ src/plugins/permissionsViewer/components/icons.tsx | 58 ++++++ 3 files changed, 458 insertions(+) create mode 100644 src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx create mode 100644 src/plugins/permissionsViewer/components/UserPermissions.tsx create mode 100644 src/plugins/permissionsViewer/components/icons.tsx (limited to 'src/plugins/permissionsViewer/components') diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx new file mode 100644 index 0000000..7a65a07 --- /dev/null +++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx @@ -0,0 +1,225 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { Flex } from "@components/Flex"; +import { InfoIcon, OwnerCrownIcon } from "@components/Icons"; +import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; +import { ContextMenu, FluxDispatcher, GuildMemberStore, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common"; +import type { Guild } from "discord-types/general"; + +import { cl, getPermissionDescription, getPermissionString } from "../utils"; +import { PermissionAllowedIcon, PermissionDefaultIcon, PermissionDeniedIcon } from "./icons"; + +export const enum PermissionType { + Role = 0, + User = 1, + Owner = 2 +} + +export interface RoleOrUserPermission { + type: PermissionType; + id?: string; + permissions?: bigint; + overwriteAllow?: bigint; + overwriteDeny?: bigint; +} + +function openRolesAndUsersPermissionsModal(permissions: Array, guild: Guild, header: string) { + return openModal(modalProps => ( + + )); +} + +function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, header }: { permissions: Array; guild: Guild; modalProps: ModalProps; header: string; }) { + permissions.sort((a, b) => a.type - b.type); + + useStateFromStores( + [GuildMemberStore], + () => GuildMemberStore.getMemberIds(guild.id), + null, + (old, current) => old.length === current.length + ); + + useEffect(() => { + const usersToRequest = permissions + .filter(p => p.type === PermissionType.User && !GuildMemberStore.isMember(guild.id, p.id!)) + .map(({ id }) => id); + + FluxDispatcher.dispatch({ + type: "GUILD_MEMBERS_REQUEST", + guildIds: [guild.id], + userIds: usersToRequest + }); + }, []); + + const [selectedItemIndex, selectItem] = useState(0); + const selectedItem = permissions[selectedItemIndex]; + + return ( + + + {header} permissions: + + + + + {!selectedItem && ( +
+ No permissions to display! +
+ )} + + {selectedItem && ( +
+
+ {permissions.map((permission, index) => { + const user = UserStore.getUser(permission.id ?? ""); + const role = guild.roles[permission.id ?? ""]; + + return ( + + ); + })} +
+
+ {Object.entries(PermissionsBits).map(([permissionName, bit]) => ( +
+
+ {(() => { + const { permissions, overwriteAllow, overwriteDeny } = selectedItem; + + if (permissions) + return (permissions & bit) === bit + ? PermissionAllowedIcon() + : PermissionDeniedIcon(); + + if (overwriteAllow && (overwriteAllow & bit) === bit) + return PermissionAllowedIcon(); + if (overwriteDeny && (overwriteDeny & bit) === bit) + return PermissionDeniedIcon(); + + return PermissionDefaultIcon(); + })()} +
+ {getPermissionString(permissionName)} + + + {props => } + +
+ ))} +
+
+ )} +
+
+ ); +} + +function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: string; onClose: () => void; }) { + return ( + + { + const role = guild.roles[roleId]; + if (!role) return; + + onClose(); + + FluxDispatcher.dispatch({ + type: "IMPERSONATE_UPDATE", + guildId: guild.id, + data: { + type: "ROLES", + roles: { + [roleId]: role + } + } + }); + }} + /> + + ); +} + +const RolesAndUsersPermissions = ErrorBoundary.wrap(RolesAndUsersPermissionsComponent); + +export default openRolesAndUsersPermissionsModal; diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx new file mode 100644 index 0000000..acffa78 --- /dev/null +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -0,0 +1,175 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { proxyLazy } from "@utils/lazy"; +import { classes } from "@utils/misc"; +import { filters, findBulk } from "@webpack"; +import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore, useState } from "@webpack/common"; +import type { Guild, GuildMember } from "discord-types/general"; + +import { settings } from ".."; +import { cl, getPermissionString, getSortedRoles, sortUserRoles } from "../utils"; +import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermission } from "./RolesAndUsersPermissions"; + +interface UserPermission { + permission: string; + roleColor: string; + rolePosition: number; +} + +type UserPermissions = Array; + +const Classes = proxyLazy(() => { + const modules = findBulk( + filters.byProps("roles", "rolePill", "rolePillBorder"), + filters.byProps("roleCircle", "dotBorderBase", "dotBorderColor"), + filters.byProps("roleNameOverflow", "root", "roleName", "roleRemoveButton") + ); + + return Object.assign({}, ...modules); +}) 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 }: { guild: Guild; guildMember: GuildMember; }) { + const [viewPermissions, setViewPermissions] = useState(settings.store.defaultPermissionsDropdownState); + + const [rolePermissions, userPermissions] = useMemo(() => { + const userPermissions: UserPermissions = []; + + const userRoles = getSortedRoles(guild, guildMember); + + const rolePermissions: Array = userRoles.map(role => ({ + type: PermissionType.Role, + ...role + })); + + if (guild.ownerId === guildMember.userId) { + rolePermissions.push({ + type: PermissionType.Owner, + permissions: Object.values(PermissionsBits).reduce((prev, curr) => prev | curr, 0n) + }); + + const OWNER = i18n.Messages.GUILD_OWNER || "Server Owner"; + userPermissions.push({ + permission: OWNER, + roleColor: "var(--primary-300)", + rolePosition: Infinity + }); + } + + sortUserRoles(userRoles); + + for (const [permission, bit] of Object.entries(PermissionsBits)) { + for (const { permissions, colorString, position, name } of userRoles) { + if ((permissions & bit) === bit) { + userPermissions.push({ + permission: getPermissionString(permission), + roleColor: colorString || "var(--primary-300)", + rolePosition: position + }); + + break; + } + } + } + + userPermissions.sort((a, b) => b.rolePosition - a.rolePosition); + + return [rolePermissions, userPermissions]; + }, []); + + const { root, role, roleRemoveButton, roleNameOverflow, roles, rolePill, rolePillBorder, roleCircle, roleName } = Classes; + + return ( +
+
+ Permissions + +
+ + {tooltipProps => ( + + )} + + + + {tooltipProps => ( + + )} + +
+
+ + {viewPermissions && userPermissions.length > 0 && ( +
+ {userPermissions.map(({ permission, roleColor }) => ( +
+
+ +
+
+ + {permission} + +
+
+ ))} +
+ )} +
+ ); +} + +export default ErrorBoundary.wrap(UserPermissionsComponent, { noop: true }); diff --git a/src/plugins/permissionsViewer/components/icons.tsx b/src/plugins/permissionsViewer/components/icons.tsx new file mode 100644 index 0000000..8b58a44 --- /dev/null +++ b/src/plugins/permissionsViewer/components/icons.tsx @@ -0,0 +1,58 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +export function PermissionDeniedIcon() { + return ( + + Denied + + + ); +} + +export function PermissionAllowedIcon() { + return ( + + Allowed + + + ); +} + +export function PermissionDefaultIcon() { + return ( + + + Not overwritten + + + + ); +} -- cgit