import React, { useState, useEffect } from 'react'; import { useDownloadStore, DownloadItem } from '../store/useDownloadStore'; import { useSettingsStore } from '../store/useSettingsStore'; import { SidebarFilter } from './Sidebar'; import { Play, Pause, Plus, Trash2, FileText, Image as ImageIcon, Music, Film, Box, Archive, FileQuestion, MoreVertical, PanelLeft, ArrowDownCircle } from 'lucide-react'; import { invoke } from '@tauri-apps/api/core'; import { homeDir } from '@tauri-apps/api/path'; interface DownloadTableProps { filter: SidebarFilter; } export const DownloadTable: React.FC = ({ filter }) => { const { downloads, toggleAddModal, updateDownload, removeDownload, clearFinished, redownload } = useDownloadStore(); const { isSidebarVisible, toggleSidebar } = useSettingsStore(); const [contextMenu, setContextMenu] = useState<{ x: number; y: number; id: string } | null>(null); const [columnWidths, setColumnWidths] = useState([340, 100, 220, 100, 80, 170]); const columnMinimums = [200, 80, 170, 80, 70, 120]; const tableGridTemplate = columnWidths.map(width => `${width}px`).join(' '); const startColumnResize = (index: number, event: React.PointerEvent) => { event.preventDefault(); event.stopPropagation(); const startX = event.clientX; const startWidth = columnWidths[index]; const handlePointerMove = (moveEvent: PointerEvent) => { const nextWidth = Math.max(columnMinimums[index], startWidth + moveEvent.clientX - startX); setColumnWidths(widths => widths.map((width, columnIndex) => columnIndex === index ? nextWidth : width)); }; const handlePointerUp = () => { window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointerup', handlePointerUp); document.body.classList.remove('is-resizing'); }; document.body.classList.add('is-resizing'); window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointerup', handlePointerUp); }; useEffect(() => { const handleCloseMenu = () => setContextMenu(null); window.addEventListener('click', handleCloseMenu); return () => window.removeEventListener('click', handleCloseMenu); }, []); const resolvePath = async (dir: string, file: string) => { let resolvedDir = dir; if (dir.startsWith('~/')) { const home = await homeDir(); resolvedDir = home + '/' + dir.slice(2); } else if (dir === '~') { resolvedDir = await homeDir(); } return resolvedDir + '/' + file; }; const filteredDownloads = downloads.filter((d: DownloadItem) => { if (filter.startsWith('queue:')) { return d.queueId === filter.replace('queue:', ''); } switch (filter) { case 'all': return true; case 'active': return d.status === 'downloading'; case 'completed': return d.status === 'completed'; case 'unfinished': return d.status !== 'completed'; default: return d.category === filter; } }); const getFilterTitle = () => { if (filter.startsWith('queue:')) { const qid = filter.replace('queue:', ''); const queue = useDownloadStore.getState().queues.find(q => q.id === qid); return queue ? queue.name : 'Unknown Queue'; } switch (filter) { case 'all': return 'All Downloads'; case 'active': return 'Active'; case 'completed': return 'Completed'; case 'unfinished': return 'Unfinished'; default: return filter; } }; const handlePause = async (id: string) => { try { await invoke('pause_download', { id }); updateDownload(id, { status: 'paused', speed: '-', eta: '-' }); } catch (e) { console.error("Failed to pause:", e); } }; const handleResume = (item: DownloadItem) => { useDownloadStore.setState((state) => ({ downloads: state.downloads.map(d => d.id === item.id ? { ...d, status: 'queued', speed: '-', eta: '-' } : d) })); useDownloadStore.getState().processQueue(); }; const handleDelete = async (id: string) => { try { await removeDownload(id); } catch (e) { console.error("Failed to delete download:", e); } }; const contextItem = contextMenu ? downloads.find(d => d.id === contextMenu.id) : null; const getCategoryIcon = (category: string) => { switch(category) { case 'Musics': return ; case 'Movies': return ; case 'Documents': return ; case 'Applications': return ; case 'Pictures': return ; case 'Compressed': return ; case 'Other': return ; default: return ; } } return (
{!isSidebarVisible && ( )}
Firelink
{getFilterTitle()} {filteredDownloads.length}
{filteredDownloads.length === 0 ? (
) : ( <>
{['File Name', 'Size', 'Status', 'Speed', 'ETA', 'Date Added'].map((label, index) => (
{label}
startColumnResize(index, event)} />
))}
{filteredDownloads.map(d => (
{ e.preventDefault(); setContextMenu({ x: e.clientX, y: e.clientY, id: d.id }); }} >
{getCategoryIcon(d.category)} {d.fileName}
{d.size && d.size !== '-' ? d.size : 'Unknown'}
{d.status === 'completed' ? ( Completed ) : ( <>
{d.status === 'downloading' ? `${((d.fraction || 0) * 100).toFixed(0)}%` : d.status.charAt(0).toUpperCase() + d.status.slice(1)} )}
{d.status === 'downloading' ? d.speed : '-'}
{d.status === 'downloading' ? d.eta : '-'}
{d.dateAdded ? new Date(d.dateAdded).toLocaleDateString() : '-'}
{d.status === 'downloading' && ( )} {d.status === 'paused' && ( )}
))} {Array.from({ length: Math.max(0, 10 - filteredDownloads.length) }).map((_, index) => (
))}
)}
{/* Floating Context Menu */} {contextMenu && contextItem && (
e.stopPropagation()} > {contextItem.status === 'completed' && ( )}
{(contextItem.status === 'downloading' || contextItem.status === 'queued') && ( )} {(contextItem.status === 'paused' || contextItem.status === 'failed') && ( )} {['completed', 'failed', 'paused'].includes(contextItem.status) && ( )}
{contextItem.status === 'completed' && ( )}
)}
); };