feat(web): второй ряд дашборда и пинг участников в голосовой комнате
- дашборд инстанса: два ряда полукругов с подписями сверху — «Контейнер инстанса» (лимиты профиля) и «Сервер целиком» (вся машина); - голосовая комната: рядом с качеством связи видно пинг участника («Отличное соединение, 42 мс») и он обновляется раз в секунду. Свой RTT измеряется по статистике WebRTC, чужой приходит по data-каналу LiveKit (`glchat.latency`, lossy), поэтому пинг виден для каждого участника; - статистика берётся с любой своей дорожки: в аудио-сессии RTT раньше был недоступен (отчёт читался только с видео); - в «Аудио-видео» появился тумблер «Показывать пинг в голосовой комнате»: выключенный не опрашивает SDK и не публикует своё значение; - тесты: ряд хоста и подписи, пинг в плитке своего и чужого участника, публикация по служебной теме, выключенный пинг не дёргает SDK.
This commit is contained in:
@@ -53,6 +53,11 @@ export interface VoiceConnectionState {
|
||||
channelId: string | null;
|
||||
/** Участники комнаты глазами SDK: речь, качество, дорожки. */
|
||||
participants: VoiceParticipant[];
|
||||
/**
|
||||
* Пинг участников до инстанса, мс: своё значение измеряем по статистике
|
||||
* WebRTC, чужие приходят по data-каналу (AGENT.md 7.14).
|
||||
*/
|
||||
latencies: Record<string, number>;
|
||||
/** Опубликован ли свой микрофон. */
|
||||
micEnabled: boolean;
|
||||
/** Локальное глушение: чужой звук не слышен. */
|
||||
@@ -105,6 +110,7 @@ const defaults = {
|
||||
guildId: null,
|
||||
channelId: null,
|
||||
participants: [] as VoiceParticipant[],
|
||||
latencies: {} as Record<string, number>,
|
||||
micEnabled: true,
|
||||
deafened: false,
|
||||
cameraEnabled: false,
|
||||
@@ -118,6 +124,8 @@ const defaults = {
|
||||
|
||||
/** Активная комната SDK. Живёт вне стора: подписки и таймеры не сериализуются. */
|
||||
let room: VoiceRoomHandle | null = null;
|
||||
/** Остановка публикации пинга: живёт вместе с активной комнатой. */
|
||||
let stopLatencyReporting: (() => void) | null = null;
|
||||
/** Канал, о котором уже знает сервер: только для него шлём PATCH и leave. */
|
||||
let joinedChannelId: string | null = null;
|
||||
/** Номер подключения: ответы устаревших операций не должны менять состояние. */
|
||||
@@ -184,6 +192,9 @@ export const useVoiceConnectionStore = create<VoiceConnectionState>((set, get) =
|
||||
|
||||
/** Отключение от SDK-комнаты без запросов: используется и в leave, и в reset. */
|
||||
const closeRoom = async (): Promise<void> => {
|
||||
stopLatencyReporting?.();
|
||||
stopLatencyReporting = null;
|
||||
set({ latencies: {} });
|
||||
const handle = room;
|
||||
room = null;
|
||||
if (handle === null) {
|
||||
@@ -396,6 +407,20 @@ export const useVoiceConnectionStore = create<VoiceConnectionState>((set, get) =
|
||||
void finishSession();
|
||||
},
|
||||
onSpeakingWhileMuted: warnSpeakingWhileMuted,
|
||||
onLatency: (identity, latency) => {
|
||||
if (mine !== generation) {
|
||||
return;
|
||||
}
|
||||
set((state) => {
|
||||
const latencies = { ...state.latencies };
|
||||
if (latency === null) {
|
||||
delete latencies[identity];
|
||||
} else {
|
||||
latencies[identity] = latency;
|
||||
}
|
||||
return { latencies };
|
||||
});
|
||||
},
|
||||
});
|
||||
if (mine !== generation) {
|
||||
handle.dispose();
|
||||
@@ -410,6 +435,10 @@ export const useVoiceConnectionStore = create<VoiceConnectionState>((set, get) =
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Пинг публикуется раз в секунду и виден всем участникам комнаты.
|
||||
stopLatencyReporting = handle.startLatencyReporting(
|
||||
() => useVoiceStore.getState().latencySharingEnabled,
|
||||
);
|
||||
await handle.setMicrophoneEnabled(preferences.micEnabled && !pushToTalk);
|
||||
await handle.setCameraEnabled(preferences.cameraEnabled);
|
||||
// Качество публикации — из предпочтений клиента.
|
||||
|
||||
Reference in New Issue
Block a user