mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-13 05:20:28 +08:00
Compare commits
3 Commits
62f1599fea
...
acf7ddf896
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acf7ddf896 | ||
|
|
1005baabe6 | ||
|
|
3aa39bff94 |
@ -92,8 +92,9 @@
|
||||
"@types/validator": "^13.15.10",
|
||||
"@vitejs/plugin-legacy": "^8.0.0",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"axios": "^1.13.6",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.13.6",
|
||||
"babel-plugin-react-compiler": "^1.0.0",
|
||||
"cli-color": "^2.0.4",
|
||||
"commander": "^14.0.3",
|
||||
"cross-env": "^10.1.0",
|
||||
|
||||
785
pnpm-lock.yaml
generated
785
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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<Box<dyn Future<Output = bool> + Send>> {
|
||||
pub fn create_window(should_create: bool) -> Pin<Box<dyn Future<Output = bool> + 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;
|
||||
}
|
||||
|
||||
|
||||
@ -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<number>()
|
||||
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<void>,
|
||||
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'],
|
||||
|
||||
@ -10,7 +10,11 @@ export default defineConfig({
|
||||
server: { port: 3000 },
|
||||
plugins: [
|
||||
svgr(),
|
||||
react(),
|
||||
react({
|
||||
babel: {
|
||||
plugins: ['babel-plugin-react-compiler'],
|
||||
},
|
||||
} as any),
|
||||
legacy({
|
||||
modernTargets: ['edge>=109', 'safari>=14'],
|
||||
renderLegacyChunks: false,
|
||||
@ -26,6 +30,24 @@ export default defineConfig({
|
||||
outDir: '../dist',
|
||||
emptyOutDir: true,
|
||||
chunkSizeWarningLimit: 4000,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: (id) => {
|
||||
if (id.includes('monaco-yaml')) return 'monaco-yaml'
|
||||
if (
|
||||
id.includes('node_modules/react/') ||
|
||||
id.includes('node_modules/react-dom/')
|
||||
)
|
||||
return 'react'
|
||||
if (id.includes('node_modules/react-router')) return 'router'
|
||||
if (
|
||||
id.includes('node_modules/i18next') ||
|
||||
id.includes('node_modules/react-i18next')
|
||||
)
|
||||
return 'i18n'
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
@ -36,4 +58,13 @@ export default defineConfig({
|
||||
define: {
|
||||
OS_PLATFORM: `"${process.platform}"`,
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-router-dom',
|
||||
'i18next',
|
||||
'react-i18next',
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user