fix(web): пинг участника показывается только при наведении

Постоянная подпись в плитке занимала место и была не нужна: задержка теперь
живёт в подсказке индикатора связи («Отличное соединение, 46 мс — задержка до
голосового сервера»), а плитка осталась компактной. Тесты и e2e проверяют
`title`/`aria-label` индикатора вместо видимого текста.
This commit is contained in:
2026-09-20 22:30:24 +03:00
parent 96acc40031
commit cc16c18b13
6 changed files with 29 additions and 50 deletions
+2 -29
View File
@@ -28,7 +28,8 @@ export function VoiceQualityDot({
if (quality === null) {
return null;
}
const label = qualityLabel(t, quality, latencyMs);
// Пинг показываем только в подсказке: в плитке он не занимает место.
const label = `${qualityLabel(t, quality, latencyMs)} — ${t('voice.latencyHint')}`;
return (
<span
role="img"
@@ -42,34 +43,6 @@ export function VoiceQualityDot({
);
}
/**
* Точка качества с видимой подписью: «Отличное соединение, 42 мс» в плитке
* участника. Пинг обновляется раз в секунду вместе с состоянием связи.
*/
export function VoiceQualityBadge({
quality,
latencyMs,
}: {
quality: VoiceQuality | null;
latencyMs?: number | undefined;
}) {
const { t } = useTranslation();
if (quality === null) {
return null;
}
return (
<span
className="inline-flex items-center gap-1.5 text-xs text-fg-muted"
data-testid="voice-quality-label"
data-latency={latencyMs ?? ''}
title={t('voice.latencyHint')}
>
<VoiceQualityDot quality={quality} latencyMs={latencyMs} />
<span className="tabular-nums">{qualityLabel(t, quality, latencyMs)}</span>
</span>
);
}
/**
* Иконка состояния участника: перечёркнутая, когда флаг выключен.
* Подпись обязательна — по ней состояние читают и тесты, и скринридер.
+2 -2
View File
@@ -6,7 +6,7 @@ import { Avatar } from '@/components/ui/Avatar';
import { Menu, MenuGroupLabel, MenuItem } from '@/components/ui/Menu';
import { Card, Button } from '@/components/ui/primitives';
import { RemoteAudio, RemoteVideo } from '@/components/voice/RemoteAudio';
import { VoiceFlags, VoiceQualityBadge } from '@/components/voice/VoiceIndicators';
import { VoiceFlags, VoiceQualityDot } from '@/components/voice/VoiceIndicators';
import { VoiceOccupantMenu } from '@/components/voice/VoiceOccupantMenu';
import { VoiceSoundboard } from '@/components/voice/VoiceSoundboard';
import { VoiceStatsButton } from '@/components/voice/VoiceStatsButton';
@@ -110,7 +110,7 @@ function VoiceTile({
<span className="min-w-0 flex-1 truncate text-sm font-medium" title={occupant.name}>
{occupant.name}
</span>
<VoiceQualityBadge quality={occupant.quality} latencyMs={latencies[occupant.userId]} />
<VoiceQualityDot quality={occupant.quality} latencyMs={latencies[occupant.userId]} />
{occupant.isLocal ? (
<>
{/* Статистика своей публикации: разрешение, fps, кодек, канал (7.14). */}
+1 -1
View File
@@ -346,7 +346,7 @@
}
},
"latency": "{{ms}} ms",
"latencyHint": "Participant ping to the instance",
"latencyHint": "latency to the voice server",
"latencyUnknown": "ping unknown"
},
"friends": {
+1 -1
View File
@@ -346,7 +346,7 @@
}
},
"latency": "{{ms}} мс",
"latencyHint": "Пинг участника до инстанса",
"latencyHint": "задержка до голосового сервера",
"latencyUnknown": "пинг неизвестен"
},
"friends": {