mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
optimize proxy list performance
This commit is contained in:
parent
2eb3370df7
commit
f9ba7996c5
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user