optimize proxy list performance

This commit is contained in:
pompurin404 2024-08-04 17:44:49 +08:00
parent 2eb3370df7
commit f9ba7996c5
No known key found for this signature in database
4 changed files with 128 additions and 127 deletions

View File

@ -1,3 +1,4 @@
import { Divider } from '@nextui-org/react'
import React from 'react' import React from 'react'
interface Props { interface Props {
title?: React.ReactNode title?: React.ReactNode
@ -14,6 +15,7 @@ const BasePage: React.FC<Props> = (props) => {
<div className="select-none title h-full text-lg leading-[32px]">{props.title}</div> <div className="select-none title h-full text-lg leading-[32px]">{props.title}</div>
<div className="header h-full">{props.header}</div> <div className="header h-full">{props.header}</div>
</div> </div>
<Divider />
</div> </div>
<div className="content">{props.children}</div> <div className="content">{props.children}</div>
</div> </div>

View File

@ -1,14 +1,14 @@
import { Button, Card, CardBody, Divider } from '@nextui-org/react' import { Button, Card, CardBody } from '@nextui-org/react'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import PubSub from 'pubsub-js' import PubSub from 'pubsub-js'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
interface Props { interface Props {
onProxyDelay: (proxy: string) => Promise<IMihomoDelay> onProxyDelay: (proxy: string, url?: string) => Promise<IMihomoDelay>
proxyDisplayMode: 'simple' | 'full' proxyDisplayMode: 'simple' | 'full'
proxy: IMihomoProxy | IMihomoGroup proxy: IMihomoProxy | IMihomoGroup
group: string group: string
onSelect: (proxy: string) => void onSelect: (group: string, proxy: string) => void
selected: boolean selected: boolean
} }
@ -61,13 +61,11 @@ const ProxyItem: React.FC<Props> = (props) => {
} }
}, []) }, [])
return ( return (
<>
<Divider />
<Card <Card
onPress={() => onSelect(proxy.name)} onPress={() => onSelect(group, proxy.name)}
isPressable isPressable
fullWidth fullWidth
className={`my-1 ${selected ? 'bg-primary/30' : ''}`} className={`${selected ? 'bg-primary/30' : ''}`}
radius="sm" radius="sm"
> >
<CardBody className="p-1"> <CardBody className="p-1">
@ -90,7 +88,6 @@ const ProxyItem: React.FC<Props> = (props) => {
</div> </div>
</CardBody> </CardBody>
</Card> </Card>
</>
) )
} }

View File

@ -1,36 +0,0 @@
import React from 'react'
import { Virtuoso } from 'react-virtuoso'
import ProxyItem from './proxy-item'
interface Props {
onProxyDelay: (proxy: string) => Promise<IMihomoDelay>
onChangeProxy: (proxy: string) => void
proxyDisplayMode: 'simple' | 'full'
proxies: (IMihomoProxy | IMihomoGroup)[]
group: string
now: string
}
const ProxyList: React.FC<Props> = (props) => {
const { proxyDisplayMode, onProxyDelay, onChangeProxy, proxies, group, now } = props
return (
<Virtuoso
style={{ height: `min(calc(100vh - 200px), ${proxies.length * 44}px)` }}
totalCount={proxies.length}
increaseViewportBy={100}
itemContent={(index) => (
<ProxyItem
onProxyDelay={onProxyDelay}
onSelect={onChangeProxy}
proxy={proxies[index]}
group={group}
proxyDisplayMode={proxyDisplayMode}
selected={proxies[index].name === now}
/>
)}
/>
)
}
export default ProxyList

View File

