mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
fix: optimize connections page performance and state management
This commit is contained in:
parent
7619b4d3e5
commit
51720296cc
@ -9,6 +9,7 @@ export const defaultConfig: IAppConfig = {
|
|||||||
appTheme: 'system',
|
appTheme: 'system',
|
||||||
useWindowFrame: false,
|
useWindowFrame: false,
|
||||||
proxyInTray: true,
|
proxyInTray: true,
|
||||||
|
showCurrentProxyInTray: false,
|
||||||
disableTrayIconColor: false,
|
disableTrayIconColor: false,
|
||||||
maxLogDays: 7,
|
maxLogDays: 7,
|
||||||
proxyCols: 'auto',
|
proxyCols: 'auto',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
import { mihomoCloseAllConnections, mihomoCloseConnection } from '@renderer/utils/ipc'
|
import { mihomoCloseAllConnections, mihomoCloseConnection } from '@renderer/utils/ipc'
|
||||||
import { Key, useCallback, useEffect, useMemo, useState } from 'react'
|
import { Key, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Badge, Button, Divider, Input, Select, SelectItem, Tab, Tabs } from '@heroui/react'
|
import { Badge, Button, Divider, Input, Select, SelectItem, Tab, Tabs } from '@heroui/react'
|
||||||
import { calcTraffic } from '@renderer/utils/calc'
|
import { calcTraffic } from '@renderer/utils/calc'
|
||||||
import ConnectionItem from '@renderer/components/connections/connection-item'
|
import ConnectionItem from '@renderer/components/connections/connection-item'
|
||||||
@ -48,6 +48,13 @@ const Connections: React.FC = () => {
|
|||||||
const [viewMode, setViewMode] = useState<'list' | 'table'>(connectionViewMode)
|
const [viewMode, setViewMode] = useState<'list' | 'table'>(connectionViewMode)
|
||||||
const [visibleColumns, setVisibleColumns] = useState<Set<string>>(new Set(connectionTableColumns))
|
const [visibleColumns, setVisibleColumns] = useState<Set<string>>(new Set(connectionTableColumns))
|
||||||
|
|
||||||
|
const activeConnectionsRef = useRef(activeConnections)
|
||||||
|
const allConnectionsRef = useRef(allConnections)
|
||||||
|
useEffect(() => {
|
||||||
|
activeConnectionsRef.current = activeConnections
|
||||||
|
allConnectionsRef.current = allConnections
|
||||||
|
}, [activeConnections, allConnections])
|
||||||
|
|
||||||
const handleColumnWidthChange = useCallback(async (widths: Record<string, number>) => {
|
const handleColumnWidthChange = useCallback(async (widths: Record<string, number>) => {
|
||||||
await patchAppConfig({ connectionTableColumnWidths: widths })
|
await patchAppConfig({ connectionTableColumnWidths: widths })
|
||||||
}, [patchAppConfig])
|
}, [patchAppConfig])
|
||||||
@ -98,71 +105,80 @@ const Connections: React.FC = () => {
|
|||||||
|
|
||||||
const closeAllConnections = useCallback((): void => {
|
const closeAllConnections = useCallback((): void => {
|
||||||
tab === 'active' ? mihomoCloseAllConnections() : trashAllClosedConnection()
|
tab === 'active' ? mihomoCloseAllConnections() : trashAllClosedConnection()
|
||||||
}, [tab, closedConnections])
|
}, [tab])
|
||||||
|
|
||||||
const closeConnection = useCallback((id: string): void => {
|
const closeConnection = useCallback((id: string): void => {
|
||||||
tab === 'active' ? mihomoCloseConnection(id) : trashClosedConnection(id)
|
tab === 'active' ? mihomoCloseConnection(id) : trashClosedConnection(id)
|
||||||
}, [tab])
|
}, [tab])
|
||||||
|
|
||||||
const trashAllClosedConnection = (): void => {
|
const trashAllClosedConnection = (): void => {
|
||||||
const trashIds = closedConnections.map((conn) => conn.id)
|
setClosedConnections((closedConns) => {
|
||||||
setAllConnections((allConns) => allConns.filter((conn) => !trashIds.includes(conn.id)))
|
const trashIds = new Set(closedConns.map((conn) => conn.id))
|
||||||
setClosedConnections([])
|
setAllConnections((allConns) => {
|
||||||
|
const filtered = allConns.filter((conn) => !trashIds.has(conn.id))
|
||||||
cachedConnections = allConnections
|
cachedConnections = filtered
|
||||||
|
return filtered
|
||||||
|
})
|
||||||
|
return []
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const trashClosedConnection = (id: string): void => {
|
const trashClosedConnection = (id: string): void => {
|
||||||
setAllConnections((allConns) => allConns.filter((conn) => conn.id != id))
|
setAllConnections((allConns) => {
|
||||||
setClosedConnections((closedConns) => closedConns.filter((conn) => conn.id != id))
|
const filtered = allConns.filter((conn) => conn.id !== id)
|
||||||
|
cachedConnections = filtered
|
||||||
cachedConnections = allConnections
|
return filtered
|
||||||
|
})
|
||||||
|
setClosedConnections((closedConns) => closedConns.filter((conn) => conn.id !== id))
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isPaused) return
|
const handler = (_e: unknown, info: IMihomoConnectionsInfo): void => {
|
||||||
window.electron.ipcRenderer.on('mihomoConnections', (_e, info: IMihomoConnectionsInfo) => {
|
|
||||||
setConnectionsInfo(info)
|
setConnectionsInfo(info)
|
||||||
|
|
||||||
if (!info.connections) return
|
if (!info.connections) return
|
||||||
const allConns = unionWith(activeConnections, allConnections, (a, b) => a.id === b.id)
|
const allConns = unionWith(
|
||||||
|
activeConnectionsRef.current,
|
||||||
|
allConnectionsRef.current,
|
||||||
|
(a, b) => a.id === b.id
|
||||||
|
)
|
||||||
|
|
||||||
|
const prevConnMap = new Map(activeConnectionsRef.current.map((c) => [c.id, c]))
|
||||||
const activeConns = info.connections.map((conn) => {
|
const activeConns = info.connections.map((conn) => {
|
||||||
const preConn = activeConnections.find((c) => c.id === conn.id)
|
const preConn = prevConnMap.get(conn.id)
|
||||||
const downloadSpeed = preConn ? conn.download - preConn.download : 0
|
|
||||||
const uploadSpeed = preConn ? conn.upload - preConn.upload : 0
|
|
||||||
return {
|
return {
|
||||||
...conn,
|
...conn,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
downloadSpeed: downloadSpeed,
|
downloadSpeed: preConn ? conn.download - preConn.download : 0,
|
||||||
uploadSpeed: uploadSpeed
|
uploadSpeed: preConn ? conn.upload - preConn.upload : 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const closedConns = differenceWith(allConns, activeConns, (a, b) => a.id === b.id).map(
|
const closedConns = differenceWith(allConns, activeConns, (a, b) => a.id === b.id).map(
|
||||||
(conn) => {
|
(conn) => ({
|
||||||
return {
|
...conn,
|
||||||
...conn,
|
isActive: false,
|
||||||
isActive: false,
|
downloadSpeed: 0,
|
||||||
downloadSpeed: 0,
|
uploadSpeed: 0
|
||||||
uploadSpeed: 0
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
setActiveConnections(activeConns)
|
setActiveConnections(activeConns)
|
||||||
setClosedConnections(closedConns)
|
setClosedConnections(closedConns)
|
||||||
setAllConnections(allConns.slice(-(activeConns.length + 200)))
|
setAllConnections(allConns.slice(-(activeConns.length + 200)))
|
||||||
|
cachedConnections = allConns
|
||||||
|
}
|
||||||
|
|
||||||
cachedConnections = allConnections
|
if (!isPaused) {
|
||||||
})
|
window.electron.ipcRenderer.on('mihomoConnections', handler)
|
||||||
|
}
|
||||||
|
|
||||||
return (): void => {
|
return (): void => {
|
||||||
window.electron.ipcRenderer.removeAllListeners('mihomoConnections')
|
window.electron.ipcRenderer.removeAllListeners('mihomoConnections')
|
||||||
}
|
}
|
||||||
}, [allConnections, activeConnections, closedConnections, isPaused])
|
}, [isPaused])
|
||||||
const togglePause = () => {
|
const togglePause = useCallback(() => {
|
||||||
setIsPaused(!isPaused)
|
setIsPaused((prev) => !prev)
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage
|
<BasePage
|
||||||
@ -182,7 +198,7 @@ const Connections: React.FC = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
showOutline={false}
|
showOutline={false}
|
||||||
content={`${filteredConnections.length}`}
|
content={filteredConnections.length}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="app-nodrag ml-1"
|
className="app-nodrag ml-1"
|
||||||
@ -237,7 +253,7 @@ const Connections: React.FC = () => {
|
|||||||
<div className="flex p-2 gap-2">
|
<div className="flex p-2 gap-2">
|
||||||
<Tabs
|
<Tabs
|
||||||
size="sm"
|
size="sm"
|
||||||
color={`${tab === 'active' ? 'primary' : 'danger'}`}
|
color={tab === 'active' ? 'primary' : 'danger'}
|
||||||
selectedKey={tab}
|
selectedKey={tab}
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
className="w-fit h-[32px]"
|
className="w-fit h-[32px]"
|
||||||
@ -249,7 +265,7 @@ const Connections: React.FC = () => {
|
|||||||
key="active"
|
key="active"
|
||||||
title={
|
title={
|
||||||
<Badge
|
<Badge
|
||||||
color={`${tab === 'active' ? 'primary' : 'default'}`}
|
color={tab === 'active' ? 'primary' : 'default'}
|
||||||
size="sm"
|
size="sm"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@ -264,7 +280,7 @@ const Connections: React.FC = () => {
|
|||||||
key="closed"
|
key="closed"
|
||||||
title={
|
title={
|
||||||
<Badge
|
<Badge
|
||||||
color={`${tab === 'closed' ? 'danger' : 'default'}`}
|
color={tab === 'closed' ? 'danger' : 'default'}
|
||||||
size="sm"
|
size="sm"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@ -339,7 +355,7 @@ const Connections: React.FC = () => {
|
|||||||
size="sm"
|
size="sm"
|
||||||
className="w-[180px] min-w-[131px]"
|
className="w-[180px] min-w-[131px]"
|
||||||
aria-label={t('connections.orderBy')}
|
aria-label={t('connections.orderBy')}
|
||||||
selectedKeys={new Set([connectionOrderBy])}
|
selectedKeys={[connectionOrderBy]}
|
||||||
disallowEmptySelection={true}
|
disallowEmptySelection={true}
|
||||||
onSelectionChange={async (v) => {
|
onSelectionChange={async (v) => {
|
||||||
await patchAppConfig({
|
await patchAppConfig({
|
||||||
@ -362,7 +378,7 @@ const Connections: React.FC = () => {
|
|||||||
size="sm"
|
size="sm"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
className="bg-content2"
|
className="bg-content2"
|
||||||
onPress={async () => {
|
onPress={() => {
|
||||||
patchAppConfig({
|
patchAppConfig({
|
||||||
connectionDirection: connectionDirection === 'asc' ? 'desc' : 'asc'
|
connectionDirection: connectionDirection === 'asc' ? 'desc' : 'asc'
|
||||||
})
|
})
|
||||||
|
|||||||
@ -44,10 +44,6 @@ const Mihomo: React.FC = () => {
|
|||||||
smartCollectorSize = 100,
|
smartCollectorSize = 100,
|
||||||
maxLogDays = 7,
|
maxLogDays = 7,
|
||||||
sysProxy,
|
sysProxy,
|
||||||
disableLoopbackDetector,
|
|
||||||
disableEmbedCA,
|
|
||||||
disableSystemCA,
|
|
||||||
skipSafePathCheck,
|
|
||||||
showMixedPort,
|
showMixedPort,
|
||||||
enableMixedPort = true,
|
enableMixedPort = true,
|
||||||
showSocksPort,
|
showSocksPort,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user