import { useState, useEffect } from 'react'; import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@heroui/modal'; import { Button } from '@heroui/button'; import { Chip } from '@heroui/chip'; import { Tooltip } from '@heroui/tooltip'; import { IoMdFlash, IoMdCheckmark, IoMdClose } from 'react-icons/io'; import clsx from 'clsx'; import MirrorManager, { MirrorTestResult } from '@/controllers/mirror_manager'; interface MirrorSelectorModalProps { isOpen: boolean; onClose: () => void; onSelect: (mirror: string | undefined) => void; currentMirror?: string; type?: 'file' | 'raw'; } export default function MirrorSelectorModal ({ isOpen, onClose, onSelect, currentMirror, type = 'file', }: MirrorSelectorModalProps) { const [mirrors, setMirrors] = useState([]); const [selectedMirror, setSelectedMirror] = useState(currentMirror || 'auto'); const [testResults, setTestResults] = useState>(new Map()); const [isTesting, setIsTesting] = useState(false); const [testProgress, setTestProgress] = useState(0); const [testMessage, setTestMessage] = useState(''); const [fastestMirror, setFastestMirror] = useState(null); // 加载镜像列表 useEffect(() => { if (isOpen) { loadMirrors(); } }, [isOpen]); const loadMirrors = async () => { try { const data = await MirrorManager.getMirrorList(); const mirrorList = type === 'raw' ? data.rawMirrors : data.fileMirrors; setMirrors(mirrorList); if (data.customMirror) { setSelectedMirror(data.customMirror); } } catch (e) { console.error('Failed to load mirrors:', e); } }; const startSpeedTest = () => { setIsTesting(true); setTestProgress(0); setTestResults(new Map()); setFastestMirror(null); setTestMessage('准备测速...'); MirrorManager.testMirrorsSSE(type, { onStart: (data) => { setTestMessage(data.message); }, onTesting: (data) => { setTestProgress((data.index / data.total) * 100); setTestMessage(data.message); }, onResult: (data) => { setTestResults((prev) => { const newMap = new Map(prev); newMap.set(data.result.mirror, data.result); return newMap; }); setTestProgress(((data.index + 1) / data.total) * 100); }, onComplete: (data) => { setIsTesting(false); setTestProgress(100); setTestMessage(data.message); if (data.fastest) { setFastestMirror(data.fastest.mirror); } }, onError: (error) => { setIsTesting(false); setTestMessage(`测速失败: ${error}`); }, }); }; const handleConfirm = () => { const mirror = selectedMirror === 'auto' ? undefined : selectedMirror; onSelect(mirror); onClose(); }; return (
选择镜像源
{isTesting && (

{testMessage}

)}
{/* 自动选择选项 */} setSelectedMirror('auto')} badge={推荐} /> {/* 原始 GitHub */} setSelectedMirror('https://github.com')} testResult={testResults.get('https://github.com (原始)')} isFastest={fastestMirror === 'https://github.com (原始)'} /> {/* 镜像列表 */} {mirrors.map((mirror) => { if (!mirror) return null; const result = testResults.get(mirror); const isFastest = fastestMirror === mirror; let hostname = mirror; try { hostname = new URL(mirror).hostname; } catch { } return ( setSelectedMirror(mirror)} testResult={result} isFastest={isFastest} /> ); })}
); } // 镜像选项组件 interface MirrorOptionProps { value: string; label: string; description: string; isSelected: boolean; onSelect: () => void; testResult?: MirrorTestResult; isFastest?: boolean; badge?: React.ReactNode; } function MirrorOption ({ label, description, isSelected, onSelect, testResult, isFastest, badge, }: MirrorOptionProps) { return (

{label}

{description}

{badge} {isFastest && !badge && ( 最快 )} {testResult && }
); } // 镜像状态显示组件 function MirrorStatus ({ result }: { result: MirrorTestResult; }) { const formatLatency = (latency: number) => { if (latency >= 5000) return '>5s'; if (latency >= 1000) return `${(latency / 1000).toFixed(1)}s`; return `${latency}ms`; }; if (!result.success) { return ( } > 失败 ); } const getColor = (): 'success' | 'warning' | 'danger' => { if (result.latency < 300) return 'success'; if (result.latency < 1000) return 'warning'; return 'danger'; }; return ( } > {formatLatency(result.latency)} ); }