Enhance SessionUsersPanel and UserCamera components to support 'full' and 'mini' modes. Implement responsive grid layout for camera display based on active participants, and adjust UserDevicesControls visibility accordingly. Refactor SessionPage to manage mode state and toggle between layouts.
This commit is contained in:
@@ -2,16 +2,18 @@ import UserCamera from "./ui/UserCamera";
|
||||
import UserDevicesControls from "./ui/UserDevicesControls";
|
||||
import DraggableContainer from "./DraggableContainer";
|
||||
import { useWebRTC } from "../hooks/useWebRTC";
|
||||
import { useCallback } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
interface SessionUsersPanelProps {
|
||||
roomId: string;
|
||||
autoJoin?: boolean;
|
||||
mode?: "full" | "mini";
|
||||
}
|
||||
|
||||
function SessionUsersPanel({
|
||||
roomId,
|
||||
autoJoin = false,
|
||||
mode = "full",
|
||||
}: SessionUsersPanelProps) {
|
||||
const {
|
||||
localStream,
|
||||
@@ -26,21 +28,38 @@ function SessionUsersPanel({
|
||||
const hasLocalStream = localStream !== null;
|
||||
|
||||
// Callback для отправки состояния speaking
|
||||
const handleSpeakingChange = useCallback((isSpeaking: boolean) => {
|
||||
const handleSpeakingChange = (isSpeaking: boolean) => {
|
||||
updateSpeakingState?.(isSpeaking);
|
||||
}, [updateSpeakingState]);
|
||||
};
|
||||
|
||||
return (
|
||||
<DraggableContainer
|
||||
enableSnapping={true}
|
||||
autoAlign={true}
|
||||
initialCorner={innerWidth >= 640 ? "bottom-right" : "top-right"}
|
||||
padding="1.111vw"
|
||||
className="flex gap-4 z-[999]"
|
||||
>
|
||||
// Вычисляем количество камер для 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 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;
|
||||
};
|
||||
|
||||
const gridColumns = getGridColumns(activeCamerasCount);
|
||||
|
||||
// Вычисляем количество рядов для правильного расчета высоты
|
||||
const gridRows = Math.ceil(activeCamerasCount / gridColumns);
|
||||
|
||||
// Рендерим камеры
|
||||
const camerasContent = (
|
||||
<>
|
||||
{/* Локальная камера пользователя - показываем только если есть разрешение */}
|
||||
{localStream && (
|
||||
<UserCamera
|
||||
mode={mode}
|
||||
name="Вы"
|
||||
isMuted={isLocalAudioMuted}
|
||||
isVideoOff={isLocalVideoMuted}
|
||||
@@ -65,12 +84,13 @@ function SessionUsersPanel({
|
||||
.map((participant) => (
|
||||
<UserCamera
|
||||
key={participant.id}
|
||||
mode={mode}
|
||||
name={participant.id}
|
||||
isMuted={participant.isMuted || false}
|
||||
isVideoOff={participant.isVideoOff || false}
|
||||
isSpeaking={participant.isSpeaking}
|
||||
isControlDisabled={true}
|
||||
isAdmin={true} // Локальный пользователь - админ своей сессии
|
||||
isAdmin={true}
|
||||
mediaStream={participant.stream}
|
||||
hasLocalMediaPermission={hasLocalStream}
|
||||
onMute={() => console.log(`Mute user ${participant.id}`)}
|
||||
@@ -82,13 +102,53 @@ function SessionUsersPanel({
|
||||
))}
|
||||
|
||||
<UserDevicesControls
|
||||
mode={mode}
|
||||
toggleAudio={toggleAudio}
|
||||
toggleVideo={toggleVideo}
|
||||
isAudioMuted={isLocalAudioMuted}
|
||||
isVideoMuted={isLocalVideoMuted}
|
||||
hasLocalStream={hasLocalStream}
|
||||
/>
|
||||
</DraggableContainer>
|
||||
</>
|
||||
);
|
||||
|
||||
// Для режима full используем DraggableContainer
|
||||
if (mode === "full") {
|
||||
return (
|
||||
<DraggableContainer
|
||||
enableSnapping={true}
|
||||
enabled={true}
|
||||
autoAlign={true}
|
||||
initialCorner={innerWidth >= 640 ? "bottom-right" : "top-right"}
|
||||
padding="1.111vw"
|
||||
className="z-[999] flex gap-4"
|
||||
>
|
||||
{camerasContent}
|
||||
</DraggableContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Для режима mini используем flex-обертку для центрирования и внутри grid
|
||||
return (
|
||||
<div className="flex justify-center items-center w-full h-full z-[99]a">
|
||||
<div
|
||||
className={clsx(
|
||||
"grid 2xl:gap-[0.556vw] gap-2",
|
||||
gridColumns === 1 && "grid-cols-1 2xl:w-[45vw] w-[calc(50vw-1rem)]",
|
||||
gridColumns === 2 && "grid-cols-2 2xl:w-[90vw] w-[calc(100vw-2rem)]",
|
||||
gridColumns === 3 && "grid-cols-3 2xl:w-[90vw] w-[calc(100vw-2rem)]",
|
||||
gridColumns === 4 && "grid-cols-4 2xl:w-[90vw] w-[calc(100vw-2rem)]",
|
||||
gridColumns === 5 && "grid-cols-5 2xl:w-[90vw] w-[calc(100vw-2rem)]",
|
||||
gridRows === 1 && "auto-rows-[calc((86vh-1.111vw)/1)]",
|
||||
gridRows === 2 && "auto-rows-[calc((86vh-1.667vw)/2)]",
|
||||
gridRows === 3 && "auto-rows-[calc((86vh-2.222vw)/3)]",
|
||||
gridRows === 4 && "auto-rows-[calc((86vh-2.778vw)/4)]",
|
||||
gridRows === 5 && "auto-rows-[calc((86vh-3.333vw)/5)]"
|
||||
)}
|
||||
>
|
||||
{camerasContent}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user