mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-15 05:40:47 +08:00
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
This commit is contained in:
@@ -94,14 +94,6 @@ pub async fn import_profile(url: std::string::String, option: Option<PrfOption>)
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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<PrfItem>
|
||||
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;
|
||||
|
||||
+3
-37
@@ -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<UnlistenFn[]>([])
|
||||
|
||||
const addListener = useCallback(
|
||||
async <T>(eventName: string, handler: EventCallback<T>) => {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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[] = []
|
||||
|
||||
+1
-54
@@ -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<string>('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 () => {
|
||||
|
||||
@@ -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(() => {})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user