diff --git a/packages/web/src/components/layout/ChannelSidebar.tsx b/packages/web/src/components/layout/ChannelSidebar.tsx index 64b0cee1..51c4ced8 100644 --- a/packages/web/src/components/layout/ChannelSidebar.tsx +++ b/packages/web/src/components/layout/ChannelSidebar.tsx @@ -602,7 +602,7 @@ export function ChannelSidebar() { {/* Channels — dynamic category layout */} -
e.preventDefault()} onContextMenu={handleSidebarContextMenu}> +
{/* Uncategorized channels */} {uncategorizedChannels.length > 0 && (
@@ -627,20 +627,17 @@ export function ChannelSidebar() { isActive={currentChannelId === channel.id} isUnread={unreadChannels.has(channel.id) && currentChannelId !== channel.id} canManage={canManageChannels} - isDragging={channelDragState?.dragType === 'channel' && channelDragState.dragId === channel.id} - dropIndicator={dropIndicator?.targetId === channel.id ? dropIndicator.position : null} + isDragging={activeDrag?.type === 'channel' && activeDrag.dragId === channel.id} + dropIndicator={dropTarget?.targetId === channel.id ? dropTarget.position : null} onChannelClick={channel.type === 'voice' ? (() => { const chPerms = channelPermissions.get(channel.id); const canConnect = hasPermissionBit(chPerms, PermissionBits.CONNECT); if (canConnect) handleVoiceJoin(channel.id); }) : (() => handleChannelClick(channel.id))} onSettingsClick={() => openModal('channelSettings', { channelId: channel.id })} - onDragStart={(e) => handleChannelDragStart(e, channel.id)} - onDragOver={(e) => handleChannelDragOver(e, channel.id, 'channel')} - onDragEnd={handleDragEnd} - voiceDragState={voiceDragState} - onVoiceDragStart={(userId: string) => setVoiceDragState({ userId, fromChannelId: channel.id })} - onVoiceDragEnd={() => setVoiceDragState(null)} + channelDragHandlers={channelHandlers(channel.id)} + voiceUserHandlers={voiceUserHandlers} + voiceChannelDropZone={voiceChannelDropZone(channel.id)} channelPermissions={channelPermissions} handleVoiceJoin={handleVoiceJoin} /> @@ -658,12 +655,9 @@ export function ChannelSidebar() { const categoryHeader = (
handleCategoryDragStart(e, category.id)} - onDragEnd={handleDragEnd} - onDragOver={(e) => handleChannelDragOver(e, category.id, 'category')} + activeDrag?.type === 'category' && activeDrag.dragId === category.id ? 'opacity-50' : '' + } ${dropTarget?.targetId === category.id && dropTarget.targetType === 'category' ? 'ring-1 ring-accent-mint/40 rounded' : ''}`} + {...categoryHandlers(category.id)} onClick={() => toggleCollapse(category.id)} >
@@ -728,20 +722,17 @@ export function ChannelSidebar() { isActive={currentChannelId === channel.id} isUnread={unreadChannels.has(channel.id) && currentChannelId !== channel.id} canManage={canManageChannels} - isDragging={channelDragState?.dragType === 'channel' && channelDragState.dragId === channel.id} - dropIndicator={dropIndicator?.targetId === channel.id ? dropIndicator.position : null} + isDragging={activeDrag?.type === 'channel' && activeDrag.dragId === channel.id} + dropIndicator={dropTarget?.targetId === channel.id ? dropTarget.position : null} onChannelClick={channel.type === 'voice' ? (() => { const chPerms = channelPermissions.get(channel.id); const canConnect = hasPermissionBit(chPerms, PermissionBits.CONNECT); if (canConnect) handleVoiceJoin(channel.id); }) : (() => handleChannelClick(channel.id))} onSettingsClick={() => openModal('channelSettings', { channelId: channel.id })} - onDragStart={(e) => handleChannelDragStart(e, channel.id)} - onDragOver={(e) => handleChannelDragOver(e, channel.id, 'channel')} - onDragEnd={handleDragEnd} - voiceDragState={voiceDragState} - onVoiceDragStart={(userId: string) => setVoiceDragState({ userId, fromChannelId: channel.id })} - onVoiceDragEnd={() => setVoiceDragState(null)} + channelDragHandlers={channelHandlers(channel.id)} + voiceUserHandlers={voiceUserHandlers} + voiceChannelDropZone={voiceChannelDropZone(channel.id)} channelPermissions={channelPermissions} handleVoiceJoin={handleVoiceJoin} /> @@ -1221,12 +1212,9 @@ function ChannelItem({ dropIndicator, onChannelClick, onSettingsClick, - onDragStart, - onDragOver, - onDragEnd, - voiceDragState, - onVoiceDragStart, - onVoiceDragEnd, + channelDragHandlers, + voiceUserHandlers, + voiceChannelDropZone, channelPermissions, handleVoiceJoin, }: { @@ -1238,12 +1226,26 @@ function ChannelItem({ dropIndicator: 'before' | 'after' | null; onChannelClick: () => void; onSettingsClick: () => void; - onDragStart: (e: React.DragEvent) => void; - onDragOver: (e: React.DragEvent) => void; - onDragEnd: () => void; - voiceDragState: { userId: string; fromChannelId: string } | null; - onVoiceDragStart: (userId: string) => void; - onVoiceDragEnd: () => void; + channelDragHandlers: { + draggable: boolean; + onDragStart: (e: React.DragEvent) => void; + onDragOver: (e: React.DragEvent) => void; + onDragEnd: () => void; + }; + voiceUserHandlers: (userId: string, channelId: string) => { + draggable: boolean; + isBeingDragged: boolean; + onDragStart: (e: React.DragEvent) => void; + onDragEnd: (e: React.DragEvent) => void; + }; + voiceChannelDropZone: { + onDragOver: (e: React.DragEvent) => void; + onDragEnter: (e: React.DragEvent) => void; + onDragLeave: (e: React.DragEvent) => void; + onDrop: (e: React.DragEvent) => void; + isDragOver: boolean; + isValidTarget: boolean; + }; channelPermissions: Map; handleVoiceJoin: (channelId: string) => void; }) { @@ -1253,10 +1255,7 @@ function ChannelItem({ return (
{dropIndicator === 'before' &&
} {dropIndicator === 'after' &&
}
@@ -1278,10 +1276,7 @@ function ChannelItem({ return (
{dropIndicator === 'before' &&
}