mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-08 02:52:07 +08:00
perf(proxy): consolidate proxy data into a single IPC (#7542)
* feat(proxy): build deterministic proxy view * feat(proxy): expose ordered proxy view command * feat(proxy): add proxy view client contract * fix(lint): allow Node test files * feat(proxy): switch UI to ordered proxy view * fix(proxy): harden ordered proxy view consumers * fix(proxy): compare chain duplicates by current id * fix(proxy): release mihomo guard after fetch * fix(proxy): preserve semantic selections across refresh * fix(proxy): block global chain without GLOBAL * fix(proxy): handle proxy view edge states
This commit is contained in:
@@ -11,7 +11,6 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { delayGroup } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import {
|
||||
BaseEmpty,
|
||||
@@ -22,9 +21,13 @@ import {
|
||||
import { useProxySelection } from '@/hooks/use-proxy-selection'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useProxiesData } from '@/providers/app-data-context'
|
||||
import { calcuProxies } from '@/services/cmds'
|
||||
import delayManager from '@/services/delay'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import {
|
||||
isInteractableMember,
|
||||
resolveMember,
|
||||
type ProxyGroupView,
|
||||
type ResolvedProxyMember,
|
||||
} from '@/types/proxy-view'
|
||||
import { debugLog } from '@/utils/debug'
|
||||
|
||||
import {
|
||||
@@ -58,6 +61,7 @@ function useProxyRenderState(
|
||||
activeSelectedGroup: string | null,
|
||||
) {
|
||||
const { verge } = useVerge()
|
||||
const { proxyView } = useProxiesData()
|
||||
const { renderList, onProxies, onHeadState } = useRenderList(
|
||||
mode,
|
||||
isChainMode,
|
||||
@@ -88,28 +92,27 @@ function useProxyRenderState(
|
||||
useLockFn(async (groupName: string) => {
|
||||
debugLog(`[ProxyGroups] 开始测试所有延迟,组: ${groupName}`)
|
||||
|
||||
const proxies = renderList
|
||||
.filter(
|
||||
(e) => e.group?.name === groupName && (e.type === 2 || e.type === 4),
|
||||
)
|
||||
.flatMap((e) => e.proxyCol || e.proxy!)
|
||||
.filter(Boolean)
|
||||
const group =
|
||||
proxyView?.groups.find(({ name }) => name === groupName) ??
|
||||
(proxyView?.global?.name === groupName ? proxyView.global : undefined)
|
||||
const occurrences =
|
||||
proxyView && group
|
||||
? group.members.map((member, memberIndex) => ({
|
||||
memberIndex,
|
||||
member: resolveMember(proxyView, member),
|
||||
}))
|
||||
: []
|
||||
const interactable = occurrences
|
||||
.map(({ member }) => member)
|
||||
.filter(isInteractableMember)
|
||||
|
||||
debugLog(`[ProxyGroups] 找到代理数量: ${proxies.length}`)
|
||||
debugLog(`[ProxyGroups] 找到代理数量: ${interactable.length}`)
|
||||
|
||||
const url = delayManager.getUrl(groupName)
|
||||
debugLog(`[ProxyGroups] 测试URL: ${url}, 超时: ${timeout}ms`)
|
||||
|
||||
try {
|
||||
await Promise.race([
|
||||
delayManager.checkListDelay(proxies, groupName, timeout),
|
||||
delayGroup(groupName, url, timeout).then((result) => {
|
||||
debugLog(
|
||||
`[ProxyGroups] getGroupProxyDelays返回结果数量:`,
|
||||
Object.keys(result || {}).length,
|
||||
)
|
||||
}), // 查询group delays 将清除fixed(不关注调用结果)
|
||||
])
|
||||
await delayManager.checkListDelay(interactable, groupName, timeout)
|
||||
debugLog(`[ProxyGroups] 延迟测试完成,组: ${groupName}`)
|
||||
} catch (error) {
|
||||
console.error(`[ProxyGroups] 延迟测试出错,组: ${groupName}`, error)
|
||||
@@ -170,16 +173,16 @@ function ChainProxyGroups(props: {
|
||||
chainConfigData?: string | null
|
||||
}) {
|
||||
const { mode, chainConfigData } = props
|
||||
const { proxies: proxiesData } = useProxiesData()
|
||||
const { proxyView } = useProxiesData()
|
||||
const [selectedGroup, setSelectedGroup] = useState<string | null>(null)
|
||||
|
||||
const availableGroups = useMemo(() => {
|
||||
const groups = proxiesData?.groups
|
||||
const groups = proxyView?.groups
|
||||
if (!groups) return []
|
||||
return groups.filter(
|
||||
(group: any) => group.type === 'Selector' || group.type === 'URLTest',
|
||||
(group) => group.type === 'Selector' || group.type === 'URLTest',
|
||||
)
|
||||
}, [proxiesData?.groups])
|
||||
}, [proxyView?.groups])
|
||||
|
||||
const defaultRuleGroup = useMemo(() => {
|
||||
if (mode === 'rule' && availableGroups.length > 0) {
|
||||
@@ -297,16 +300,16 @@ function ChainProxyGroups(props: {
|
||||
scrollTopRef.current = 0
|
||||
}, [])
|
||||
|
||||
const handleLocation = useStableCallback((group: IProxyGroupItem) => {
|
||||
const handleLocation = useStableCallback((group: ProxyGroupView) => {
|
||||
if (!group) return
|
||||
const { name, now } = group
|
||||
|
||||
const index = renderList.findIndex(
|
||||
(item) =>
|
||||
item.group?.name === name &&
|
||||
((item.type === 2 && item.proxy?.name === now) ||
|
||||
((item.type === 2 && item.member?.member.ref.name === now) ||
|
||||
(item.type === 4 &&
|
||||
item.proxyCol?.some((proxy) => proxy.name === now))),
|
||||
item.memberCol?.some(({ member }) => member.ref.name === now))),
|
||||
)
|
||||
|
||||
if (index >= 0) {
|
||||
@@ -449,24 +452,26 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
})
|
||||
|
||||
const handleChangeProxy = useCallback(
|
||||
(group: IProxyGroupItem, proxy: IProxyItem) => {
|
||||
(group: ProxyGroupView, member: ResolvedProxyMember) => {
|
||||
if (!['Selector', 'URLTest', 'Fallback'].includes(group.type)) return
|
||||
if (!isInteractableMember(member)) return
|
||||
|
||||
handleProxyGroupChange(group, proxy)
|
||||
handleProxyGroupChange(group, { name: member.ref.name })
|
||||
},
|
||||
[handleProxyGroupChange],
|
||||
)
|
||||
|
||||
// 滚到对应的节点
|
||||
const handleLocation = useStableCallback((group: IProxyGroupItem) => {
|
||||
const handleLocation = useStableCallback((group: ProxyGroupView) => {
|
||||
if (!group) return
|
||||
const { name, now } = group
|
||||
|
||||
const index = renderList.findIndex(
|
||||
(e) =>
|
||||
e.group?.name === name &&
|
||||
((e.type === 2 && e.proxy?.name === now) ||
|
||||
(e.type === 4 && e.proxyCol?.some((p) => p.name === now))),
|
||||
((e.type === 2 && e.member?.member.ref.name === now) ||
|
||||
(e.type === 4 &&
|
||||
e.memberCol?.some(({ member }) => member.ref.name === now))),
|
||||
)
|
||||
|
||||
if (index >= 0) {
|
||||
@@ -503,7 +508,7 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
|
||||
// 点击代理组改变展开状态,先滚动到sticky的代理组位置,再收起展开状态
|
||||
const handleGroupToggle = useCallback(
|
||||
async (group: IProxyGroupItem) => {
|
||||
async (group: ProxyGroupView) => {
|
||||
const index = renderList.findIndex(
|
||||
(item) => item.type === 0 && item.group.name === group.name,
|
||||
)
|
||||
@@ -594,17 +599,6 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
export const ProxyGroups = (props: Props) => {
|
||||
const { mode, isChainMode = false, chainConfigData } = props
|
||||
|
||||
// Drive 3s polling on the shared TQ cache; data is read via granular context below
|
||||
useQuery({
|
||||
queryKey: ['getProxies'],
|
||||
queryFn: calcuProxies,
|
||||
refetchInterval: 3000,
|
||||
refetchIntervalInBackground: false,
|
||||
staleTime: 1500,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
})
|
||||
|
||||
if (mode === 'direct') {
|
||||
return <BaseEmpty textKey="proxies.page.messages.directMode" />
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user