import { listen } from "@tauri-apps/api/event"; import React, { useCallback, useEffect, useMemo } from "react"; import useSWR from "swr"; import { getBaseConfig, getRuleProviders, getRules, } from "tauri-plugin-mihomo-api"; import { useVerge } from "@/hooks/use-verge"; import { calcuProxies, calcuProxyProviders, getAppUptime, getRunningMode, getSystemProxy, } from "@/services/cmds"; import { AppDataContext, AppDataContextType } from "./app-data-context"; // 全局数据提供者组件 export const AppDataProvider = ({ children, }: { children: React.ReactNode; }) => { const { verge } = useVerge(); // 基础数据 - 中频率更新 (5秒) const { data: proxiesData, mutate: refreshProxy } = useSWR( "getProxies", calcuProxies, { refreshInterval: 5000, revalidateOnFocus: true, suspense: false, errorRetryCount: 3, }, ); const { data: clashConfig, mutate: refreshClashConfig } = useSWR( "getClashConfig", getBaseConfig, { refreshInterval: 60000, // 60秒刷新间隔,减少频繁请求 revalidateOnFocus: false, suspense: false, errorRetryCount: 3, }, ); // 提供者数据 const { data: proxyProviders, mutate: refreshProxyProviders } = useSWR( "getProxyProviders", calcuProxyProviders, { revalidateOnFocus: false, revalidateOnReconnect: false, dedupingInterval: 3000, suspense: false, errorRetryCount: 3, }, ); const { data: ruleProviders, mutate: refreshRuleProviders } = useSWR( "getRuleProviders", getRuleProviders, { revalidateOnFocus: false, suspense: false, errorRetryCount: 3, }, ); // 低频率更新数据 const { data: rulesData, mutate: refreshRules } = useSWR( "getRules", getRules, { revalidateOnFocus: false, suspense: false, errorRetryCount: 3, }, ); // 监听profile和clash配置变更事件 useEffect(() => { let lastProfileId: string | null = null; let lastUpdateTime = 0; const refreshThrottle = 500; let isUnmounted = false; const scheduledTimeouts = new Set(); const cleanupFns: Array<() => void> = []; const registerCleanup = (fn: () => void) => { if (isUnmounted) { fn(); } else { cleanupFns.push(fn); } }; const addWindowListener = (eventName: string, handler: EventListener) => { // eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener -- cleanup is returned by this helper window.addEventListener(eventName, handler); return () => window.removeEventListener(eventName, handler); }; const scheduleTimeout = ( callback: () => void | Promise, delay: number, ) => { const timeoutId = window.setTimeout(() => { scheduledTimeouts.delete(timeoutId); void callback(); }, delay); scheduledTimeouts.add(timeoutId); return timeoutId; }; const clearAllTimeouts = () => { scheduledTimeouts.forEach((timeoutId) => clearTimeout(timeoutId)); scheduledTimeouts.clear(); }; const handleProfileChanged = (event: { payload: string }) => { const newProfileId = event.payload; const now = Date.now(); console.log(`[AppDataProvider] Profile切换事件: ${newProfileId}`); if ( lastProfileId === newProfileId && now - lastUpdateTime < refreshThrottle ) { console.log("[AppDataProvider] 重复事件被防抖,跳过"); return; } lastProfileId = newProfileId; lastUpdateTime = now; // 刷新规则数据 refreshRules().catch((error) => console.warn("[AppDataProvider] 规则刷新失败:", error), ); refreshRuleProviders().catch((error) => console.warn("[AppDataProvider] 规则提供者刷新失败:", error), ); }; const handleRefreshClash = () => { const now = Date.now(); console.log("[AppDataProvider] Clash配置刷新事件"); if (now - lastUpdateTime <= refreshThrottle) { return; } lastUpdateTime = now; scheduleTimeout(async () => { try { console.log("[AppDataProvider] Clash刷新 - 刷新代理数据"); await refreshProxy(); } catch (error) { console.error( "[AppDataProvider] Clash刷新时刷新代理数据失败:", error, ); } }, 0); }; const handleRefreshProxy = () => { const now = Date.now(); console.log("[AppDataProvider] 代理配置刷新事件"); if (now - lastUpdateTime <= refreshThrottle) { return; } lastUpdateTime = now; scheduleTimeout(() => { refreshProxy().catch((error) => console.warn("[AppDataProvider] 代理刷新失败:", error), ); }, 100); }; const initializeListeners = async () => { try { const unlistenProfile = await listen( "profile-changed", handleProfileChanged, ); registerCleanup(unlistenProfile); } catch (error) { console.error("[AppDataProvider] 监听 Profile 事件失败:", error); } try { const unlistenClash = await listen( "verge://refresh-clash-config", handleRefreshClash, ); const unlistenProxy = await listen( "verge://refresh-proxy-config", handleRefreshProxy, ); registerCleanup(() => { unlistenClash(); unlistenProxy(); }); } catch (error) { console.warn("[AppDataProvider] 设置 Tauri 事件监听器失败:", error); const fallbackHandlers: Array<[string, EventListener]> = [ ["verge://refresh-clash-config", handleRefreshClash], ["verge://refresh-proxy-config", handleRefreshProxy], ]; fallbackHandlers.forEach(([eventName, handler]) => { registerCleanup(addWindowListener(eventName, handler)); }); } }; void initializeListeners(); return () => { isUnmounted = true; clearAllTimeouts(); cleanupFns.splice(0).forEach((fn) => fn()); }; }, [refreshProxy, refreshRules, refreshRuleProviders]); const { data: sysproxy, mutate: refreshSysproxy } = useSWR( "getSystemProxy", getSystemProxy, { revalidateOnFocus: true, revalidateOnReconnect: true, suspense: false, errorRetryCount: 3, }, ); const { data: runningMode } = useSWR("getRunningMode", getRunningMode, { revalidateOnFocus: false, suspense: false, errorRetryCount: 3, }); // 高频率更新数据 (2秒) const { data: uptimeData } = useSWR("appUptime", getAppUptime, { // TODO: 运行时间 refreshInterval: 2000, revalidateOnFocus: false, suspense: false, }); // 提供统一的刷新方法 const refreshAll = useCallback(async () => { await Promise.all([ refreshProxy(), refreshClashConfig(), refreshRules(), refreshSysproxy(), refreshProxyProviders(), refreshRuleProviders(), ]); }, [ refreshProxy, refreshClashConfig, refreshRules, refreshSysproxy, refreshProxyProviders, refreshRuleProviders, ]); // 聚合所有数据 const value = useMemo(() => { // 计算系统代理地址 const calculateSystemProxyAddress = () => { if (!verge || !clashConfig) return "-"; const isPacMode = verge.proxy_auto_config ?? false; if (isPacMode) { // PAC模式:显示我们期望设置的代理地址 const proxyHost = verge.proxy_host || "127.0.0.1"; const proxyPort = verge.verge_mixed_port || clashConfig.mixedPort || 7897; return `${proxyHost}:${proxyPort}`; } else { // HTTP代理模式:优先使用系统地址,但如果格式不正确则使用期望地址 const systemServer = sysproxy?.server; if ( systemServer && systemServer !== "-" && !systemServer.startsWith(":") ) { return systemServer; } else { // 系统地址无效,返回期望的代理地址 const proxyHost = verge.proxy_host || "127.0.0.1"; const proxyPort = verge.verge_mixed_port || clashConfig.mixedPort || 7897; return `${proxyHost}:${proxyPort}`; } } }; return { // 数据 proxies: proxiesData, clashConfig, rules: rulesData?.rules || [], sysproxy, runningMode, uptime: uptimeData || 0, // 提供者数据 proxyProviders: proxyProviders || {}, ruleProviders: ruleProviders?.providers || {}, systemProxyAddress: calculateSystemProxyAddress(), // 刷新方法 refreshProxy, refreshClashConfig, refreshRules, refreshSysproxy, refreshProxyProviders, refreshRuleProviders, refreshAll, } as AppDataContextType; }, [ proxiesData, clashConfig, rulesData, sysproxy, runningMode, uptimeData, proxyProviders, ruleProviders, verge, refreshProxy, refreshClashConfig, refreshRules, refreshSysproxy, refreshProxyProviders, refreshRuleProviders, refreshAll, ]); return {children}; };