feat: allow manually unfixing proxy nodes (#7756)

* feat(proxy): add ability to clear persisted node selection for groups

* feat(hooks): make proxy selection operations asynchronous

* chore: update tauri-plugin-mihomo deps

* refactor: rename clear_selected_node to forget_selected_node

* refactor(hooks): rename isFixed to isFixedProxy in useProxySelection

* docs: update changelog with manual node cancellation support

* chore(layout): remove dev-only mihomo websocket debugging interval
This commit is contained in:
oomeow
2026-08-18 12:16:42 +00:00
committed by GitHub
parent 9fa0e91e25
commit daadb126e0
12 changed files with 205 additions and 89 deletions
+54 -29
View File
@@ -1,11 +1,15 @@
import { useCallback, useMemo, useRef } from 'react'
import { useCallback, useRef } from 'react'
import {
closeConnection,
getConnections,
selectNodeForGroup,
unfixedProxy,
} from 'tauri-plugin-mihomo-api'
import { useRecordSelection } from '@/hooks/use-record-selection'
import {
useForgetSelection,
useRecordSelection,
} from '@/hooks/use-record-selection'
import { useVerge } from '@/hooks/use-verge'
import { syncTrayProxySelection } from '@/services/cmds'
import { debugLog } from '@/utils/debug'
@@ -37,25 +41,20 @@ interface ProxyChangeRequest {
groupName: string
proxyName: string
previousProxy?: string
fixed?: string
}
// 代理选择 Hook
export const useProxySelection = (options: ProxySelectionOptions = {}) => {
const recordSelection = useRecordSelection()
const forgetSelection = useForgetSelection()
const { verge } = useVerge()
const pendingRequestRef = useRef<ProxyChangeRequest | null>(null)
const isProcessingRef = useRef(false)
const { onSuccess, onError, enableConnectionCleanup = true } = options
// 缓存
const config = useMemo(
() => ({
autoCloseConnection: verge?.auto_close_connection ?? false,
enableConnectionCleanup,
}),
[verge?.auto_close_connection, enableConnectionCleanup],
)
const autoCloseConnection = verge?.auto_close_connection ?? false
// 切换节点
const syncTraySelection = useCallback(() => {
@@ -66,23 +65,33 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => {
const executeChange = useCallback(
async (request: ProxyChangeRequest) => {
const { groupName, proxyName, previousProxy } = request
debugLog(`[ProxySelection] 代理切换: ${groupName} -> ${proxyName}`)
const { groupName, proxyName, previousProxy, fixed } = request
const isFixedProxy = fixed === proxyName
if (isFixedProxy) {
debugLog(`[ProxySelection] 代理取消固定: ${groupName} -> ${proxyName}`)
} else {
debugLog(`[ProxySelection] 代理切换: ${groupName} -> ${proxyName}`)
}
try {
await selectNodeForGroup(groupName, proxyName)
if (isFixedProxy) {
await unfixedProxy(groupName)
} else {
await selectNodeForGroup(groupName, proxyName)
}
onSuccess?.()
syncTraySelection()
recordSelection(groupName, proxyName)
debugLog(
`[ProxySelection] 代理和状态同步完成: ${groupName} -> ${proxyName}`,
)
if (isFixedProxy) {
await forgetSelection(groupName)
debugLog(`[ProxySelection] 代理和状态同步完成: ${groupName}`)
} else {
await recordSelection(groupName, proxyName)
debugLog(
`[ProxySelection] 代理和状态同步完成: ${groupName} -> ${proxyName}`,
)
}
if (
config.enableConnectionCleanup &&
config.autoCloseConnection &&
previousProxy
) {
if (enableConnectionCleanup && autoCloseConnection && previousProxy) {
void cleanupConnections(previousProxy)
}
} catch (error) {
@@ -93,7 +102,15 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => {
onError?.(error)
}
},
[config, onError, onSuccess, recordSelection, syncTraySelection],
[
autoCloseConnection,
forgetSelection,
enableConnectionCleanup,
onError,
onSuccess,
recordSelection,
syncTraySelection,
],
)
const flushChangeQueue = useCallback(async () => {
@@ -115,11 +132,17 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => {
}, [executeChange])
const changeProxy = useCallback(
(groupName: string, proxyName: string, previousProxy?: string) => {
(
groupName: string,
proxyName: string,
previousProxy?: string,
fixed?: string,
) => {
pendingRequestRef.current = {
groupName,
proxyName,
previousProxy,
fixed,
}
void flushChangeQueue()
},
@@ -127,17 +150,19 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => {
)
const handleSelectChange = useCallback(
(groupName: string, previousProxy?: string) =>
(groupName: string, previousProxy?: string, fixed?: string) =>
(event: { target: { value: string } }) => {
const newProxy = event.target.value
changeProxy(groupName, newProxy, previousProxy)
changeProxy(groupName, event.target.value, previousProxy, fixed)
},
[changeProxy],
)
const handleProxyGroupChange = useCallback(
(group: { name: string; now?: string }, proxy: { name: string }) => {
changeProxy(group.name, proxy.name, group.now)
(
group: { name: string; now?: string; fixed?: string },
proxy: { name: string },
) => {
changeProxy(group.name, proxy.name, group.now, group.fixed)
},
[changeProxy],
)