PermViewer: Fix context menu for roleless users & muted channels (#1138)
Co-authored-by: V <vendicated@riseup.net> Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
This commit is contained in:
parent
bb83c0b672
commit
263884cbd8
|
@ -25,14 +25,14 @@ type ContextMenuPatchCallbackReturn = (() => void) | void;
|
||||||
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
|
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
|
||||||
* @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates)
|
* @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates)
|
||||||
*/
|
*/
|
||||||
export type NavContextMenuPatchCallback = (children: Array<React.ReactElement>, ...args: Array<any>) => ContextMenuPatchCallbackReturn;
|
export type NavContextMenuPatchCallback = (children: Array<ReactElement | null>, ...args: Array<any>) => ContextMenuPatchCallbackReturn;
|
||||||
/**
|
/**
|
||||||
* @param navId The navId of the context menu being patched
|
* @param navId The navId of the context menu being patched
|
||||||
* @param children The rendered context menu elements
|
* @param children The rendered context menu elements
|
||||||
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
|
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
|
||||||
* @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates)
|
* @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates)
|
||||||
*/
|
*/
|
||||||
export type GlobalContextMenuPatchCallback = (navId: string, children: Array<React.ReactElement>, ...args: Array<any>) => ContextMenuPatchCallbackReturn;
|
export type GlobalContextMenuPatchCallback = (navId: string, children: Array<ReactElement | null>, ...args: Array<any>) => ContextMenuPatchCallbackReturn;
|
||||||
|
|
||||||
const ContextMenuLogger = new Logger("ContextMenu");
|
const ContextMenuLogger = new Logger("ContextMenu");
|
||||||
|
|
||||||
|
@ -89,15 +89,18 @@ export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallba
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper function for finding the children array of a group nested inside a context menu based on the id of one of its childs
|
* A helper function for finding the children array of a group nested inside a context menu based on the id(s) of its children
|
||||||
* @param id The id of the child
|
* @param id The id of the child. If an array is specified, all ids will be tried
|
||||||
* @param children The context menu children
|
* @param children The context menu children
|
||||||
*/
|
*/
|
||||||
export function findGroupChildrenByChildId(id: string, children: Array<React.ReactElement>, _itemsArray?: Array<React.ReactElement>): Array<React.ReactElement> | null {
|
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement | null>, _itemsArray?: Array<ReactElement | null>): Array<ReactElement | null> | null {
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
if (child == null) continue;
|
if (child == null) continue;
|
||||||
|
|
||||||
if (child.props?.id === id) return _itemsArray ?? null;
|
if (
|
||||||
|
(Array.isArray(id) && id.some(id => child.props?.id === id))
|
||||||
|
|| child.props?.id === id
|
||||||
|
) return _itemsArray ?? null;
|
||||||
|
|
||||||
let nextChildren = child.props?.children;
|
let nextChildren = child.props?.children;
|
||||||
if (nextChildren) {
|
if (nextChildren) {
|
||||||
|
@ -117,7 +120,7 @@ export function findGroupChildrenByChildId(id: string, children: Array<React.Rea
|
||||||
interface ContextMenuProps {
|
interface ContextMenuProps {
|
||||||
contextMenuApiArguments?: Array<any>;
|
contextMenuApiArguments?: Array<any>;
|
||||||
navId: string;
|
navId: string;
|
||||||
children: Array<ReactElement>;
|
children: Array<ReactElement | null>;
|
||||||
"aria-label": string;
|
"aria-label": string;
|
||||||
onSelect: (() => void) | undefined;
|
onSelect: (() => void) | undefined;
|
||||||
onClose: (callback: (...args: Array<any>) => any) => void;
|
onClose: (callback: (...args: Array<any>) => any) => void;
|
||||||
|
|
|
@ -57,6 +57,8 @@ export const settings = definePluginSettings({
|
||||||
});
|
});
|
||||||
|
|
||||||
function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) {
|
function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) {
|
||||||
|
if (type === MenuItemParentType.User && !GuildMemberStore.isMember(guildId, id!)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
id="perm-viewer-permissions"
|
id="perm-viewer-permissions"
|
||||||
|
@ -122,25 +124,32 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeContextMenuPatch(childId: string, type?: MenuItemParentType): NavContextMenuPatchCallback {
|
function makeContextMenuPatch(childId: string | string[], type?: MenuItemParentType): NavContextMenuPatchCallback {
|
||||||
return (children, props) => () => {
|
return (children, props) => () => {
|
||||||
if (!props) return children;
|
if (!props) return children;
|
||||||
|
|
||||||
const group = findGroupChildrenByChildId(childId, children);
|
const group = findGroupChildrenByChildId(childId, children);
|
||||||
|
|
||||||
if (group) {
|
const item = (() => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MenuItemParentType.User:
|
case MenuItemParentType.User:
|
||||||
group.push(MenuItem(props.guildId, props.user.id, type));
|
return MenuItem(props.guildId, props.user.id, type);
|
||||||
break;
|
|
||||||
case MenuItemParentType.Channel:
|
case MenuItemParentType.Channel:
|
||||||
group.push(MenuItem(props.guild.id, props.channel.id, type));
|
return MenuItem(props.guild.id, props.channel.id, type);
|
||||||
break;
|
|
||||||
case MenuItemParentType.Guild:
|
case MenuItemParentType.Guild:
|
||||||
group.push(MenuItem(props.guild.id));
|
return MenuItem(props.guild.id);
|
||||||
break;
|
default:
|
||||||
}
|
return null;
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (item == null) return;
|
||||||
|
|
||||||
|
if (group)
|
||||||
|
group.push(item);
|
||||||
|
else if (childId === "roles" && props.guildId)
|
||||||
|
// "roles" may not be present due to the member not having any roles. In that case, add it above "Copy ID"
|
||||||
|
children.splice(-1, 0, <Menu.MenuGroup>{item}</Menu.MenuGroup>);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,10 +169,10 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
UserPermissions: (guild: Guild, guildMember: GuildMember) => <UserPermissions guild={guild} guildMember={guildMember} />,
|
UserPermissions: (guild: Guild, guildMember?: GuildMember) => !!guildMember && <UserPermissions guild={guild} guildMember={guildMember} />,
|
||||||
|
|
||||||
userContextMenuPatch: makeContextMenuPatch("roles", MenuItemParentType.User),
|
userContextMenuPatch: makeContextMenuPatch("roles", MenuItemParentType.User),
|
||||||
channelContextMenuPatch: makeContextMenuPatch("mute-channel", MenuItemParentType.Channel),
|
channelContextMenuPatch: makeContextMenuPatch(["mute-channel", "unmute-channel"], MenuItemParentType.Channel),
|
||||||
guildContextMenuPatch: makeContextMenuPatch("privacy", MenuItemParentType.Guild),
|
guildContextMenuPatch: makeContextMenuPatch("privacy", MenuItemParentType.Guild),
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 5px;
|
padding: 8px 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 165px;
|
width: 230px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-permviewer-perms-list-item > div {
|
.vc-permviewer-perms-list-item > div {
|
||||||
|
@ -121,6 +121,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
scale: 0.9;
|
scale: 0.9;
|
||||||
|
transition: color ease-in 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-permviewer-perms-perms-item .vc-info-icon:hover {
|
.vc-permviewer-perms-perms-item .vc-info-icon:hover {
|
||||||
|
|
|
@ -38,7 +38,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag
|
||||||
// dms and group chats
|
// dms and group chats
|
||||||
const dmGroup = findGroupChildrenByChildId("pin", children);
|
const dmGroup = findGroupChildrenByChildId("pin", children);
|
||||||
if (dmGroup && !dmGroup.some(child => child?.props?.id === "reply")) {
|
if (dmGroup && !dmGroup.some(child => child?.props?.id === "reply")) {
|
||||||
const pinIndex = dmGroup.findIndex(c => c.props.id === "pin");
|
const pinIndex = dmGroup.findIndex(c => c?.props.id === "pin");
|
||||||
return dmGroup.splice(pinIndex + 1, 0, (
|
return dmGroup.splice(pinIndex + 1, 0, (
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
id="reply"
|
id="reply"
|
||||||
|
|
Loading…
Reference in a new issue