/** * DetailView - Detailed view for a single torrent. * * Provides tabbed navigation for viewing: * - Files: List of files in the torrent * - Peers: Connected peers * - Trackers: Tracker status * - Log: Activity log * * @module ui/views/DetailView */ import React, { useState, useCallback } from 'react'; import { Box, Text } from 'ink'; import type { Torrent, Peer } from '../../engine/types.js'; import { colors, borders } from '../theme/index.js'; import { useKeyboard } from '../hooks/useKeyboard.js'; import { Tabs, Tab, getAdjacentTab, getTabByNumber, } from '../components/Tabs.js'; import { PeerList } from '../components/PeerList.js'; import { FileList } from '../components/FileList.js'; import { TrackerList } from '../components/TrackerList.js'; import { LogView, LogEntry } from '../components/LogView.js'; import { formatBytes, formatEta, formatSpeed, formatProgress, } from '../utils/format.js'; import { ProgressBar } from '../components/ProgressBar.js'; /** * Tab identifiers for detail view sections */ type TabId = 'files' | 'peers' | 'trackers' | 'log'; /** * Tab configuration for the detail view */ const DETAIL_TABS: Tab[] = [ { id: 'files', label: 'Files' }, { id: 'peers', label: 'Peers' }, { id: 'trackers', label: 'Trackers' }, { id: 'log', label: 'Log' }, ]; export interface DetailViewProps { /** The torrent to display details for */ torrent: Torrent; /** Connected peers for this torrent */ peers?: Peer[]; /** Activity log entries */ logs?: LogEntry[]; /** Callback to return to main view */ onBack: () => void; /** Whether keyboard input is enabled (disable when modals are open) */ keyboardEnabled?: boolean; } /** * Header component showing torrent name and back button */ const DetailHeader: React.FC<{ name: string }> = ({ name }) => { return ( {'\u2190'} Back (Esc) {name.length > 50 ? name.slice(0, 47) + '\u2026' : name} ); }; /** * Footer component showing torrent progress and stats */ const DetailFooter: React.FC<{ torrent: Torrent }> = ({ torrent }) => { const percentage = formatProgress(torrent.progress); const downloaded = formatBytes(torrent.downloaded); const total = formatBytes(torrent.size); const eta = formatEta(torrent.eta); const downloadSpeed = formatSpeed(torrent.downloadSpeed); const uploadSpeed = formatSpeed(torrent.uploadSpeed); return ( {/* Separator line */} {borders.horizontal.repeat(70)} {/* Progress line */} Progress: {percentage} {'\u2022'} {downloaded} / {total} {'\u2022'} ETA: {eta} {/* Stats line */} Seeds: {torrent.seeds} {'\u2022'} Peers: {torrent.peers} {'\u2022'} {'\u2193'} {downloadSpeed} {'\u2022'} {'\u2191'} {uploadSpeed} ); }; /** * DetailView component for displaying torrent details * * Shows comprehensive information about a single torrent with * tabbed navigation between Files, Peers, Trackers, and Log views. * * Layout: * ``` * ┌─────────────────────────────────────────────────────────────┐ * │ ← Back ubuntu-24.04.iso │ * ├─────────────────────────────────────────────────────────────┤ * │ [1:Files] 2:Peers 3:Trackers 4:Log │ * ├─────────────────────────────────────────────────────────────┤ * │ │ * │ Tab content area (varies by active tab) │ * │ │ * ├─────────────────────────────────────────────────────────────┤ * │ Progress: ████████░░ 80% • 3.8 GB / 4.7 GB • ETA: 5m │ * │ Seeds: 12 • Peers: 45 • ↓ 2.1 MB/s • ↑ 256 KB/s │ * └─────────────────────────────────────────────────────────────┘ * ``` * * Keyboard shortcuts: * - `Esc` or `Backspace`: Return to main view * - `←/→` or `h/l`: Switch tabs * - `1-4`: Jump to tab by number * * @example * setView('main')} * /> */ export const DetailView: React.FC = ({ torrent, peers = [], logs = [], onBack, keyboardEnabled = true, }) => { // Tab state const [activeTab, setActiveTab] = useState('files'); // Tab navigation handlers const handlePrevTab = useCallback(() => { setActiveTab( (current) => getAdjacentTab(DETAIL_TABS, current, -1) as TabId ); }, []); const handleNextTab = useCallback(() => { setActiveTab((current) => getAdjacentTab(DETAIL_TABS, current, 1) as TabId); }, []); const handleTabNumber = useCallback((num: number) => { const tabId = getTabByNumber(DETAIL_TABS, num); if (tabId) { setActiveTab(tabId as TabId); } }, []); // Keyboard handling useKeyboard({ handlers: { // Back navigation escape: onBack, backspace: onBack, // Tab navigation left: handlePrevTab, right: handleNextTab, h: handlePrevTab, l: handleNextTab, // Direct tab selection '1': () => handleTabNumber(1), '2': () => handleTabNumber(2), '3': () => handleTabNumber(3), '4': () => handleTabNumber(4), }, enabled: keyboardEnabled, }); // Render tab content const renderTabContent = () => { switch (activeTab) { case 'files': return ; case 'peers': return ; case 'trackers': return ; case 'log': return ; default: return null; } }; return ( {/* Header with back button and torrent name */} {/* Separator */} {borders.horizontal.repeat(70)} {/* Tab bar */} setActiveTab(id as TabId)} /> {/* Tab content */} {renderTabContent()} {/* Footer with progress and stats */} ); }; export default DetailView;