Expose outbound traffic totals and simplify Direct chart breakdown
This commit is contained in:
@@ -27,7 +27,7 @@ function chartTime(value: string) {
|
||||
|
||||
type ChartSample = TrafficSample | OutboundTrafficSample;
|
||||
type ChartValueKey = 'gateway' | 'proxy' | 'directIpv4' | 'unknown';
|
||||
type ChartYKey = 'gatewayY' | 'proxyY' | 'directIpv4Y' | 'unknownY';
|
||||
type ChartYKey = 'gatewayY' | 'proxyY' | 'unknownY';
|
||||
|
||||
interface ChartPoint {
|
||||
sample: ChartSample;
|
||||
@@ -38,7 +38,6 @@ interface ChartPoint {
|
||||
unknown: bigint;
|
||||
gatewayY: number;
|
||||
proxyY: number;
|
||||
directIpv4Y: number;
|
||||
unknownY: number;
|
||||
}
|
||||
|
||||
@@ -68,13 +67,11 @@ function trafficPathAnimationSource(points: ChartPoint[], previousPoints: ChartP
|
||||
x: source.x,
|
||||
gatewayY: source.gatewayY,
|
||||
proxyY: source.proxyY,
|
||||
directIpv4Y: source.directIpv4Y,
|
||||
unknownY: source.unknownY,
|
||||
} : {
|
||||
...point,
|
||||
gatewayY: 100,
|
||||
proxyY: 100,
|
||||
directIpv4Y: 100,
|
||||
unknownY: 100,
|
||||
};
|
||||
});
|
||||
@@ -133,7 +130,7 @@ export function TrafficChart({
|
||||
return {
|
||||
sample,
|
||||
gateway: byteString(outbound.vpnBytes),
|
||||
proxy: byteString(outbound.directTrackedBytes),
|
||||
proxy: byteString(outbound.directTrackedBytes) + byteString(outbound.directIpv4Bytes),
|
||||
directIpv4: byteString(outbound.directIpv4Bytes),
|
||||
unknown: byteString(outbound.unknownBytes),
|
||||
};
|
||||
@@ -148,8 +145,8 @@ export function TrafficChart({
|
||||
unknown: 0n,
|
||||
};
|
||||
});
|
||||
const max = trafficSeriesMax(values.map(({ gateway, proxy, directIpv4, unknown }) => ({
|
||||
gateway, proxy, directIpv4, unknown,
|
||||
const max = trafficSeriesMax(values.map(({ gateway, proxy, unknown }) => ({
|
||||
gateway, proxy, directIpv4: 0n, unknown,
|
||||
})));
|
||||
const mid = trafficAxisMid(max, scale);
|
||||
const firstSlot = capacity - visibleSamples.length;
|
||||
@@ -163,7 +160,6 @@ export function TrafficChart({
|
||||
unknown,
|
||||
gatewayY: trafficChartY(trafficScaleRatio(gateway, max, scale)),
|
||||
proxyY: trafficChartY(trafficScaleRatio(proxy, max, scale)),
|
||||
directIpv4Y: trafficChartY(trafficScaleRatio(directIpv4, max, scale)),
|
||||
unknownY: trafficChartY(trafficScaleRatio(unknown, max, scale)),
|
||||
};
|
||||
});
|
||||
@@ -177,7 +173,6 @@ export function TrafficChart({
|
||||
? [
|
||||
{ valueKey: 'gateway', yKey: 'gatewayY' },
|
||||
{ valueKey: 'proxy', yKey: 'proxyY' },
|
||||
{ valueKey: 'directIpv4', yKey: 'directIpv4Y' },
|
||||
{ valueKey: 'unknown', yKey: 'unknownY' },
|
||||
]
|
||||
: [
|
||||
@@ -230,11 +225,13 @@ export function TrafficChart({
|
||||
<span className={routeLabel === 'Gateway' ? 'is-gateway' : 'is-direct'}>{routeLabel} {formatByteString(hovered.sample.gatewayBytes)}</span>
|
||||
{byteString(hovered.sample.proxyBytes) > 0n && <span className="is-proxy">Proxy {formatByteString(hovered.sample.proxyBytes)}</span>}
|
||||
</> : series === 'outbound' ? <>
|
||||
{hovered.gateway > 0n && <strong className="is-vpn">VPN · sing-box {formatByteString(hovered.gateway)}</strong>}
|
||||
{hovered.proxy > 0n && <span className="is-direct-tracked">Direct · sing-box {formatByteString(hovered.proxy)}</span>}
|
||||
{hovered.directIpv4 > 0n && <span className="is-direct-ipv4">Direct · IPv4 {formatByteString(hovered.directIpv4)}</span>}
|
||||
<strong className="is-total">Учтено ≈ {formatByteString(hovered.gateway + hovered.proxy + hovered.unknown)}</strong>
|
||||
{hovered.gateway > 0n && <span className="is-vpn">VPN {formatByteString(hovered.gateway)}</span>}
|
||||
{hovered.proxy > 0n && <span className="is-direct-total">Direct ≈ {formatByteString(hovered.proxy)}</span>}
|
||||
{hovered.proxy - hovered.directIpv4 > 0n && <span className="is-direct-detail">через sing-box {formatByteString(hovered.proxy - hovered.directIpv4)}</span>}
|
||||
{hovered.directIpv4 > 0n && <span className="is-direct-detail">мимо sing-box · IPv4 {formatByteString(hovered.directIpv4)}</span>}
|
||||
{hovered.unknown > 0n && <span className="is-unknown">Неизвестно · sing-box {formatByteString(hovered.unknown)}</span>}
|
||||
<span className="is-interval">Разные уровни учёта не складываются</span>
|
||||
<span className="is-interval">Оценка за 15-секундный интервал</span>
|
||||
</> : <>
|
||||
<strong className="is-total">Всего {formatByteString(hovered.gateway + hovered.proxy)}</strong>
|
||||
<span className={routeLabel === 'Gateway' ? 'is-gateway' : 'is-direct'}>{routeLabel} {formatByteString(hovered.gateway)}</span>
|
||||
@@ -269,19 +266,19 @@ export function TrafficChart({
|
||||
<line x1="0" x2="100" y1="100" y2="100" />
|
||||
</g>}
|
||||
<g className="client-device-traffic-lines">
|
||||
{visibleLines.map((line) => previous.length > 0 && <path key={`old-${line.valueKey}`} className={line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-tracked' : series === 'speed' ? 'is-upload' : 'is-proxy' : line.valueKey === 'directIpv4' ? 'is-direct-ipv4' : 'is-unknown'} d={smoothTrafficPath(previous, line.yKey)}>
|
||||
{visibleLines.map((line) => previous.length > 0 && <path key={`old-${line.valueKey}`} className={line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-total' : series === 'speed' ? 'is-upload' : 'is-proxy' : 'is-unknown'} d={smoothTrafficPath(previous, line.yKey)}>
|
||||
{animatePaths && <animate key={`${line.valueKey}-${motionKey}`} attributeName="d" from={smoothTrafficPath(previousMotionFrom, line.yKey)} to={smoothTrafficPath(previous, line.yKey)} dur="520ms" calcMode="spline" keyTimes="0;1" keySplines="0.16 1 0.3 1" fill="freeze" />}
|
||||
</path>)}
|
||||
{visibleLines.map((line) => penultimate && newest && <path key={`new-${line.valueKey}`} className={line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-tracked' : series === 'speed' ? 'is-upload' : 'is-proxy' : line.valueKey === 'directIpv4' ? 'is-direct-ipv4' : 'is-unknown'} d={smoothTrafficPath([penultimate, newest], line.yKey)}>
|
||||
{visibleLines.map((line) => penultimate && newest && <path key={`new-${line.valueKey}`} className={line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-total' : series === 'speed' ? 'is-upload' : 'is-proxy' : 'is-unknown'} d={smoothTrafficPath([penultimate, newest], line.yKey)}>
|
||||
{animatePaths && <animate key={`${line.valueKey}-new-${motionKey}`} attributeName="d" from={smoothTrafficPath(newestMotionFrom, line.yKey)} to={smoothTrafficPath([penultimate, newest], line.yKey)} dur="520ms" calcMode="spline" keyTimes="0;1" keySplines="0.16 1 0.3 1" fill="freeze" />}
|
||||
</path>)}
|
||||
{visibleLines.map((line) => !penultimate && newest && <line key={`point-${line.valueKey}`} className={`${line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-tracked' : series === 'speed' ? 'is-upload' : 'is-proxy' : line.valueKey === 'directIpv4' ? 'is-direct-ipv4' : 'is-unknown'} is-point`} x1={newest.x} x2={newest.x} y1={newest[line.yKey]} y2={newest[line.yKey]}>
|
||||
{visibleLines.map((line) => !penultimate && newest && <line key={`point-${line.valueKey}`} className={`${line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-total' : series === 'speed' ? 'is-upload' : 'is-proxy' : 'is-unknown'} is-point`} x1={newest.x} x2={newest.x} y1={newest[line.yKey]} y2={newest[line.yKey]}>
|
||||
{animatePaths && <animate key={`${line.valueKey}-point-${motionKey}`} attributeName="opacity" from="0" to="1" dur="220ms" fill="freeze" />}
|
||||
</line>)}
|
||||
</g>
|
||||
{hovered && <g className="client-device-traffic-cursor">
|
||||
<line className="is-guide" x1={hovered.x} x2={hovered.x} y1={TRAFFIC_CHART_HEADROOM} y2="100" />
|
||||
{visibleLines.map((line) => hovered[line.valueKey] > 0n && <line key={line.valueKey} className={`is-point ${line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-tracked' : series === 'speed' ? 'is-upload' : 'is-proxy' : line.valueKey === 'directIpv4' ? 'is-direct-ipv4' : 'is-unknown'}`} x1={hovered.x} x2={hovered.x} y1={hovered[line.yKey]} y2={hovered[line.yKey]} />)}
|
||||
{visibleLines.map((line) => hovered[line.valueKey] > 0n && <line key={line.valueKey} className={`is-point ${line.valueKey === 'gateway' ? series === 'outbound' ? 'is-vpn' : series === 'speed' ? 'is-download' : 'is-gateway' : line.valueKey === 'proxy' ? series === 'outbound' ? 'is-direct-total' : series === 'speed' ? 'is-upload' : 'is-proxy' : 'is-unknown'}`} x1={hovered.x} x2={hovered.x} y1={hovered[line.yKey]} y2={hovered[line.yKey]} />)}
|
||||
</g>}
|
||||
</svg>
|
||||
</span>
|
||||
@@ -289,8 +286,7 @@ export function TrafficChart({
|
||||
<time dateTime={visibleSamples[0].observedAt}>{chartTime(visibleSamples[0].observedAt)}</time>
|
||||
{series === 'outbound' ? <span className="client-device-traffic-legend">
|
||||
<span className="is-vpn">VPN</span>
|
||||
<span className="is-direct-tracked">Direct · sing-box</span>
|
||||
<span className="is-direct-ipv4">Direct · IPv4</span>
|
||||
<span className="is-direct-total">Direct ≈</span>
|
||||
<span className="is-unknown">?</span>
|
||||
</span> : <span>{series === 'speed' ? '↓ / ↑' : 'Вход'}</span>}
|
||||
<time dateTime={visibleSamples[visibleSamples.length - 1].observedAt}>{chartTime(visibleSamples[visibleSamples.length - 1].observedAt)}</time>
|
||||
|
||||
Reference in New Issue
Block a user