import * as React from 'react'; import { fetchConfigs } from 'src/api/configs'; import { BackendList } from 'src/components/BackendList'; import { addClashAPIConfig, getClashAPIConfig } from 'src/store/app'; import { State } from 'src/store/types'; import { ClashAPIConfig } from 'src/types'; import s0 from './APIConfig.module.scss'; import Button from './Button'; import Field from './Field'; import { connect } from './StateProvider'; import SvgYacd from './SvgYacd'; const { useState, useRef, useCallback, useEffect } = React; const Ok = 0; const mapState = (s: State) => ({ apiConfig: getClashAPIConfig(s), }); function APIConfig({ dispatch }) { const [baseURL, setBaseURL] = useState(''); const [secret, setSecret] = useState(''); const [errMsg, setErrMsg] = useState(''); const userTouchedFlagRef = useRef(false); const contentEl = useRef(null); const handleInputOnChange = useCallback((e) => { userTouchedFlagRef.current = true; setErrMsg(''); const target = e.target; const { name } = target; const value = target.value; switch (name) { case 'baseURL': setBaseURL(value); break; case 'secret': setSecret(value); break; default: throw new Error(`unknown input name ${name}`); } }, []); const onConfirm = useCallback(() => { verify({ baseURL, secret }).then((ret) => { if (ret[0] !== Ok) { setErrMsg(ret[1]); } else { dispatch(addClashAPIConfig({ baseURL, secret })); } }); }, [baseURL, secret, dispatch]); const handleContentOnKeyDown = useCallback( (e: React.KeyboardEvent) => { if ( e.target instanceof Element && (!e.target.tagName || e.target.tagName.toUpperCase() !== 'INPUT') ) { return; } if (e.key !== 'Enter') return; onConfirm(); }, [onConfirm] ); const detectApiServer = async () => { // if there is already a clash API server at `/`, just use it as default value const res = await fetch('/'); res.json().then(data => { if (data['hello'] === 'clash') { setBaseURL(window.location.origin) } }); }; useEffect(() => { detectApiServer(); }, []); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions
{errMsg ? errMsg : null}
); } export default connect(mapState)(APIConfig); async function verify(apiConfig: ClashAPIConfig): Promise<[number, string?]> { try { new URL(apiConfig.baseURL); } catch (e) { if (apiConfig.baseURL) { const prefix = apiConfig.baseURL.substring(0, 7); if (prefix !== 'http://' && prefix !== 'https:/') { return [1, 'Must starts with http:// or https://']; } } return [1, 'Invalid URL']; } try { const res = await fetchConfigs(apiConfig); if (res.status > 399) { return [1, res.statusText]; } return [Ok]; } catch (e) { return [1, 'Failed to connect']; } }