diff --git a/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.module.css b/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.module.css index e06bf7b1f..e0d985d0e 100644 --- a/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.module.css +++ b/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.module.css @@ -174,7 +174,8 @@ cursor: pointer; background: transparent; } -.overwriteItem:not(.overwriteItemSelected):hover { +.overwriteItem:not(.overwriteItemSelected):hover, +.overwriteItemContextMenuOpen:not(.overwriteItemSelected) { background: var(--background-modifier-hover); } .overwriteItemSelected { diff --git a/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.tsx b/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.tsx index 466d35199..6f277a7fa 100644 --- a/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.tsx +++ b/fluxer_app/src/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.tsx @@ -519,27 +519,34 @@ const ChannelPermissionsTab: React.FC<{channelId: string}> = observer(({channelI }); setSelectedOverwriteId(id); }, []); - const handleDeleteOverride = useCallback(() => { - if (!selectedOverwrite || !guild || selectedOverwrite.id === guild.id) return; - const futureOverwrites = overwritesWithUpdates.filter((o) => o.id !== selectedOverwrite.id); - const currentIndex = overwritesWithUpdates.findIndex((o) => o.id === selectedOverwrite.id); - const nextOverwrite = futureOverwrites[currentIndex] ?? futureOverwrites[0]; - if (newOverwriteIds.has(selectedOverwrite.id)) { - setNewOverwriteIds((prev) => { - const newSet = new Set(prev); - newSet.delete(selectedOverwrite.id); - return newSet; + const removeOverwrite = useCallback( + (overwriteId: string) => { + if (!guild || overwriteId === guild.id) return; + const futureOverwrites = overwritesWithUpdates.filter((o) => o.id !== overwriteId); + const currentIndex = overwritesWithUpdates.findIndex((o) => o.id === overwriteId); + const nextOverwrite = futureOverwrites[currentIndex] ?? futureOverwrites[0]; + if (newOverwriteIds.has(overwriteId)) { + setNewOverwriteIds((prev) => { + const newSet = new Set(prev); + newSet.delete(overwriteId); + return newSet; + }); + } else { + setDeletedOverwriteIds((prev) => new Set(prev).add(overwriteId)); + } + setOverwriteUpdates((prev) => { + const newMap = new Map(prev); + newMap.delete(overwriteId); + return newMap; }); - } else { - setDeletedOverwriteIds((prev) => new Set(prev).add(selectedOverwrite.id)); - } - setOverwriteUpdates((prev) => { - const newMap = new Map(prev); - newMap.delete(selectedOverwrite.id); - return newMap; - }); - setSelectedOverwriteId(nextOverwrite?.id ?? null); - }, [selectedOverwrite, guild, overwritesWithUpdates, newOverwriteIds]); + setSelectedOverwriteId((prev) => (prev === overwriteId ? (nextOverwrite?.id ?? null) : prev)); + }, + [guild, overwritesWithUpdates, newOverwriteIds], + ); + const handleDeleteOverride = useCallback(() => { + if (!selectedOverwrite) return; + removeOverwrite(selectedOverwrite.id); + }, [selectedOverwrite, removeOverwrite]); const getOverwriteName = useCallback( (overwrite: PermissionOverwrite): string => { if (!guild) return ''; @@ -585,6 +592,8 @@ const ChannelPermissionsTab: React.FC<{channelId: string}> = observer(({channelI selectedOverwriteId={selectedOverwriteId} canManageChannels={canManageChannels} canManageRoles={canManageRoles} + canManageOverwrites={canManageChannels && canManageRoles} + onDeleteOverwrite={removeOverwrite} isAddOverrideOpen={isAddOverrideOpen} setIsAddOverrideOpen={setIsAddOverrideOpen} existingOverwriteIds={existingOverwriteIds} @@ -615,6 +624,7 @@ const ChannelPermissionsTab: React.FC<{channelId: string}> = observer(({channelI overwritesWithUpdates, canManageChannels, canManageRoles, + removeOverwrite, isAddOverrideOpen, addOverrideContext, addOverrideFloatingStyles, diff --git a/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/ChannelPermissionsSidebar.tsx b/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/ChannelPermissionsSidebar.tsx index cb82f297c..a928f8352 100644 --- a/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/ChannelPermissionsSidebar.tsx +++ b/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/ChannelPermissionsSidebar.tsx @@ -17,6 +17,8 @@ interface ChannelPermissionsSidebarProps { selectedOverwriteId: string | null; canManageChannels: boolean; canManageRoles: boolean; + canManageOverwrites: boolean; + onDeleteOverwrite: (overwriteId: string) => void; isAddOverrideOpen: boolean; setIsAddOverrideOpen: (open: boolean) => void; existingOverwriteIds: Set; @@ -40,6 +42,8 @@ export const ChannelPermissionsSidebar: React.FC selectedOverwriteId, canManageChannels, canManageRoles, + canManageOverwrites, + onDeleteOverwrite, isAddOverrideOpen, setIsAddOverrideOpen, existingOverwriteIds, @@ -136,6 +140,8 @@ export const ChannelPermissionsSidebar: React.FC roleId={roleId} isSelected={isSelected} isEveryone={isEveryone} + canDelete={canManageOverwrites && !isEveryone} + onDelete={onDeleteOverwrite} onClick={() => onSelectOverwrite(overwrite.id)} guildId={guildId} data-flx="channel.channel-tabs.channel-permissions-tab.sidebar-content.overwrite-item.set-selected-overwrite-id" diff --git a/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteContextMenu.tsx b/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteContextMenu.tsx new file mode 100644 index 000000000..8cd4c69ef --- /dev/null +++ b/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteContextMenu.tsx @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import type {PermissionOverwrite} from '@app/features/channel/components/modals/channel_tabs/channel_permissions_tab/shared'; +import {DeleteIcon} from '@app/features/ui/action_menu/ContextMenuIcons'; +import {CopyRoleIdMenuItem, CopyUserIdMenuItem} from '@app/features/ui/action_menu/items/CopyMenuItems'; +import {MenuGroup} from '@app/features/ui/action_menu/MenuGroup'; +import {MenuItem} from '@app/features/ui/action_menu/MenuItem'; +import type {User} from '@app/features/user/models/User'; +import {msg} from '@lingui/core/macro'; +import {useLingui} from '@lingui/react/macro'; +import {observer} from 'mobx-react-lite'; +import type React from 'react'; + +const DELETE_ROLE_DESCRIPTOR = msg({ + message: 'Delete role', + comment: + 'Danger action in the channel permissions overwrite context menu that removes a role permission override from the channel.', +}); +const DELETE_USER_DESCRIPTOR = msg({ + message: 'Delete user', + comment: + 'Danger action in the channel permissions overwrite context menu that removes a member permission override from the channel.', +}); + +interface OverwriteContextMenuProps { + overwrite: PermissionOverwrite; + roleId: string | null; + user: User | null; + canDelete: boolean; + onDelete: () => void; + onClose: () => void; +} + +export const OverwriteContextMenu: React.FC = observer( + ({overwrite, roleId, user, canDelete, onDelete, onClose}) => { + const {i18n} = useLingui(); + return ( + <> + + {overwrite.type === 0 && roleId ? ( + + ) : user ? ( + + ) : null} + + {canDelete && ( + + + } + danger + onClick={onDelete} + data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-context-menu.menu-item.delete" + > + {overwrite.type === 0 ? i18n._(DELETE_ROLE_DESCRIPTOR) : i18n._(DELETE_USER_DESCRIPTOR)} + + + )} + + ); + }, +); diff --git a/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteItem.tsx b/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteItem.tsx index 14a3cd156..116195a6b 100644 --- a/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteItem.tsx +++ b/fluxer_app/src/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteItem.tsx @@ -1,16 +1,18 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import {DEFAULT_ROLE_COLOR_HEX, getRoleColor} from '@app/features/app/components/dialogs/shared/PermissionComponents'; +import {useContextMenuHoverState} from '@app/features/app/hooks/useContextMenuHoverState'; import styles from '@app/features/channel/components/modals/channel_tabs/ChannelPermissionsTab.module.css'; +import {OverwriteContextMenu} from '@app/features/channel/components/modals/channel_tabs/channel_permissions_tab/OverwriteContextMenu'; import type {PermissionOverwrite} from '@app/features/channel/components/modals/channel_tabs/channel_permissions_tab/shared'; -import {openRoleContextMenu} from '@app/features/ui/action_menu/RoleContextMenu'; +import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands'; import {Avatar} from '@app/features/ui/components/Avatar'; import type {User} from '@app/features/user/models/User'; import {UsersIcon} from '@phosphor-icons/react'; import {clsx} from 'clsx'; import {observer} from 'mobx-react-lite'; import type React from 'react'; -import {useCallback} from 'react'; +import {useCallback, useRef} from 'react'; interface OverwriteItemProps { overwrite: PermissionOverwrite; @@ -20,26 +22,43 @@ interface OverwriteItemProps { roleId?: string | null; isSelected: boolean; isEveryone: boolean; + canDelete: boolean; + onDelete: (overwriteId: string) => void; onClick: () => void; guildId: string; } export const OverwriteItem: React.FC = observer( - ({overwrite, name, color, user, roleId, isSelected, isEveryone, onClick, guildId}) => { + ({overwrite, name, color, user, roleId, isSelected, isEveryone, canDelete, onDelete, onClick, guildId}) => { + const buttonRef = useRef(null); + const contextMenuOpen = useContextMenuHoverState(buttonRef); const handleContextMenu = useCallback( (event: React.MouseEvent) => { - if (!roleId) return; - openRoleContextMenu(event, roleId); + ContextMenuCommands.openFromEvent(event, ({onClose}) => ( + onDelete(overwrite.id)} + onClose={onClose} + data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-item.handle-context-menu.overwrite-context-menu" + /> + )); }, - [roleId], + [overwrite, roleId, user, canDelete, onDelete], ); return (