feat(NetworkMapUnifi): add short speed formatting and country flag display to network edges
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m56s

This commit is contained in:
2026-02-18 13:36:26 +07:00
parent 283ab53b80
commit fdc39060a1
+45 -13
View File
@@ -1,5 +1,6 @@
import { useMemo, useCallback, useState, useRef, useEffect } from 'react';
import { IconRefresh, IconMaximize, IconMinimize } from '@tabler/icons-react';
import { countryToFlag } from './utils/serverUtils.js';
const UNIFI_BG = '#0f172a';
const UNIFI_CARD_BG = '#1e293b';
@@ -64,6 +65,22 @@ function formatMbps(bps) {
return `${mbps.toFixed(1)} Мбит/с`;
}
/** Короткий формат скорости для подписи на ребре: "↓287 ↑170 Мбит/с" */
function formatSpeedShort(downloadBps, uploadBps) {
const fmt = (v) => {
if (v == null || Number.isNaN(v)) return '—';
const mbps = v / 1_000_000;
if (!Number.isFinite(mbps)) return '—';
return mbps >= 10 ? Math.round(mbps) : mbps.toFixed(1);
};
const down = fmt(downloadBps);
const up = fmt(uploadBps);
if (down === '—' && up === '—') return null;
if (down === '—') return `${up} Мбит/с`;
if (up === '—') return `${down} Мбит/с`;
return `${down}${up} Мбит/с`;
}
function speedKey(key1, key2) {
return [String(key1), String(key2)].sort().join(':');
}
@@ -197,7 +214,7 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
const speed = speedMap[sk];
const speedLabel =
speed && (speed.tcpDownloadBps != null || speed.tcpUploadBps != null)
? `${formatMbps(speed.tcpDownloadBps)}${formatMbps(speed.tcpUploadBps)}`
? formatSpeedShort(speed.tcpDownloadBps, speed.tcpUploadBps)
: null;
return {
edgeKey: `${from}-${to}-${c.tunnelType || ''}`,
@@ -299,9 +316,9 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
<g>
{(() => {
const used = [];
const LABEL_W = 64;
const LABEL_H = 20;
const LABEL_GAP = 4;
const LABEL_W = 100;
const LABEL_H = 18;
const LABEL_GAP = 6;
return edgesWithPing.map((e, idx) => {
const dimFactor =
hoveredEdgeKey && hoveredEdgeKey !== e.edgeKey ? 0.25 : 1;
@@ -331,39 +348,40 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
const labels = [e.pingLabel, e.speedLabel].filter(Boolean);
if (labels.length === 0) return null;
let level = 0;
const totalH = labels.length * LABEL_H + (labels.length - 1) * LABEL_GAP;
while (
level < 10 &&
used.some(
(p) =>
Math.abs(p.x - x) < LABEL_W &&
Math.abs(p.y - (y + level * (LABEL_H + LABEL_GAP))) < (labels.length * (LABEL_H + LABEL_GAP))
Math.abs(p.y - (y + level * (totalH + 8))) < totalH + 16
)
) {
level += 1;
}
y = y + level * (LABEL_H + LABEL_GAP);
y = y + level * (totalH + 8);
used.push({ x, y });
const pad = 6;
const pad = 8;
return (
<g>
{labels.map((text, i) => {
const yy = y + i * (LABEL_H + LABEL_GAP);
const w = Math.min(text.length * 5.5 + pad * 2, 140);
const h = 18;
const yy = y + i * (LABEL_H + LABEL_GAP) + LABEL_H / 2;
const w = Math.max(LABEL_W, Math.min(text.length * 6 + pad * 2, 160));
const h = LABEL_H + 4;
return (
<g key={i}>
<rect x={x - w / 2} y={yy - h / 2} width={w} height={h} rx={4} fill="rgba(15,23,42,0.9)" stroke={UNIFI_GREEN} strokeWidth={1} />
<rect x={x - w / 2} y={yy - h / 2} width={w} height={h} rx={4} fill="rgba(15,23,42,0.92)" stroke={UNIFI_GREEN} strokeWidth={1} />
<text
x={x}
y={yy}
textAnchor="middle"
dominantBaseline="middle"
fill={UNIFI_GREEN}
fontSize={i === 0 ? 11 : 10}
fontSize={i === 0 ? 12 : 11}
fontWeight={600}
>
{text.length > 24 ? text.slice(0, 22) + '…' : text}
{text}
</text>
</g>
);
@@ -400,6 +418,20 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
strokeWidth={1}
/>
<g transform={`translate(${NODE_WIDTH / 2}, ${NODE_HEIGHT / 2})`}>
{/* Флаг страны в левом верхнем углу карточки */}
{s.country && (
<text
x={-NODE_WIDTH / 2 + 14}
y={-NODE_HEIGHT / 2 + 14}
textAnchor="middle"
dominantBaseline="middle"
fontSize={14}
style={{ pointerEvents: 'none' }}
title={s.country}
>
{countryToFlag(s.country)}
</text>
)}
{/* Иконка сервера в стиле UNIFI: прямоугольник с «портами» */}
<g transform="translate(-10, -16)">
<rect x={2} y={0} width={16} height={12} rx={2} fill="none" stroke={UNIFI_GREEN} strokeWidth={1.5} />