import { useTranslation } from "react-i18next"; import { Box, Typography, Chip, Button, alpha, useTheme, Select, MenuItem, FormControl, InputLabel, SelectChangeEvent, Tooltip, } from "@mui/material"; import { useEffect, useState, useMemo, useCallback, useRef } from "react"; import { SignalWifi4Bar as SignalStrong, SignalWifi3Bar as SignalGood, SignalWifi2Bar as SignalMedium, SignalWifi1Bar as SignalWeak, SignalWifi0Bar as SignalNone, WifiOff as SignalError, ChevronRight, } from "@mui/icons-material"; import { useNavigate } from "react-router-dom"; import { useCurrentProxy } from "@/hooks/use-current-proxy"; import { EnhancedCard } from "@/components/home/enhanced-card"; import { getProxies, updateProxy, getConnections, deleteConnection, } from "@/services/api"; import delayManager from "@/services/delay"; import { useVerge } from "@/hooks/use-verge"; // 本地存储的键名 const STORAGE_KEY_GROUP = "clash-verge-selected-proxy-group"; const STORAGE_KEY_PROXY = "clash-verge-selected-proxy"; // 代理节点信息接口 interface ProxyOption { name: string; } // 将delayManager返回的颜色格式转换为MUI Chip组件需要的格式 function convertDelayColor(delayValue: number) { const colorStr = delayManager.formatDelayColor(delayValue); if (!colorStr) return "default"; // 从"error.main"这样的格式转为"error" const mainColor = colorStr.split(".")[0]; switch (mainColor) { case "success": return "success"; case "warning": return "warning"; case "error": return "error"; case "primary": return "primary"; default: return "default"; } } // 根据延迟值获取合适的WiFi信号图标 function getSignalIcon(delay: number) { if (delay < 0) return { icon: , text: "未测试", color: "text.secondary" }; if (delay >= 10000) return { icon: , text: "超时", color: "error.main" }; if (delay >= 500) return { icon: , text: "延迟较高", color: "error.main" }; if (delay >= 300) return { icon: , text: "延迟中等", color: "warning.main" }; if (delay >= 200) return { icon: , text: "延迟良好", color: "info.main" }; return { icon: , text: "延迟极佳", color: "success.main" }; } // 简单的防抖函数 function debounce(fn: Function, ms = 100) { let timeoutId: ReturnType; return function (this: any, ...args: any[]) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), ms); }; } export const CurrentProxyCard = () => { const { t } = useTranslation(); const { currentProxy, primaryGroupName, mode, refreshProxy } = useCurrentProxy(); const navigate = useNavigate(); const theme = useTheme(); const { verge } = useVerge(); // 判断模式 const isGlobalMode = mode === "global"; const isDirectMode = mode === "direct"; // 使用 useRef 存储最后一次刷新时间和是否正在刷新 const lastRefreshRef = useRef(0); const isRefreshingRef = useRef(false); const pendingRefreshRef = useRef(false); // 定义状态类型 type ProxyState = { proxyData: { groups: { name: string; now: string; all: string[] }[]; records: Record; globalProxy: string; directProxy: any; }; selection: { group: string; proxy: string; }; displayProxy: any; }; // 合并状态,减少状态更新次数 const [state, setState] = useState({ proxyData: { groups: [], records: {}, globalProxy: "", directProxy: null, }, selection: { group: "", proxy: "", }, displayProxy: null, }); // 初始化选择的组 useEffect(() => { // 根据模式确定初始组 if (isGlobalMode) { setState((prev) => ({ ...prev, selection: { ...prev.selection, group: "GLOBAL", }, })); } else if (isDirectMode) { setState((prev) => ({ ...prev, selection: { ...prev.selection, group: "DIRECT", }, })); } else { const savedGroup = localStorage.getItem(STORAGE_KEY_GROUP); setState((prev) => ({ ...prev, selection: { ...prev.selection, group: savedGroup || primaryGroupName || "", }, })); } }, [isGlobalMode, isDirectMode, primaryGroupName]); // 带锁的代理数据获取函数,防止并发请求 const fetchProxyData = useCallback( async (force = false) => { // 防止重复请求 if (isRefreshingRef.current) { pendingRefreshRef.current = true; return; } // 检查刷新间隔 const now = Date.now(); if (!force && now - lastRefreshRef.current < 1000) { return; } isRefreshingRef.current = true; lastRefreshRef.current = now; try { const data = await getProxies(); // 过滤和格式化组 const filteredGroups = data.groups .filter((g) => g.name !== "DIRECT" && g.name !== "REJECT") .map((g) => ({ name: g.name, now: g.now || "", all: g.all.map((p) => p.name), })); // 使用函数式更新确保状态更新的原子性 setState((prev) => { let newProxy = ""; let newDisplayProxy = null; let newGroup = prev.selection.group; // 根据模式确定新代理 if (isDirectMode) { newGroup = "DIRECT"; newProxy = "DIRECT"; newDisplayProxy = data.records?.DIRECT || null; } else if (isGlobalMode && data.global) { newGroup = "GLOBAL"; newProxy = data.global.now || ""; newDisplayProxy = data.records?.[newProxy] || null; } else { // 普通模式 - 检查当前选择的组是否存在 const currentGroup = filteredGroups.find( (g) => g.name === prev.selection.group, ); // 如果当前组不存在或为空,自动选择第一个组 if (!currentGroup && filteredGroups.length > 0) { newGroup = filteredGroups[0].name; const firstGroup = filteredGroups[0]; newProxy = firstGroup.now; newDisplayProxy = data.records?.[newProxy] || null; // 保存到本地存储 if (!isGlobalMode && !isDirectMode) { localStorage.setItem(STORAGE_KEY_GROUP, newGroup); if (newProxy) { localStorage.setItem(STORAGE_KEY_PROXY, newProxy); } } } else if (currentGroup) { // 使用当前组的代理 newProxy = currentGroup.now; newDisplayProxy = data.records?.[newProxy] || null; } } // 返回新状态 return { proxyData: { groups: filteredGroups, records: data.records || {}, globalProxy: data.global?.now || "", directProxy: data.records?.DIRECT || null, }, selection: { group: newGroup, proxy: newProxy, }, displayProxy: newDisplayProxy, }; }); } catch (error) { console.error("获取代理信息失败", error); } finally { isRefreshingRef.current = false; // 处理待处理的刷新请求 if (pendingRefreshRef.current) { pendingRefreshRef.current = false; setTimeout(() => fetchProxyData(), 100); } } }, [isGlobalMode, isDirectMode], ); // 响应 currentProxy 变化 useEffect(() => { if ( currentProxy && (!state.displayProxy || currentProxy.name !== state.displayProxy.name) ) { fetchProxyData(true); } }, [currentProxy, fetchProxyData, state.displayProxy]); // 平滑的定期刷新,使用固定间隔 useEffect(() => { fetchProxyData(); const intervalId = setInterval(() => { fetchProxyData(); }, 3000); // 使用固定的3秒间隔,平衡响应速度和性能 return () => clearInterval(intervalId); }, [fetchProxyData]); // 计算要显示的代理选项 - 使用 useMemo 优化 const proxyOptions = useMemo(() => { if (isDirectMode) { return [{ name: "DIRECT" }]; } if (isGlobalMode && state.proxyData.records) { // 全局模式下的选项 return Object.keys(state.proxyData.records) .filter((name) => name !== "DIRECT" && name !== "REJECT") .map((name) => ({ name })); } // 普通模式 const group = state.proxyData.groups.find( (g) => g.name === state.selection.group, ); if (group) { return group.all.map((name) => ({ name })); } return []; }, [isDirectMode, isGlobalMode, state.proxyData, state.selection.group]); // 使用防抖包装状态更新,避免快速连续更新 const debouncedSetState = useCallback( debounce((updateFn: (prev: ProxyState) => ProxyState) => { setState(updateFn); }, 50), [], ); // 处理代理组变更 const handleGroupChange = useCallback( (event: SelectChangeEvent) => { if (isGlobalMode || isDirectMode) return; const newGroup = event.target.value; // 保存到本地存储 localStorage.setItem(STORAGE_KEY_GROUP, newGroup); // 获取该组当前选中的代理 setState((prev) => { const group = prev.proxyData.groups.find((g) => g.name === newGroup); if (group) { return { ...prev, selection: { group: newGroup, proxy: group.now, }, displayProxy: prev.proxyData.records[group.now] || null, }; } return { ...prev, selection: { ...prev.selection, group: newGroup, }, }; }); }, [isGlobalMode, isDirectMode], ); // 处理代理节点变更 const handleProxyChange = useCallback( async (event: SelectChangeEvent) => { if (isDirectMode) return; const newProxy = event.target.value; const currentGroup = state.selection.group; const previousProxy = state.selection.proxy; // 立即更新UI,优化体验 debouncedSetState((prev: ProxyState) => ({ ...prev, selection: { ...prev.selection, proxy: newProxy, }, displayProxy: prev.proxyData.records[newProxy] || null, })); // 非特殊模式下保存到本地存储 if (!isGlobalMode && !isDirectMode) { localStorage.setItem(STORAGE_KEY_PROXY, newProxy); } try { // 更新代理设置 await updateProxy(currentGroup, newProxy); // 自动关闭连接设置 if (verge?.auto_close_connection && previousProxy) { getConnections().then(({ connections }) => { connections.forEach((conn) => { if (conn.chains.includes(previousProxy)) { deleteConnection(conn.id); } }); }); } // 刷新代理信息,使用较短的延迟 setTimeout(() => { refreshProxy(); fetchProxyData(true); }, 200); } catch (error) { console.error("更新代理失败", error); } }, [ isDirectMode, isGlobalMode, state.proxyData.records, state.selection, verge?.auto_close_connection, refreshProxy, fetchProxyData, debouncedSetState, ], ); // 导航到代理页面 const goToProxies = useCallback(() => { navigate("/"); }, [navigate]); // 获取要显示的代理节点 const proxyToDisplay = state.displayProxy || currentProxy; // 获取当前节点的延迟 const currentDelay = proxyToDisplay ? delayManager.getDelayFix(proxyToDisplay, state.selection.group) : -1; // 获取信号图标 const signalInfo = getSignalIcon(currentDelay); // 自定义渲染选择框中的值 const renderProxyValue = useCallback( (selected: string) => { if (!selected || !state.proxyData.records[selected]) return selected; const delayValue = delayManager.getDelayFix( state.proxyData.records[selected], state.selection.group, ); return ( {selected} ); }, [state.proxyData.records, state.selection.group], ); return ( {proxyToDisplay ? signalInfo.icon : } } iconColor={proxyToDisplay ? "primary" : undefined} action={ } > {proxyToDisplay ? ( {/* 代理节点信息显示 */} {proxyToDisplay.name} {proxyToDisplay.type} {isGlobalMode && ( )} {isDirectMode && ( )} {/* 节点特性 */} {proxyToDisplay.udp && ( )} {proxyToDisplay.tfo && ( )} {proxyToDisplay.xudp && ( )} {proxyToDisplay.mptcp && ( )} {proxyToDisplay.smux && ( )} {/* 显示延迟 */} {proxyToDisplay && !isDirectMode && ( )} {/* 代理组选择器 */} {t("Group")} {/* 代理节点选择器 */} {t("Proxy")} ) : ( {t("No active proxy node")} )} ); };