import type { Torrent } from '../shared'; import { CircleAlert, CircleCheck, Pencil, RefreshCw } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { formatDate } from '../format'; import { useTorrentMutations } from '../useTransmissionData'; // One card per tracker, grouped by tier. The point of this tab is diagnosing a torrent that isn't finding // peers, so the failure text gets as much room as the success numbers. type TrackersTabProps = { torrent: Torrent; onEdit: () => void }; export const TrackersTab = ({ torrent, onEdit }: TrackersTabProps) => { const { act } = useTorrentMutations(); const trackers = torrent.trackerStats ?? []; const tiers = new Map(); for (const tracker of trackers) { tiers.set(tracker.tier, [...(tiers.get(tracker.tier) ?? []), tracker]); } return (
{trackers.length} tracker{trackers.length === 1 ? '' : 's'} in {tiers.size} tier {tiers.size === 1 ? '' : 's'}
{trackers.length === 0 && (

No trackers. This torrent relies on DHT and peer exchange.

)}
{[...tiers.entries()] .sort((a, b) => a[0] - b[0]) .map(([tier, list]) => (

Tier {tier + 1}

{list.map((tracker) => (
{tracker.lastAnnounceSucceeded ? ( ) : ( )}
{tracker.announce}
{tracker.isBackup && (
Backup — only tried if the tier above fails entirely.
)}
{tracker.seederCount >= 0 ? `${tracker.seederCount} seeds` : 'seeds unknown'}
{tracker.leecherCount >= 0 ? `${tracker.leecherCount} peers` : 'peers unknown'}
))}
))}
); }; const Row = ({ label, value, tone }: { label: string; value: string; tone?: string }) => (
{label}
{value}
);