mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 13:10:30 +08:00
feat: add animation during latency testing for better user feedback
This commit is contained in:
parent
d6e456302e
commit
0b8c77d200
@ -1,6 +1,7 @@
|
|||||||
import { Button, Card, CardBody } from '@heroui/react'
|
import { Button, Card, CardBody } from '@heroui/react'
|
||||||
import { mihomoUnfixedProxy } from '@renderer/utils/ipc'
|
import { mihomoUnfixedProxy } from '@renderer/utils/ipc'
|
||||||
import React, { useMemo, useState } from 'react'
|
import React from 'react'
|
||||||
|
import { useMemo, useState } from 'react'
|
||||||
import { FaMapPin } from 'react-icons/fa6'
|
import { FaMapPin } from 'react-icons/fa6'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
@ -12,11 +13,12 @@ interface Props {
|
|||||||
group: IMihomoMixedGroup
|
group: IMihomoMixedGroup
|
||||||
onSelect: (group: string, proxy: string) => void
|
onSelect: (group: string, proxy: string) => void
|
||||||
selected: boolean
|
selected: boolean
|
||||||
|
isGroupTesting?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProxyItem: React.FC<Props> = (props) => {
|
const ProxyItem: React.FC<Props> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { mutateProxies, proxyDisplayMode, group, proxy, selected, onSelect, onProxyDelay } = props
|
const { mutateProxies, proxyDisplayMode, group, proxy, selected, onSelect, onProxyDelay, isGroupTesting = false } = props
|
||||||
|
|
||||||
const delay = useMemo(() => {
|
const delay = useMemo(() => {
|
||||||
if (proxy.history.length > 0) {
|
if (proxy.history.length > 0) {
|
||||||
@ -26,6 +28,9 @@ const ProxyItem: React.FC<Props> = (props) => {
|
|||||||
}, [proxy])
|
}, [proxy])
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
const isLoading = loading || isGroupTesting
|
||||||
|
|
||||||
function delayColor(delay: number): 'primary' | 'success' | 'warning' | 'danger' {
|
function delayColor(delay: number): 'primary' | 'success' | 'warning' | 'danger' {
|
||||||
if (delay === -1) return 'primary'
|
if (delay === -1) return 'primary'
|
||||||
if (delay === 0) return 'danger'
|
if (delay === 0) return 'danger'
|
||||||
@ -106,7 +111,7 @@ const ProxyItem: React.FC<Props> = (props) => {
|
|||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
title={proxy.type}
|
title={proxy.type}
|
||||||
isLoading={loading}
|
isLoading={isLoading}
|
||||||
color={delayColor(delay)}
|
color={delayColor(delay)}
|
||||||
onPress={onDelay}
|
onPress={onDelay}
|
||||||
variant="light"
|
variant="light"
|
||||||
@ -144,11 +149,11 @@ const ProxyItem: React.FC<Props> = (props) => {
|
|||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
title={proxy.type}
|
title={proxy.type}
|
||||||
isLoading={loading}
|
isLoading={isLoading}
|
||||||
color={delayColor(delay)}
|
color={delayColor(delay)}
|
||||||
onPress={onDelay}
|
onPress={onDelay}
|
||||||
variant="light"
|
variant="light"
|
||||||
className="h-[24px] text-sm px-2 relative w-min whitespace-nowrap"
|
className="h-full text-sm px-2 relative w-min whitespace-nowrap"
|
||||||
>
|
>
|
||||||
<div className="w-full h-full flex items-center justify-end">
|
<div className="w-full h-full flex items-center justify-end">
|
||||||
{delayText(delay)}
|
{delayText(delay)}
|
||||||
|
|||||||
@ -29,7 +29,6 @@ const useProxyState = (groups: IMihomoMixedGroup[]): {
|
|||||||
virtuosoRef: React.RefObject<GroupedVirtuosoHandle>;
|
virtuosoRef: React.RefObject<GroupedVirtuosoHandle>;
|
||||||
isOpen: boolean[];
|
isOpen: boolean[];
|
||||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean[]>>;
|
setIsOpen: React.Dispatch<React.SetStateAction<boolean[]>>;
|
||||||
onScroll: (e: React.UIEvent<HTMLElement>) => void;
|
|
||||||
} => {
|
} => {
|
||||||
const virtuosoRef = useRef<GroupedVirtuosoHandle>(null)
|
const virtuosoRef = useRef<GroupedVirtuosoHandle>(null)
|
||||||
|
|
||||||
@ -56,10 +55,7 @@ const useProxyState = (groups: IMihomoMixedGroup[]): {
|
|||||||
return {
|
return {
|
||||||
virtuosoRef,
|
virtuosoRef,
|
||||||
isOpen,
|
isOpen,
|
||||||
setIsOpen,
|
setIsOpen
|
||||||
onScroll: useCallback((_e: React.UIEvent<HTMLElement>) => {
|
|
||||||
// 空实现,不再保存滚动位置
|
|
||||||
}, [])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,8 +74,9 @@ const Proxies: React.FC = () => {
|
|||||||
} = appConfig || {}
|
} = appConfig || {}
|
||||||
|
|
||||||
const [cols, setCols] = useState(1)
|
const [cols, setCols] = useState(1)
|
||||||
const { virtuosoRef, isOpen, setIsOpen, onScroll } = useProxyState(groups)
|
const { virtuosoRef, isOpen, setIsOpen } = useProxyState(groups)
|
||||||
const [delaying, setDelaying] = useState(Array(groups.length).fill(false))
|
const [delaying, setDelaying] = useState(Array(groups.length).fill(false))
|
||||||
|
const [proxyDelaying, setProxyDelaying] = useState<Record<string, boolean>>({})
|
||||||
const [searchValue, setSearchValue] = useState(Array(groups.length).fill(''))
|
const [searchValue, setSearchValue] = useState(Array(groups.length).fill(''))
|
||||||
const { groupCounts, allProxies } = useMemo(() => {
|
const { groupCounts, allProxies } = useMemo(() => {
|
||||||
const groupCounts: number[] = []
|
const groupCounts: number[] = []
|
||||||
@ -139,6 +136,16 @@ const Proxies: React.FC = () => {
|
|||||||
return newDelaying
|
return newDelaying
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 本组测试状态
|
||||||
|
const groupProxies = allProxies[index]
|
||||||
|
setProxyDelaying((prev) => {
|
||||||
|
const newProxyDelaying = { ...prev }
|
||||||
|
groupProxies.forEach(proxy => {
|
||||||
|
newProxyDelaying[proxy.name] = true
|
||||||
|
})
|
||||||
|
return newProxyDelaying
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 限制并发数量
|
// 限制并发数量
|
||||||
const result: Promise<void>[] = []
|
const result: Promise<void>[] = []
|
||||||
@ -150,6 +157,12 @@ const Proxies: React.FC = () => {
|
|||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
} finally {
|
} finally {
|
||||||
|
// 立即更新状态
|
||||||
|
setProxyDelaying((prev) => {
|
||||||
|
const newProxyDelaying = { ...prev }
|
||||||
|
delete newProxyDelaying[proxy.name]
|
||||||
|
return newProxyDelaying
|
||||||
|
})
|
||||||
mutate()
|
mutate()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -169,6 +182,14 @@ const Proxies: React.FC = () => {
|
|||||||
newDelaying[index] = false
|
newDelaying[index] = false
|
||||||
return newDelaying
|
return newDelaying
|
||||||
})
|
})
|
||||||
|
// 状态清理
|
||||||
|
setProxyDelaying((prev) => {
|
||||||
|
const newProxyDelaying = { ...prev }
|
||||||
|
groupProxies.forEach(proxy => {
|
||||||
|
delete newProxyDelaying[proxy.name]
|
||||||
|
})
|
||||||
|
return newProxyDelaying
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [allProxies, groups, delayTestConcurrency, mutate])
|
}, [allProxies, groups, delayTestConcurrency, mutate])
|
||||||
|
|
||||||
@ -256,7 +277,6 @@ const Proxies: React.FC = () => {
|
|||||||
<GroupedVirtuoso
|
<GroupedVirtuoso
|
||||||
ref={virtuosoRef}
|
ref={virtuosoRef}
|
||||||
groupCounts={groupCounts}
|
groupCounts={groupCounts}
|
||||||
onScroll={onScroll}
|
|
||||||
defaultItemHeight={80}
|
defaultItemHeight={80}
|
||||||
increaseViewportBy={{ top: 300, bottom: 300 }}
|
increaseViewportBy={{ top: 300, bottom: 300 }}
|
||||||
overscan={500}
|
overscan={500}
|
||||||
@ -434,6 +454,7 @@ const Proxies: React.FC = () => {
|
|||||||
allProxies[groupIndex][innerIndex * cols + i]?.name ===
|
allProxies[groupIndex][innerIndex * cols + i]?.name ===
|
||||||
groups[groupIndex].now
|
groups[groupIndex].now
|
||||||
}
|
}
|
||||||
|
isGroupTesting={!!proxyDelaying[allProxies[groupIndex][innerIndex * cols + i].name]}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user