Enhance SessionUsersPanel and UserCamera components by implementing local speaking state management with debounce for efficient updates. Refactor grid column calculation and participant filtering for improved performance. Adjust logging in useVoiceActivity and useWebRTC hooks to reduce console noise.

This commit is contained in:
2025-10-30 18:14:44 +05:00
parent af4ca0637a
commit 1cacd070eb
4 changed files with 94 additions and 83 deletions
+55 -34
View File
@@ -1,9 +1,9 @@
import { useMemo } from "react";
import UserCamera from "./ui/UserCamera";
import UserDevicesControls from "./ui/UserDevicesControls";
import DraggableContainer from "./DraggableContainer";
import { useWebRTC } from "../hooks/useWebRTC";
import clsx from "clsx";
import { useEffect, useRef, useState } from "react";
interface SessionUsersPanelProps {
roomId: string;
@@ -28,47 +28,62 @@ function SessionUsersPanel({
const hasLocalStream = localStream !== null;
// Callback для отправки состояния speaking
// State для хранения состояния speaking
const [localSpeaking, setLocalSpeaking] = useState<boolean>(false);
const lastSentSpeakingRef = useRef<boolean>(false);
const speakingStateTimeoutRef = useRef<NodeJS.Timeout | null>(null);
// Callback для получения изменений состояния speaking от UserCamera
const handleSpeakingChange = (isSpeaking: boolean) => {
updateSpeakingState?.(isSpeaking);
setLocalSpeaking(isSpeaking);
};
// Вычисляем количество камер для grid - мемоизируем для избежания лишних вычислений
const activeCamerasCount = useMemo(
() =>
(localStream ? 1 : 0) +
participants.filter(
(p) => p.stream != null && p.stream.getTracks().length > 0
).length,
[localStream, participants]
);
// useEffect для throttle и отправки состояния speaking через socket
useEffect(() => {
// Отправляем только если состояние действительно изменилось
if (lastSentSpeakingRef.current === localSpeaking) {
return;
}
// Очищаем предыдущий таймер
if (speakingStateTimeoutRef.current) {
clearTimeout(speakingStateTimeoutRef.current);
}
// Отправляем состояние с задержкой (debounce 300ms)
speakingStateTimeoutRef.current = setTimeout(() => {
lastSentSpeakingRef.current = localSpeaking;
updateSpeakingState?.(localSpeaking);
speakingStateTimeoutRef.current = null;
}, 300);
return () => {
if (speakingStateTimeoutRef.current) {
clearTimeout(speakingStateTimeoutRef.current);
}
};
}, [localSpeaking, updateSpeakingState]);
// Вычисляем количество камер для grid
const activeCamerasCount =
(localStream ? 8 : 0) +
participants.filter(
(p) => p.stream != null && p.stream.getTracks().length > 0
).length;
// Определяем количество колонок в зависимости от количества камер
// 1-2 камеры: 1 колонка (друг под другом), 3-4: 2 колонки, 5-9: 3 колонки, 10-16: 4 колонки, 17-25: 5 колонок
const gridColumns = useMemo(() => {
if (activeCamerasCount <= 2) return 1;
if (activeCamerasCount <= 4) return 2;
if (activeCamerasCount <= 9) return 3;
if (activeCamerasCount <= 16) return 4;
const getGridColumns = (count: number): number => {
if (count <= 2) return 1;
if (count <= 4) return 2;
if (count <= 9) return 3;
if (count <= 16) return 4;
return 5;
}, [activeCamerasCount]);
};
const gridColumns = getGridColumns(activeCamerasCount);
// Вычисляем количество рядов для правильного расчета высоты
const gridRows = useMemo(
() => Math.ceil(activeCamerasCount / gridColumns),
[activeCamerasCount, gridColumns]
);
// Фильтруем участников с активными потоками - мемоизируем для избежания лишних фильтраций
const activeParticipants = useMemo(
() =>
participants.filter(
(participant) =>
participant.stream != null &&
participant.stream.getTracks().length > 0
),
[participants]
);
const gridRows = Math.ceil(activeCamerasCount / gridColumns);
// Рендерим камеры
const camerasContent = (
@@ -92,7 +107,13 @@ function SessionUsersPanel({
)}
{/* Камеры удаленных участников - показываем только если есть поток с активными треками */}
{activeParticipants.map((participant) => (
{participants
.filter(
(participant) =>
participant.stream != null &&
participant.stream.getTracks().length > 0
)
.map((participant) => (
<UserCamera
key={participant.id}
mode={mode}