import { lazy, Suspense, useMemo, type KeyboardEvent } from 'react' import { FaMicrophoneSlash, FaUserTimes } from 'react-icons/fa' import { VideoroomParticipantDto, VideoroomLayoutDto } from '@/proxy/videoroom/models' import { Button } from '@/components/ui' const VideoPlayer = lazy(() => import('./VideoPlayer').then((module) => ({ default: module.VideoPlayer })), ) interface RoomParticipantProps { participants: VideoroomParticipantDto[] localStream?: MediaStream | null currentUserId: string currentUserName: string isTeacher: boolean isAudioEnabled: boolean isVideoEnabled: boolean onMuteParticipant?: (participantId: string, isMuted: boolean, isTeacher: boolean) => void layout?: VideoroomLayoutDto focusedParticipant?: string onParticipantFocus?: (participantId: string | undefined) => void onKickParticipant?: (participantId: string) => void hasSidePanel?: boolean } /** Katılımcı sayısına göre kolon sayısı. Satırlar `auto-rows-fr` ile eşit yüksekliğe dağıtılır. */ const getGridColumns = (count: number) => { if (count <= 1) return 'grid-cols-1' if (count <= 2) return 'grid-cols-1 sm:grid-cols-2' if (count <= 4) return 'grid-cols-2 sm:grid-cols-2' if (count <= 9) return 'grid-cols-2 sm:grid-cols-3' return 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4' } const VideoTileFallback = () =>
export const RoomParticipant = ({ participants, localStream, currentUserId, currentUserName, isTeacher, isAudioEnabled, isVideoEnabled, onMuteParticipant, layout, focusedParticipant, onParticipantFocus, onKickParticipant, hasSidePanel = false, }: RoomParticipantProps) => { const allParticipants = useMemo(() => { const remote = (participants ?? []).filter((p) => p.id !== currentUserId) const self: VideoroomParticipantDto = { id: currentUserId, name: currentUserName, sessionId: remote[0]?.sessionId ?? '', isTeacher, isAudioMuted: !isAudioEnabled, isVideoMuted: !isVideoEnabled, isActive: true, stream: localStream ?? undefined, } return [self, ...remote] }, [ participants, currentUserId, currentUserName, isTeacher, isAudioEnabled, isVideoEnabled, localStream, ]) const renderParticipant = ( participant: VideoroomParticipantDto, { isMain = false }: { isMain?: boolean } = {}, ) => { const isSelf = participant.id === currentUserId const isFocusable = !isMain && !!onParticipantFocus const canModerate = isTeacher && !isSelf return (
onParticipantFocus?.(participant.id), onKeyDown: (event: KeyboardEvent) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault() onParticipantFocus?.(participant.id) } }, } : {})} > }> {participant.isHandRaised && !isSelf && (
)} {canModerate && (
{onKickParticipant && ( )}
)}
) } const renderGridLayout = () => { const count = allParticipants.length const gap = count <= 4 ? 'gap-2 sm:gap-3' : 'gap-1 sm:gap-2' const padding = count === 1 ? 'p-0' : count <= 4 ? 'p-2 sm:p-4' : 'p-1 sm:p-2' return (
{allParticipants.map((participant, index) => (
2 ? 'col-span-2 sm:col-span-1' : '' }`} > {renderParticipant(participant)}
))}
) } const renderSidebarLayout = () => { const mainParticipant = allParticipants.find((p) => p.id === focusedParticipant) ?? allParticipants[0] const otherParticipants = allParticipants.filter((p) => p.id !== mainParticipant.id) const sidebarWidth = hasSidePanel ? 'w-20 sm:w-24 md:w-32 lg:w-40' : 'w-24 sm:w-32 md:w-40 lg:w-48' return (
{renderParticipant(mainParticipant, { isMain: true })}
{otherParticipants.length > 0 && (
{otherParticipants.map((participant) => (
{renderParticipant(participant)}
))}
)}
) } const renderTeacherFocusLayout = () => { const teacher = allParticipants.find((p) => p.isTeacher) ?? allParticipants[0] return (
{renderParticipant(teacher, { isMain: true })}
) } // Ne yerel yayın ne de katılımcı varsa gösterilecek bir şey yok if (!localStream && allParticipants.length <= 1) { return null } switch (layout?.type) { case 'sidebar': return
{renderSidebarLayout()}
case 'teacher-focus': return
{renderTeacherFocusLayout()}
default: return
{renderGridLayout()}
} }