Render latency change immediately

This commit is contained in:
Haishan 2023-02-12 18:43:26 +08:00
parent e0bc53d5e0
commit 759d742b8b
5 changed files with 84 additions and 47 deletions

View file

@ -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
<span className={s0.proxyType} style={{ opacity: now ? 0.6 : 0.2 }}>
{formatProxyType(proxy.type)}
</span>
<ProxyLatency number={latency?.number} color={color} />
<ProxyLatency latency={latency} color={color} />
</div>
</div>
);

View file

@ -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 (
<span className={s0.proxyLatency} style={{ color }}>
{typeof number === 'number' && number !== 0 ? number + ' ms' : ' '}
{text}
</span>
);
}

View file

@ -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)

View file

@ -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<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];
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);

View file

@ -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<string, ProxyItem>;
export type DelayMapping = Record<string, ProxyDelayItem>;