From 56141e6dfa74dad6cb5dcd064c2513cee409865a Mon Sep 17 00:00:00 2001 From: wonfen Date: Tue, 17 Mar 2026 12:50:44 +0800 Subject: [PATCH] feat: use optimistic updates for system proxy toggle --- .../shared/proxy-control-switches.tsx | 37 ++++++++++++------ src/hooks/use-system-proxy-state.ts | 39 ++++++++++--------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/components/shared/proxy-control-switches.tsx b/src/components/shared/proxy-control-switches.tsx index 837312e8b..9210c8703 100644 --- a/src/components/shared/proxy-control-switches.tsx +++ b/src/components/shared/proxy-control-switches.tsx @@ -8,11 +8,10 @@ import { } from '@mui/icons-material' import { Box, Typography, alpha, useTheme } from '@mui/material' import { useLockFn } from 'ahooks' -import React, { useCallback, useRef } from 'react' +import React, { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { DialogRef, Switch, TooltipIcon } from '@/components/base' -import { GuardState } from '@/components/setting/mods/guard-state' import { SysproxyViewer } from '@/components/setting/mods/sysproxy-viewer' import { TunViewer } from '@/components/setting/mods/tun-viewer' import { useServiceInstaller } from '@/hooks/use-service-installer' @@ -42,6 +41,7 @@ interface SwitchRowProps { /** * 抽取的子组件:统一的开关 UI + * active = 真实状态OS/配置 乐观更新 */ const SwitchRow = ({ label, @@ -55,6 +55,24 @@ const SwitchRow = ({ highlight, }: SwitchRowProps) => { const theme = useTheme() + const [checked, setChecked] = useState(active) + const pendingRef = useRef(false) + + if (pendingRef.current) { + if (active === checked) pendingRef.current = false + } else if (checked !== active) { + setChecked(active) + } + + const handleChange = (_: React.ChangeEvent, value: boolean) => { + pendingRef.current = true + setChecked(value) + onToggle(value).catch((err: any) => { + pendingRef.current = false + onError?.(err) + }) + } + return ( - v} - onGuard={onToggle} - > - - + ) } diff --git a/src/hooks/use-system-proxy-state.ts b/src/hooks/use-system-proxy-state.ts index e2d2be991..7b96182f8 100644 --- a/src/hooks/use-system-proxy-state.ts +++ b/src/hooks/use-system-proxy-state.ts @@ -1,4 +1,4 @@ -import { useLockFn } from 'ahooks' +import { useRef } from 'react' import useSWR, { mutate } from 'swr' import { closeAllConnections } from 'tauri-plugin-mihomo-api' @@ -36,28 +36,31 @@ export const useSystemProxyState = () => { } })() - const toggleSystemProxy = useLockFn(async (enabled: boolean) => { - mutateVerge({ ...verge, enable_system_proxy: enabled }, false) + // "最后一次生效"模式:快速连续点击时,只执行最终状态 + const pendingRef = useRef(null) + const busyRef = useRef(false) - const updateProxyStatus = async () => { - await new Promise((resolve) => setTimeout(resolve, enabled ? 20 : 10)) - await mutate('getSystemProxy') - await mutate('getAutotemProxy') - } + const toggleSystemProxy = async (enabled: boolean) => { + mutateVerge({ ...verge, enable_system_proxy: enabled }, false) + pendingRef.current = enabled + + if (busyRef.current) return + busyRef.current = true try { - if (!enabled && verge?.auto_close_connection) { - await closeAllConnections() + while (pendingRef.current !== null) { + const target = pendingRef.current + pendingRef.current = null + if (!target && verge?.auto_close_connection) { + await closeAllConnections().catch(() => {}) + } + await patchVerge({ enable_system_proxy: target }) } - await patchVerge({ enable_system_proxy: enabled }) - await updateProxyStatus() - } catch (error) { - console.warn('[useSystemProxyState] toggleSystemProxy failed:', error) - mutateVerge({ ...verge, enable_system_proxy: !enabled }, false) - await updateProxyStatus() - throw error + } finally { + busyRef.current = false + await Promise.all([mutate('getSystemProxy'), mutate('getAutotemProxy')]) } - }) + } return { indicator,