902 lines
34 KiB
JavaScript
902 lines
34 KiB
JavaScript
/**
|
||
* @name PermissionsViewer
|
||
* @description Allows you to view a user's permissions. Thanks to Noodlebox for the idea!
|
||
* @version 0.2.9
|
||
* @author Zerebos
|
||
* @authorId 249746236008169473
|
||
* @website https://github.com/rauenzi/BetterDiscordAddons/tree/master/Plugins/PermissionsViewer
|
||
* @source https://raw.githubusercontent.com/rauenzi/BetterDiscordAddons/master/Plugins/PermissionsViewer/PermissionsViewer.plugin.js
|
||
*/
|
||
/*@cc_on
|
||
@if (@_jscript)
|
||
|
||
// Offer to self-install for clueless users that try to run this directly.
|
||
var shell = WScript.CreateObject("WScript.Shell");
|
||
var fs = new ActiveXObject("Scripting.FileSystemObject");
|
||
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
|
||
var pathSelf = WScript.ScriptFullName;
|
||
// Put the user at ease by addressing them in the first person
|
||
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
|
||
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
|
||
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
|
||
} else if (!fs.FolderExists(pathPlugins)) {
|
||
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
|
||
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
|
||
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
|
||
// Show the user where to put plugins in the future
|
||
shell.Exec("explorer " + pathPlugins);
|
||
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
|
||
}
|
||
WScript.Quit();
|
||
|
||
@else@*/
|
||
const config = {
|
||
info: {
|
||
name: "PermissionsViewer",
|
||
authors: [
|
||
{
|
||
name: "Zerebos",
|
||
discord_id: "249746236008169473",
|
||
github_username: "rauenzi",
|
||
twitter_username: "ZackRauen"
|
||
}
|
||
],
|
||
version: "0.2.9",
|
||
description: "Allows you to view a user's permissions. Thanks to Noodlebox for the idea!",
|
||
github: "https://github.com/rauenzi/BetterDiscordAddons/tree/master/Plugins/PermissionsViewer",
|
||
github_raw: "https://raw.githubusercontent.com/rauenzi/BetterDiscordAddons/master/Plugins/PermissionsViewer/PermissionsViewer.plugin.js"
|
||
},
|
||
changelog: [
|
||
{
|
||
title: "Fixes",
|
||
type: "fixed",
|
||
items: [
|
||
"Fixed for the latest Discord changes!"
|
||
]
|
||
}
|
||
],
|
||
defaultConfig: [
|
||
{
|
||
type: "switch",
|
||
id: "contextMenus",
|
||
name: "Context Menus",
|
||
value: true
|
||
},
|
||
{
|
||
type: "switch",
|
||
id: "popouts",
|
||
name: "Popouts",
|
||
value: true
|
||
},
|
||
{
|
||
type: "radio",
|
||
id: "displayMode",
|
||
name: "Modal Display Mode",
|
||
value: "compact",
|
||
options: [
|
||
{
|
||
name: "Cozy",
|
||
value: "cozy"
|
||
},
|
||
{
|
||
name: "Compact",
|
||
value: "compact"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
strings: {
|
||
es: {
|
||
contextMenuLabel: "Permisos",
|
||
popoutLabel: "Permisos",
|
||
modal: {
|
||
header: "Permisos de {{name}}",
|
||
rolesLabel: "Roles",
|
||
permissionsLabel: "Permisos",
|
||
owner: "@propietario"
|
||
},
|
||
settings: {
|
||
popouts: {
|
||
name: "Mostrar en Popouts",
|
||
note: "Mostrar los permisos de usuario en popouts como los roles."
|
||
},
|
||
contextMenus: {
|
||
name: "Botón de menú contextual",
|
||
note: "Añadir un botón para ver permisos en los menús contextuales."
|
||
}
|
||
}
|
||
},
|
||
pt: {
|
||
contextMenuLabel: "Permissões",
|
||
popoutLabel: "Permissões",
|
||
modal: {
|
||
header: "Permissões de {{name}}",
|
||
rolesLabel: "Cargos",
|
||
permissionsLabel: "Permissões",
|
||
owner: "@dono"
|
||
},
|
||
settings: {
|
||
popouts: {
|
||
name: "Mostrar em Popouts",
|
||
note: "Mostrar as permissões em popouts como os cargos."
|
||
},
|
||
contextMenus: {
|
||
name: "Botão do menu de contexto",
|
||
note: "Adicionar um botão parar ver permissões ao menu de contexto."
|
||
}
|
||
}
|
||
},
|
||
de: {
|
||
contextMenuLabel: "Berechtigungen",
|
||
popoutLabel: "Berechtigungen",
|
||
modal: {
|
||
header: "{{name}}s Berechtigungen",
|
||
rolesLabel: "Rollen",
|
||
permissionsLabel: "Berechtigungen",
|
||
owner: "@eigentümer"
|
||
},
|
||
settings: {
|
||
popouts: {
|
||
name: "In Popouts anzeigen",
|
||
note: "Zeigt die Gesamtberechtigungen eines Benutzers in seinem Popup ähnlich den Rollen an."
|
||
},
|
||
contextMenus: {
|
||
name: "Kontextmenü-Schaltfläche",
|
||
note: "Fügt eine Schaltfläche hinzu, um die Berechtigungen mithilfe von Kontextmenüs anzuzeigen."
|
||
}
|
||
}
|
||
},
|
||
en: {
|
||
contextMenuLabel: "Permissions",
|
||
popoutLabel: "Permissions",
|
||
modal: {
|
||
header: "{{name}}'s Permissions",
|
||
rolesLabel: "Roles",
|
||
permissionsLabel: "Permissions",
|
||
owner: "@owner"
|
||
},
|
||
settings: {
|
||
popouts: {
|
||
name: "Show In Popouts",
|
||
note: "Shows a user's total permissions in their popout similar to roles."
|
||
},
|
||
contextMenus: {
|
||
name: "Context Menu Button",
|
||
note: "Adds a button to view the permissions modal to select context menus."
|
||
},
|
||
displayMode: {
|
||
name: "Modal Display Mode"
|
||
}
|
||
}
|
||
},
|
||
ru: {
|
||
contextMenuLabel: "Полномочия",
|
||
popoutLabel: "Полномочия",
|
||
modal: {
|
||
header: "Полномочия {{name}}",
|
||
rolesLabel: "Роли",
|
||
permissionsLabel: "Полномочия",
|
||
owner: "Владелец"
|
||
},
|
||
settings: {
|
||
popouts: {
|
||
name: "Показать во всплывающих окнах",
|
||
note: "Отображает полномочия пользователя в их всплывающем окне, аналогичном ролям."
|
||
},
|
||
contextMenus: {
|
||
name: "Кнопка контекстного меню",
|
||
note: "Добавить кнопку для отображения полномочий с помощью контекстных меню."
|
||
}
|
||
}
|
||
}
|
||
},
|
||
main: "index.js"
|
||
};
|
||
class Dummy {
|
||
constructor() {this._config = config;}
|
||
start() {}
|
||
stop() {}
|
||
}
|
||
|
||
if (!global.ZeresPluginLibrary) {
|
||
BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.name ?? config.info.name} is missing. Please click Download Now to install it.`, {
|
||
confirmText: "Download Now",
|
||
cancelText: "Cancel",
|
||
onConfirm: () => {
|
||
require("request").get("https://betterdiscord.app/gh-redirect?id=9", async (err, resp, body) => {
|
||
if (err) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
|
||
if (resp.statusCode === 302) {
|
||
require("request").get(resp.headers.location, async (error, response, content) => {
|
||
if (error) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
|
||
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), content, r));
|
||
});
|
||
}
|
||
else {
|
||
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
module.exports = !global.ZeresPluginLibrary ? Dummy : (([Plugin, Api]) => {
|
||
const plugin = (Plugin, Api) => {
|
||
const {ContextMenu, DOM, Utils} = window.BdApi;
|
||
const {DiscordModules, WebpackModules, Toasts, DiscordClasses, Utilities, DOMTools, ColorConverter, ReactTools} = Api;
|
||
|
||
const GuildStore = DiscordModules.GuildStore;
|
||
const SelectedGuildStore = DiscordModules.SelectedGuildStore;
|
||
const MemberStore = DiscordModules.GuildMemberStore;
|
||
const UserStore = DiscordModules.UserStore;
|
||
const DiscordPerms = Object.assign({}, DiscordModules.DiscordPermissions);
|
||
const AvatarDefaults = WebpackModules.getByProps("DEFAULT_AVATARS");
|
||
const ModalClasses = WebpackModules.getByProps("root", "header", "small");
|
||
const Strings = WebpackModules.getModule(m => m.Messages && m.Messages.COPY_ID).Messages;
|
||
const UserPopoutClasses = Object.assign({}, WebpackModules.getByProps("userPopoutOuter"), WebpackModules.getByProps("rolePill"), WebpackModules.getByProps("eyebrow"));
|
||
const RoleClasses = Object.assign({}, DiscordClasses.PopoutRoles, WebpackModules.getByProps("rolePill"), WebpackModules.getByProps("roleName", "roleIcon"));
|
||
|
||
if (DiscordPerms.STREAM) {
|
||
DiscordPerms.VIDEO = DiscordPerms.STREAM;
|
||
delete DiscordPerms.STREAM;
|
||
}
|
||
if (DiscordPerms.MANAGE_GUILD) {
|
||
DiscordPerms.MANAGE_SERVER = DiscordPerms.MANAGE_GUILD;
|
||
delete DiscordPerms.MANAGE_GUILD;
|
||
}
|
||
|
||
return class PermissionsViewer extends Plugin {
|
||
constructor() {
|
||
super();
|
||
this.css = `.perm-user-avatar {
|
||
border-radius: 50%;
|
||
width: 16px;
|
||
height: 16px;
|
||
margin-right: 3px;
|
||
}
|
||
|
||
.member-perms-header {
|
||
color: var(--header-secondary);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.member-perms {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
margin-top: 2px;
|
||
max-height: 160px;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
.member-perms .member-perm .perm-circle {
|
||
border-radius: 50%;
|
||
height: 12px;
|
||
margin-right: 4px;
|
||
width: 12px;
|
||
}
|
||
|
||
.member-perms .member-perm .name {
|
||
margin-right: 4px;
|
||
max-width: 200px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.perm-details-button {
|
||
cursor: pointer;
|
||
height: 12px;
|
||
}
|
||
|
||
.perm-details {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.member-perm-details {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.member-perm-details-button {
|
||
fill: #72767d;
|
||
height: 10px;
|
||
}
|
||
|
||
/* Modal */
|
||
|
||
@keyframes permissions-backdrop {
|
||
to { opacity: 0.85; }
|
||
}
|
||
|
||
@keyframes permissions-modal-wrapper {
|
||
to { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
@keyframes permissions-backdrop-closing {
|
||
to { opacity: 0; }
|
||
}
|
||
|
||
@keyframes permissions-modal-wrapper-closing {
|
||
to { transform: scale(0.7); opacity: 0; }
|
||
}
|
||
|
||
#permissions-modal-wrapper {
|
||
z-index: 100;
|
||
}
|
||
|
||
#permissions-modal-wrapper .callout-backdrop {
|
||
animation: permissions-backdrop 250ms ease;
|
||
animation-fill-mode: forwards;
|
||
opacity: 0;
|
||
background-color: rgb(0, 0, 0);
|
||
transform: translateZ(0px);
|
||
}
|
||
|
||
#permissions-modal-wrapper.closing .callout-backdrop {
|
||
animation: permissions-backdrop-closing 200ms linear;
|
||
animation-fill-mode: forwards;
|
||
animation-delay: 50ms;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
#permissions-modal-wrapper.closing .modal-wrapper {
|
||
animation: permissions-modal-wrapper-closing 250ms cubic-bezier(0.19, 1, 0.22, 1);
|
||
animation-fill-mode: forwards;
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
|
||
#permissions-modal-wrapper .modal-wrapper {
|
||
animation: permissions-modal-wrapper 250ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
animation-fill-mode: forwards;
|
||
transform: scale(0.7);
|
||
transform-origin: 50% 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
box-sizing: border-box;
|
||
contain: content;
|
||
justify-content: center;
|
||
top: 0;
|
||
left: 0;
|
||
bottom: 0;
|
||
right: 0;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
position: absolute;
|
||
user-select: none;
|
||
z-index: 1000;
|
||
}
|
||
|
||
#permissions-modal-wrapper .modal-body {
|
||
background-color: #36393f;
|
||
height: 440px;
|
||
width: auto;
|
||
/*box-shadow: 0 0 0 1px rgba(32,34,37,.6), 0 2px 10px 0 rgba(0,0,0,.2);*/
|
||
flex-direction: row;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex: 1;
|
||
contain: layout;
|
||
position: relative;
|
||
}
|
||
|
||
#permissions-modal-wrapper #permissions-modal {
|
||
contain: layout;
|
||
flex-direction: column;
|
||
pointer-events: auto;
|
||
border: 1px solid rgba(28,36,43,.6);
|
||
border-radius: 5px;
|
||
box-shadow: 0 2px 10px 0 rgba(0,0,0,.2);
|
||
overflow: hidden;
|
||
}
|
||
|
||
#permissions-modal-wrapper .header {
|
||
background-color: #35393e;
|
||
box-shadow: 0 2px 3px 0 rgba(0,0,0,.2);
|
||
padding: 12px 20px;
|
||
z-index: 1;
|
||
color: #fff;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
line-height: 19px;
|
||
}
|
||
|
||
.role-side, .perm-side {
|
||
flex-direction: column;
|
||
padding-left: 6px;
|
||
}
|
||
|
||
.role-scroller, .perm-scroller {
|
||
contain: layout;
|
||
flex: 1;
|
||
min-height: 1px;
|
||
overflow-y: scroll;
|
||
}
|
||
|
||
#permissions-modal-wrapper .scroller-title {
|
||
color: #fff;
|
||
padding: 8px 0 4px 4px;
|
||
margin-right: 8px;
|
||
border-bottom: 1px solid rgba(0,0,0,0.3);
|
||
display: none;
|
||
}
|
||
|
||
#permissions-modal-wrapper .role-side {
|
||
width: auto;
|
||
min-width: 150px;
|
||
background: #2f3136;
|
||
flex: 0 0 auto;
|
||
overflow: hidden;
|
||
display: flex;
|
||
min-height: 1px;
|
||
position: relative;
|
||
}
|
||
|
||
#permissions-modal-wrapper .role-scroller {
|
||
contain: layout;
|
||
flex: 1;
|
||
min-height: 1px;
|
||
overflow-y: scroll;
|
||
padding-top: 8px;
|
||
}
|
||
|
||
#permissions-modal-wrapper .role-item {
|
||
display: flex;
|
||
border-radius: 2px;
|
||
padding: 6px;
|
||
margin-bottom: 5px;
|
||
cursor: pointer;
|
||
color: #dcddde;
|
||
}
|
||
|
||
#permissions-modal-wrapper .role-item:hover {
|
||
background-color: rgba(0,0,0,0.1);
|
||
}
|
||
|
||
#permissions-modal-wrapper .role-item.selected {
|
||
background-color: rgba(0,0,0,0.2);
|
||
}
|
||
|
||
#permissions-modal-wrapper .perm-side {
|
||
width: 273px;
|
||
background-color: #36393f;
|
||
flex: 0 0 auto;
|
||
display: flex;
|
||
min-height: 1px;
|
||
position: relative;
|
||
padding-left: 10px;
|
||
}
|
||
|
||
#permissions-modal-wrapper .perm-item {
|
||
box-shadow: inset 0 -1px 0 rgba(79,84,92,.3);
|
||
box-sizing: border-box;
|
||
height: 44px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
flex-direction: row;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
display: flex;
|
||
}
|
||
|
||
#permissions-modal-wrapper .perm-item.allowed svg {
|
||
fill: #43B581;
|
||
}
|
||
|
||
#permissions-modal-wrapper .perm-item.denied svg {
|
||
fill: #F04747;
|
||
}
|
||
|
||
#permissions-modal-wrapper .perm-name {
|
||
display: inline;
|
||
flex: 1;
|
||
font-size: 16px;
|
||
font-weight: 400;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
user-select: text;
|
||
color: #dcddde;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
|
||
.member-perms::-webkit-scrollbar-thumb, .member-perms::-webkit-scrollbar-track,
|
||
#permissions-modal-wrapper *::-webkit-scrollbar-thumb, #permissions-modal-wrapper *::-webkit-scrollbar-track {
|
||
background-clip: padding-box;
|
||
border-radius: 7.5px;
|
||
border-style: solid;
|
||
border-width: 3px;
|
||
visibility: hidden;
|
||
}
|
||
|
||
.member-perms:hover::-webkit-scrollbar-thumb, .member-perms:hover::-webkit-scrollbar-track,
|
||
#permissions-modal-wrapper *:hover::-webkit-scrollbar-thumb, #permissions-modal-wrapper *:hover::-webkit-scrollbar-track {
|
||
visibility: visible;
|
||
}
|
||
|
||
.member-perms::-webkit-scrollbar-track,
|
||
#permissions-modal-wrapper *::-webkit-scrollbar-track {
|
||
border-width: initial;
|
||
background-color: transparent;
|
||
border: 2px solid transparent;
|
||
}
|
||
|
||
.member-perms::-webkit-scrollbar-thumb,
|
||
#permissions-modal-wrapper *::-webkit-scrollbar-thumb {
|
||
border: 2px solid transparent;
|
||
border-radius: 4px;
|
||
cursor: move;
|
||
background-color: rgba(32,34,37,.6);
|
||
}
|
||
|
||
.member-perms::-webkit-scrollbar,
|
||
#permissions-modal-wrapper *::-webkit-scrollbar {
|
||
height: 8px;
|
||
width: 8px;
|
||
}
|
||
|
||
|
||
|
||
.theme-light #permissions-modal-wrapper #permissions-modal {
|
||
background: #fff;
|
||
}
|
||
|
||
.theme-light #permissions-modal-wrapper .modal-body {
|
||
background: transparent;
|
||
}
|
||
|
||
.theme-light #permissions-modal-wrapper .header {
|
||
background: transparent;
|
||
color: #000;
|
||
}
|
||
|
||
.theme-light #permissions-modal-wrapper .role-side {
|
||
background: rgba(0,0,0,.2);
|
||
}
|
||
|
||
.theme-light #permissions-modal-wrapper .perm-side {
|
||
background: rgba(0,0,0,.1);
|
||
}
|
||
|
||
.theme-light #permissions-modal-wrapper .role-item,
|
||
.theme-light #permissions-modal-wrapper .perm-name {
|
||
color: #000;
|
||
}
|
||
|
||
#permissions-modal-wrapper #permissions-modal {
|
||
width: auto;
|
||
}`;
|
||
this.jumbo = `#permissions-modal-wrapper #permissions-modal {
|
||
height: 840px;
|
||
}
|
||
|
||
#permissions-modal-wrapper #permissions-modal .perm-side {
|
||
width: 500px;
|
||
}
|
||
|
||
#permissions-modal .perm-scroller {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-content: flex-start;
|
||
}
|
||
|
||
#permissions-modal .perm-item {
|
||
width: 50%;
|
||
}`;
|
||
this.sectionHTML = `<div class="section__6f61e" id="permissions-popout">
|
||
<h2 class="member-perms-header defaultColor__77578 eyebrow_e5a66c defaultColor__87d87 title_ef4a6d" data-text-variant="eyebrow">
|
||
<div class="member-perms-title">{{label}}</div>
|
||
<span class="perm-details">
|
||
<svg name="Details" viewBox="0 0 24 24" class="perm-details-button" fill="currentColor">
|
||
<path d="M0 0h24v24H0z" fill="none"/>
|
||
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
|
||
</svg>
|
||
</span>
|
||
</h2>
|
||
<div class="member-perms {{root}} {{roles}}"></div>
|
||
</div>`;
|
||
this.itemHTML = `<div class="member-perm {{role}} {{rolePill}}">
|
||
<div class="perm-circle {{roleCircle}}"></div>
|
||
<div class="name {{roleName}}"></div>
|
||
</div>`;
|
||
this.modalHTML = `<div id="permissions-modal-wrapper">
|
||
<div class="callout-backdrop {{backdrop}}"></div>
|
||
<div class="modal-wrapper">
|
||
<div id="permissions-modal" class="{{root}} {{small}}">
|
||
<div class="header"><div class="title">{{header}}</div></div>
|
||
<div class="modal-body">
|
||
<div class="role-side">
|
||
<span class="scroller-title role-list-title">{{rolesLabel}}</span>
|
||
<div class="role-scroller">
|
||
|
||
</div>
|
||
</div>
|
||
<div class="perm-side">
|
||
<span class="scroller-title perm-list-title">{{permissionsLabel}}</span>
|
||
<div class="perm-scroller">
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>`;
|
||
this.modalItem = `<div class="perm-item"><span class="perm-name"></span></div>`;
|
||
this.modalButton = `<div class="role-item"><span class="role-name"></span></div>`;
|
||
this.modalButtonUser = `<div class="role-item"><div class="wrapper_de5239 xsmall_d82b57"><div class="image__25781 xsmall_d82b57 perm-user-avatar" style="background-image: url('\\{{avatarUrl}}');"></div></div><span class="role-name marginLeft8_ff311d"></span></div>`;
|
||
this.permAllowedIcon = `<svg height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
|
||
this.permDeniedIcon = `<svg height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"/></svg>`;
|
||
|
||
this.cancelUserPopout = () => {};
|
||
this.contextMenuPatches = [];
|
||
}
|
||
|
||
onStart() {
|
||
DOM.addStyle(this.name, this.css);
|
||
|
||
this.sectionHTML = Utilities.formatString(this.sectionHTML, DiscordClasses.UserPopout);
|
||
this.sectionHTML = Utilities.formatString(this.sectionHTML, RoleClasses);
|
||
this.sectionHTML = Utilities.formatString(this.sectionHTML, UserPopoutClasses);
|
||
this.itemHTML = Utilities.formatString(this.itemHTML, RoleClasses);
|
||
this.modalHTML = Utilities.formatString(this.modalHTML, DiscordClasses.Backdrop);
|
||
this.modalHTML = Utilities.formatString(this.modalHTML, {root: ModalClasses.root, small: ModalClasses.small});
|
||
|
||
this.promises = {state: {cancelled: false}, cancel() {this.state.cancelled = true;}};
|
||
if (this.settings.popouts) this.bindPopouts();
|
||
if (this.settings.contextMenus) this.bindContextMenus();
|
||
this.setDisplayMode(this.settings.displayMode);
|
||
}
|
||
|
||
onStop() {
|
||
DOM.removeStyle(this.name);
|
||
this.promises.cancel();
|
||
this.unbindPopouts();
|
||
this.unbindContextMenus();
|
||
}
|
||
|
||
setDisplayMode(mode) {
|
||
if (mode === "cozy") DOM.addStyle(this.name + "-jumbo", this.jumbo);
|
||
else DOM.removeStyle(this.name + "-jumbo");
|
||
}
|
||
|
||
patchPopouts(e) {
|
||
const popoutMount = (props) => {
|
||
const popout = document.querySelector(`[class*="userPopout_"], [class*="userPopoutOuter_"]`);
|
||
if (!popout || popout.querySelector("#permissions-popout")) return;
|
||
const user = MemberStore.getMember(props.guildId, props.user.id);
|
||
const guild = GuildStore.getGuild(props.guildId);
|
||
const name = MemberStore.getNick(props.guildId, props.user.id) ?? props.user.username;
|
||
if (!user || !guild || !name) return;
|
||
|
||
const userRoles = user.roles.slice(0);
|
||
userRoles.push(guild.id);
|
||
userRoles.reverse();
|
||
let perms = 0n;
|
||
const permBlock = DOMTools.createElement(Utilities.formatString(this.sectionHTML, {label: this.strings.popoutLabel}));
|
||
const memberPerms = permBlock.querySelector(".member-perms");
|
||
const strings = Strings;
|
||
|
||
for (let r = 0; r < userRoles.length; r++) {
|
||
const role = userRoles[r];
|
||
if (!guild.roles[role]) continue;
|
||
perms = perms | guild.roles[role].permissions;
|
||
for (const perm in DiscordPerms) {
|
||
const permName = strings[perm] || perm.split("_").map(n => n[0].toUpperCase() + n.slice(1).toLowerCase()).join(" ");
|
||
const hasPerm = (perms & DiscordPerms[perm]) == DiscordPerms[perm];
|
||
if (hasPerm && !memberPerms.querySelector(`[data-name="${permName}"]`)) {
|
||
const element = DOMTools.createElement(this.itemHTML);
|
||
element.classList.add(RoleClasses.rolePill);
|
||
let roleColor = guild.roles[role].colorString;
|
||
element.querySelector(".name").textContent = permName;
|
||
element.setAttribute("data-name", permName);
|
||
if (!roleColor) roleColor = "#B9BBBE";
|
||
element.querySelector(".perm-circle").style.backgroundColor = ColorConverter.rgbToAlpha(roleColor, 1);
|
||
element.style.borderColor = ColorConverter.rgbToAlpha(roleColor, 0.6);
|
||
memberPerms.prepend(element);
|
||
}
|
||
}
|
||
}
|
||
|
||
permBlock.querySelector(".perm-details").addEventListener("click", () => {
|
||
this.showModal(this.createModalUser(name, user, guild));
|
||
});
|
||
let roleList = popout.querySelector(`[class*="roles_"]`);
|
||
roleList = roleList?.parentElement;
|
||
roleList?.parentNode?.insertBefore(permBlock, roleList.nextSibling);
|
||
|
||
|
||
|
||
const popoutInstance = ReactTools.getOwnerInstance(popout, {include: ["Popout"]});
|
||
if (!popoutInstance || !popoutInstance.updateOffsets) return;
|
||
popoutInstance.updateOffsets();
|
||
};
|
||
|
||
if (!e.addedNodes.length || !(e.addedNodes[0] instanceof Element)) return;
|
||
// console.log(e)
|
||
const element = e.addedNodes[0];
|
||
const popout = element.querySelector(`[class*="userPopout_"], [class*="userPopoutOuter_"]`) ?? element;
|
||
if (!popout || !popout.matches(`[class*="userPopout_"], [class*="userPopoutOuter_"]`)) return;
|
||
const props = Utilities.findInTree(ReactTools.getReactInstance(popout), m => m && m.user, {walkable: ["memoizedProps", "return"]});
|
||
popoutMount(props);
|
||
}
|
||
|
||
bindPopouts() {
|
||
this.observer = this.patchPopouts.bind(this);
|
||
}
|
||
|
||
unbindPopouts() {
|
||
this.observer = undefined;
|
||
}
|
||
|
||
async bindContextMenus() {
|
||
this.patchChannelContextMenu();
|
||
this.patchGuildContextMenu();
|
||
this.patchUserContextMenu();
|
||
}
|
||
|
||
unbindContextMenus() {
|
||
for (const cancel of this.contextMenuPatches) cancel();
|
||
}
|
||
|
||
patchGuildContextMenu() {
|
||
this.contextMenuPatches.push(ContextMenu.patch("guild-context", (retVal, props) => {
|
||
if (!props?.guild) return retVal; // Ignore non-guild items
|
||
const newItem = ContextMenu.buildItem({
|
||
label: this.strings.contextMenuLabel,
|
||
action: () => {
|
||
this.showModal(this.createModalGuild(props.guild.name, props.guild));
|
||
}
|
||
});
|
||
retVal.props.children.splice(1, 0, newItem);
|
||
}));
|
||
}
|
||
|
||
patchChannelContextMenu() {
|
||
this.contextMenuPatches.push(ContextMenu.patch("channel-context", (retVal, props) => {
|
||
const newItem = ContextMenu.buildItem({
|
||
label: this.strings.contextMenuLabel,
|
||
action: () => {
|
||
if (!Object.keys(props.channel.permissionOverwrites).length) return Toasts.info(`#${props.channel.name} has no permission overrides`);
|
||
this.showModal(this.createModalChannel(props.channel.name, props.channel, props.guild));
|
||
}
|
||
});
|
||
retVal.props.children.splice(1, 0, newItem);
|
||
}));
|
||
}
|
||
|
||
patchUserContextMenu() {
|
||
this.contextMenuPatches.push(ContextMenu.patch("user-context", (retVal, props) => {
|
||
const guild = GuildStore.getGuild(props.guildId);
|
||
if (!guild) return;
|
||
|
||
const newItem = ContextMenu.buildItem({
|
||
label: this.strings.contextMenuLabel,
|
||
action: () => {
|
||
const user = MemberStore.getMember(props.guildId, props.user.id);
|
||
const name = user.nick ? user.nick : props.user.username;
|
||
this.showModal(this.createModalUser(name, user, guild));
|
||
}
|
||
});
|
||
retVal?.props?.children[0]?.props?.children.splice(2, 0, newItem);
|
||
}));
|
||
}
|
||
|
||
showModal(modal) {
|
||
const popout = document.querySelector(`[class*="userPopoutOuter-"]`);
|
||
if (popout) popout.style.display = "none";
|
||
const app = document.querySelector(".app-19_DXt");
|
||
if (app) app.append(modal);
|
||
else document.querySelector("#app-mount").append(modal);
|
||
|
||
const closeModal = (event) => {
|
||
if (!event.key === "Escape") return;
|
||
modal.classList.add("closing");
|
||
setTimeout(() => {modal.remove();}, 300);
|
||
};
|
||
document.addEventListener("keydown", closeModal, true);
|
||
DOMTools.onRemoved(modal, () => document.removeEventListener("keydown", closeModal, true));
|
||
}
|
||
|
||
createModalChannel(name, channel, guild) {
|
||
return this.createModal(`#${name}`, channel.permissionOverwrites, guild.roles, true);
|
||
}
|
||
|
||
createModalUser(name, user, guild) {
|
||
const guildRoles = Object.assign({}, guild.roles);
|
||
const userRoles = user.roles.slice(0).filter(r => typeof(guildRoles[r]) !== "undefined");
|
||
|
||
userRoles.push(guild.id);
|
||
userRoles.sort((a, b) => {return guildRoles[b].position - guildRoles[a].position;});
|
||
|
||
if (user.userId == guild.ownerId) {
|
||
const ALL_PERMISSIONS = Object.values(DiscordModules.DiscordPermissions).reduce((all, p) => all | p);
|
||
userRoles.push(user.userId);
|
||
guildRoles[user.userId] = {name: this.strings.modal.owner, permissions: ALL_PERMISSIONS};
|
||
}
|
||
return this.createModal(name, userRoles, guildRoles);
|
||
}
|
||
|
||
createModalGuild(name, guild) {
|
||
return this.createModal(name, guild.roles);
|
||
}
|
||
|
||
createModal(title, displayRoles, referenceRoles, isOverride = false) {
|
||
if (!referenceRoles) referenceRoles = displayRoles;
|
||
const modal = DOMTools.createElement(Utilities.formatString(Utilities.formatString(this.modalHTML, this.strings.modal), {name: Utils.escapeHTML(title)}));
|
||
const closeModal = () => {
|
||
modal.classList.add("closing");
|
||
setTimeout(() => {modal.remove();}, 300);
|
||
};
|
||
modal.querySelector(".callout-backdrop").addEventListener("click", closeModal);
|
||
|
||
const strings = Strings || {};
|
||
for (const r in displayRoles) {
|
||
const role = Array.isArray(displayRoles) ? displayRoles[r] : r;
|
||
const user = UserStore.getUser(role) || {getAvatarURL: () => AvatarDefaults.DEFAULT_AVATARS[Math.floor(Math.random() * AvatarDefaults.DEFAULT_AVATARS.length)], username: role};
|
||
const member = MemberStore.getMember(SelectedGuildStore.getGuildId(), role) || {colorString: ""};
|
||
const item = DOMTools.createElement(!isOverride || displayRoles[role].type == 0 ? this.modalButton : Utilities.formatString(this.modalButtonUser, {avatarUrl: user.getAvatarURL(null, 16, true)})); // getAvatarURL(guildId, size, canAnimate);
|
||
if (!isOverride || displayRoles[role].type == 0) item.style.color = referenceRoles[role].colorString;
|
||
else item.style.color = member.colorString;
|
||
if (isOverride) item.querySelector(".role-name").innerHTML = Utils.escapeHTML(displayRoles[role].type == 0 ? referenceRoles[role].name : user.username);
|
||
else item.querySelector(".role-name").innerHTML = Utils.escapeHTML(referenceRoles[role].name);
|
||
modal.querySelector(".role-scroller").append(item);
|
||
item.addEventListener("click", () => {
|
||
modal.querySelectorAll(".role-item.selected").forEach(e => e.classList.remove("selected"));
|
||
item.classList.add("selected");
|
||
const allowed = isOverride ? displayRoles[role].allow : referenceRoles[role].permissions;
|
||
const denied = isOverride ? displayRoles[role].deny : null;
|
||
|
||
const permList = modal.querySelector(".perm-scroller");
|
||
permList.innerHTML = "";
|
||
for (const perm in DiscordPerms) {
|
||
const element = DOMTools.createElement(this.modalItem);
|
||
const permAllowed = (allowed & DiscordPerms[perm]) == DiscordPerms[perm];
|
||
const permDenied = isOverride ? (denied & DiscordPerms[perm]) == DiscordPerms[perm] : !permAllowed;
|
||
if (!permAllowed && !permDenied) continue;
|
||
if (permAllowed) {
|
||
element.classList.add("allowed");
|
||
element.prepend(DOMTools.createElement(this.permAllowedIcon));
|
||
}
|
||
if (permDenied) {
|
||
element.classList.add("denied");
|
||
element.prepend(DOMTools.createElement(this.permDeniedIcon));
|
||
}
|
||
element.querySelector(".perm-name").textContent = strings[perm] || perm.split("_").map(n => n[0].toUpperCase() + n.slice(1).toLowerCase()).join(" ");
|
||
permList.append(element);
|
||
}
|
||
});
|
||
item.addEventListener("contextmenu", (e) => {
|
||
ContextMenu.open(e, ContextMenu.buildMenu([
|
||
{label: Strings.COPY_ID ?? "Copy Id", action: () => {DiscordModules.ElectronModule.copy(role);}}
|
||
]));
|
||
});
|
||
}
|
||
|
||
modal.querySelector(".role-item").click();
|
||
|
||
return modal;
|
||
}
|
||
|
||
getSettingsPanel() {
|
||
const panel = this.buildSettingsPanel();
|
||
panel.addListener((id, checked) => {
|
||
if (id == "popouts") {
|
||
if (checked) this.bindPopouts();
|
||
else this.unbindPopouts();
|
||
}
|
||
if (id == "contextMenus") {
|
||
if (checked) this.bindContextMenus();
|
||
this.unbindContextMenus();
|
||
}
|
||
if (id == "displayMode") this.setDisplayMode(checked);
|
||
});
|
||
return panel.getElement();
|
||
}
|
||
|
||
};
|
||
};
|
||
return plugin(Plugin, Api);
|
||
})(global.ZeresPluginLibrary.buildPlugin(config));
|
||
/*@end@*/ |