feat: use optimistic updates for system proxy toggle

This commit is contained in:
wonfen 2026-03-17 12:50:44 +08:00
parent b69a97a7c1
commit 56141e6dfa
No known key found for this signature in database
GPG Key ID: CEAFD6C73AB2001F
2 changed files with 47 additions and 29 deletions

View File

@ -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 (
<Box
sx={{
@ -92,15 +110,12 @@ const SwitchRow = ({
{extraIcons}
</Box>
<GuardState
value={active}
valueProps="checked"
onCatch={onError}
onFormat={(_, v) => v}
onGuard={onToggle}
>
<Switch edge="end" disabled={disabled} />
</GuardState>
<Switch
edge="end"
disabled={disabled}
checked={checked}
onChange={handleChange}
/>
</Box>
)
}

View File

@ -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<boolean | null>(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,