diff --git a/src-tauri/src/feat/clash.rs b/src-tauri/src/feat/clash.rs index fe2de5c9b..77645d51a 100644 --- a/src-tauri/src/feat/clash.rs +++ b/src-tauri/src/feat/clash.rs @@ -3,7 +3,7 @@ use crate::{ core::{CoreManager, handle, tray}, feat::clean_async, process::AsyncHandler, - utils::{self, resolve::reset_resolve_done}, + utils, }; use clash_verge_logging::{Type, logging}; use serde_yaml_ng::{Mapping, Value}; @@ -42,7 +42,6 @@ pub async fn restart_app() { if cleanup_result { 0 } else { 1 } ); - reset_resolve_done(); let app_handle = handle::Handle::app_handle(); app_handle.restart(); } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e4af659bf..efb8ae042 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -251,7 +251,6 @@ pub fn run() { resolve::resolve_setup_async(); resolve::resolve_setup_sync(); resolve::init_signal(); - resolve::resolve_done(); logging!(info, Type::Setup, "初始化已启动"); Ok(()) diff --git a/src-tauri/src/utils/resolve/mod.rs b/src-tauri/src/utils/resolve/mod.rs index 635886347..bd91bc987 100644 --- a/src-tauri/src/utils/resolve/mod.rs +++ b/src-tauri/src/utils/resolve/mod.rs @@ -62,14 +62,9 @@ pub fn resolve_setup_async() { init_system_proxy_guard().await; }); - let tray_init = async { - init_tray().await; - refresh_tray_menu().await; - }; - let _ = futures::join!( core_init, - tray_init, + init_tray(), init_timer(), init_hotkey(), init_auto_lightweight_boot(), @@ -79,6 +74,7 @@ pub fn resolve_setup_async() { Handle::refresh_clash(); refresh_tray_menu().await; + resolve_done(); }); } @@ -220,7 +216,3 @@ pub fn resolve_done() { pub fn is_resolve_done() -> bool { RESOLVE_DONE.load(Ordering::Acquire) } - -pub fn reset_resolve_done() { - RESOLVE_DONE.store(false, Ordering::Release); -} diff --git a/src-tauri/src/utils/window_manager.rs b/src-tauri/src/utils/window_manager.rs index 7b4e5fa77..aa3d6c1c1 100644 --- a/src-tauri/src/utils/window_manager.rs +++ b/src-tauri/src/utils/window_manager.rs @@ -129,7 +129,7 @@ impl WindowManager { logging!(info, Type::Window, "窗口不存在,创建新窗口"); if Self::create_window(true).await { logging!(info, Type::Window, "窗口创建成功"); - std::thread::sleep(std::time::Duration::from_millis(50)); + tokio::time::sleep(std::time::Duration::from_millis(50)).await; WindowOperationResult::Created } else { logging!(warn, Type::Window, "窗口创建失败"); @@ -286,11 +286,11 @@ impl WindowManager { /// 创建新窗口,防抖避免重复调用 /// 窗口创建后保持隐藏,由前端 index.html 在 overlay 渲染后调用 show,避免主题闪烁 - pub fn create_window(is_show: bool) -> Pin + Send>> { + pub fn create_window(should_create: bool) -> Pin + Send>> { Box::pin(async move { - logging!(info, Type::Window, "开始创建/显示主窗口, is_show={}", is_show); + logging!(info, Type::Window, "开始创建主窗口, should_create={}", should_create); - if !is_show { + if !should_create { return false; } diff --git a/src/providers/app-data-provider.tsx b/src/providers/app-data-provider.tsx index c6b7565cc..df6f49a1e 100644 --- a/src/providers/app-data-provider.tsx +++ b/src/providers/app-data-provider.tsx @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query' import { listen } from '@tauri-apps/api/event' -import React, { useCallback, useEffect, useMemo } from 'react' +import React, { useCallback, useEffect, useMemo, useRef } from 'react' import { getBaseConfig, getRuleProviders, @@ -79,106 +79,45 @@ export const AppDataProvider = ({ ...TQ_MIHOMO, }) + const refreshProxyRef = useRef(refreshProxy) + const refreshRulesRef = useRef(refreshRules) + const refreshRuleProvidersRef = useRef(refreshRuleProviders) + useEffect(() => { + refreshProxyRef.current = refreshProxy + }, [refreshProxy]) + useEffect(() => { + refreshRulesRef.current = refreshRules + }, [refreshRules]) + useEffect(() => { + refreshRuleProvidersRef.current = refreshRuleProviders + }, [refreshRuleProviders]) + useEffect(() => { let lastProfileId: string | null = null let lastUpdateTime = 0 const refreshThrottle = 800 - - let isUnmounted = false - const scheduledTimeouts = new Set() const cleanupFns: Array<() => void> = [] - const registerCleanup = (fn: () => void) => { - if (isUnmounted) { - try { - fn() - } catch (error) { - console.error('[DataProvider] Immediate cleanup failed:', error) - } - } else { - cleanupFns.push(fn) - } - } - - const addWindowListener = (eventName: string, handler: EventListener) => { - // eslint-disable-next-line @eslint-react/web-api-no-leaked-event-listener - window.addEventListener(eventName, handler) - return () => window.removeEventListener(eventName, handler) - } - - const scheduleTimeout = ( - callback: () => void | Promise, - delay: number, - ) => { - if (isUnmounted) return -1 - - const timeoutId = window.setTimeout(() => { - scheduledTimeouts.delete(timeoutId) - if (!isUnmounted) { - 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() - if ( lastProfileId === newProfileId && now - lastUpdateTime < refreshThrottle ) { return } - lastProfileId = newProfileId lastUpdateTime = now - - scheduleTimeout(() => { - refreshRules().catch((error) => - console.warn('[DataProvider] Rules refresh failed:', error), - ) - refreshRuleProviders().catch((error) => - console.warn('[DataProvider] Rule providers refresh failed:', error), - ) - }, 200) - } - - const handleRefreshClash = () => { - const now = Date.now() - if (now - lastUpdateTime <= refreshThrottle) return - - lastUpdateTime = now - scheduleTimeout(async () => { - await Promise.all([ - refreshProxy().catch((error) => - console.error('[DataProvider] Proxy refresh failed:', error), - ), - refreshClashConfig().catch((error) => - console.error('[DataProvider] Clash config refresh failed:', error), - ), - ]) - }, 200) + refreshRulesRef.current().catch(() => {}) + refreshRuleProvidersRef.current().catch(() => {}) } const handleRefreshProxy = () => { const now = Date.now() if (now - lastUpdateTime <= refreshThrottle) return - lastUpdateTime = now - scheduleTimeout(() => { - refreshProxy().catch((error) => - console.warn('[DataProvider] Proxy refresh failed:', error), - ) - }, 200) + refreshProxyRef.current().catch(() => {}) } const initializeListeners = async () => { @@ -187,62 +126,34 @@ export const AppDataProvider = ({ 'profile-changed', handleProfileChanged, ) - registerCleanup(unlistenProfile) + cleanupFns.push(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() - }) + cleanupFns.push(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() - - const errors: Error[] = [] - cleanupFns.splice(0).forEach((fn) => { + cleanupFns.forEach((fn) => { try { fn() } catch (error) { - errors.push(error instanceof Error ? error : new Error(String(error))) + console.error('[DataProvider] Cleanup error:', error) } }) - - if (errors.length > 0) { - console.error( - `[DataProvider] ${errors.length} errors during cleanup:`, - errors, - ) - } } - }, [refreshProxy, refreshClashConfig, refreshRules, refreshRuleProviders]) + }, []) const { data: sysproxy, refetch: refreshSysproxy } = useQuery({ queryKey: ['getSystemProxy'],