1+ import { mdiMinus , mdiPlus } from '@mdi/js' ;
2+ import Icon from '@mdi/react' ;
13import { ChangeEvent , useCallback , useEffect , useRef , useState } from 'react' ;
24
35import { ISchema } from '../config/schema' ;
46import { sendUserAlert } from '../helpers/alert/sendUserAlert' ;
57
68const defaultConfig = {
7- endpoint : 'ntp://pool.ntp.org:123' ,
9+ pool : [ '' ] ,
810 timeout : 5 ,
911 retry : 5
1012} ;
@@ -19,20 +21,68 @@ export const NtpClient = ({ onCreate, onUpdate }: ISchema) => {
1921 }
2022 } , [ currentConfig , onCreate ] ) ;
2123
22- const [ ntpHostname , setNtpHostname ] = useState ( 'pool.ntp.org' ) ;
23- const [ ntpPort , setNtpPort ] = useState ( '123' ) ;
24+ const [ ntpAddrs , setNtpAddrs ] = useState ( [
25+ { address : 'ntp-a2.nict.go.jp' , port : '123' } ,
26+ { address : 'ntp-a3.nict.go.jp' , port : '123' } ,
27+ { address : 'ntp-b2.nict.go.jp' , port : '123' } ,
28+ { address : 'ntp-b3.nict.go.jp' , port : '123' } ,
29+ { address : 'ntp-k1.nict.jp' , port : '123' } ,
30+ { address : 'ptbtime1.ptb.de' , port : '123' } ,
31+ { address : 'ptbtime2.ptb.de' , port : '123' } ,
32+ { address : 'ptbtime3.ptb.de' , port : '123' } ,
33+ { address : 'stdtime.gov.hk' , port : '123' } ,
34+ { address : 'time1.chu.nrc.ca' , port : '123' } ,
35+ { address : 'time2.chu.nrc.ca' , port : '123' } ,
36+ { address : 'ntp.metas.ch' , port : '123' } ,
37+ { address : 'hora.roa.es' , port : '123' } ,
38+ { address : 'minuto.roa.es' , port : '123' }
39+ ] ) ;
2440
25- const updateEndpoint = useCallback ( ( ) => {
26- const url = new URL ( 'ntp://' ) ;
27- url . hostname = ntpHostname ;
28- url . port = ntpPort ;
29- return url . toString ( ) ;
30- } , [ ntpHostname , ntpPort ] ) ;
41+ const handleUpdateNtpAddr = ( index : number , value : string ) => {
42+ setNtpAddrs ( ( prev ) =>
43+ prev . map ( ( item , i ) => ( i === index ? { ...item , address : value } : item ) )
44+ ) ;
45+ } ;
46+
47+ const handleUpdateNtpPort = ( index : number , value : number ) => {
48+ if ( value < 0 || value > 65535 ) {
49+ sendUserAlert ( 'Port must be between 0 and 65535' , true ) ;
50+ return ;
51+ }
52+ setNtpAddrs ( ( prev ) => {
53+ const newNtpAddrs = [ ...prev ] ;
54+ newNtpAddrs [ index ] = { ...newNtpAddrs [ index ] , port : String ( value ) } ;
55+ return newNtpAddrs ;
56+ } ) ;
57+ } ;
58+
59+ const handleAddNtpItem = ( ) => {
60+ setNtpAddrs ( [ ...ntpAddrs , { address : '' , port : '123' } ] ) ;
61+ } ;
62+
63+ const handleRemoveNtpItem = ( index : number ) => {
64+ if ( ntpAddrs . length > 1 ) {
65+ setNtpAddrs ( ntpAddrs . filter ( ( _ , i ) => i !== index ) ) ;
66+ } else {
67+ sendUserAlert ( 'At least one address is required' , true ) ;
68+ }
69+ } ;
70+
71+ const updatePool = useCallback ( ( ) => {
72+ return ntpAddrs
73+ . filter ( ( addr ) => addr . address . length > 0 )
74+ . map ( ( addr ) => {
75+ const url = new URL ( 'ntp://' ) ;
76+ url . hostname = addr . address ;
77+ url . port = addr . port ;
78+ return url . toString ( ) ;
79+ } ) ;
80+ } , [ ntpAddrs ] ) ;
3181
3282 useEffect ( ( ) => {
33- const newEndpoint = updateEndpoint ( ) ;
34- setCurrentConfig ( ( prev ) => ( { ...prev , endpoint : newEndpoint } ) ) ;
35- } , [ updateEndpoint ] ) ;
83+ const newPool = updatePool ( ) ;
84+ setCurrentConfig ( ( prev ) => ( { ...prev , pool : newPool } ) ) ;
85+ } , [ updatePool ] ) ;
3686
3787 const prevConfigRef = useRef ( currentConfig ) ;
3888 useEffect ( ( ) => {
@@ -55,30 +105,43 @@ export const NtpClient = ({ onCreate, onUpdate }: ISchema) => {
55105 return (
56106 < fieldset className = "fieldset bg-base-200 border-base-300 rounded-box space-y-2 border p-4" >
57107 < div className = "flex flex-col space-y-2" >
58- < label className = "label" > NTP Server Hostname</ label >
59- < input
60- type = "text"
61- className = "input w-full"
62- value = { ntpHostname }
63- onChange = { ( { target } ) => setNtpHostname ( target . value ) }
64- />
65- </ div >
66-
67- < div className = "flex flex-col space-y-2" >
68- < label className = "label" > NTP Server Port</ label >
69- < input
70- type = "number"
71- className = "input w-full"
72- value = { ntpPort }
73- onChange = { ( { target } ) => {
74- const numVal = Number ( target . value ) ;
75- if ( numVal < 0 || numVal > 65535 ) {
76- sendUserAlert ( 'Port must be between 0 and 65535' , true ) ;
77- return ;
78- }
79- setNtpPort ( target . value ) ;
80- } }
81- />
108+ { ntpAddrs . map ( ( addr , index ) => (
109+ < div className = "flex items-center justify-center space-x-2" key = { index } >
110+ < div className = "w-1/2 space-y-2 lg:w-4/6" >
111+ < label className = "label" > Host { index + 1 } </ label >
112+ < input
113+ type = "text"
114+ className = "input w-full"
115+ value = { addr . address }
116+ placeholder = 'e.g. "ntp-a2.nict.go.jp"'
117+ onChange = { ( { target } ) => handleUpdateNtpAddr ( index , target . value ) }
118+ />
119+ </ div >
120+ < div className = "w-1/2 space-y-2 lg:w-2/6" >
121+ < label className = "label" > Port</ label >
122+ < input
123+ type = "number"
124+ className = "input w-full"
125+ value = { addr . port }
126+ onChange = { ( { target } ) =>
127+ handleUpdateNtpPort ( index , Number ( target . value ) )
128+ }
129+ />
130+ </ div >
131+ < button
132+ className = "btn btn-circle btn-sm mt-6 bg-red-400 hover:bg-red-500"
133+ onClick = { ( ) => handleRemoveNtpItem ( index ) }
134+ >
135+ < Icon className = "text-white" path = { mdiMinus } size = { 0.8 } />
136+ </ button >
137+ </ div >
138+ ) ) }
139+ < button
140+ className = "btn btn-circle btn-sm bg-purple-500 hover:bg-purple-600"
141+ onClick = { handleAddNtpItem }
142+ >
143+ < Icon className = "text-white" path = { mdiPlus } size = { 0.8 } />
144+ </ button >
82145 </ div >
83146
84147 < div className = "flex flex-col space-y-2" >
0 commit comments