From c18c23963dbc8ebc0b24833db01de472f5b1d234 Mon Sep 17 00:00:00 2001 From: Tunglies Date: Sat, 23 May 2026 12:52:52 +0800 Subject: [PATCH] refactor: frontend (#7097) * refactor: remove unused window show/hide event listeners * refactor: deduplicate profile-changed event handling * refactor: remove unused setupCloseListener from use-listen * refactor: remove unused removeAllListeners from use-listen * fix(backend): dedupe profile changed notification on import * fix: avoid duplicate timer update completed event * refactor(frontend): simplify proxy switch fallback path --- src-tauri/src/cmd/profile.rs | 8 --- src-tauri/src/core/timer.rs | 3 +- src-tauri/src/utils/resolve/scheme.rs | 4 -- src/hooks/use-listen.ts | 40 ++------------ src/hooks/use-proxy-selection.ts | 19 +------ src/pages/_layout/hooks/use-layout-events.ts | 16 ------ src/pages/profiles.tsx | 55 +------------------- src/providers/app-data-provider.tsx | 2 + 8 files changed, 9 insertions(+), 138 deletions(-) diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index 00ac2b679..932f466a0 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -94,14 +94,6 @@ pub async fn import_profile(url: std::string::String, option: Option) handle::Handle::notify_profile_changed(uid); } - // 异步保存配置文件并发送全局通知 - if let Some(uid) = &item.uid { - // 延迟发送,确保文件已完全写入 - tokio::time::sleep(Duration::from_millis(100)).await; - logging!(info, Type::Cmd, "[导入订阅] 发送配置变更通知: {}", uid); - handle::Handle::notify_profile_changed(uid); - } - logging!(info, Type::Cmd, "[导入订阅] 导入完成: {}", help::mask_url(&url)); Ok(()) } diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 173eddd61..a6040ac85 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -1,4 +1,4 @@ -use crate::{config::Config, core::handle, feat, process::AsyncHandler, singleton, utils::resolve::is_resolve_done}; +use crate::{config::Config, feat, process::AsyncHandler, singleton, utils::resolve::is_resolve_done}; use anyhow::Result; use clash_verge_logging::{Type, logging, logging_error}; use parking_lot::{Mutex, RwLock}; @@ -396,7 +396,6 @@ impl Timer { uid, task_start.elapsed().as_millis() ); - handle::Handle::notify_profile_update_completed(uid); } Ok(Err(e)) => logging_error!(Type::Timer, "Failed to update profile uid {}: {}", uid, e), Err(_) => logging_error!(Type::Timer, "Timer task timed out for uid: {}", uid), diff --git a/src-tauri/src/utils/resolve/scheme.rs b/src-tauri/src/utils/resolve/scheme.rs index 7cee228ef..efdaa5e38 100644 --- a/src-tauri/src/utils/resolve/scheme.rs +++ b/src-tauri/src/utils/resolve/scheme.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use anyhow::Result; use percent_encoding::percent_decode_str; use smartstring::alias::String; @@ -123,7 +121,6 @@ async fn fetch_profile_item(url: &str, name: Option<&String>) -> Option async fn post_import_updates(uid: &String, had_current_profile: bool) { handle::Handle::refresh_verge(); handle::Handle::notify_profile_changed(uid); - tokio::time::sleep(Duration::from_millis(100)).await; let should_update_core = if uid.is_empty() || had_current_profile { false @@ -131,7 +128,6 @@ async fn post_import_updates(uid: &String, had_current_profile: bool) { let profiles = Config::profiles().await; profiles.latest_arc().is_current_profile_index(uid) }; - handle::Handle::notify_profile_changed(uid); if should_update_core { refresh_core_config().await; diff --git a/src/hooks/use-listen.ts b/src/hooks/use-listen.ts index 53902b828..dcabcc087 100644 --- a/src/hooks/use-listen.ts +++ b/src/hooks/use-listen.ts @@ -1,49 +1,15 @@ -import { event } from '@tauri-apps/api' -import { listen, UnlistenFn, EventCallback } from '@tauri-apps/api/event' -import { useCallback, useRef } from 'react' +import { listen, EventCallback } from '@tauri-apps/api/event' +import { useCallback } from 'react' export const useListen = () => { - const unlistenFnsRef = useRef([]) - const addListener = useCallback( async (eventName: string, handler: EventCallback) => { - const unlisten = await listen(eventName, handler) - unlistenFnsRef.current.push(unlisten) - return unlisten + return await listen(eventName, handler) }, [], ) - const removeAllListeners = useCallback(() => { - const errors: Error[] = [] - - unlistenFnsRef.current.forEach((unlisten) => { - try { - unlisten() - } catch (error) { - errors.push(error instanceof Error ? error : new Error(String(error))) - } - }) - - if (errors.length > 0) { - console.warn( - `[useListen] 清理监听器时发生 ${errors.length} 个错误`, - errors, - ) - } - - unlistenFnsRef.current.length = 0 - }, []) - - const setupCloseListener = useCallback(async () => { - await event.once('tauri://close-requested', async () => { - removeAllListeners() - }) - }, [removeAllListeners]) - return { addListener, - removeAllListeners, - setupCloseListener, } } diff --git a/src/hooks/use-proxy-selection.ts b/src/hooks/use-proxy-selection.ts index 5b8ceb10f..cc956f844 100644 --- a/src/hooks/use-proxy-selection.ts +++ b/src/hooks/use-proxy-selection.ts @@ -104,29 +104,14 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => { config.autoCloseConnection && previousProxy ) { - setTimeout(() => cleanupConnections(previousProxy), 0) + void cleanupConnections(previousProxy) } } catch (error) { console.error( `[ProxySelection] 代理切换失败: ${groupName} -> ${proxyName}`, error, ) - - try { - await selectNodeForGroup(groupName, proxyName) - onSuccess?.() - syncTraySelection() - persistSelection(groupName, proxyName, skipConfigSave) - debugLog( - `[ProxySelection] 代理切换回退成功: ${groupName} -> ${proxyName}`, - ) - } catch (fallbackError) { - console.error( - `[ProxySelection] 代理切换回退也失败: ${groupName} -> ${proxyName}`, - fallbackError, - ) - onError?.(fallbackError) - } + onError?.(error) } }, [config, onError, onSuccess, persistSelection, syncTraySelection], diff --git a/src/pages/_layout/hooks/use-layout-events.ts b/src/pages/_layout/hooks/use-layout-events.ts index 409ba1578..af28785b2 100644 --- a/src/pages/_layout/hooks/use-layout-events.ts +++ b/src/pages/_layout/hooks/use-layout-events.ts @@ -1,5 +1,3 @@ -import { listen } from '@tauri-apps/api/event' -import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' import { useEffect } from 'react' import { useListen } from '@/hooks/use-listen' @@ -73,20 +71,6 @@ export const useLayoutEvents = ( ), ) - const appWindow = getCurrentWebviewWindow() - register( - (async () => { - const [hideUnlisten, showUnlisten] = await Promise.all([ - listen('verge://hide-window', () => appWindow.hide()), - listen('verge://show-window', () => appWindow.show()), - ]) - return () => { - hideUnlisten() - showUnlisten() - } - })(), - ) - return () => { disposed = true const errors: Error[] = [] diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx index 455be5585..35d1062f9 100644 --- a/src/pages/profiles.tsx +++ b/src/pages/profiles.tsx @@ -22,7 +22,7 @@ import { } from '@mui/icons-material' import { Box, Button, Divider, Grid, IconButton, Stack } from '@mui/material' import { useQuery } from '@tanstack/react-query' -import { listen, TauriEvent } from '@tauri-apps/api/event' +import { TauriEvent } from '@tauri-apps/api/event' import { readText } from '@tauri-apps/plugin-clipboard-manager' import { readTextFile } from '@tauri-apps/plugin-fs' import { useLockFn } from 'ahooks' @@ -742,59 +742,6 @@ const ProfilePage = () => { ? 'rgba(0, 0, 0, 0.06)' : 'rgba(255, 255, 255, 0.06)' - // 监听后端配置变更 - useEffect(() => { - let unlistenPromise: Promise<() => void> | undefined - let lastProfileId: string | null = null - let lastUpdateTime = 0 - const debounceDelay = 200 - - let refreshTimer: number | null = null - - const setupListener = async () => { - unlistenPromise = listen('profile-changed', (event) => { - const newProfileId = event.payload - const now = Date.now() - - debugLog(`[Profile] 收到配置变更事件: ${newProfileId}`) - - if ( - lastProfileId === newProfileId && - now - lastUpdateTime < debounceDelay - ) { - debugLog(`[Profile] 重复事件被防抖,跳过`) - return - } - - lastProfileId = newProfileId - lastUpdateTime = now - - debugLog(`[Profile] 执行配置数据刷新`) - - if (refreshTimer !== null) { - window.clearTimeout(refreshTimer) - } - - // 使用异步调度避免阻塞事件处理 - refreshTimer = window.setTimeout(() => { - mutateProfiles().catch((error) => { - console.error('[Profile] 配置数据刷新失败:', error) - }) - refreshTimer = null - }, 0) - }) - } - - setupListener() - - return () => { - if (refreshTimer !== null) { - window.clearTimeout(refreshTimer) - } - unlistenPromise?.then((unlisten) => unlisten()).catch(console.error) - } - }, [mutateProfiles]) - // 组件卸载时清理中断控制器 useEffect(() => { return () => { diff --git a/src/providers/app-data-provider.tsx b/src/providers/app-data-provider.tsx index d788370a5..f2cdec6a2 100644 --- a/src/providers/app-data-provider.tsx +++ b/src/providers/app-data-provider.tsx @@ -15,6 +15,7 @@ import { getRunningMode, getSystemProxy, } from '@/services/cmds' +import { queryClient } from '@/services/query-client' import { ClashConfigContext, @@ -137,6 +138,7 @@ export const AppDataProvider = ({ } lastProfileId = newProfileId lastUpdateTime = now + void queryClient.invalidateQueries({ queryKey: ['getProfiles'] }) refreshRules().catch(() => {}) refreshRuleProviders().catch(() => {}) }