diff --git a/src/components/proxies/Proxy.tsx b/src/components/proxies/Proxy.tsx
index 47a3d54..4b63921 100644
--- a/src/components/proxies/Proxy.tsx
+++ b/src/components/proxies/Proxy.tsx
@@ -2,7 +2,7 @@ import { TooltipPopup, useTooltip } from '@reach/tooltip';
import cx from 'clsx';
import * as React from 'react';
-import { State } from '$src/store/types';
+import { ProxyDelayItem, State } from '$src/store/types';
import { getDelay, getProxies, NonProxyTypes } from '../../store/proxies';
import { connect } from '../StateProvider';
@@ -22,7 +22,9 @@ const colorMap = {
na: '#909399',
};
-function getLabelColor({ number }: { number?: number } = {}) {
+function getLabelColor(latency: ProxyDelayItem) {
+ if (!latency || latency.kind !== 'Result') return colorMap.na;
+ const number = latency.number;
if (number === 0) {
return colorMap.na;
} else if (number < 200) {
@@ -35,7 +37,7 @@ function getLabelColor({ number }: { number?: number } = {}) {
return colorMap.na;
}
-function getProxyDotStyle(latency: { number?: number }, proxyType: string) {
+function getProxyDotStyle(latency: ProxyDelayItem, proxyType: string) {
if (NonProxyTypes.indexOf(proxyType) > -1) {
return { border: '1px dotted #777' };
}
@@ -47,7 +49,7 @@ type ProxyProps = {
name: string;
now?: boolean;
proxy: any;
- latency?: { number?: number };
+ latency?: ProxyDelayItem;
isSelectable?: boolean;
onClick?: (proxyName: string) => unknown;
};
@@ -56,7 +58,7 @@ function ProxySmallImpl({ now, name, proxy, latency, isSelectable, onClick }: Pr
const style = useMemo(() => getProxyDotStyle(latency, proxy.type), [latency, proxy]);
const title = useMemo(() => {
let ret = name;
- if (latency && typeof latency.number === 'number') {
+ if (latency && latency.kind === 'Result' && typeof latency.number === 'number') {
ret += ' ' + latency.number + ' ms';
}
return ret;
@@ -152,7 +154,7 @@ function ProxyImpl({ now, name, proxy, latency, isSelectable, onClick }: ProxyPr
{formatProxyType(proxy.type)}
-
+
);
diff --git a/src/components/proxies/ProxyLatency.tsx b/src/components/proxies/ProxyLatency.tsx
index 29036d5..7ba2604 100644
--- a/src/components/proxies/ProxyLatency.tsx
+++ b/src/components/proxies/ProxyLatency.tsx
@@ -1,16 +1,32 @@
import * as React from 'react';
+import { ProxyDelayItem } from '$src/store/types';
+
import s0 from './ProxyLatency.module.scss';
type ProxyLatencyProps = {
- number?: number;
+ latency: ProxyDelayItem | undefined;
color: string;
};
-export function ProxyLatency({ number, color }: ProxyLatencyProps) {
+export function ProxyLatency({ latency, color }: ProxyLatencyProps) {
+ let text = ' ';
+ if (latency) {
+ switch (latency.kind) {
+ case 'Error':
+ case 'Testing':
+ text = '- ms';
+ break;
+ case 'Result':
+ text = (latency.number !== 0 ? latency.number : '-') + ' ms';
+ break;
+ default:
+ break;
+ }
+ }
return (
- {typeof number === 'number' && number !== 0 ? number + ' ms' : ' '}
+ {text}
);
}
diff --git a/src/misc/i18n.ts b/src/misc/i18n.ts
index 13cedd4..e8515c4 100644
--- a/src/misc/i18n.ts
+++ b/src/misc/i18n.ts
@@ -5,11 +5,7 @@ import { initReactI18next } from 'react-i18next';
const LngBackend = {
type: 'backend' as const,
- read: (
- lng: string,
- _namespace: string,
- callback: ReadCallback,
- ) => {
+ read: (lng: string, _namespace: string, callback: ReadCallback) => {
let p: PromiseLike<{ data: any }>;
switch (lng) {
case 'zh':
@@ -22,12 +18,15 @@ const LngBackend = {
break;
}
if (p) {
- p.then(d => callback(null, d.data), err => callback(err, null));
+ p.then(
+ (d) => callback(null, d.data),
+ (err) => callback(err, null)
+ );
} else {
- callback(new Error(`unable to load translation file for language ${lng}`), null)
+ callback(new Error(`unable to load translation file for language ${lng}`), null);
}
- }
-}
+ },
+};
i18next
.use(initReactI18next)
diff --git a/src/store/proxies.tsx b/src/store/proxies.tsx
index 5f4c0aa..0022816 100644
--- a/src/store/proxies.tsx
+++ b/src/store/proxies.tsx
@@ -59,7 +59,7 @@ function mapLatency(names: string[], getProxy: (name: string) => { history: Late
const history = p.history;
const h = history[history.length - 1];
if (h && typeof h.delay === 'number') {
- result[name] = { number: h.delay };
+ result[name] = { kind: 'Result', number: h.delay };
}
}
return result;
@@ -268,18 +268,35 @@ export function switchProxy(apiConfig: ClashAPIConfig, groupName: string, itemNa
function requestDelayForProxyOnce(apiConfig: ClashAPIConfig, name: string) {
return async (dispatch: DispatchFn, getState: GetStateFn) => {
- const latencyTestUrl = getLatencyTestUrl(getState());
- const res = await proxiesAPI.requestDelayForProxy(apiConfig, name, latencyTestUrl);
- let error = '';
- if (res.ok === false) {
- error = res.statusText;
- }
- const { delay } = await res.json();
- const delayNext = { ...getDelay(getState()), [name]: { error, number: delay } };
-
- dispatch('requestDelayForProxyOnce', (s) => {
- s.proxies.delay = delayNext;
+ dispatch('set latency state to testing in progress', (s) => {
+ s.proxies.delay = { ...getDelay(getState()), [name]: { kind: 'Testing' } };
});
+
+ const latencyTestUrl = getLatencyTestUrl(getState());
+
+ try {
+ const res = await proxiesAPI.requestDelayForProxy(apiConfig, name, latencyTestUrl);
+ if (res.ok) {
+ const { delay } = await res.json();
+ dispatch('set latency result', (s) => {
+ s.proxies.delay = { ...getDelay(getState()), [name]: { kind: 'Result', number: delay } };
+ });
+ } else {
+ dispatch('set latency testing error', (s) => {
+ s.proxies.delay = {
+ ...getDelay(getState()),
+ [name]: { kind: 'Error', message: res.statusText },
+ };
+ });
+ }
+ } catch (err) {
+ dispatch('set latency testing networkish error', (s) => {
+ s.proxies.delay = {
+ ...getDelay(getState()),
+ [name]: { kind: 'Error', message: err.message || err.type },
+ };
+ });
+ }
};
}
@@ -292,30 +309,31 @@ export function requestDelayForProxy(apiConfig: ClashAPIConfig, name: string) {
export function requestDelayForProxies(apiConfig: ClashAPIConfig, names: string[]) {
return async (dispatch: DispatchFn, getState: GetStateFn) => {
const proxies = getProxies(getState());
- const latencyTestUrl = getLatencyTestUrl(getState());
const proxyDedupMap = new Map();
const providerDedupMap = new Map();
- const works = names.map((name) => {
+ const works: Array> = [];
+
+ names.forEach((name) => {
const p = proxies[name];
if (!p.__provider) {
- if (proxyDedupMap.get(name)) {
- return undefined;
- } else {
+ if (!proxyDedupMap.get(name)) {
proxyDedupMap.set(name, true);
- return proxiesAPI.requestDelayForProxy(apiConfig, name, latencyTestUrl);
+ dispatch(requestDelayForProxyOnce(apiConfig, name));
}
} else if (p.__provider) {
- // this one is from a proxy provider
- if (providerDedupMap.get(p.__provider)) {
- return undefined;
- } else {
- providerDedupMap.set(p.__provider, true);
- return healthcheckProviderByNameInternal(apiConfig, p.__provider);
+ if (!proxyDedupMap.get(name)) {
+ proxyDedupMap.set(name, true);
+ dispatch('set latency state to testing in progress', (s) => {
+ s.proxies.delay = { ...getDelay(getState()), [name]: { kind: 'Testing' } };
+ });
+ }
+ // this one is from a proxy provider
+ if (!providerDedupMap.get(p.__provider)) {
+ providerDedupMap.set(p.__provider, true);
+ works.push(healthcheckProviderByNameInternal(apiConfig, p.__provider));
}
- } else {
- return undefined;
}
});
await Promise.all(works);
diff --git a/src/store/types.ts b/src/store/types.ts
index b0b457e..867e437 100644
--- a/src/store/types.ts
+++ b/src/store/types.ts
@@ -40,9 +40,11 @@ export type ProxyItem = {
__provider?: string;
};
-export type ProxyDelayItem = {
- number?: number;
-};
+export type ProxyDelayItem =
+ | { kind: 'Result'; number: number }
+ | { kind: 'Testing' }
+ | { kind: 'Error'; message: string }
+ | { kind: 'None' };
export type ProxiesMapping = Record;
export type DelayMapping = Record;