import { useState, useEffect } from 'react'; import { useDownloadStore, DownloadItem } from '../store/useDownloadStore'; import { useSettingsStore } from '../store/useSettingsStore'; import { ChevronDown, ChevronRight, FolderPlus, Info, CheckCircle, AlertCircle, Play, Pause } from 'lucide-react'; import { open } from '@tauri-apps/plugin-dialog'; type LoginMode = 'matching' | 'custom' | 'none'; export const PropertiesModal = () => { const { selectedPropertiesDownloadId, setSelectedPropertiesDownloadId, downloads, updateDownload } = useDownloadStore(); const { defaultDownloadPath } = useSettingsStore(); const [item, setItem] = useState(null); // Form states const [url, setUrl] = useState(''); const [fileName, setFileName] = useState(''); const [saveLocation, setSaveLocation] = useState(''); const [connections, setConnections] = useState(16); const [speedLimitEnabled, setSpeedLimitEnabled] = useState(false); const [speedLimitValue, setSpeedLimitValue] = useState('1024'); // KiB/s const [loginMode, setLoginMode] = useState('matching'); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [advancedExpanded, setAdvancedExpanded] = useState(false); const [checksumEnabled, setChecksumEnabled] = useState(false); const [checksumAlgorithm, setChecksumAlgorithm] = useState('SHA-256'); const [checksumValue, setChecksumValue] = useState(''); const [cookies, setCookies] = useState(''); const [headers, setHeaders] = useState(''); const [mirrors, setMirrors] = useState(''); const [errorMessage, setErrorMessage] = useState(''); useEffect(() => { if (selectedPropertiesDownloadId) { const activeItem = downloads.find(d => d.id === selectedPropertiesDownloadId); if (activeItem) { setItem(activeItem); setUrl(activeItem.url); setFileName(activeItem.fileName); setSaveLocation(activeItem.destination || defaultDownloadPath || '~/Downloads'); setConnections(activeItem.connections || 16); if (activeItem.speedLimit) { setSpeedLimitEnabled(true); setSpeedLimitValue(activeItem.speedLimit.replace(/[^0-9]/g, '')); } else { setSpeedLimitEnabled(false); } if (activeItem.username || activeItem.password) { setLoginMode('custom'); setUsername(activeItem.username || ''); setPassword(activeItem.password || ''); } else { setLoginMode('matching'); setUsername(''); setPassword(''); } setHeaders(activeItem.headers || ''); setChecksumEnabled(!!activeItem.checksum); if (activeItem.checksum) { const [algo, val] = activeItem.checksum.split('='); if (val) { setChecksumAlgorithm(algo); setChecksumValue(val); } } else { setChecksumAlgorithm('SHA-256'); setChecksumValue(''); } setCookies(activeItem.cookies || ''); setMirrors(activeItem.mirrors || ''); setErrorMessage(''); } else { setItem(null); } } else { setItem(null); } }, [selectedPropertiesDownloadId, downloads, defaultDownloadPath]); if (!selectedPropertiesDownloadId || !item) return null; const handleBrowse = async () => { if (isLocked) return; try { const selected = await open({ directory: true, multiple: false, defaultPath: saveLocation.startsWith('~') ? undefined : saveLocation }); if (selected && typeof selected === 'string') { setSaveLocation(selected); } } catch (e) { console.error("Failed to select folder:", e); } }; const handleSave = () => { if (!url.trim()) { setErrorMessage("Enter a valid URL."); return; } if (!fileName.trim()) { setErrorMessage("File name cannot be empty."); return; } const updates: Partial = { url, fileName, destination: saveLocation, connections: Number(connections), speedLimit: speedLimitEnabled && speedLimitValue ? `${speedLimitValue}K` : undefined, username: loginMode === 'custom' ? username.trim() : undefined, password: loginMode === 'custom' ? password.trim() : undefined, headers: headers.trim() || undefined, checksum: checksumEnabled && checksumValue.trim() ? `${checksumAlgorithm}=${checksumValue.trim()}` : undefined, cookies: cookies.trim() || undefined, mirrors: mirrors.trim() || undefined, }; updateDownload(item.id, updates); setSelectedPropertiesDownloadId(null); }; const isLocked = ['downloading', 'completed'].includes(item.status); const isTransferLocked = item.status === 'downloading'; let statusColor = 'text-text-secondary'; let StatusIcon = Info; if (item.status === 'completed') { statusColor = 'text-green-500'; StatusIcon = CheckCircle; } else if (item.status === 'downloading') { statusColor = 'text-blue-500'; StatusIcon = Play; } else if (item.status === 'paused') { statusColor = 'text-orange-500'; StatusIcon = Pause; } else if (item.status === 'failed') { statusColor = 'text-red-500'; StatusIcon = AlertCircle; } return (
{/* Header Summary */}

{item.fileName}

{item.status}
Progress{item.status === 'completed' ? '100%' : ((item.fraction || 0) * 100).toFixed(0) + '%'}
Size-
Speed{item.status === 'completed' ? '-' : item.speed || '-'}
ETA{item.status === 'completed' ? '-' : item.eta || '-'}
Live connections-
Speed cap{item.speedLimit || '-'}
Category{item.category}
Last try-
Date added{new Date(item.dateAdded).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })}
Destination{item.destination || defaultDownloadPath}
{/* Scrollable Form Content */}
{isLocked && (
{item.status === 'completed' ? : } {item.status === 'completed' ? "File identity is read-only. Transfer settings are saved for redownload." : "Only the speed limit applies to the current transfer. Other settings can be changed after stopping or pausing."}
)} {/* Download Section */}

Download

setUrl(e.target.value)} disabled={isLocked} className="bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary font-mono focus:outline-none focus:border-accent disabled:opacity-50" /> setFileName(e.target.value)} disabled={isLocked} className="bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary focus:outline-none focus:border-accent disabled:opacity-50" />
setConnections(Number(e.target.value))} disabled={isTransferLocked} className="w-16 bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary focus:outline-none focus:border-accent disabled:opacity-50" /> per file
{speedLimitEnabled && (
setSpeedLimitValue(e.target.value)} className="w-20 bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary focus:outline-none focus:border-accent" /> KiB/s
)}
{/* Site Login Section */}

{item.status === 'completed' ? 'Site Login for Redownload' : 'Site Login'}

{(['matching', 'custom', 'none'] as const).map((mode) => ( ))}
{loginMode === 'matching' && (
Will use saved login if available.
)} {loginMode === 'custom' && ( <> setUsername(e.target.value)} disabled={isTransferLocked} placeholder="Username" className="max-w-[250px] bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary focus:outline-none focus:border-accent disabled:opacity-50" /> setPassword(e.target.value)} disabled={isTransferLocked} placeholder="Password" className="max-w-[250px] bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary focus:outline-none focus:border-accent disabled:opacity-50" /> )}
{/* Advanced Transfer Section */}
{advancedExpanded && (
{checksumEnabled && ( <> setChecksumValue(e.target.value)} disabled={isTransferLocked} placeholder="Expected digest" className="bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary font-mono focus:outline-none focus:border-accent disabled:opacity-50" /> )} setCookies(e.target.value)} disabled={isTransferLocked} placeholder="Cookies" className="bg-bg-input border border-border-modal rounded-lg px-2.5 py-1.5 text-xs text-text-primary font-mono focus:outline-none focus:border-accent disabled:opacity-50" />
)}
{/* Footer */}
{errorMessage}
); };