Classroom SignalR ve WebRtc güvenlik düzenlemesi

This commit is contained in:
Sedat Öztürk 2025-08-30 23:30:09 +03:00
parent a09f65f53d
commit d8c4f39bd3
2 changed files with 75 additions and 28 deletions

View file

@ -18,6 +18,8 @@ interface ParticipantsPanelProps {
attendanceRecords: ClassroomAttendanceDto[]
onMuteParticipant: (participantId: string, isMuted: boolean, isTeacher: boolean) => void
onKickParticipant: (participantId: string) => void
onApproveHandRaise: (participantId: string) => void
onDismissHandRaise: (participantId: string) => void
onClose: () => void
formatTime: (timestamp: string) => string
formatDuration: (minutes: number) => string
@ -30,11 +32,16 @@ const ParticipantsPanel: React.FC<ParticipantsPanelProps> = ({
onMuteParticipant,
onKickParticipant,
onClose,
onApproveHandRaise,
onDismissHandRaise,
formatTime,
formatDuration,
}) => {
const [activeTab, setActiveTab] = useState<'participants' | 'attendance'>('participants')
// El kaldıranları bul
const handRaised = participants.filter((p) => p.isHandRaised)
return (
<div className="h-full bg-white flex flex-col text-gray-900">
{/* Header */}
@ -48,6 +55,19 @@ const ParticipantsPanel: React.FC<ParticipantsPanelProps> = ({
</button>
</div>
{/* El kaldıranlar göstergesi */}
{user.role === 'teacher' && handRaised.length > 0 && (
<div className="mb-2 flex items-center space-x-2 p-2 bg-yellow-50 rounded">
<FaHandPaper className="text-yellow-600" />
<span className="font-medium text-yellow-800">
{handRaised.length} kişi el kaldırdı:
</span>
<span className="text-yellow-900 text-sm truncate">
{handRaised.map((p) => p.name).join(', ')}
</span>
</div>
)}
{/* Tab Navigation */}
<div className="flex space-x-1 bg-gray-100 rounded-lg p-1">
<button
@ -109,34 +129,24 @@ const ParticipantsPanel: React.FC<ParticipantsPanelProps> = ({
</div>
<span className="text-gray-900">{participant.name}</span>
{/* Hand Raise Indicator */}
{participant.isHandRaised && (
<FaHandPaper className="text-yellow-600 ml-2" title="Parmak kaldırdı" />
)}
{/* Hand Raise Indicator & Teacher Control */}
{participant.isHandRaised && (
user.role === 'teacher' && !participant.isTeacher ? (
<button
onClick={() => onDismissHandRaise(participant.id)}
className="ml-2 p-1 rounded bg-yellow-100 hover:bg-yellow-200"
title="El kaldırmayı kaldır"
>
<FaHandPaper className="text-yellow-600" />
</button>
) : (
<FaHandPaper className="text-yellow-600 ml-2" title="Parmak kaldırdı" />
)
)}
</div>
<div className="flex items-center space-x-1">
{/* Hand Raise Controls (Teacher Only) */}
{user.role === 'teacher' &&
!participant.isTeacher &&
participant.isHandRaised && (
<>
<button
onClick={() => console.log('approveHandRaise', participant.id)}
className="p-1 text-green-600 hover:bg-green-50 rounded transition-colors"
title="El kaldırmayı onayla"
>
<FaCheck size={12} />
</button>
<button
onClick={() => console.log('dismissHandRaise', participant.id)}
className="p-1 text-red-600 hover:bg-red-50 rounded transition-colors"
title="El kaldırmayı reddet"
>
<FaTimes size={12} />
</button>
</>
)}
{/* Hand Raise Controls kaldırıldı, kontrol yukarıya taşındı */}
{/* Mute / Unmute Button */}
{user.role === 'teacher' && !participant.isTeacher && (

View file

@ -83,6 +83,20 @@ const newClassSession: ClassroomDto = {
}
const RoomDetail: React.FC = () => {
// El kaldırma onayla/iptal fonksiyonları en başta tanımlanmalı
// (props olarak kullanılmadan önce)
const handleApproveHandRaise = async (participantId: string) => {
if (signalRServiceRef.current && user.role === 'teacher') {
await signalRServiceRef.current.approveHandRaise(classSession.id, participantId)
}
}
const handleDismissHandRaise = async (participantId: string) => {
if (signalRServiceRef.current && user.role === 'teacher') {
await signalRServiceRef.current.dismissHandRaise(classSession.id, participantId)
}
}
const params = useParams()
const navigate = useNavigate()
const { user } = useStoreState((state) => state.auth)
@ -119,7 +133,7 @@ const RoomDetail: React.FC = () => {
const [selectedRecipient, setSelectedRecipient] = useState<{ id: string; name: string } | null>(
null,
)
const [dragOver, setDragOver] = useState(false)
const raisedHandsCount = participants.filter((p) => p.isHandRaised).length
const fileInputRef = useRef<HTMLInputElement>(null)
const messagesEndRef = useRef<HTMLDivElement>(null)
const [classSettings, setClassSettings] = useState<ClassroomSettingsDto>({
@ -298,7 +312,7 @@ const RoomDetail: React.FC = () => {
}
// ✅ öğretmen ise her zaman offer başlatır
if (isTeacher || (isActive && user.id < userId)) {
if (isActive && user.id < userId) {
const offer = await webRTCServiceRef.current!.createOffer(userId)
await signalRServiceRef.current?.sendOffer(classSession.id, userId, offer)
}
@ -724,6 +738,9 @@ const RoomDetail: React.FC = () => {
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
// ...existing code...
// ...existing code...
// --- yan panel fonksiyonu ---
const renderSidePanel = () => {
if (!activeSidePanel) return null
@ -754,12 +771,15 @@ const RoomDetail: React.FC = () => {
attendanceRecords={attendanceRecords}
onMuteParticipant={handleMuteParticipant}
onKickParticipant={handleKickParticipant}
onApproveHandRaise={handleApproveHandRaise}
onDismissHandRaise={handleDismissHandRaise}
onClose={() => setActiveSidePanel(null)}
formatTime={formatTime}
formatDuration={formatDuration}
/>
)
// ...existing code...
case 'documents':
return (
<DocumentsPanel
@ -1022,7 +1042,16 @@ const RoomDetail: React.FC = () => {
}}
className={`flex items-center space-x-2 p-3 rounded-lg transition-all text-base ${activeSidePanel === 'participants' ? 'bg-blue-100 text-blue-700' : 'hover:bg-gray-100 text-gray-700'}`}
>
<FaUserFriends /> <span>Katılımcılar</span>
<FaUserFriends />
<span>Katılımcılar</span>
<span className="absolute -top-1 -right-1 bg-blue-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center text-[10px]">
{participants.length}
</span>
{raisedHandsCount > 0 && (
<span className="absolute -top-1 -right-6 bg-yellow-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center text-[10px]">
{raisedHandsCount > 9 ? '9+' : raisedHandsCount}
</span>
)}
</button>
<button
onClick={() => {
@ -1241,6 +1270,14 @@ const RoomDetail: React.FC = () => {
title="Katılımcılar"
>
<FaUserFriends size={14} />
<span className="absolute -top-1 -right-1 bg-blue-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center text-[10px]">
{participants.length}
</span>
{raisedHandsCount > 0 && (
<span className="absolute -top-1 -right-6 bg-yellow-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center text-[10px]">
{raisedHandsCount > 9 ? '9+' : raisedHandsCount}
</span>
)}
</button>
{/* Teacher Only Options */}