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,36 +61,33 @@ const ProxyItem: React.FC<Props> = (props) => {
} }
}, []) }, [])
return ( return (
<> <Card
<Divider /> onPress={() => onSelect(group, proxy.name)}
<Card isPressable
onPress={() => onSelect(proxy.name)} fullWidth
isPressable className={`${selected ? 'bg-primary/30' : ''}`}
fullWidth radius="sm"
className={`my-1 ${selected ? 'bg-primary/30' : ''}`} >
radius="sm" <CardBody className="p-1">
> <div className="flex justify-between items-center">
<CardBody className="p-1"> <div>
<div className="flex justify-between items-center"> <div className="inline">{proxy.name}</div>
<div> {proxyDisplayMode === 'full' && (
<div className="inline">{proxy.name}</div> <div className="inline ml-2 text-default-500">{proxy.type}</div>
{proxyDisplayMode === 'full' && ( )}
<div className="inline ml-2 text-default-500">{proxy.type}</div>
)}
</div>
<Button
isLoading={loading}
color={delayColor(delay)}
onPress={onDelay}
variant="light"
className="h-full min-w-[50px] p-0 mx-2 text-sm hover:bg-content"
>
{delayText(delay)}
</Button>
</div> </div>
</CardBody> <Button
</Card> isLoading={loading}
</> color={delayColor(delay)}
onPress={onDelay}
variant="light"
className="h-full min-w-[50px] p-0 mx-2 text-sm hover:bg-content"
>
{delayText(delay)}
</Button>
</div>
</CardBody>
</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
<div className="flex justify-between"> onPress={() => {
<div className=""> setIsOpen((prev) => {
<div className="inline">{group.name}</div> const newOpen = [...prev]
{proxyDisplayMode === 'full' && ( newOpen[index] = !prev[index]
<> return newOpen
<div className="inline ml-2 text-sm text-default-500">{group.type}</div> })
<div className="inline ml-2 text-sm text-default-500">{group.now}</div> }}
</> >
)} <CardBody>
<div className="flex justify-between">
<div className="flex">
{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' && (
<>
<div className="inline ml-2 text-sm text-default-500">
{groups[index].type}
</div>
<div className="inline ml-2 text-sm text-default-500">
{groups[index].now}
</div>
</>
)}
</div>
</div>
<div className="flex">
<Button
variant="light"
size="sm"
isIconOnly
onPress={() => {
PubSub.publish(`${groups[index].name}-delay`)
}}
>
<MdOutlineSpeed className="text-lg text-default-500" />
</Button>
<IoIosArrowBack
className={`transition duration-200 ml-2 h-[32px] text-lg text-default-500 ${isOpen[index] ? '-rotate-90' : ''}`}
/>
</div>
</div> </div>
<Button </CardBody>
variant="light" </Card>
size="sm" </div>
isIconOnly
onPress={() => {
PubSub.publish(`${group.name}-delay`)
}}
>
<MdOutlineSpeed className="text-lg text-default-500" />
</Button>
</div>
}
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>
) )
})} }}
</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>
) )
} }