diff --git a/src/components/DownloadItem.tsx b/src/components/DownloadItem.tsx index d0e5e50..970ce7c 100644 --- a/src/components/DownloadItem.tsx +++ b/src/components/DownloadItem.tsx @@ -15,7 +15,7 @@ interface DownloadItemProps { handlePause: (id: string, skipConfirm?: boolean) => void; handleResume: (item: DownloadItemType) => void; getCategoryIcon: (category: string) => React.ReactNode; - isSelected: boolean; + selectedIds: Set; onClick: (e: React.MouseEvent, item: DownloadItemType) => void; } @@ -27,7 +27,7 @@ export const DownloadItem = React.memo(({ handlePause, handleResume, getCategoryIcon, - isSelected, + selectedIds, onClick, }) => { const download = useDownloadStore(state => state.downloads.find(d => d.id === downloadId)); @@ -83,7 +83,7 @@ export const DownloadItem = React.memo(({ return (
onClick(e, download)} onContextMenu={(e) => { @@ -200,7 +200,7 @@ export const DownloadItem = React.memo(({ {(download.status === 'queued' || download.status === 'staged') && queueIndex !== -1 && ( <>
) : ( -
+
{sortedDownloads.map((d, index) => ( = ({ filter }) => { handlePause={handlePause} handleResume={handleResume} getCategoryIcon={getCategoryIcon} - isSelected={selectedIds.has(d.id)} + selectedIds={selectedIds} onClick={handleItemClick} /> ))} diff --git a/src/store/useDownloadStore.ts b/src/store/useDownloadStore.ts index caf4758..fc347c6 100644 --- a/src/store/useDownloadStore.ts +++ b/src/store/useDownloadStore.ts @@ -178,7 +178,7 @@ interface DownloadState { registerBackendIds: (ids: string[]) => void; unregisterBackendIds: (ids: string[]) => void; applyProperties: (id: string, updates: Partial) => Promise; - moveInQueue: (id: string, direction: 'up' | 'down') => Promise; + moveInQueue: (ids: string | string[], direction: 'up' | 'down') => Promise; removeFromQueue: (id: string) => Promise; isAddModalOpen: boolean; pendingAddUrls: string; @@ -222,10 +222,15 @@ export const useDownloadStore = create((set, get) => ({ queues: [{ id: MAIN_QUEUE_ID, name: 'Main Queue', isMain: true }], pendingOrder: [], setPendingOrder: (order) => set({ pendingOrder: order }), - moveInQueue: async (id, direction) => { - const item = get().downloads.find(download => download.id === id); - if (!item) return; - const queueId = item.queueId || MAIN_QUEUE_ID; + moveInQueue: async (idOrIds, direction) => { + const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds]; + if (ids.length === 0) return; + + // Assume all items belong to the same queue as the first item + const firstItem = get().downloads.find(d => d.id === ids[0]); + if (!firstItem) return; + const queueId = firstItem.queueId || MAIN_QUEUE_ID; + const queueItems = get().downloads .filter(download => (download.queueId || MAIN_QUEUE_ID) === queueId && @@ -233,11 +238,28 @@ export const useDownloadStore = create((set, get) => ({ !(isActiveDownloadStatus(download.status) && download.status !== 'queued') ) .sort((left, right) => (left.queuePosition ?? 0) - (right.queuePosition ?? 0)); - const index = queueItems.findIndex(download => download.id === id); - const target = direction === 'up' ? index - 1 : index + 1; - if (index < 0 || target < 0 || target >= queueItems.length) return; - const reordered = [...queueItems]; - [reordered[index], reordered[target]] = [reordered[target], reordered[index]]; + + const selectedItems = queueItems.filter(item => ids.includes(item.id)); + if (selectedItems.length === 0) return; + + const unselectedItems = queueItems.filter(item => !ids.includes(item.id)); + const selectedIndices = selectedItems.map(item => queueItems.findIndex(d => d.id === item.id)); + + let insertIndex = 0; + if (direction === 'up') { + const firstSelectedIndex = Math.min(...selectedIndices); + insertIndex = Math.max(0, firstSelectedIndex - 1); + } else { + const lastSelectedIndex = Math.max(...selectedIndices); + insertIndex = Math.min(unselectedItems.length, lastSelectedIndex - selectedItems.length + 2); + } + + const reordered = [ + ...unselectedItems.slice(0, insertIndex), + ...selectedItems, + ...unselectedItems.slice(insertIndex) + ]; + const positions = new Map(reordered.map((download, position) => [download.id, position])); const previousDownloads = get().downloads; set(state => ({ @@ -246,12 +268,25 @@ export const useDownloadStore = create((set, get) => ({ : download) })); - if (!get().backendRegisteredIds.has(id)) return; + const registeredIdsToMove = selectedItems + .filter(item => get().backendRegisteredIds.has(item.id)) + .map(item => item.id); + + if (registeredIdsToMove.length === 0) return; + + // For backend sync, we must call move_in_queue in the correct order to maintain the block + const idsToMove = direction === 'up' ? registeredIdsToMove : [...registeredIdsToMove].reverse(); + try { - const order = await invoke('move_in_queue', { id, queueId, direction }); - set({ pendingOrder: order as string[] }); + let order: string[] = []; + for (const id of idsToMove) { + order = (await invoke('move_in_queue', { id, queueId, direction })) as string[]; + } + if (order.length > 0) { + set({ pendingOrder: order }); + } } catch (e) { - console.error("Failed to move item in queue:", e); + console.error("Failed to move in queue backend:", e); set({ downloads: previousDownloads }); } },