@ -1,13 +1,14 @@
import { Accordion, AccordionItem, Avatar, Button } from '@nextui-org/react' import { Avatar, Button, Card, CardBody } from '@nextui-org/react'
import BasePage from '@renderer/components/base/base-page' import BasePage from '@renderer/components/base/base-page'
import ProxyList from '@renderer/components/proxies/proxy-list'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import { MdOutlineSpeed } from 'react-icons/md'
import { mihomoChangeProxy, mihomoProxies, mihomoProxyDelay } from '@renderer/utils/ipc' import { mihomoChangeProxy, mihomoProxies, mihomoProxyDelay } from '@renderer/utils/ipc'
import { CgDetailsLess, CgDetailsMore } from 'react-icons/cg' import { CgDetailsLess, CgDetailsMore } from 'react-icons/cg'
import { useEffect, useMemo } from 'react' import { useMemo, useState } from 'react'
import PubSub from 'pubsub-js'
import useSWR from 'swr' import useSWR from 'swr'
import { GroupedVirtuoso } from 'react-virtuoso'
import ProxyItem from '@renderer/components/proxies/proxy-item'
import { IoIosArrowBack } from 'react-icons/io'
import { MdOutlineSpeed } from 'react-icons/md'
const Proxies: React.FC = () => { const Proxies: React.FC = () => {
const { data: proxies, mutate } = useSWR('mihomoProxies', mihomoProxies) const { data: proxies, mutate } = useSWR('mihomoProxies', mihomoProxies)
@ -34,15 +35,21 @@ const Proxies: React.FC = () => {
return groups return groups
}, [proxies]) }, [proxies])
const groupProxies = useMemo(() => { const [isOpen, setIsOpen] = useState(Array(groups.length).fill(false))
const groupProxies: Record<string, (IMihomoProxy | IMihomoGroup)[]> = {}
if (proxies) { const { groupCounts, allProxies } = useMemo(() => {
for (const group of groups) { const groupCounts = groups.map((group, index) => {
groupProxies[group.name] = group.all.map((name) => proxies.proxies[name]) return isOpen[index] ? group.all.length : 0
})
const allProxies: (IMihomoProxy | IMihomoGroup)[] = []
groups.forEach((group, index) => {
if (isOpen[index] && proxies) {
allProxies.push(...group.all.map((name) => proxies.proxies[name]))
} }
} })
return groupProxies
}, [proxies]) return { groupCounts, allProxies }
}, [groups, isOpen])
const onChangeProxy = (group: string, proxy: string): void => { const onChangeProxy = (group: string, proxy: string): void => {
mihomoChangeProxy(group, proxy).then(() => { mihomoChangeProxy(group, proxy).then(() => {
@ -54,7 +61,6 @@ const Proxies: React.FC = () => {
return await mihomoProxyDelay(proxy, url) return await mihomoProxyDelay(proxy, url)
} }
useEffect(() => {}, [])
return ( return (
<BasePage <BasePage
title="代理组" title="代理组"
@ -74,54 +80,86 @@ const Proxies: React.FC = () => {
</Button> </Button>
} }
> >
<Accordion variant="splitted" className="p-2"> <GroupedVirtuoso
{groups.map((group) => { style={{ height: 'calc(100vh - 50px)' }}
groupCounts={groupCounts}
groupContent={(index) => {
return ( return (
<AccordionItem <div className={`w-full pt-2 ${index === groupCounts.length - 1 ? 'pb-2' : ''} px-2`}>
textValue={group.name} <Card
key={group.name} isPressable
title={ fullWidth
onPress={() => {
setIsOpen((prev) => {
const newOpen = [...prev]
newOpen[index] = !prev[index]
return newOpen
})
}}
>
<CardBody>
<div className="flex justify-between"> <div className="flex justify-between">
<div className=""> <div className="flex">
<div className="inline">{group.name}</div> {groups[index].icon.length > 0 ? (
<Avatar
className="bg-transparent mr-2"
size="sm"
radius="sm"
src={groups[index].icon}
/>
) : null}
<div className="h-[32px] text-md leading-[32px]">
{groups[index].name}
{proxyDisplayMode === 'full' && ( {proxyDisplayMode === 'full' && (
<> <>
<div className="inline ml-2 text-sm text-default-500">{group.type}</div> <div className="inline ml-2 text-sm text-default-500">
<div className="inline ml-2 text-sm text-default-500">{group.now}</div> {groups[index].type}
</div>
<div className="inline ml-2 text-sm text-default-500">
{groups[index].now}
</div>
</> </>
)} )}
</div> </div>
</div>
<div className="flex">
<Button <Button
variant="light" variant="light"
size="sm" size="sm"
isIconOnly isIconOnly
onPress={() => { onPress={() => {
PubSub.publish(`${group.name}-delay`) PubSub.publish(`${groups[index].name}-delay`)
}} }}
> >
<MdOutlineSpeed className="text-lg text-default-500" /> <MdOutlineSpeed className="text-lg text-default-500" />
</Button> </Button>
</div> <IoIosArrowBack
} className={`transition duration-200 ml-2 h-[32px] text-lg text-default-500 ${isOpen[index] ? '-rotate-90' : ''}`}
classNames={{ title: 'select-none', base: 'px-2', content: 'pt-2', trigger: 'py-2' }}
startContent={
group.icon.length > 0 ? (
<Avatar className="bg-transparent" size="sm" radius="sm" src={group.icon} />
) : null
}
>
<ProxyList
onProxyDelay={(proxy) => onProxyDelay(proxy, group.testUrl)}
onChangeProxy={(proxy) => onChangeProxy(group.name, proxy)}
proxyDisplayMode={proxyDisplayMode}
proxies={groupProxies[group.name]}
group={group.name}
now={group.now}
/> />
</AccordionItem> </div>
</div>
</CardBody>
</Card>
</div>
) )
})} }}
</Accordion> itemContent={(index, groupIndex) => {
return allProxies[index] ? (
<div className="pt-2 mx-2">
<ProxyItem
onProxyDelay={onProxyDelay}
onSelect={onChangeProxy}
proxy={allProxies[index]}
group={groups[groupIndex].name}
proxyDisplayMode={proxyDisplayMode}
selected={allProxies[index]?.name === groups[groupIndex].now}
/>
</div>
) : (
<div>Never See This</div>
)
}}
/>
</BasePage> </BasePage>
) )
} }