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' } from '@mui/icons-material'
import { Box, Typography, alpha, useTheme } from '@mui/material' import { Box, Typography, alpha, useTheme } from '@mui/material'
import { useLockFn } from 'ahooks' import { useLockFn } from 'ahooks'
import React, { useCallback, useRef } from 'react' import React, { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { DialogRef, Switch, TooltipIcon } from '@/components/base' import { DialogRef, Switch, TooltipIcon } from '@/components/base'
import { GuardState } from '@/components/setting/mods/guard-state'
import { SysproxyViewer } from '@/components/setting/mods/sysproxy-viewer' import { SysproxyViewer } from '@/components/setting/mods/sysproxy-viewer'
import { TunViewer } from '@/components/setting/mods/tun-viewer' import { TunViewer } from '@/components/setting/mods/tun-viewer'
import { useServiceInstaller } from '@/hooks/use-service-installer' import { useServiceInstaller } from '@/hooks/use-service-installer'
@ -42,6 +41,7 @@ interface SwitchRowProps {
/** /**
* UI * UI
* active = OS/
*/ */
const SwitchRow = ({ const SwitchRow = ({
label, label,
@ -55,6 +55,24 @@ const SwitchRow = ({
highlight, highlight,
}: SwitchRowProps) => { }: SwitchRowProps) => {
const theme = useTheme() 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 ( return (
<Box <Box
sx={{ sx={{
@ -92,15 +110,12 @@ const SwitchRow = ({
{extraIcons} {extraIcons}
</Box> </Box>
<GuardState <Switch
value={active} edge="end"
valueProps="checked" disabled={disabled}
onCatch={onError} checked={checked}
onFormat={(_, v) => v} onChange={handleChange}
onGuard={onToggle} />
>
<Switch edge="end" disabled={disabled} />
</GuardState>
</Box> </Box>
) )
} }

View File

@ -1,4 +1,4 @@
import { useLockFn } from 'ahooks' import { useRef } from 'react'
import useSWR, { mutate } from 'swr' import useSWR, { mutate } from 'swr'
import { closeAllConnections } from 'tauri-plugin-mihomo-api' 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 () => { const toggleSystemProxy = async (enabled: boolean) => {
await new Promise((resolve) => setTimeout(resolve, enabled ? 20 : 10)) mutateVerge({ ...verge, enable_system_proxy: enabled }, false)
await mutate('getSystemProxy') pendingRef.current = enabled
await mutate('getAutotemProxy')
} if (busyRef.current) return
busyRef.current = true
try { try {
if (!enabled && verge?.auto_close_connection) { while (pendingRef.current !== null) {
await closeAllConnections() const target = pendingRef.current
pendingRef.current = null
if (!target && verge?.auto_close_connection) {
await closeAllConnections().catch(() => {})
}
await patchVerge({ enable_system_proxy: target })
}
} finally {
busyRef.current = false
await Promise.all([mutate('getSystemProxy'), mutate('getAutotemProxy')])
} }
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
} }
})
return { return {
indicator, indicator,