Compare commits

...

4 Commits

Author SHA1 Message Date
renovate[bot]
d8216ac337
chore(deps): update npm dependencies 2026-03-31 13:09:37 +00:00
Tunglies
9bcb79465c
fix: resolve frontend data race conditions in hooks
- use-system-state: convert module-level `disablingTunMode` to useRef
  to isolate state per hook instance, fix no-op clearTimeout, add
  proper effect cleanup
- use-profiles: convert forEach to for..of so selectNodeForGroup is
  properly awaited, remove fire-and-forget setTimeout around mutate
- use-clash: add useLockFn to patchInfo for concurrency safety
2026-03-31 20:26:34 +08:00
renovate[bot]
b62d89e163
chore(deps): update github/gh-aw-actions action to v0.64.4 (#6665)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-30 17:12:08 +00:00
wysha-object
b7230967b4
feat: show detailed results in hotkey notifications (#6639)
* feat: show detailed results in hotkey notifications

* fix: Japanese locale appears to have a truncated translation key label

* fix: variable naming

* Update documentation to English

* Remove unnecessary mut

* feat: enhance system proxy notifications with toggle state

* chore: update changelog to include new shortcut notification feature

* fix: remove unnecessary quotes from system proxy toggle messages in localization files

* fix: tun mode toggled hotkey notifications

* fix: correct toggle_tun_mode logic to handle current state and errors

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2026-03-30 17:06:45 +00:00
23 changed files with 154 additions and 91 deletions

View File

@ -65,7 +65,7 @@ jobs:
title: ${{ steps.sanitized.outputs.title }}
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
uses: github/gh-aw-actions/setup@dc2e3faa962b8cd6219ca125f4e3989bf731e535 # v0.64.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
- name: Generate agentic run info
@ -277,7 +277,7 @@ jobs:
output_types: ${{ steps.collect_output.outputs.output_types }}
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
uses: github/gh-aw-actions/setup@dc2e3faa962b8cd6219ca125f4e3989bf731e535 # v0.64.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
- name: Set runtime paths
@ -800,7 +800,7 @@ jobs:
total_count: ${{ steps.missing_tool.outputs.total_count }}
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
uses: github/gh-aw-actions/setup@dc2e3faa962b8cd6219ca125f4e3989bf731e535 # v0.64.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
- name: Download agent output artifact
@ -895,7 +895,7 @@ jobs:
detection_success: ${{ steps.detection_conclusion.outputs.success }}
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
uses: github/gh-aw-actions/setup@dc2e3faa962b8cd6219ca125f4e3989bf731e535 # v0.64.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
- name: Download agent output artifact
@ -1056,7 +1056,7 @@ jobs:
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
uses: github/gh-aw-actions/setup@dc2e3faa962b8cd6219ca125f4e3989bf731e535 # v0.64.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
- name: Download agent output artifact

View File

@ -11,6 +11,8 @@
### ✨ 新增功能
- 快捷键操作通知操作结果
### 🚀 优化改进
- 优化 macOS 读取系统代理性能

View File

@ -8,10 +8,12 @@ notifications:
body: تم التبديل إلى {mode}.
systemProxyToggled:
title: وكيل النظام
body: تم تحديث حالة وكيل النظام.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: وضع TUN
body: تم تحديث حالة وضع TUN.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: الوضع الخفيف
body: تم الدخول إلى الوضع الخفيف.

View File

@ -8,10 +8,12 @@ notifications:
body: Auf {mode} umgeschaltet.
systemProxyToggled:
title: Systemproxy
body: Der Status des Systemproxys wurde aktualisiert.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: TUN-Modus
body: Der Status des TUN-Modus wurde aktualisiert.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: Leichtmodus
body: Leichtmodus aktiviert.

View File

@ -8,10 +8,12 @@ notifications:
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.

View File

@ -8,10 +8,12 @@ notifications:
body: Cambiado a {mode}.
systemProxyToggled:
title: Proxy del sistema
body: El estado del proxy del sistema se ha actualizado.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: Modo TUN
body: El estado del modo TUN se ha actualizado.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: Modo ligero
body: Se ha entrado en el modo ligero.

View File

@ -8,10 +8,12 @@ notifications:
body: به {mode} تغییر کرد.
systemProxyToggled:
title: پروکسی سیستم
body: وضعیت پروکسی سیستم به‌روزرسانی شد.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: حالت TUN
body: وضعیت حالت TUN به‌روزرسانی شد.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: حالت سبک
body: به حالت سبک وارد شد.

View File

@ -8,10 +8,12 @@ notifications:
body: Beralih ke {mode}.
systemProxyToggled:
title: Proksi Sistem
body: Status proksi sistem telah diperbarui.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: Mode TUN
body: Status mode TUN telah diperbarui.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: Mode Ringan
body: Masuk ke mode ringan.

View File

@ -8,10 +8,12 @@ notifications:
body: '{mode} に切り替えました。'
systemProxyToggled:
title: システムプロキシ
body: システムプロキシの状態が更新されました。
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: TUN モード
body: TUN モードの状態が更新されました。
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: 軽量モード
body: 軽量モードに入りました。

View File

@ -8,10 +8,12 @@ notifications:
body: '{mode}(으)로 전환되었습니다.'
systemProxyToggled:
title: 시스템 프록시
body: 시스템 프록시 상태가 업데이트되었습니다.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: TUN 모드
body: TUN 모드 상태가 업데이트되었습니다.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: 경량 모드
body: 경량 모드에 진입했습니다.

View File

@ -8,10 +8,12 @@ notifications:
body: Переключено на {mode}.
systemProxyToggled:
title: Системный прокси
body: Статус системного прокси обновлен.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: Режим TUN
body: Статус режима TUN обновлен.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: Легкий режим
body: Включен легкий режим.

View File

@ -8,10 +8,12 @@ notifications:
body: '{mode} moduna geçildi.'
systemProxyToggled:
title: Sistem Vekil'i
body: Sistem vekil'i durumu güncellendi.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: TUN Modu
body: TUN modu durumu güncellendi.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: Hafif Mod
body: Hafif moda geçildi.

View File

@ -8,10 +8,12 @@ notifications:
body: '{mode} режимына күчтел.'
systemProxyToggled:
title: Системалы прокси
body: Системалы прокси хәле яңартылды.
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: TUN режимы
body: TUN режимы хәле яңартылды.
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: Җиңел режим
body: Җиңел режимга күчелде.

View File

@ -8,10 +8,12 @@ notifications:
body: 已切换至 {mode}。
systemProxyToggled:
title: 系统代理
body: 系统代理状态已更新。
'on': 系统代理已启用。
'off': 系统代理已禁用。
tunModeToggled:
title: TUN 模式
body: TUN 模式状态已更新。
'on': TUN 模式已开启。
'off': TUN 模式已关闭。
lightweightModeEntered:
title: 轻量模式
body: 已进入轻量模式。

View File

@ -8,10 +8,12 @@ notifications:
body: 已切換至 {mode}。
systemProxyToggled:
title: 系統代理
body: 系統代理狀態已更新。
'on': System proxy has been enabled.
'off': System proxy has been disabled.
tunModeToggled:
title: 虛擬網路介面卡模式
body: 已更新虛擬網路介面卡模式狀態。
'on': TUN mode has been enabled.
'off': TUN mode has been disabled.
lightweightModeEntered:
title: 輕量模式
body: 已進入輕量模式。

View File

@ -60,7 +60,7 @@
"dayjs": "1.11.20",
"foxact": "^0.3.0",
"foxts": "^5.3.0",
"i18next": "^25.10.4",
"i18next": "^26.0.0",
"js-yaml": "^4.1.1",
"lodash-es": "^4.17.23",
"meta-json-schema": "^1.19.21",
@ -71,7 +71,7 @@
"react-dom": "19.2.4",
"react-error-boundary": "6.1.1",
"react-hook-form": "^7.72.0",
"react-i18next": "16.6.6",
"react-i18next": "17.0.2",
"react-markdown": "10.1.0",
"react-router": "^7.13.1",
"react-virtuoso": "^4.18.3",
@ -120,7 +120,7 @@
"typescript": "^6.0.0",
"typescript-eslint": "^8.57.1",
"vite": "^8.0.1",
"vite-plugin-svgr": "^4.5.0"
"vite-plugin-svgr": "^5.0.0"
},
"lint-staged": {
"*.{ts,tsx}": [

36
pnpm-lock.yaml generated
View File

@ -87,8 +87,8 @@ importers:
specifier: ^5.3.0
version: 5.4.0
i18next:
specifier: ^25.10.4
version: 25.10.10(typescript@6.0.2)
specifier: ^26.0.0
version: 26.0.3(typescript@6.0.2)
js-yaml:
specifier: ^4.1.1
version: 4.1.1
@ -120,8 +120,8 @@ importers:
specifier: ^7.72.0
version: 7.72.0(react@19.2.4)
react-i18next:
specifier: 16.6.6
version: 16.6.6(i18next@25.10.10(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2)
specifier: 17.0.2
version: 17.0.2(i18next@26.0.3(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2)
react-markdown:
specifier: 10.1.0
version: 10.1.0(@types/react@19.2.14)(react@19.2.4)
@ -262,8 +262,8 @@ importers:
specifier: ^8.0.1
version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(yaml@2.8.3)
vite-plugin-svgr:
specifier: ^4.5.0
version: 4.5.0(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(yaml@2.8.3))
specifier: ^5.0.0
version: 5.0.0(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(yaml@2.8.3))
packages:
@ -2494,8 +2494,8 @@ packages:
engines: {node: '>=18'}
hasBin: true
i18next@25.10.10:
resolution: {integrity: sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==}
i18next@26.0.3:
resolution: {integrity: sha512-1571kXINxHKY7LksWp8wP+zP0YqHSSpl/OW0Y0owFEf2H3s8gCAffWaZivcz14rMkOvn3R/psiQxVsR9t2Nafg==}
peerDependencies:
typescript: ^5 || ^6
peerDependenciesMeta:
@ -3059,10 +3059,10 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
react-i18next@16.6.6:
resolution: {integrity: sha512-ZgL2HUoW34UKUkOV7uSQFE1CDnRPD+tCR3ywSuWH7u2iapnz86U8Bi3Vrs620qNDzCf1F47NxglCEkchCTDOHw==}
react-i18next@17.0.2:
resolution: {integrity: sha512-shBftH2vaTWK2Bsp7FiL+cevx3xFJlvFxmsDFQSrJc+6twHkP0tv/bGa01VVWzpreUVVwU+3Hev5iFqRg65RwA==}
peerDependencies:
i18next: '>= 25.10.9'
i18next: '>= 26.0.1'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
@ -3438,10 +3438,10 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
vite-plugin-svgr@4.5.0:
resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==}
vite-plugin-svgr@5.0.0:
resolution: {integrity: sha512-CZFWDtbWSLnF6C+uv8u7E5Ao6UVQYBpJrS6212XsEod/Lm4ErhOoFc01/po4ie5hqvMCr5KYrlMrSGQQEtMtBg==}
peerDependencies:
vite: '>=2.6.0'
vite: '>=3.0.0'
vite@8.0.3:
resolution: {integrity: sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==}
@ -6023,7 +6023,7 @@ snapshots:
husky@9.1.7: {}
i18next@25.10.10(typescript@6.0.2):
i18next@26.0.3(typescript@6.0.2):
dependencies:
'@babel/runtime': 7.29.2
optionalDependencies:
@ -6663,11 +6663,11 @@ snapshots:
dependencies:
react: 19.2.4
react-i18next@16.6.6(i18next@25.10.10(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2):
react-i18next@17.0.2(i18next@26.0.3(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2):
dependencies:
'@babel/runtime': 7.29.2
html-parse-stringify: 3.0.1
i18next: 25.10.10(typescript@6.0.2)
i18next: 26.0.3(typescript@6.0.2)
react: 19.2.4
use-sync-external-store: 1.6.0(react@19.2.4)
optionalDependencies:
@ -7088,7 +7088,7 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
vite-plugin-svgr@4.5.0(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(yaml@2.8.3)):
vite-plugin-svgr@5.0.0(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(yaml@2.8.3)):
dependencies:
'@rollup/pluginutils': 5.3.0
'@svgr/core': 8.1.0(typescript@6.0.2)

View File

@ -135,14 +135,14 @@ impl Hotkey {
}
HotkeyFunction::ToggleSystemProxy => {
AsyncHandler::spawn(async move || {
feat::toggle_system_proxy().await;
notify_event(NotificationEvent::SystemProxyToggled).await;
let is_proxy_enabled = feat::toggle_system_proxy().await;
notify_event(NotificationEvent::SystemProxyToggled(is_proxy_enabled)).await;
});
}
HotkeyFunction::ToggleTunMode => {
AsyncHandler::spawn(async move || {
feat::toggle_tun_mode(None).await;
notify_event(NotificationEvent::TunModeToggled).await;
let is_tun_enable = feat::toggle_tun_mode(None).await;
notify_event(NotificationEvent::TunModeToggled(is_tun_enable)).await;
});
}
HotkeyFunction::EntryLightweightMode => {

View File

@ -7,7 +7,7 @@ use std::env;
use tauri_plugin_clipboard_manager::ClipboardExt as _;
/// Toggle system proxy on/off
pub async fn toggle_system_proxy() {
pub async fn toggle_system_proxy() -> bool {
let verge = Config::verge().await;
let enable = verge.latest_arc().enable_system_proxy.unwrap_or(false);
let auto_close_connection = verge.latest_arc().auto_close_connection.unwrap_or(false);
@ -20,9 +20,10 @@ pub async fn toggle_system_proxy() {
logging!(error, Type::ProxyMode, "Failed to close all connections: {err}");
}
let enable = !enable;
let patch_result = super::patch_verge(
&IVerge {
enable_system_proxy: Some(!enable),
enable_system_proxy: Some(enable),
..IVerge::default()
},
false,
@ -33,24 +34,33 @@ pub async fn toggle_system_proxy() {
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => logging!(error, Type::ProxyMode, "{err}"),
}
enable
}
/// Toggle TUN mode on/off
pub async fn toggle_tun_mode(not_save_file: Option<bool>) {
let enable = Config::verge().await.latest_arc().enable_tun_mode;
let enable = enable.unwrap_or(false);
/// Returns the updated toggle state
pub async fn toggle_tun_mode(not_save_file: Option<bool>) -> bool {
let current = Config::verge().await.latest_arc().enable_tun_mode.unwrap_or(false);
let enable = !current;
match super::patch_verge(
&IVerge {
enable_tun_mode: Some(!enable),
enable_tun_mode: Some(enable),
..IVerge::default()
},
not_save_file.unwrap_or(false),
)
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => logging!(error, Type::ProxyMode, "{err}"),
Ok(_) => {
handle::Handle::refresh_verge();
enable
}
Err(err) => {
logging!(error, Type::ProxyMode, "{err}");
current
}
}
}

View File

@ -9,8 +9,8 @@ pub enum NotificationEvent<'a> {
ClashModeChanged {
mode: &'a str,
},
SystemProxyToggled,
TunModeToggled,
SystemProxyToggled(bool),
TunModeToggled(bool),
LightweightModeEntered,
ProfilesReactivated,
AppQuit,
@ -37,14 +37,25 @@ pub async fn notify_event<'a>(event: NotificationEvent<'a>) {
.into();
notify(title, body);
}
NotificationEvent::SystemProxyToggled => {
NotificationEvent::SystemProxyToggled(enabled) => {
let title = clash_verge_i18n::t!("notifications.systemProxyToggled.title");
let body = clash_verge_i18n::t!("notifications.systemProxyToggled.body");
let key = if enabled {
"notifications.systemProxyToggled.on"
} else {
"notifications.systemProxyToggled.off"
};
let body = clash_verge_i18n::t!(key);
notify(title, body);
}
NotificationEvent::TunModeToggled => {
NotificationEvent::TunModeToggled(enabled) => {
let title = clash_verge_i18n::t!("notifications.tunModeToggled.title");
let body = clash_verge_i18n::t!("notifications.tunModeToggled.body");
let key = if enabled {
"notifications.tunModeToggled.on"
} else {
"notifications.tunModeToggled.off"
};
let body = clash_verge_i18n::t!(key);
notify(title, body);
}
NotificationEvent::LightweightModeEntered => {

View File

@ -87,7 +87,7 @@ export const useClashInfo = () => {
getClashInfo,
)
const patchInfo = async (patch: ClashInfoPatch) => {
const patchInfo = useLockFn(async (patch: ClashInfoPatch) => {
if (!hasClashInfoPayload(patch)) return
validatePorts(patch)
@ -95,7 +95,7 @@ export const useClashInfo = () => {
await patchClashConfig(patch)
mutateInfo()
mutate('getClashConfig')
}
})
return {
clashInfo,

View File

@ -120,9 +120,9 @@ export const useProfiles = () => {
])
// 处理所有代理组
;[global, ...groups].forEach((group) => {
for (const group of [global, ...groups]) {
if (!group) {
return
continue
}
const { type, name, now } = group
@ -134,14 +134,14 @@ export const useProfiles = () => {
const preferredProxy = now ? now : savedProxy
newSelected.push({ name, now: preferredProxy })
}
return
continue
}
if (savedProxy == null) {
if (now != null) {
newSelected.push({ name, now })
}
return
continue
}
const existsInGroup = availableProxies.some((proxy) => {
@ -158,7 +158,7 @@ export const useProfiles = () => {
)
hasChange = true
newSelected.push({ name, now: now ?? savedProxy })
return
continue
}
if (savedProxy !== now) {
@ -166,11 +166,18 @@ export const useProfiles = () => {
`[ActivateSelected] 需要切换代理组 ${name}: ${now} -> ${savedProxy}`,
)
hasChange = true
selectNodeForGroup(name, savedProxy)
try {
await selectNodeForGroup(name, savedProxy)
} catch (error: any) {
console.warn(
`[ActivateSelected] 切换代理组 ${name} 失败:`,
error.message,
)
}
}
newSelected.push({ name, now: savedProxy })
})
}
if (!hasChange) {
debugLog('[ActivateSelected] 所有代理选择已经是目标状态,无需更新')
@ -183,9 +190,7 @@ export const useProfiles = () => {
await patchProfile(profileData.current!, { selected: newSelected })
debugLog('[ActivateSelected] 代理选择配置保存成功')
setTimeout(() => {
mutate('getProxies', calcuProxies())
}, 100)
await mutate('getProxies', calcuProxies())
} catch (error: any) {
console.error('[ActivateSelected] 保存代理选择配置失败:', error.message)
}

View File

@ -1,4 +1,4 @@
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import useSWR from 'swr'
import { getRunningMode, isAdmin, isServiceAvailable } from '@/services/cmds'
@ -18,14 +18,13 @@ const defaultSystemState = {
isServiceOk: false,
} as SystemState
let disablingTunMode = false
/**
* hook
*
*/
export function useSystemState() {
const { verge, patchVerge } = useVerge()
const disablingTunRef = useRef(false)
const {
data: systemState,
@ -53,16 +52,18 @@ export function useSystemState() {
const isTunModeAvailable = systemState.isAdminMode || systemState.isServiceOk
const enable_tun_mode = verge?.enable_tun_mode
const cooldownTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
useEffect(() => {
if (enable_tun_mode === undefined) return
if (
!disablingTunMode &&
!disablingTunRef.current &&
enable_tun_mode &&
!isTunModeAvailable &&
!isLoading
) {
disablingTunMode = true
disablingTunRef.current = true
patchVerge({ enable_tun_mode: false })
.then(() => {
showNotice.info(
@ -76,13 +77,21 @@ export function useSystemState() {
)
})
.finally(() => {
const tid = setTimeout(() => {
// 避免 verge 数据更新不及时导致重复执行关闭 Tun 模式
disablingTunMode = false
clearTimeout(tid)
// 避免 verge 数据更新不及时导致重复执行关闭 Tun 模式
cooldownTimerRef.current = setTimeout(() => {
disablingTunRef.current = false
cooldownTimerRef.current = null
}, 1000)
})
}
return () => {
if (cooldownTimerRef.current != null) {
clearTimeout(cooldownTimerRef.current)
cooldownTimerRef.current = null
disablingTunRef.current = false
}
}
}, [enable_tun_mode, isTunModeAvailable, patchVerge, isLoading])
return {