@@ -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,33 @@ 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
+ if ( a . history [ a . history . length - 1 ] . delay === 0 ) return 1
57
+ if ( b . history [ b . history . length - 1 ] . delay === 0 ) return - 1
58
+ return a . history [ a . history . length - 1 ] . delay - b . history [ b . history . length - 1 ] . delay
59
+ } )
60
+ }
61
+ if ( proxyDisplayOrder === 'name' ) {
62
+ groupProxies = groupProxies . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
63
+ }
64
+ allProxies . push ( ...groupProxies )
48
65
}
49
66
} )
50
67
51
68
return { groupCounts, allProxies }
52
- } , [ groups , isOpen ] )
69
+ } , [ groups , isOpen , proxyDisplayOrder ] )
53
70
54
71
const onChangeProxy = ( group : string , proxy : string ) : void => {
55
72
mihomoChangeProxy ( group , proxy ) . then ( ( ) => {
@@ -69,22 +86,50 @@ const Proxies: React.FC = () => {
69
86
< BasePage
70
87
title = "代理组"
71
88
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 >
89
+ < div >
90
+ < Button
91
+ size = "sm"
92
+ isIconOnly
93
+ onPress = { ( ) => {
94
+ patchAppConfig ( {
95
+ proxyDisplayOrder :
96
+ proxyDisplayOrder === 'default'
97
+ ? 'delay'
98
+ : proxyDisplayOrder === 'delay'
99
+ ? 'name'
100
+ : 'default'
101
+ } )
102
+ } }
103
+ >
104
+ { proxyDisplayOrder === 'default' ? (
105
+ < TbCircleLetterD size = { 20 } title = "默认" />
106
+ ) : proxyDisplayOrder === 'delay' ? (
107
+ < FaBoltLightning size = { 20 } title = "延迟" />
108
+ ) : (
109
+ < RxLetterCaseCapitalize size = { 20 } title = "名称" />
110
+ ) }
111
+ </ Button >
112
+ < Button
113
+ size = "sm"
114
+ isIconOnly
115
+ className = "ml-2"
116
+ onPress = { ( ) => {
117
+ patchAppConfig ( {
118
+ proxyDisplayMode : proxyDisplayMode === 'simple' ? 'full' : 'simple'
119
+ } )
120
+ } }
121
+ >
122
+ { proxyDisplayMode === 'simple' ? (
123
+ < CgDetailsMore size = { 20 } title = "详细信息" />
124
+ ) : (
125
+ < CgDetailsLess size = { 20 } title = "简洁信息" />
126
+ ) }
127
+ </ Button >
128
+ </ div >
85
129
}
86
130
>
87
131
< GroupedVirtuoso
132
+ ref = { virtuosoRef }
88
133
style = { { height : 'calc(100vh - 50px)' } }
89
134
groupCounts = { groupCounts }
90
135
groupContent = { ( index ) => {
@@ -112,7 +157,7 @@ const Proxies: React.FC = () => {
112
157
src = { groups [ index ] . icon }
113
158
/>
114
159
) : null }
115
- < div className = "h-[32px] text-md leading-[32px]" >
160
+ < div className = "h-[32px] text-ellipsis whitespace-nowrap overflow-hidden text- md leading-[32px]" >
116
161
{ groups [ index ] . name }
117
162
{ proxyDisplayMode === 'full' && (
118
163
< >
@@ -128,6 +173,29 @@ const Proxies: React.FC = () => {
128
173
</ div >
129
174
< div className = "flex" >
130
175
< Button
176
+ title = "定位到当前节点"
177
+ variant = "light"
178
+ size = "sm"
179
+ isIconOnly
180
+ onPress = { ( ) => {
181
+ if ( ! isOpen [ index ] ) return
182
+ let i = 0
183
+ for ( let j = 0 ; j < index ; j ++ ) {
184
+ i += groupCounts [ j ]
185
+ }
186
+ for ( let j = 0 ; j < groupCounts [ index ] ; j ++ ) {
187
+ if ( allProxies [ i + j ] . name === groups [ index ] . now ) {
188
+ i += j
189
+ break
190
+ }
191
+ }
192
+ virtuosoRef . current ?. scrollToIndex ( { index : i , align : 'start' } )
193
+ } }
194
+ >
195
+ < FaLocationCrosshairs className = "text-lg text-default-500" />
196
+ </ Button >
197
+ < Button
198
+ title = "延迟测试"
131
199
variant = "light"
132
200
size = "sm"
133
201
isIconOnly
0 commit comments