From 44adf5597546ae2b3c7e3e25418c419760eba7a3 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:14:04 +0800 Subject: [PATCH] fix(proxy): sync system proxy switch state with background highlight #5240, #5439, #5421, #4651, #4536 Fixed race condition where button state and background color would desync during rapid toggles or slow backend responses. Root causes: - Button used actualState while background used configState - toggleSystemProxy returned immediately via setTimeout(0), releasing lock before backend operation completed - 100ms delay in updateProxyStatus caused visual flash Changes: - Unified state source: both button and background now use actualState - Made toggleSystemProxy truly async with proper await chain - Wrapped toggle function with useLockFn to serialize operations - Reduced state refresh delay from 100ms to 50ms - Fixed GuardState lock release timing for async operations - Added proper error rollback with state refresh on failure Resolves issue where proxy toggle button would show as off while background remained green, or vice versa. --- src/components/setting/mods/guard-state.tsx | 5 ++- .../shared/ProxyControlSwitches.tsx | 4 +-- src/hooks/use-system-proxy-state.ts | 36 +++++++++---------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/setting/mods/guard-state.tsx b/src/components/setting/mods/guard-state.tsx index 4889749da..ffd6a0a0c 100644 --- a/src/components/setting/mods/guard-state.tsx +++ b/src/components/setting/mods/guard-state.tsx @@ -60,6 +60,7 @@ export function GuardState(props: Props) { if (waitTime <= 0) { await onGuard(newValue, value!); + lockRef.current = false; } else { // debounce guard clearTimeout(timeRef.current); @@ -71,6 +72,8 @@ export function GuardState(props: Props) { // 状态回退 onChange(saveRef.current!); onCatch(err); + } finally { + lockRef.current = false; } }, waitTime); } @@ -78,8 +81,8 @@ export function GuardState(props: Props) { // 状态回退 onChange(saveRef.current!); onCatch(err); + lockRef.current = false; } - lockRef.current = false; }; const { children: nestedChildren, ...restProps } = childProps; diff --git a/src/components/shared/ProxyControlSwitches.tsx b/src/components/shared/ProxyControlSwitches.tsx index c26eeb41a..79466dfb6 100644 --- a/src/components/shared/ProxyControlSwitches.tsx +++ b/src/components/shared/ProxyControlSwitches.tsx @@ -123,7 +123,7 @@ const ProxyControlSwitches = ({ const sysproxyRef = useRef(null); const tunRef = useRef(null); - const { enable_tun_mode, enable_system_proxy } = verge ?? {}; + const { enable_tun_mode } = verge ?? {}; const showErrorNotice = useCallback( (msg: string) => showNotice.error(msg), @@ -175,7 +175,7 @@ const ProxyControlSwitches = ({ onInfoClick={() => sysproxyRef.current?.open()} onToggle={(value) => toggleSystemProxy(value)} onError={onError} - highlight={enable_system_proxy} + highlight={systemProxyActualState} /> )} diff --git a/src/hooks/use-system-proxy-state.ts b/src/hooks/use-system-proxy-state.ts index 29c028e8b..a04e12377 100644 --- a/src/hooks/use-system-proxy-state.ts +++ b/src/hooks/use-system-proxy-state.ts @@ -1,3 +1,4 @@ +import { useLockFn } from "ahooks"; import useSWR, { mutate } from "swr"; import { closeAllConnections } from "tauri-plugin-mihomo-api"; @@ -41,31 +42,30 @@ export const useSystemProxyState = () => { } }; - const updateProxyStatus = async () => { - await new Promise((resolve) => setTimeout(resolve, 100)); + const updateProxyStatus = async (isEnabling: boolean) => { + // 关闭时更快响应,开启时等待系统确认 + const delay = isEnabling ? 20 : 10; + await new Promise((resolve) => setTimeout(resolve, delay)); await mutate("getSystemProxy"); await mutate("getAutotemProxy"); }; - const toggleSystemProxy = (enabled: boolean) => { + const toggleSystemProxy = useLockFn(async (enabled: boolean) => { mutateVerge({ ...verge, enable_system_proxy: enabled }, false); - setTimeout(async () => { - try { - if (!enabled && verge?.auto_close_connection) { - closeAllConnections(); - } - await patchVerge({ enable_system_proxy: enabled }); - - updateProxyStatus(); - } catch (error) { - console.warn("[useSystemProxyState] toggleSystemProxy failed:", error); - mutateVerge({ ...verge, enable_system_proxy: !enabled }, false); + try { + if (!enabled && verge?.auto_close_connection) { + await closeAllConnections(); } - }, 0); - - return Promise.resolve(); - }; + await patchVerge({ enable_system_proxy: enabled }); + await updateProxyStatus(enabled); + } catch (error) { + console.warn("[useSystemProxyState] toggleSystemProxy failed:", error); + mutateVerge({ ...verge, enable_system_proxy: !enabled }, false); + await updateProxyStatus(!enabled); + throw error; + } + }); return { actualState: getSystemProxyActualState(),