@@ -4,11 +4,14 @@ import { MdDeleteForever } from 'react-icons/md'
4
4
import SettingCard from '@renderer/components/base/base-setting-card'
5
5
import SettingItem from '@renderer/components/base/base-setting-item'
6
6
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
7
+ import { useAppConfig } from '@renderer/hooks/use-app-config'
7
8
import { restartCore } from '@renderer/utils/ipc'
8
- import React , { Key , useState } from 'react'
9
+ import React , { Key , ReactNode , useState } from 'react'
9
10
10
11
const DNS : React . FC = ( ) => {
11
12
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig ( )
13
+ const { appConfig, patchAppConfig } = useAppConfig ( )
14
+ const { nameserverPolicy, useNameserverPolicy } = appConfig || { }
12
15
const { dns, hosts } = controledMihomoConfig || { }
13
16
const {
14
17
ipv6 = false ,
@@ -24,7 +27,9 @@ const DNS: React.FC = () => {
24
27
'enhanced-mode' : enhancedMode = 'fake-ip' ,
25
28
'use-hosts' : useHosts = false ,
26
29
'use-system-hosts' : useSystemHosts = false ,
27
- nameserver = [ 'https://doh.pub/dns-query' , 'https://dns.alidns.com/dns-query' ]
30
+ 'respect-rules' : respectRules = false ,
31
+ nameserver = [ 'https://doh.pub/dns-query' , 'https://dns.alidns.com/dns-query' ] ,
32
+ 'proxy-server-nameserver' : proxyServerNameserver = [ ]
28
33
} = dns || { }
29
34
30
35
const [ values , setValues ] = useState ( {
@@ -34,45 +39,76 @@ const DNS: React.FC = () => {
34
39
fakeIPRange,
35
40
fakeIPFilter,
36
41
useSystemHosts,
42
+ respectRules,
37
43
nameserver,
44
+ proxyServerNameserver,
45
+ useNameserverPolicy,
46
+ nameserverPolicy : Object . entries ( nameserverPolicy || { } ) . map ( ( [ domain , value ] ) => ( {
47
+ domain,
48
+ value
49
+ } ) ) ,
38
50
hosts : Object . entries ( hosts || { } ) . map ( ( [ domain , value ] ) => ( { domain, value } ) )
39
51
} )
40
52
41
53
const handleListChange = ( type : string , value : string , index : number ) : void => {
42
- const newValues = [ ...values [ type ] ]
43
- if ( index === newValues . length ) {
44
- if ( value . trim ( ) !== '' ) {
45
- newValues . push ( value )
54
+ const list = [ ...values [ type ] ]
55
+ if ( value . trim ( ) ) {
56
+ if ( index < list . length ) {
57
+ list [ index ] = value
58
+ } else if ( list . length < 4 ) {
59
+ list . push ( value )
46
60
}
47
61
} else {
48
- if ( value . trim ( ) === '' ) {
49
- newValues . splice ( index , 1 )
50
- } else {
51
- newValues [ index ] = value
52
- }
62
+ list . splice ( index , 1 )
53
63
}
54
- setValues ( { ...values , [ type ] : newValues } )
64
+ setValues ( { ...values , [ type ] : list . slice ( 0 , 4 ) } )
55
65
}
56
- const handleHostsChange = ( domain : string , value : string , index : number ) : void => {
57
- const processValue = ( val : string ) : string | string [ ] =>
58
- val . includes ( ',' ) ? val . split ( ',' ) . map ( ( s ) => s . trim ( ) ) : val . trim ( )
59
- const isEmpty = ( d : string , v : string | string [ ] ) : boolean =>
60
- d === '' && ( Array . isArray ( v ) ? v . every ( ( item ) => item === '' ) : v === '' )
61
66
62
- const newHosts = [ ...values . hosts ]
63
- if ( ! isEmpty ( domain . trim ( ) , processValue ( value ) ) ) {
64
- if ( index === newHosts . length ) {
65
- newHosts . push ( { domain : domain . trim ( ) , value : processValue ( value ) } )
66
- } else {
67
- newHosts [ index ] = { domain : domain . trim ( ) , value : processValue ( value ) }
68
- }
69
- } else if ( index < newHosts . length ) {
70
- newHosts . splice ( index , 1 )
71
- }
72
- setValues ( { ...values , hosts : newHosts } )
67
+ const renderListInputs = ( type : string , placeholder : string ) : ReactNode => {
68
+ const currentItems = values [ type ] . slice ( 0 , 4 )
69
+ const showNewLine = currentItems . length < 4 && currentItems . every ( ( item ) => item . trim ( ) !== '' )
70
+
71
+ return [ ...currentItems , ...( showNewLine ? [ '' ] : [ ] ) ] . slice ( 0 , 4 ) . map ( ( item , index ) => (
72
+ < div key = { index } className = "mt-2 flex" >
73
+ < Input
74
+ fullWidth
75
+ size = "sm"
76
+ placeholder = { placeholder }
77
+ value = { typeof item === 'string' ? item : item . domain }
78
+ onValueChange = { ( v ) => handleListChange ( type , v , index ) }
79
+ />
80
+ { index < values [ type ] . length && (
81
+ < Button
82
+ className = "ml-2"
83
+ size = "sm"
84
+ variant = "flat"
85
+ color = "warning"
86
+ onClick = { ( ) => handleListChange ( type , '' , index ) }
87
+ >
88
+ < MdDeleteForever className = "text-lg" />
89
+ </ Button >
90
+ ) }
91
+ </ div >
92
+ ) )
93
+ }
94
+
95
+ const handleSubkeyChange = ( type : string , domain : string , value : string , index : number ) : void => {
96
+ const list = [ ...values [ type ] ]
97
+ const processedValue = value . includes ( ',' )
98
+ ? value . split ( ',' ) . map ( ( s : string ) => s . trim ( ) )
99
+ : value . trim ( )
100
+ if ( domain || processedValue ) list [ index ] = { domain : domain . trim ( ) , value : processedValue }
101
+ else list . splice ( index , 1 )
102
+ setValues ( { ...values , [ type ] : list } )
73
103
}
74
104
75
105
const onSave = async ( patch : Partial < IMihomoConfig > ) : Promise < void > => {
106
+ await patchAppConfig ( {
107
+ nameserverPolicy : Object . fromEntries (
108
+ values . nameserverPolicy . map ( ( { domain, value } ) => [ domain , value ] )
109
+ ) ,
110
+ useNameserverPolicy : values . useNameserverPolicy
111
+ } )
76
112
await patchControledMihomoConfig ( patch )
77
113
await restartCore ( )
78
114
}
@@ -85,22 +121,29 @@ const DNS: React.FC = () => {
85
121
size = "sm"
86
122
color = "primary"
87
123
onPress = { ( ) => {
88
- const hostsObject = values . hosts . reduce ( ( acc , { domain, value } ) => {
89
- if ( domain ) {
90
- acc [ domain ] = value
91
- }
92
- return acc
93
- } , { } )
124
+ const hostsObject = Object . fromEntries (
125
+ values . hosts . map ( ( { domain, value } ) => [ domain , value ] )
126
+ )
127
+ const dnsConfig = {
128
+ ipv6 : values . ipv6 ,
129
+ 'fake-ip-range' : values . fakeIPRange ,
130
+ 'fake-ip-filter' : values . fakeIPFilter ,
131
+ 'enhanced-mode' : values . enhancedMode ,
132
+ 'use-hosts' : values . useHosts ,
133
+ 'use-system-hosts' : values . useSystemHosts ,
134
+ 'respect-rules' : values . respectRules ,
135
+ nameserver : values . nameserver ,
136
+ 'proxy-server-nameserver' : values . proxyServerNameserver ,
137
+ fallback : [ ] ,
138
+ 'fallback-filter' : { }
139
+ }
140
+ if ( values . useNameserverPolicy ) {
141
+ dnsConfig [ 'nameserver-policy' ] = Object . fromEntries (
142
+ values . nameserverPolicy . map ( ( { domain, value } ) => [ domain , value ] )
143
+ )
144
+ }
94
145
onSave ( {
95
- dns : {
96
- ipv6 : values . ipv6 ,
97
- 'fake-ip-range' : values . fakeIPRange ,
98
- 'fake-ip-filter' : values . fakeIPFilter ,
99
- 'enhanced-mode' : values . enhancedMode ,
100
- 'use-hosts' : values . useHosts ,
101
- 'use-system-hosts' : values . useSystemHosts ,
102
- nameserver : values . nameserver
103
- } ,
146
+ dns : dnsConfig ,
104
147
hosts : hostsObject
105
148
} )
106
149
} }
@@ -136,28 +179,7 @@ const DNS: React.FC = () => {
136
179
</ SettingItem >
137
180
< div className = "flex flex-col items-stretch" >
138
181
< h3 className = "select-none" > 真实IP回应</ h3 >
139
- { [ ...values . fakeIPFilter , '' ] . map ( ( ns , index ) => (
140
- < div key = { index } className = "mt-2 flex" >
141
- < Input
142
- fullWidth
143
- size = "sm"
144
- placeholder = "例: +.lan"
145
- value = { ns }
146
- onValueChange = { ( v ) => handleListChange ( 'fakeIPFilter' , v , index ) }
147
- />
148
- { index < values . fakeIPFilter . length && (
149
- < Button
150
- className = "ml-2"
151
- size = "sm"
152
- variant = "flat"
153
- color = "warning"
154
- onClick = { ( ) => handleListChange ( 'fakeIPFilter' , '' , index ) }
155
- >
156
- < MdDeleteForever className = "text-lg" />
157
- </ Button >
158
- ) }
159
- </ div >
160
- ) ) }
182
+ { renderListInputs ( 'fakeIPFilter' , '例: +.lan' ) }
161
183
</ div >
162
184
< Divider className = "my-2" />
163
185
</ >
@@ -171,32 +193,87 @@ const DNS: React.FC = () => {
171
193
} }
172
194
/>
173
195
</ SettingItem >
196
+ < SettingItem title = "连接遵守规则" divider >
197
+ < Switch
198
+ size = "sm"
199
+ isSelected = { values . respectRules }
200
+ onValueChange = { ( v ) => {
201
+ setValues ( { ...values , respectRules : v } )
202
+ } }
203
+ />
204
+ </ SettingItem >
205
+
206
+ < div className = "flex flex-col items-stretch" >
207
+ < h3 className = "select-none" > 代理节点域名解析</ h3 >
208
+ { renderListInputs ( 'proxyServerNameserver' , '例: tls://223.5.5.5' ) }
209
+ </ div >
210
+ < Divider className = "my-2" />
174
211
< div className = "flex flex-col items-stretch" >
175
212
< h3 className = "select-none" > DNS服务器</ h3 >
176
- { [ ...values . nameserver , '' ] . map ( ( ns , index ) => (
177
- < div key = { index } className = "mt-2 flex" >
178
- < Input
179
- fullWidth
180
- size = "sm"
181
- placeholder = "例: tls://223.5.5.5"
182
- value = { ns }
183
- onValueChange = { ( v ) => handleListChange ( 'nameserver' , v , index ) }
184
- />
185
- { index < values . nameserver . length && (
186
- < Button
187
- className = "ml-2"
188
- size = "sm"
189
- variant = "flat"
190
- color = "warning"
191
- onClick = { ( ) => handleListChange ( 'nameserver' , '' , index ) }
192
- >
193
- < MdDeleteForever className = "text-lg" />
194
- </ Button >
195
- ) }
196
- </ div >
197
- ) ) }
213
+ { renderListInputs ( 'nameserver' , '例: tls://223.5.5.5' ) }
198
214
</ div >
199
215
< Divider className = "my-2" />
216
+ < SettingItem title = "覆盖DNS策略" divider >
217
+ < Switch
218
+ size = "sm"
219
+ isSelected = { values . useNameserverPolicy }
220
+ onValueChange = { ( v ) => {
221
+ setValues ( { ...values , useNameserverPolicy : v } )
222
+ } }
223
+ />
224
+ </ SettingItem >
225
+ { values . useNameserverPolicy && (
226
+ < div className = "flex flex-col items-stretch" >
227
+ < div className = "flex flex-col items-stretch" >
228
+ < h3 className = "mb-2" > </ h3 >
229
+ { [ ...values . nameserverPolicy , { domain : '' , value : '' } ] . map (
230
+ ( { domain, value } , index ) => (
231
+ < div key = { index } className = "flex mb-2" >
232
+ < div className = "flex-[4]" >
233
+ < Input
234
+ size = "sm"
235
+ fullWidth
236
+ placeholder = "域名"
237
+ value = { domain }
238
+ onValueChange = { ( v ) =>
239
+ handleSubkeyChange (
240
+ 'nameserverPolicy' ,
241
+ v ,
242
+ Array . isArray ( value ) ? value . join ( ',' ) : value ,
243
+ index
244
+ )
245
+ }
246
+ />
247
+ </ div >
248
+ < span className = "select-none mx-2" > :</ span >
249
+ < div className = "flex-[6] flex" >
250
+ < Input
251
+ size = "sm"
252
+ fullWidth
253
+ placeholder = "DNS服务器"
254
+ value = { Array . isArray ( value ) ? value . join ( ',' ) : value }
255
+ onValueChange = { ( v ) =>
256
+ handleSubkeyChange ( 'nameserverPolicy' , domain , v , index )
257
+ }
258
+ />
259
+ { index < values . nameserverPolicy . length && (
260
+ < Button
261
+ size = "sm"
262
+ color = "warning"
263
+ variant = "flat"
264
+ className = "ml-2"
265
+ onClick = { ( ) => handleSubkeyChange ( 'nameserverPolicy' , '' , '' , index ) }
266
+ >
267
+ < MdDeleteForever className = "text-lg" />
268
+ </ Button >
269
+ ) }
270
+ </ div >
271
+ </ div >
272
+ )
273
+ ) }
274
+ </ div >
275
+ </ div >
276
+ ) }
200
277
< SettingItem title = "使用系统hosts" divider >
201
278
< Switch
202
279
size = "sm"
@@ -227,7 +304,12 @@ const DNS: React.FC = () => {
227
304
placeholder = "域名"
228
305
value = { domain }
229
306
onValueChange = { ( v ) =>
230
- handleHostsChange ( v , Array . isArray ( value ) ? value . join ( ',' ) : value , index )
307
+ handleSubkeyChange (
308
+ 'hosts' ,
309
+ v ,
310
+ Array . isArray ( value ) ? value . join ( ',' ) : value ,
311
+ index
312
+ )
231
313
}
232
314
/>
233
315
</ div >
@@ -238,15 +320,15 @@ const DNS: React.FC = () => {
238
320
fullWidth
239
321
placeholder = "域名或IP"
240
322
value = { Array . isArray ( value ) ? value . join ( ',' ) : value }
241
- onValueChange = { ( v ) => handleHostsChange ( domain , v , index ) }
323
+ onValueChange = { ( v ) => handleSubkeyChange ( 'hosts' , domain , v , index ) }
242
324
/>
243
325
{ index < values . hosts . length && (
244
326
< Button
245
327
size = "sm"
246
328
color = "warning"
247
329
variant = "flat"
248
330
className = "ml-2"
249
- onClick = { ( ) => handleHostsChange ( '' , '' , index ) }
331
+ onClick = { ( ) => handleSubkeyChange ( 'hosts' , '' , '' , index ) }
250
332
>
251
333
< MdDeleteForever className = "text-lg" />
252
334
</ Button >
0 commit comments