mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(app): overwrite context menu hover and delete danger item (#2106)
This commit is contained in:
1 parent
8f20b29b16
commit
b1f7c78c7e
5 files changed
+136
-29
No files matched your search
+2
-1
@@ -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 {
|
||||
|
||||
+30
-20
@@ -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,
|
||||
|
||||
+6
@@ -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<string>;
|
||||
@@ -40,6 +42,8 @@ export const ChannelPermissionsSidebar: React.FC<ChannelPermissionsSidebarProps>
|
||||
selectedOverwriteId,
|
||||
canManageChannels,
|
||||
canManageRoles,
|
||||
canManageOverwrites,
|
||||
onDeleteOverwrite,
|
||||
isAddOverrideOpen,
|
||||
setIsAddOverrideOpen,
|
||||
existingOverwriteIds,
|
||||
@@ -136,6 +140,8 @@ export const ChannelPermissionsSidebar: React.FC<ChannelPermissionsSidebarProps>
|
||||
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"
|
||||
|
||||
+71
@@ -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<OverwriteContextMenuProps> = observer(
|
||||
({overwrite, roleId, user, canDelete, onDelete, onClose}) => {
|
||||
const {i18n} = useLingui();
|
||||
return (
|
||||
<>
|
||||
<MenuGroup data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-context-menu.menu-group">
|
||||
{overwrite.type === 0 && roleId ? (
|
||||
<CopyRoleIdMenuItem
|
||||
roleId={roleId}
|
||||
onClose={onClose}
|
||||
data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-context-menu.copy-role-id-menu-item"
|
||||
/>
|
||||
) : user ? (
|
||||
<CopyUserIdMenuItem
|
||||
user={user}
|
||||
onClose={onClose}
|
||||
data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-context-menu.copy-user-id-menu-item"
|
||||
/>
|
||||
) : null}
|
||||
</MenuGroup>
|
||||
{canDelete && (
|
||||
<MenuGroup data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-context-menu.menu-group--2">
|
||||
<MenuItem
|
||||
icon={
|
||||
<DeleteIcon data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-context-menu.delete-icon" />
|
||||
}
|
||||
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)}
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
+27
-8
@@ -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<OverwriteItemProps> = observer(
|
||||
({overwrite, name, color, user, roleId, isSelected, isEveryone, onClick, guildId}) => {
|
||||
({overwrite, name, color, user, roleId, isSelected, isEveryone, canDelete, onDelete, onClick, guildId}) => {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const contextMenuOpen = useContextMenuHoverState(buttonRef);
|
||||
const handleContextMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (!roleId) return;
|
||||
openRoleContextMenu(event, roleId);
|
||||
ContextMenuCommands.openFromEvent(event, ({onClose}) => (
|
||||
<OverwriteContextMenu
|
||||
overwrite={overwrite}
|
||||
roleId={roleId ?? null}
|
||||
user={user ?? null}
|
||||
canDelete={canDelete}
|
||||
onDelete={() => 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 (
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
aria-pressed={isSelected}
|
||||
className={clsx(styles.overwriteItem, {[styles.overwriteItemSelected]: isSelected})}
|
||||
className={clsx(styles.overwriteItem, {
|
||||
[styles.overwriteItemSelected]: isSelected,
|
||||
[styles.overwriteItemContextMenuOpen]: contextMenuOpen && !isSelected,
|
||||
})}
|
||||
onClick={onClick}
|
||||
onContextMenu={roleId ? handleContextMenu : undefined}
|
||||
onContextMenu={handleContextMenu}
|
||||
data-flx="channel.channel-tabs.channel-permissions-tab.overwrite-item.overwrite-item.click.button"
|
||||
>
|
||||
{overwrite.type === 0 && !isEveryone ? (
|
||||
|
||||
Reference in new issue
Block a user