Render latency change immediately
This commit is contained in:
parent
e0bc53d5e0
commit
759d742b8b
5 changed files with 84 additions and 47 deletions
|
@ -2,7 +2,7 @@ import { TooltipPopup, useTooltip } from '@reach/tooltip';
|
||||||
import cx from 'clsx';
|
import cx from 'clsx';
|
||||||
import * as React from 'react';
|
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 { getDelay, getProxies, NonProxyTypes } from '../../store/proxies';
|
||||||
import { connect } from '../StateProvider';
|
import { connect } from '../StateProvider';
|
||||||
|
@ -22,7 +22,9 @@ const colorMap = {
|
||||||
na: '#909399',
|
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) {
|
if (number === 0) {
|
||||||
return colorMap.na;
|
return colorMap.na;
|
||||||
} else if (number < 200) {
|
} else if (number < 200) {
|
||||||
|
@ -35,7 +37,7 @@ function getLabelColor({ number }: { number?: number } = {}) {
|
||||||
return colorMap.na;
|
return colorMap.na;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProxyDotStyle(latency: { number?: number }, proxyType: string) {
|
function getProxyDotStyle(latency: ProxyDelayItem, proxyType: string) {
|
||||||
if (NonProxyTypes.indexOf(proxyType) > -1) {
|
if (NonProxyTypes.indexOf(proxyType) > -1) {
|
||||||
return { border: '1px dotted #777' };
|
return { border: '1px dotted #777' };
|
||||||
}
|
}
|
||||||
|
@ -47,7 +49,7 @@ type ProxyProps = {
|
||||||
name: string;
|
name: string;
|
||||||
now?: boolean;
|
now?: boolean;
|
||||||
proxy: any;
|
proxy: any;
|
||||||
latency?: { number?: number };
|
latency?: ProxyDelayItem;
|
||||||
isSelectable?: boolean;
|
isSelectable?: boolean;
|
||||||
onClick?: (proxyName: string) => unknown;
|
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 style = useMemo(() => getProxyDotStyle(latency, proxy.type), [latency, proxy]);
|
||||||
const title = useMemo(() => {
|
const title = useMemo(() => {
|
||||||
let ret = name;
|
let ret = name;
|
||||||
if (latency && typeof latency.number === 'number') {
|
if (latency && latency.kind === 'Result' && typeof latency.number === 'number') {
|
||||||
ret += ' ' + latency.number + ' ms';
|
ret += ' ' + latency.number + ' ms';
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -152,7 +154,7 @@ function ProxyImpl({ now, name, proxy, latency, isSelectable, onClick }: ProxyPr
|
||||||
<span className={s0.proxyType} style={{ opacity: now ? 0.6 : 0.2 }}>
|
<span className={s0.proxyType} style={{ opacity: now ? 0.6 : 0.2 }}>
|
||||||
{formatProxyType(proxy.type)}
|
{formatProxyType(proxy.type)}
|
||||||
</span>
|
</span>
|
||||||
<ProxyLatency number={latency?.number} color={color} />
|
<ProxyLatency latency={latency} color={color} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { ProxyDelayItem } from '$src/store/types';
|
||||||
|
|
||||||
import s0 from './ProxyLatency.module.scss';
|
import s0 from './ProxyLatency.module.scss';
|
||||||
|
|
||||||
type ProxyLatencyProps = {
|
type ProxyLatencyProps = {
|
||||||
number?: number;
|
latency: ProxyDelayItem | undefined;
|
||||||
color: string;
|
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 (
|
return (
|
||||||
<span className={s0.proxyLatency} style={{ color }}>
|
<span className={s0.proxyLatency} style={{ color }}>
|
||||||
{typeof number === 'number' && number !== 0 ? number + ' ms' : ' '}
|
{text}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,7 @@ import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
const LngBackend = {
|
const LngBackend = {
|
||||||
type: 'backend' as const,
|
type: 'backend' as const,
|
||||||
read: (
|
read: (lng: string, _namespace: string, callback: ReadCallback) => {
|
||||||
lng: string,
|
|
||||||
_namespace: string,
|
|
||||||
callback: ReadCallback,
|
|
||||||
) => {
|
|
||||||
let p: PromiseLike<{ data: any }>;
|
let p: PromiseLike<{ data: any }>;
|
||||||
switch (lng) {
|
switch (lng) {
|
||||||
case 'zh':
|
case 'zh':
|
||||||
|
@ -22,12 +18,15 @@ const LngBackend = {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (p) {
|
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 {
|
} 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
|
i18next
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
|
|
|
@ -59,7 +59,7 @@ function mapLatency(names: string[], getProxy: (name: string) => { history: Late
|
||||||
const history = p.history;
|
const history = p.history;
|
||||||
const h = history[history.length - 1];
|
const h = history[history.length - 1];
|
||||||
if (h && typeof h.delay === 'number') {
|
if (h && typeof h.delay === 'number') {
|
||||||
result[name] = { number: h.delay };
|
result[name] = { kind: 'Result', number: h.delay };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -268,18 +268,35 @@ export function switchProxy(apiConfig: ClashAPIConfig, groupName: string, itemNa
|
||||||
|
|
||||||
function requestDelayForProxyOnce(apiConfig: ClashAPIConfig, name: string) {
|
function requestDelayForProxyOnce(apiConfig: ClashAPIConfig, name: string) {
|
||||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||||
const latencyTestUrl = getLatencyTestUrl(getState());
|
dispatch('set latency state to testing in progress', (s) => {
|
||||||
const res = await proxiesAPI.requestDelayForProxy(apiConfig, name, latencyTestUrl);
|
s.proxies.delay = { ...getDelay(getState()), [name]: { kind: 'Testing' } };
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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[]) {
|
export function requestDelayForProxies(apiConfig: ClashAPIConfig, names: string[]) {
|
||||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||||
const proxies = getProxies(getState());
|
const proxies = getProxies(getState());
|
||||||
const latencyTestUrl = getLatencyTestUrl(getState());
|
|
||||||
|
|
||||||
const proxyDedupMap = new Map<string, boolean>();
|
const proxyDedupMap = new Map<string, boolean>();
|
||||||
const providerDedupMap = new Map<string, boolean>();
|
const providerDedupMap = new Map<string, boolean>();
|
||||||
|
|
||||||
const works = names.map((name) => {
|
const works: Array<Promise<void>> = [];
|
||||||
|
|
||||||
|
names.forEach((name) => {
|
||||||
const p = proxies[name];
|
const p = proxies[name];
|
||||||
if (!p.__provider) {
|
if (!p.__provider) {
|
||||||
if (proxyDedupMap.get(name)) {
|
if (!proxyDedupMap.get(name)) {
|
||||||
return undefined;
|
|
||||||
} else {
|
|
||||||
proxyDedupMap.set(name, true);
|
proxyDedupMap.set(name, true);
|
||||||
return proxiesAPI.requestDelayForProxy(apiConfig, name, latencyTestUrl);
|
dispatch(requestDelayForProxyOnce(apiConfig, name));
|
||||||
}
|
}
|
||||||
} else if (p.__provider) {
|
} else if (p.__provider) {
|
||||||
// this one is from a proxy provider
|
if (!proxyDedupMap.get(name)) {
|
||||||
if (providerDedupMap.get(p.__provider)) {
|
proxyDedupMap.set(name, true);
|
||||||
return undefined;
|
dispatch('set latency state to testing in progress', (s) => {
|
||||||
} else {
|
s.proxies.delay = { ...getDelay(getState()), [name]: { kind: 'Testing' } };
|
||||||
providerDedupMap.set(p.__provider, true);
|
});
|
||||||
return healthcheckProviderByNameInternal(apiConfig, p.__provider);
|
}
|
||||||
|
// 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);
|
await Promise.all(works);
|
||||||
|
|
|
@ -40,9 +40,11 @@ export type ProxyItem = {
|
||||||
__provider?: string;
|
__provider?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProxyDelayItem = {
|
export type ProxyDelayItem =
|
||||||
number?: number;
|
| { kind: 'Result'; number: number }
|
||||||
};
|
| { kind: 'Testing' }
|
||||||
|
| { kind: 'Error'; message: string }
|
||||||
|
| { kind: 'None' };
|
||||||
|
|
||||||
export type ProxiesMapping = Record<string, ProxyItem>;
|
export type ProxiesMapping = Record<string, ProxyItem>;
|
||||||
export type DelayMapping = Record<string, ProxyDelayItem>;
|
export type DelayMapping = Record<string, ProxyDelayItem>;
|
||||||
|
|
Loading…
Reference in a new issue