mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-04 04:05:04 +03:00
feat(ui): show per-inbound live speed (#5261)
* feat(utils): add speedFormat utility and tests * feat(inbounds): add InboundSpeedEntry type * feat(inbounds): add speed column to inbound list * feat(inbounds): show speed in inbound stats modal * feat(inbounds): compute inbound speed from traffic deltas * feat(inbounds): wire inbound speed through page * feat(i18n): add speed translation for all locales * refactor(inbounds): dedupe live-speed UI and harden formatting Extract a shared InboundSpeedTag component and isActiveSpeed guard used by the speed column and stats modal, unify InboundSpeedEntry into a single type, and route speedFormat through sizeFormat. Also guard sizeFormat against non-finite input (no more "NaN PB/s") and clear stale per-inbound speeds when a traffic poll returns no deltas. --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -13,7 +13,8 @@ import {
|
||||
tunnelNetworkLabel,
|
||||
mixedNetworkLabel,
|
||||
} from './helpers';
|
||||
import type { ClientCountEntry, DBInboundRecord } from './types';
|
||||
import { InboundSpeedTag, isActiveSpeed } from './InboundSpeedTag';
|
||||
import type { ClientCountEntry, DBInboundRecord, InboundSpeedEntry } from './types';
|
||||
|
||||
interface InboundStatsModalProps {
|
||||
open: boolean;
|
||||
@@ -21,6 +22,7 @@ interface InboundStatsModalProps {
|
||||
hasActiveNode: boolean;
|
||||
nodesById: Map<number, NodeRecord>;
|
||||
clientCount: Record<number, ClientCountEntry>;
|
||||
inboundSpeed: Record<number, InboundSpeedEntry>;
|
||||
trafficDiff: number;
|
||||
expireDiff: number;
|
||||
onClose: () => void;
|
||||
@@ -32,6 +34,7 @@ export default function InboundStatsModal({
|
||||
hasActiveNode,
|
||||
nodesById,
|
||||
clientCount,
|
||||
inboundSpeed,
|
||||
trafficDiff,
|
||||
expireDiff,
|
||||
onClose,
|
||||
@@ -109,6 +112,16 @@ export default function InboundStatsModal({
|
||||
{record.total > 0 ? SizeFormatter.sizeFormat(record.total) : <InfinityIcon />}
|
||||
</Tag>
|
||||
</div>
|
||||
{(() => {
|
||||
const speed = inboundSpeed[record.id];
|
||||
if (!isActiveSpeed(speed)) return null;
|
||||
return (
|
||||
<div className="stat-row">
|
||||
<span className="stat-label">{t('pages.inbounds.speed')}</span>
|
||||
<InboundSpeedTag speed={speed} />
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
{clientCount[record.id] && (
|
||||
<div className="stat-row">
|
||||
<span className="stat-label">{t('clients')}</span>
|
||||
|
||||
Reference in New Issue
Block a user