diff --git a/src/components/Latency.tsx b/src/components/Latency.tsx index 8cd3670..ee550d6 100644 --- a/src/components/Latency.tsx +++ b/src/components/Latency.tsx @@ -1,17 +1,23 @@ import { JSX, ParentComponent } from 'solid-js' import { twMerge } from 'tailwind-merge' import { getLatencyClassName } from '~/helpers' -import { useProxies } from '~/signals' +import { urlForLatencyTest, useProxies } from '~/signals' interface Props extends JSX.HTMLAttributes { proxyName: string + testUrl: string | null } export const Latency: ParentComponent = (props) => { const [local, others] = splitProps(props, ['class']) const { getLatencyByName } = useProxies() const [textClassName, setTextClassName] = createSignal('') - const latency = createMemo(() => getLatencyByName(others.proxyName || '')) + const latency = createMemo(() => + getLatencyByName( + others.proxyName || '', + others.testUrl || urlForLatencyTest(), + ), + ) createEffect(() => { setTextClassName(getLatencyClassName(latency())) @@ -19,7 +25,11 @@ export const Latency: ParentComponent = (props) => { return ( {latency() || '---'} diff --git a/src/components/ProxyNodeCard.tsx b/src/components/ProxyNodeCard.tsx index 687b60c..709746d 100644 --- a/src/components/ProxyNodeCard.tsx +++ b/src/components/ProxyNodeCard.tsx @@ -8,10 +8,12 @@ import { formatProxyType, getLatencyClassName, } from '~/helpers' -import { rootElement, useProxies } from '~/signals' +import { rootElement, urlForLatencyTest, useProxies } from '~/signals' export const ProxyNodeCard = (props: { proxyName: string + testUrl: string | null + timeout: number | null isSelected?: boolean onClick?: () => void }) => { @@ -36,6 +38,9 @@ export const ProxyNodeCard = (props: { [proxyName, specialTypes()].filter(Boolean).join(' - '), ) + const latencyTestHistory = + proxyNode().latencyTestHistory[props.testUrl || urlForLatencyTest()] || [] + return ( { e.stopPropagation() - void proxyLatencyTest(proxyName, proxyNode().provider) + void proxyLatencyTest( + proxyName, + proxyNode().provider, + props.testUrl, + props.timeout, + ) }} /> @@ -87,12 +98,10 @@ export const ProxyNodeCard = (props: {

{proxyName}

-
- {specialTypes()} -
+
{specialTypes()}
    - + {(latencyTestResult, index) => (
  • 0}> @@ -120,11 +129,7 @@ export const ProxyNodeCard = (props: {
- +
diff --git a/src/components/ProxyNodePreview.tsx b/src/components/ProxyNodePreview.tsx index d96ae4b..cd88c84 100644 --- a/src/components/ProxyNodePreview.tsx +++ b/src/components/ProxyNodePreview.tsx @@ -4,6 +4,7 @@ import { proxiesPreviewType } from '~/signals' export const ProxyNodePreview = (props: { proxyNameList: string[] + testUrl: string | null now?: string }) => { const off = () => proxiesPreviewType() === PROXIES_PREVIEW_TYPE.OFF @@ -34,6 +35,7 @@ export const ProxyNodePreview = (props: { @@ -41,6 +43,7 @@ export const ProxyNodePreview = (props: { diff --git a/src/components/ProxyPreviewBar.tsx b/src/components/ProxyPreviewBar.tsx index eaf3082..1c9ab25 100644 --- a/src/components/ProxyPreviewBar.tsx +++ b/src/components/ProxyPreviewBar.tsx @@ -3,11 +3,12 @@ import { latencyQualityMap, useProxies } from '~/signals' export const ProxyPreviewBar = (props: { proxyNameList: string[] + testUrl: string | null now?: string }) => { const { getLatencyByName } = useProxies() const latencyList = createMemo(() => - props.proxyNameList.map((name) => getLatencyByName(name)), + props.proxyNameList.map((name) => getLatencyByName(name, props.testUrl)), ) const all = createMemo(() => latencyList().length) @@ -69,7 +70,7 @@ export const ProxyPreviewBar = (props: { - + ) diff --git a/src/components/ProxyPreviewDots.tsx b/src/components/ProxyPreviewDots.tsx index b8fbc03..c372883 100644 --- a/src/components/ProxyPreviewDots.tsx +++ b/src/components/ProxyPreviewDots.tsx @@ -38,6 +38,7 @@ const LatencyDot = (props: { export const ProxyPreviewDots = (props: { proxyNameList: string[] + testUrl: string | null now?: string }) => { const { getLatencyByName } = useProxies() @@ -48,7 +49,7 @@ export const ProxyPreviewDots = (props: { [ name, - getLatencyByName(name), + getLatencyByName(name, props.testUrl), ])} > {([name, latency]) => ( @@ -62,7 +63,7 @@ export const ProxyPreviewDots = (props: { - + ) diff --git a/src/helpers/proxies.ts b/src/helpers/proxies.ts index 8addf7b..f18a237 100644 --- a/src/helpers/proxies.ts +++ b/src/helpers/proxies.ts @@ -1,6 +1,6 @@ import { LATENCY_QUALITY_MAP_HTTP, PROXIES_ORDERING_TYPE } from '~/constants' import { useI18n } from '~/i18n' -import { latencyQualityMap, useProxies } from '~/signals' +import { latencyQualityMap, urlForLatencyTest, useProxies } from '~/signals' export const formatProxyType = (type = '') => { const [t] = useI18n() @@ -55,19 +55,26 @@ export const filterSpecialProxyType = (type = '') => { return !conditions.includes(type.toLowerCase()) } -export const sortProxiesByOrderingType = ( - proxyNames: string[], - orderingType: PROXIES_ORDERING_TYPE, -) => { +export const sortProxiesByOrderingType = ({ + proxyNames, + orderingType, + testUrl, +}: { + proxyNames: string[] + orderingType: PROXIES_ORDERING_TYPE + testUrl: string | null +}) => { const { getLatencyByName } = useProxies() if (orderingType === PROXIES_ORDERING_TYPE.NATURAL) { return proxyNames } + const finalTestUrl = testUrl || urlForLatencyTest() + return proxyNames.sort((a, b) => { - const prevLatency = getLatencyByName(a) - const nextLatency = getLatencyByName(b) + const prevLatency = getLatencyByName(a, finalTestUrl) + const nextLatency = getLatencyByName(b, finalTestUrl) switch (orderingType) { case PROXIES_ORDERING_TYPE.LATENCY_ASC: @@ -96,19 +103,27 @@ export const sortProxiesByOrderingType = ( }) } -export const filterProxiesByAvailability = ( - proxyNames: string[], - enabled?: boolean, -) => { +export const filterProxiesByAvailability = ({ + proxyNames, + enabled, + testUrl, +}: { + proxyNames: string[] + enabled?: boolean + testUrl: string | null +}) => { const { getLatencyByName, isProxyGroup } = useProxies() + const finalTestUrl = testUrl || urlForLatencyTest() + return enabled ? proxyNames.filter( // we need proxy node with connected or the node is a group it self (name) => { return ( isProxyGroup(name) || - getLatencyByName(name) !== latencyQualityMap().NOT_CONNECTED + getLatencyByName(name, finalTestUrl) !== + latencyQualityMap().NOT_CONNECTED ) }, ) diff --git a/src/pages/Proxies.tsx b/src/pages/Proxies.tsx index 244ad15..1d0095a 100644 --- a/src/pages/Proxies.tsx +++ b/src/pages/Proxies.tsx @@ -198,13 +198,15 @@ export default () => { {(proxyGroup) => { const sortedProxyNames = createMemo(() => - filterProxiesByAvailability( - sortProxiesByOrderingType( - proxyGroup.all ?? [], - proxiesOrderingType(), - ), - hideUnAvailableProxies(), - ), + filterProxiesByAvailability({ + proxyNames: sortProxiesByOrderingType({ + proxyNames: proxyGroup.all ?? [], + orderingType: proxiesOrderingType(), + testUrl: proxyGroup.testUrl || null, + }), + enabled: hideUnAvailableProxies(), + testUrl: proxyGroup.testUrl || null, + }), ) const title = ( @@ -291,6 +293,7 @@ export default () => {
@@ -308,6 +311,8 @@ export default () => { {(proxyName) => ( void selectProxyInGroup(proxyGroup, proxyName) @@ -327,10 +332,12 @@ export default () => { {(proxyProvider) => { const sortedProxyNames = createMemo(() => - sortProxiesByOrderingType( - proxyProvider.proxies.map((i) => i.name) ?? [], - proxiesOrderingType(), - ), + sortProxiesByOrderingType({ + orderingType: proxiesOrderingType(), + proxyNames: + proxyProvider.proxies.map((i) => i.name) ?? [], + testUrl: proxyProvider.testUrl, + }), ) const title = ( @@ -397,7 +404,10 @@ export default () => { - + ) @@ -411,7 +421,13 @@ export default () => { } > - {(proxyName) => } + {(proxyName) => ( + + )} ) diff --git a/src/signals/proxies.ts b/src/signals/proxies.ts index f9b2233..353b31f 100644 --- a/src/signals/proxies.ts +++ b/src/signals/proxies.ts @@ -23,10 +23,7 @@ type ProxyInfo = { name: string udp: boolean tfo: boolean - latencyTestHistory: { - time: string - delay: number - }[] + latencyTestHistory: Record latency: string xudp: boolean type: string @@ -59,31 +56,61 @@ const [proxyProviders, setProxyProviders] = createSignal< >([]) // DO NOT use latency from latency map directly use getLatencyByName instead -const [latencyMap, setLatencyMap] = createSignal>({}) +const [latencyMap, setLatencyMap] = createSignal< + Record | undefined> +>({}) + const [proxyNodeMap, setProxyNodeMap] = createSignal>( {}, ) +type AllTestUrlLatencyInfo = { + allTestUrlLatency: Record + allTestUrlLatencyHistory: Record +} + const getLatencyFromProxy = ( proxy: Pick, - url: string, fallbackDefault = true, -) => { - const extra = proxy.extra?.[url] as Proxy['history'] | undefined - - if (Array.isArray(extra)) { - const delay = extra.at(-1)?.delay - - if (delay) { - return delay +): AllTestUrlLatencyInfo => { + const extra = (proxy.extra || {}) as Record< + string, + { + history?: Proxy['history'] } + > + + if (!Object.keys(extra).length && fallbackDefault) { + const testUrl = urlForLatencyTest() + + const delay = + proxy.history?.at(-1)?.delay ?? latencyQualityMap().NOT_CONNECTED + + const allTestUrlLatency = { [testUrl]: delay } + const allTestUrlLatencyHistory = { [testUrl]: proxy.history } + + return { + allTestUrlLatency, + allTestUrlLatencyHistory, + } as AllTestUrlLatencyInfo } - if (!fallbackDefault) { - return latencyQualityMap().NOT_CONNECTED - } + return Object.keys(extra).reduce( + (acc, testUrl) => { + const data = extra[testUrl] + const delay = + data?.history?.at(-1)?.delay ?? latencyQualityMap().NOT_CONNECTED - return proxy.history?.at(-1)?.delay ?? latencyQualityMap().NOT_CONNECTED + acc.allTestUrlLatency[testUrl] = delay + acc.allTestUrlLatencyHistory[testUrl] = data.history + + return acc + }, + { + allTestUrlLatency: {}, + allTestUrlLatencyHistory: {}, + } as AllTestUrlLatencyInfo, + ) } const setProxiesInfo = ( @@ -93,20 +120,23 @@ const setProxiesInfo = ( const newLatencyMap = { ...latencyMap() } proxies.forEach((proxy) => { - const { udp, xudp, type, now, history, name, tfo, provider = '' } = proxy + const { allTestUrlLatency, allTestUrlLatencyHistory } = + getLatencyFromProxy(proxy) + + const { udp, xudp, type, now, name, tfo, provider = '' } = proxy newProxyNodeMap[proxy.name] = { udp, xudp, type, latency: now, - latencyTestHistory: history, + latencyTestHistory: allTestUrlLatencyHistory, name, tfo, provider, } - newLatencyMap[proxy.name] = getLatencyFromProxy(proxy, urlForLatencyTest()) + newLatencyMap[proxy.name] = allTestUrlLatency }) batch(() => { @@ -122,8 +152,18 @@ export const useProxies = () => { fetchProxiesAPI(), ]) + const proxiesWithTestUrl = Object.values(proxies).map((proxy) => { + if (proxy.all?.length) { + const { testUrl, timeout } = providers?.[proxy.name] || {} + + return { ...proxy, testUrl, timeout } + } else { + return proxy + } + }) + const sortIndex = [...(proxies['GLOBAL'].all ?? []), 'GLOBAL'] - const sortedProxies = Object.values(proxies) + const sortedProxies = Object.values(proxiesWithTestUrl) .filter((proxy) => proxy.all?.length) .sort( (prev, next) => @@ -135,7 +175,7 @@ export const useProxies = () => { ) const allProxies = [ - ...Object.values(proxies), + ...proxiesWithTestUrl, ...sortedProviders.flatMap((provider) => provider.proxies .filter((proxy) => !(proxy.name in proxies)) @@ -177,37 +217,53 @@ export const useProxies = () => { } } - const proxyLatencyTest = async (proxyName: string, provider: string) => { + const proxyLatencyTest = async ( + proxyName: string, + provider: string, + testUrl: string | null, + timmeout: number | null, + ) => { const nodeName = getNowProxyNodeName(proxyName) setProxyLatencyTestingMap(nodeName, async () => { + const finalTestUrl = testUrl || urlForLatencyTest() + const currentNodeLatency = latencyMap()?.[nodeName] || {} try { const { delay } = await proxyLatencyTestAPI( nodeName, provider, - urlForLatencyTest(), - latencyTestTimeoutDuration(), + finalTestUrl, + timmeout ?? latencyTestTimeoutDuration(), ) - setLatencyMap((latencyMap) => ({ - ...latencyMap, - [nodeName]: delay, - })) + currentNodeLatency[finalTestUrl] = delay + + setLatencyMap((latencyMap) => { + return { + ...latencyMap, + [nodeName]: currentNodeLatency, + } + }) } catch { + currentNodeLatency[finalTestUrl] = latencyQualityMap().NOT_CONNECTED setLatencyMap((latencyMap) => ({ ...latencyMap, - [nodeName]: latencyQualityMap().NOT_CONNECTED, + [nodeName]: currentNodeLatency, })) } }) } const proxyGroupLatencyTest = async (proxyGroupName: string) => { + const currentProxyGroups = proxies() setProxyGroupLatencyTestingMap(proxyGroupName, async () => { + const currentProxyGroup = currentProxyGroups.find( + (item) => item.name === proxyGroupName, + ) await proxyGroupLatencyTestAPI( proxyGroupName, - urlForLatencyTest(), - latencyTestTimeoutDuration(), + currentProxyGroup?.testUrl || urlForLatencyTest(), + currentProxyGroup?.timeout ?? latencyTestTimeoutDuration(), ) await fetchProxies() }) @@ -263,8 +319,10 @@ export const useProxies = () => { return node.name } - const getLatencyByName = (name: string) => { - return latencyMap()[getNowProxyNodeName(name)] + const getLatencyByName = (name: string, testUrl: string | null) => { + const finalTestUrl = testUrl || urlForLatencyTest() + + return latencyMap()[getNowProxyNodeName(name)]?.[finalTestUrl] || 0 } const isProxyGroup = (name: string) => { diff --git a/src/types/index.d.ts b/src/types/index.d.ts index af04a4f..5bd61c0 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -23,6 +23,8 @@ export type Proxy = { xudp: boolean tfo: boolean now: string + testUrl?: string + timeout?: number } export type ProxyNode = { @@ -53,6 +55,7 @@ export type ProxyProvider = { name: string proxies: ProxyNode[] testUrl: string + timeout?: number updatedAt: string vehicleType: string }