/* * 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 { getIntlMessage, getUniqueUsername } from "@utils/discord"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { findByCodeLazy } from "@webpack"; import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, useMemo, UserStore, useState, useStateFromStores } from "@webpack/common"; import { UnicodeEmoji } from "@webpack/types"; import type { Guild, Role, User } from "discord-types/general"; import { settings } from ".."; import { cl, getGuildPermissionSpecMap } 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; } type GetRoleIconData = (role: Role, size: number) => { customIconSrc?: string; unicodeEmoji?: UnicodeEmoji; }; const getRoleIconData: GetRoleIconData = findByCodeLazy("convertSurrogateToName", "customIconSrc", "unicodeEmoji"); function getRoleIconSrc(role: Role) { const icon = getRoleIconData(role, 20); if (!icon) return; const { customIconSrc, unicodeEmoji } = icon; return customIconSrc ?? unicodeEmoji?.url; } function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, header }: { permissions: Array; guild: Guild; modalProps: ModalProps; header: string; }) { const guildPermissionSpecMap = useMemo(() => getGuildPermissionSpecMap(guild), [guild.id]); useStateFromStores( [GuildMemberStore], () => GuildMemberStore.getMemberIds(guild.id), null, (old, current) => old.length === current.length ); useEffect(() => { permissions.sort((a, b) => a.type - b.type); }, [permissions]); 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]; const roles = GuildStore.getRoles(guild.id); return ( {header} permissions: {!selectedItem && (
No permissions to display!
)} {selectedItem && (
{permissions.map((permission, index) => { const user: User | undefined = UserStore.getUser(permission.id ?? ""); const role: Role | undefined = roles[permission.id ?? ""]; const roleIconSrc = role != null ? getRoleIconSrc(role) : undefined; return (
selectItem(index)} role="button" tabIndex={0} >
{ if (permission.type === PermissionType.Role) ContextMenuApi.openContextMenu(e, () => ( )); else if (permission.type === PermissionType.User) { ContextMenuApi.openContextMenu(e, () => ( )); } }} > {(permission.type === PermissionType.Role || permission.type === PermissionType.Owner) && ( )} {permission.type === PermissionType.Role && roleIconSrc != null && ( )} {permission.type === PermissionType.User && user != null && ( )} { permission.type === PermissionType.Role ? role?.name ?? "Unknown Role" : permission.type === PermissionType.User ? (user != null && getUniqueUsername(user)) ?? "Unknown User" : ( @owner ) }
); })}
{Object.values(PermissionsBits).map(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(); })()}
{guildPermissionSpecMap[String(bit)].title} { const { description } = guildPermissionSpecMap[String(bit)]; return typeof description === "function" ? i18n.intl.format(description, {}) : description; })() }> {props => }
))}
)} ); } function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: string; onClose: () => void; }) { return ( { Clipboard.copy(roleId); }} /> {(settings.store as any).unsafeViewAsRole && ( { const role = GuildStore.getRole(guild.id, roleId); if (!role) return; onClose(); FluxDispatcher.dispatch({ type: "IMPERSONATE_UPDATE", guildId: guild.id, data: { type: "ROLES", roles: { [roleId]: role } } }); }} /> )} ); } function UserContextMenu({ userId }: { userId: string; }) { return ( { Clipboard.copy(userId); }} /> ); } const RolesAndUsersPermissions = ErrorBoundary.wrap(RolesAndUsersPermissionsComponent); export default function openRolesAndUsersPermissionsModal(permissions: Array, guild: Guild, header: string) { return openModal(modalProps => ( )); }