@@ -14,75 +14,136 @@ import {
1414 type SelectChangeEvent ,
1515} from '@mui/material' ;
1616import SearchIcon from '@mui/icons-material/Search' ;
17- import { useState } from 'react' ;
17+ import { useEffect , useState } from 'react' ;
1818import { useNavigate } from 'react-router-dom' ;
19+ import { AuthTypeEnum , useGbfsAuth } from '../../context/GbfsAuthProvider' ;
20+ import { useSelector } from 'react-redux' ;
21+ import { selectGbfsValidationParams } from '../../store/gbfs-validator-selectors' ;
1922
20- enum AuthTypeEnum {
21- BASIC = 'Basic Auth' ,
22- BEARER = 'Bearer Token' ,
23- OAUTH = 'Oauth Client Credentials Grant' ,
24- CUSTOM = 'Custom Headers (e.g. API Key)' ,
23+ interface GbfsFeedSearchInputProps {
24+ initialFeedUrl ?: string ;
25+ triggerDataFetch ?: ( ) => void ;
2526}
2627
27- export default function GbfsFeedSearchInput ( ) : React . ReactElement {
28+ export default function GbfsFeedSearchInput ( {
29+ initialFeedUrl,
30+ triggerDataFetch,
31+ } : GbfsFeedSearchInputProps ) : React . ReactElement {
32+ const lastSearchParams = useSelector ( selectGbfsValidationParams ) ;
2833 const theme = useTheme ( ) ;
2934 const navigate = useNavigate ( ) ;
30- const [ autoDiscoveryUrlInput , setAutoDiscoveryUrlInput ] =
31- useState < string > ( '' ) ;
35+ const { auth, setAuth } = useGbfsAuth ( ) ;
36+ const [ autoDiscoveryUrlInput , setAutoDiscoveryUrlInput ] = useState < string > (
37+ initialFeedUrl ?? '' ,
38+ ) ;
3239 const [ requiresAuth , setRequiresAuth ] = useState ( false ) ;
3340 const [ authType , setAuthType ] = useState < string > ( '' ) ;
34- const [ basicAuthUsername , setBasicAuthUsername ] = useState < string > ( '' ) ;
35- const [ basicAuthPassword , setBasicAuthPassword ] = useState < string > ( '' ) ;
36- const [ bearerAuthValue , setBearerAuthValue ] = useState < string > ( '' ) ;
37- const [ oauthClientId , setOauthClientId ] = useState < string > ( '' ) ;
38- const [ oauthClientSecret , setOauthClientSecret ] = useState < string > ( '' ) ;
39- const [ oauthTokenUrl , setOauthTokenUrl ] = useState < string > ( '' ) ;
41+ const [ basicAuthUsername , setBasicAuthUsername ] = useState <
42+ string | undefined
43+ > ( undefined ) ;
44+ const [ basicAuthPassword , setBasicAuthPassword ] = useState <
45+ string | undefined
46+ > ( undefined ) ;
47+ const [ bearerAuthValue , setBearerAuthValue ] = useState < string | undefined > (
48+ undefined ,
49+ ) ;
50+ const [ oauthClientId , setOauthClientId ] = useState < string | undefined > (
51+ undefined ,
52+ ) ;
53+ const [ oauthClientSecret , setOauthClientSecret ] = useState <
54+ string | undefined
55+ > ( undefined ) ;
56+ const [ oauthTokenUrl , setOauthTokenUrl ] = useState < string | undefined > (
57+ undefined ,
58+ ) ;
59+
60+ // Used to keep the text input up to date with back navigation in browser
61+ useEffect ( ( ) => {
62+ setAutoDiscoveryUrlInput ( initialFeedUrl ?? '' ) ;
63+ } , [ initialFeedUrl ] ) ;
64+
65+ // Used to keep the auth inputs up to date
66+ useEffect ( ( ) => {
67+ setRequiresAuth ( auth !== undefined ) ;
68+ setAuthType ( auth == undefined ? '' : auth . authType ?? '' ) ;
69+ setBasicAuthUsername (
70+ auth != null && 'username' in auth ? auth . username : undefined ,
71+ ) ;
72+ setBasicAuthPassword (
73+ auth != null && 'password' in auth ? auth . password : undefined ,
74+ ) ;
75+ setBearerAuthValue (
76+ auth != null && 'token' in auth ? auth . token : undefined ,
77+ ) ;
78+ setOauthClientId (
79+ auth != null && 'clientId' in auth ? auth . clientId : undefined ,
80+ ) ;
81+ setOauthClientSecret (
82+ auth != null && 'clientSecret' in auth ? auth . clientSecret : undefined ,
83+ ) ;
84+ setOauthTokenUrl (
85+ auth != null && 'tokenUrl' in auth ? auth . tokenUrl : undefined ,
86+ ) ;
87+ } , [ auth ] ) ;
4088
4189 const handleChange = ( event : React . ChangeEvent < HTMLInputElement > ) : void => {
4290 setRequiresAuth ( event . target . checked ) ;
4391 } ;
4492
4593 const handleAuthTypeChange = ( event : SelectChangeEvent < string > ) : void => {
4694 setAuthType ( event . target . value ) ;
47- setBasicAuthUsername ( '' ) ;
48- setBasicAuthPassword ( '' ) ;
49- setBearerAuthValue ( '' ) ;
50- setOauthClientId ( '' ) ;
51- setOauthClientSecret ( '' ) ;
52- setOauthTokenUrl ( '' ) ;
95+ setBasicAuthUsername ( undefined ) ;
96+ setBasicAuthPassword ( undefined ) ;
97+ setBearerAuthValue ( undefined ) ;
98+ setOauthClientId ( undefined ) ;
99+ setOauthClientSecret ( undefined ) ;
100+ setOauthTokenUrl ( undefined ) ;
53101 } ;
54102
55103 const isSubmitBoxDisabled = ( ) : boolean => {
56104 if ( autoDiscoveryUrlInput === '' ) return true ;
57- if ( requiresAuth ) {
58- if ( authType === '' ) return true ;
59- if ( authType === AuthTypeEnum . BASIC ) {
60- if ( basicAuthUsername === '' || basicAuthPassword === '' ) return true ;
61- }
62- if ( authType === AuthTypeEnum . BEARER ) {
63- if ( bearerAuthValue === '' ) return true ;
64- }
65- if ( authType === AuthTypeEnum . OAUTH ) {
66- if (
67- oauthClientId === '' ||
68- oauthClientSecret === '' ||
69- oauthTokenUrl === ''
70- )
71- return true ;
72- }
73- }
105+ if ( requiresAuth && authType === '' ) return true ;
74106 return false ;
75107 } ;
76108
77109 const validateGBFSFeed = ( ) : void => {
78- // 1. dispatch action with url and auth details (state -> loading)
79- // once done then
80- // 2. navigate to /gbfs-validator?AutoDiscoveryUrl=url
81- // or
82- // navigate to /gbfs-validator?AutoDiscoveryUrl=url&auth details
83- // store the auth details in context
84- // let the GbfsValidator component handle the loading state
85- // I'm sure if a query param exists, instead of navigation, we will update the param, and have a useEffect to call the new feed to validate
110+ if ( requiresAuth ) {
111+ switch ( authType ) {
112+ case AuthTypeEnum . BASIC :
113+ setAuth ( {
114+ authType : AuthTypeEnum . BASIC ,
115+ username : basicAuthUsername ,
116+ password : basicAuthPassword ,
117+ } ) ;
118+ break ;
119+ case AuthTypeEnum . BEARER :
120+ setAuth ( { authType : AuthTypeEnum . BEARER , token : bearerAuthValue } ) ;
121+ break ;
122+ case AuthTypeEnum . OAUTH :
123+ setAuth ( {
124+ authType : AuthTypeEnum . OAUTH ,
125+ clientId : oauthClientId ,
126+ clientSecret : oauthClientSecret ,
127+ tokenUrl : oauthTokenUrl ,
128+ } ) ;
129+ break ;
130+ default :
131+ setAuth ( undefined ) ;
132+ }
133+ } else {
134+ setAuth ( undefined ) ;
135+ }
136+
137+ // If the URL is the same React will ignore navigation, so we trigger the data fetch manually when it's the same url
138+ // Auth change will also trigger a fetch via useEffect in ValidationState
139+ if (
140+ ! requiresAuth &&
141+ lastSearchParams ?. feedUrl === autoDiscoveryUrlInput &&
142+ triggerDataFetch != undefined
143+ ) {
144+ triggerDataFetch ( ) ;
145+ return ;
146+ }
86147 navigate (
87148 `/gbfs-validator?AutoDiscoveryUrl=${ encodeURIComponent (
88149 autoDiscoveryUrlInput ,
@@ -116,9 +177,10 @@ export default function GbfsFeedSearchInput(): React.ReactElement {
116177 variant = 'outlined'
117178 label = 'GBFS Auto-Discovery URL'
118179 placeholder = 'eg: https://example.com/gbfs.json'
180+ value = { autoDiscoveryUrlInput ?? '' }
119181 sx = { { width : '100%' , mr : 2 } }
120182 onChange = { ( e ) => {
121- setAutoDiscoveryUrlInput ( e . target . value ) ;
183+ setAutoDiscoveryUrlInput ( e . target . value . trim ( ) ) ;
122184 } }
123185 InputProps = { {
124186 startAdornment : < SearchIcon sx = { { mr : 1 } } > </ SearchIcon > ,
@@ -178,6 +240,7 @@ export default function GbfsFeedSearchInput(): React.ReactElement {
178240 variant = 'outlined'
179241 label = 'Username'
180242 placeholder = 'Enter Username'
243+ value = { basicAuthUsername ?? '' }
181244 fullWidth
182245 onChange = { ( e ) => {
183246 setBasicAuthUsername ( e . target . value ) ;
@@ -188,6 +251,7 @@ export default function GbfsFeedSearchInput(): React.ReactElement {
188251 variant = 'outlined'
189252 label = 'Password'
190253 placeholder = 'Enter Password'
254+ value = { basicAuthPassword ?? '' }
191255 type = 'password'
192256 fullWidth
193257 onChange = { ( e ) => {
@@ -203,6 +267,7 @@ export default function GbfsFeedSearchInput(): React.ReactElement {
203267 variant = 'outlined'
204268 label = 'Token'
205269 placeholder = 'Enter Bearer Token'
270+ value = { bearerAuthValue ?? '' }
206271 sx = { { mt : 2 } }
207272 fullWidth
208273 onChange = { ( e ) => {
@@ -224,6 +289,7 @@ export default function GbfsFeedSearchInput(): React.ReactElement {
224289 size = 'small'
225290 variant = 'outlined'
226291 placeholder = 'Client Id'
292+ value = { oauthClientId ?? '' }
227293 label = 'Client Id'
228294 fullWidth
229295 onChange = { ( e ) => {
@@ -236,6 +302,7 @@ export default function GbfsFeedSearchInput(): React.ReactElement {
236302 placeholder = 'Enter Client Secret'
237303 label = 'Client Secret'
238304 fullWidth
305+ value = { oauthClientSecret ?? '' }
239306 onChange = { ( e ) => {
240307 setOauthClientSecret ( e . target . value ) ;
241308 } }
@@ -246,6 +313,7 @@ export default function GbfsFeedSearchInput(): React.ReactElement {
246313 placeholder = 'Enter Token Url'
247314 label = 'Token Url'
248315 fullWidth
316+ value = { oauthTokenUrl ?? '' }
249317 onChange = { ( e ) => {
250318 setOauthTokenUrl ( e . target . value ) ;
251319 } }
0 commit comments