import { TooltipPopup, useTooltip } from '@reach/tooltip'; import cx from 'clsx'; import * as React from 'react'; import { keyCodes } from '~/misc/keycode'; import { DispatchFn, ProxyItem } from '~/store/types'; import { ClashAPIConfig } from '~/types'; import { healthcheckProxy } from '../../store/proxies'; import s0 from './Proxy.module.scss'; import { ProxyLatency } from './ProxyLatency'; const { memo, useMemo } = React; const colorMap = { // green good: '#67c23a', // yellow normal: '#d4b75c', // orange bad: '#e67f3c', // bad: '#F56C6C', na: '#909399', }; function getLabelColor( { number, }: { number?: number; } = {}, httpsTest: boolean ) { const delayMap = { good: httpsTest ? 800 : 200, normal: httpsTest ? 1500 : 500, }; if (number === 0) { return colorMap.na; } else if (number < delayMap.good) { return colorMap.good; } else if (number < delayMap.normal) { return colorMap.normal; } else if (typeof number === 'number') { return colorMap.bad; } return colorMap.na; } function getProxyDotBackgroundColor( latency: { number?: number; }, // proxyType: string, httpsTest: boolean ) { // if (NonProxyTypes.indexOf(proxyType) > -1) { // return 'linear-gradient(135deg, white 15%, #999 15% 30%, white 30% 45%, #999 45% 60%, white 60% 75%, #999 75% 90%, white 90% 100%)'; // } return getLabelColor(latency, httpsTest); } type ProxyProps = { name: string; now?: boolean; proxy: ProxyItem; latency: { number?: number; error?: string; testing?: boolean }; httpsLatencyTest: boolean; isSelectable?: boolean; onClick?: (proxyName: string) => unknown; apiConfig: ClashAPIConfig; dispatch: DispatchFn; }; export const ProxySmall = memo(function ProxySmall({ now, name, proxy, latency, httpsLatencyTest, isSelectable, onClick, }: ProxyProps) { const delay = proxy.history[proxy.history.length - 1]?.delay; const latencyNumber = latency?.number ?? delay; const color = useMemo( () => getProxyDotBackgroundColor({ number: latencyNumber }, httpsLatencyTest), [latencyNumber, httpsLatencyTest] ); const title = useMemo(() => { let ret = name; if (latency && typeof latency.number === 'number') { ret += ' ' + latency.number + ' ms'; } return ret; }, [name, latency]); const doSelect = React.useCallback(() => { isSelectable && onClick && onClick(name); }, [name, onClick, isSelectable]); const handleKeyDown = React.useCallback( (e: React.KeyboardEvent) => { if (e.keyCode === keyCodes.Enter) { doSelect(); } }, [doSelect] ); return (
{now &&
}
); }); function formatProxyType(t: string) { if (t === 'Shadowsocks') return 'SS'; return t; } const positionProxyNameTooltip = (triggerRect: { left: number; top: number }) => { return { left: triggerRect.left + window.scrollX - 5, top: triggerRect.top + window.scrollY - 38, }; }; function ProxyNameTooltip({ children, label, 'aria-label': ariaLabel }) { const [trigger, tooltip] = useTooltip(); return ( <> {React.cloneElement(children, trigger)} ); } export const Proxy = memo(function Proxy({ now, name, proxy, latency, httpsLatencyTest, isSelectable, onClick, apiConfig, dispatch, }: ProxyProps) { const delay = proxy.history[proxy.history.length - 1]?.delay; const latencyNumber = typeof latency?.number === 'number' ? latency.number : typeof delay === 'number' ? delay : undefined; const hasLatencyNumber = typeof latencyNumber === 'number' && latencyNumber > 0; const color = useMemo( () => getLabelColor({ number: hasLatencyNumber ? latencyNumber : undefined }, httpsLatencyTest), [hasLatencyNumber, latencyNumber, httpsLatencyTest] ); const isTestingLatency = Boolean(latency?.testing); const doSelect = React.useCallback(() => { isSelectable && onClick && onClick(name); }, [name, onClick, isSelectable]); function formatUdpType(udp: boolean, xudp?: boolean) { if (!udp) return ''; return xudp ? 'XUDP' : 'UDP'; } function formatTfo(t: boolean) { if (!t) return ''; return ( ); } const handleKeyDown = React.useCallback( (e: React.KeyboardEvent) => { if (e.keyCode === keyCodes.Enter) { doSelect(); } }, [doSelect] ); const className = useMemo(() => { return cx(s0.proxy, { [s0.now]: now, [s0.error]: latency && latency.error, [s0.selectable]: isSelectable, }); }, [isSelectable, now, latency]); const runLatencyTest = React.useCallback(() => { if (isTestingLatency) return; dispatch(healthcheckProxy(apiConfig, name)); }, [apiConfig, dispatch, isTestingLatency, name]); return (
{name} {formatUdpType(proxy.udp, proxy.xudp)}
{formatProxyType(proxy.type)} {formatTfo(proxy.tfo)}
); });