@@ -3,17 +3,21 @@ import BasePage from '@renderer/components/base/base-page'
3
3
import { useAppConfig } from '@renderer/hooks/use-app-config'
4
4
import { mihomoChangeProxy , mihomoProxies , mihomoProxyDelay } from '@renderer/utils/ipc'
5
5
import { CgDetailsLess , CgDetailsMore } from 'react-icons/cg'
6
- import { useMemo , useState } from 'react'
6
+ import { FaBoltLightning } from 'react-icons/fa6'
7
+ import { TbCircleLetterD } from 'react-icons/tb'
8
+ import { FaLocationCrosshairs } from 'react-icons/fa6'
9
+ import { RxLetterCaseCapitalize } from 'react-icons/rx'
10
+ import { useMemo , useRef , useState } from 'react'
7
11
import useSWR from 'swr'
8
- import { GroupedVirtuoso } from 'react-virtuoso'
12
+ import { GroupedVirtuoso , GroupedVirtuosoHandle } from 'react-virtuoso'
9
13
import ProxyItem from '@renderer/components/proxies/proxy-item'
10
14
import { IoIosArrowBack } from 'react-icons/io'
11
15
import { MdOutlineSpeed } from 'react-icons/md'
12
16
13
17
const Proxies : React . FC = ( ) => {
14
18
const { data : proxies , mutate } = useSWR ( 'mihomoProxies' , mihomoProxies )
15
19
const { appConfig, patchAppConfig } = useAppConfig ( )
16
- const { proxyDisplayMode = 'simple' } = appConfig || { }
20
+ const { proxyDisplayMode = 'simple' , proxyDisplayOrder = 'default' } = appConfig || { }
17
21
18
22
const groups = useMemo ( ( ) => {
19
23
const groups : IMihomoGroup [ ] = [ ]
@@ -36,20 +40,31 @@ const Proxies: React.FC = () => {
36
40
} , [ proxies ] )
37
41
38
42
const [ isOpen , setIsOpen ] = useState ( Array ( groups . length ) . fill ( false ) )
39
-
43
+ const virtuosoRef = useRef < GroupedVirtuosoHandle > ( null )
40
44
const { groupCounts, allProxies } = useMemo ( ( ) => {
41
45
const groupCounts = groups . map ( ( group , index ) => {
42
46
return isOpen [ index ] ? group . all . length : 0
43
47
} )
44
48
const allProxies : ( IMihomoProxy | IMihomoGroup ) [ ] = [ ]
45
49
groups . forEach ( ( group , index ) => {
46
50
if ( isOpen [ index ] && proxies ) {
47
- allProxies . push ( ...group . all . map ( ( name ) => proxies . proxies [ name ] ) )
51
+ let groupProxies = group . all . map ( ( name ) => proxies . proxies [ name ] )
52
+ if ( proxyDisplayOrder === 'delay' ) {
53
+ groupProxies = groupProxies . sort ( ( a , b ) => {
54
+ if ( a . history . length === 0 ) return 1
55
+ if ( b . history . length === 0 ) return - 1
56
+ return a . history [ a . history . length - 1 ] . delay - b . history [ b . history . length - 1 ] . delay
57
+ } )
58
+ }
59
+ if ( proxyDisplayOrder === 'name' ) {
60
+ groupProxies = groupProxies . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
61
+ }
62
+ allProxies . push ( ...groupProxies )
48
63
}
49
64
} )
50
65
51
66
return { groupCounts, allProxies }
52
- } , [ groups , isOpen ] )
67
+ } , [ groups , isOpen , proxyDisplayOrder ] )
53
68
54
69
const onChangeProxy = ( group : string , proxy : string ) : void => {
55
70
mihomoChangeProxy ( group , proxy ) . then ( ( ) => {
@@ -69,22 +84,50 @@ const Proxies: React.FC = () => {
69
84
< BasePage
70
85
title = "代理组"
71
86
header = {
72
- < Button
73
- size = "sm"
74
- isIconOnly
75
- onPress = { ( ) => {
76
- patchAppConfig ( { proxyDisplayMode : proxyDisplayMode === 'simple' ? 'full' : 'simple' } )
77
- } }
78
- >
79
- { proxyDisplayMode === 'simple' ? (
80
- < CgDetailsMore size = { 20 } />
81
- ) : (
82
- < CgDetailsLess size = { 20 } />
83
- ) }
84
- </ Button >
87
+ < div >
88
+ < Button
89
+ size = "sm"
90
+ isIconOnly
91
+ onPress = { ( ) => {
92
+ patchAppConfig ( {
93
+ proxyDisplayOrder :
94
+ proxyDisplayOrder === 'default'
95
+ ? 'delay'
96
+ : proxyDisplayOrder === 'delay'
97
+ ? 'name'
98
+ : 'default'
99
+ } )
100
+ } }
101
+ >
102
+ { proxyDisplayOrder === 'default' ? (
103
+ < TbCircleLetterD size = { 20 } title = "默认" />
104
+ ) : proxyDisplayOrder === 'delay' ? (
105
+ < FaBoltLightning size = { 16 } title = "延迟" />
106
+ ) : (
107
+ < RxLetterCaseCapitalize size = { 22 } title = "名称" />
108
+ ) }
109
+ </ Button >
110
+ < Button
111
+ size = "sm"
112
+ isIconOnly
113
+ className = "ml-2"
114
+ onPress = { ( ) => {
115
+ patchAppConfig ( {
116
+ proxyDisplayMode : proxyDisplayMode === 'simple' ? 'full' : 'simple'
117
+ } )
118
+ } }
119
+ >
120
+ { proxyDisplayMode === 'simple' ? (
121
+ < CgDetailsMore size = { 20 } title = "详细信息" />
122
+ ) : (
123
+ < CgDetailsLess size = { 20 } title = "简洁信息" />
124
+ ) }
125
+ </ Button >
126
+ </ div >
85
127
}
86
128
>
87
129
< GroupedVirtuoso
130
+ ref = { virtuosoRef }
88
131
style = { { height : 'calc(100vh - 50px)' } }
89
132
groupCounts = { groupCounts }
90
133
groupContent = { ( index ) => {
@@ -112,7 +155,7 @@ const Proxies: React.FC = () => {
112
155
src = { groups [ index ] . icon }
113
156
/>
114
157
) : null }
115
- < div className = "h-[32px] text-md leading-[32px]" >
158
+ < div className = "h-[32px] text-ellipsis whitespace-nowrap overflow-hidden text- md leading-[32px]" >
116
159
{ groups [ index ] . name }
117
160
{ proxyDisplayMode === 'full' && (
118
161
< >
@@ -128,6 +171,29 @@ const Proxies: React.FC = () => {
128
171
</ div >
129
172
< div className = "flex" >
130
173
< Button
174
+ title = "定位到当前节点"
175
+ variant = "light"
176
+ size = "sm"
177
+ isIconOnly
178
+ onPress = { ( ) => {
179
+ if ( ! isOpen [ index ] ) return
180
+ let i = 0
181
+ for ( let j = 0 ; j < index ; j ++ ) {
182
+ i += groupCounts [ j ]
183
+ }
184
+ for ( let j = 0 ; j < groupCounts [ index ] ; j ++ ) {
185
+ if ( allProxies [ i + j ] . name === groups [ index ] . now ) {
186
+ i += j
187
+ break
188
+ }
189
+ }
190
+ virtuosoRef . current ?. scrollToIndex ( { index : i , align : 'start' } )
191
+ } }
192
+ >
193
+ < FaLocationCrosshairs className = "text-lg text-default-500" />
194
+ </ Button >
195
+ < Button
196
+ title = "延迟测试"
131
197
variant = "light"
132
198
size = "sm"
133
199
isIconOnly
0 commit comments