@@ -70,7 +70,14 @@ export default function ClassroomContent({ classId }: { classId: string }) {
7070 const [ whiteboardVisible , setWhiteboardVisible ] = useState ( false ) ;
7171
7272 // Whiteboard drawing function - always available
73- const applyWhiteboardUpdate = useCallback ( ( data : any ) => {
73+ const applyWhiteboardUpdate = useCallback ( ( data : {
74+ action ?: string ;
75+ x ?: number ;
76+ y ?: number ;
77+ tool ?: string ;
78+ size ?: number ;
79+ color ?: string ;
80+ } ) => {
7481 const canvas = document . querySelector ( 'canvas' ) ;
7582 if ( ! canvas ) {
7683 console . log ( '❌ No canvas found for whiteboard update' ) ;
@@ -93,22 +100,22 @@ export default function ClassroomContent({ classId }: { classId: string }) {
93100 ctx . strokeStyle = data . color || '#ffffff' ;
94101 }
95102
96- ctx . lineWidth = data . size ;
97- ctx . lineTo ( data . x , data . y ) ;
103+ ctx . lineWidth = data . size || 3 ;
104+ ctx . lineTo ( data . x || 0 , data . y || 0 ) ;
98105 ctx . stroke ( ) ;
99106 ctx . beginPath ( ) ;
100- ctx . moveTo ( data . x , data . y ) ;
107+ ctx . moveTo ( data . x || 0 , data . y || 0 ) ;
101108
102109 console . log ( '✅ Applied whiteboard drawing:' , data ) ;
103110 } , [ ] ) ;
104111
105112 // Register whiteboard function globally
106113 useEffect ( ( ) => {
107- ( window as any ) . applyWhiteboardUpdate = applyWhiteboardUpdate ;
114+ ( window as unknown as Record < string , unknown > ) . applyWhiteboardUpdate = applyWhiteboardUpdate ;
108115 console . log ( '✅ Whiteboard function registered at parent level' ) ;
109116
110117 return ( ) => {
111- ( window as any ) . applyWhiteboardUpdate = null ;
118+ ( window as unknown as Record < string , unknown > ) . applyWhiteboardUpdate = null ;
112119 } ;
113120 } , [ applyWhiteboardUpdate ] ) ;
114121
@@ -214,8 +221,8 @@ export default function ClassroomContent({ classId }: { classId: string }) {
214221 case 'whiteboard_update' :
215222 // Apply whiteboard drawing update
216223 console . log ( '📝 Student received whiteboard update:' , data . drawing_data ) ;
217- if ( ( window as any ) . applyWhiteboardUpdate ) {
218- ( window as any ) . applyWhiteboardUpdate ( data . drawing_data ) ;
224+ if ( ( window as unknown as Record < string , unknown > ) . applyWhiteboardUpdate ) {
225+ ( ( window as unknown as Record < string , unknown > ) . applyWhiteboardUpdate as ( data : unknown ) => void ) ( data . drawing_data ) ;
219226 console . log ( '✅ Applied whiteboard update to canvas' ) ;
220227 } else {
221228 console . log ( '❌ applyWhiteboardUpdate function not available' ) ;
@@ -245,8 +252,12 @@ export default function ClassroomContent({ classId }: { classId: string }) {
245252 if ( studentPeerConnectionRef . current ) {
246253 studentPeerConnectionRef . current . close ( ) ;
247254 }
248- peerConnectionsRef . current . forEach ( pc => pc . close ( ) ) ;
249- peerConnectionsRef . current . clear ( ) ;
255+ // eslint-disable-next-line react-hooks/exhaustive-deps
256+ const connections = peerConnectionsRef . current ;
257+ // Copy to variable to avoid ref changes during cleanup
258+ const connectionsToClose = Array . from ( connections . values ( ) ) ;
259+ connectionsToClose . forEach ( pc => pc . close ( ) ) ;
260+ connections . clear ( ) ;
250261 } ;
251262 // eslint-disable-next-line react-hooks/exhaustive-deps
252263 } , [ classId , role , userName ] ) ;
@@ -260,25 +271,13 @@ export default function ClassroomContent({ classId }: { classId: string }) {
260271 const setupStudentPeerConnection = useCallback ( ( ) => {
261272 const configuration : RTCConfiguration = {
262273 iceServers : [
263- { urls : 'stun:stun.l.google.com:19302' } ,
264- { urls : 'stun:stun1.l.google.com:19302' } ,
265- { urls : 'stun:stun2.l.google.com:19302' }
266- ] ,
267- iceCandidatePoolSize : 10 ,
268- bundlePolicy : 'max-bundle' ,
269- rtcpMuxPolicy : 'require'
274+ { urls : 'stun:stun.l.google.com:19302' }
275+ ]
270276 } ;
271277
272278 const peerConnection = new RTCPeerConnection ( configuration ) ;
273279 studentPeerConnectionRef . current = peerConnection ;
274280
275- // Optimize for Chrome
276- peerConnection . addEventListener ( 'connectionstatechange' , ( ) => {
277- if ( peerConnection . connectionState === 'failed' ) {
278- peerConnection . restartIce ( ) ;
279- }
280- } ) ;
281-
282281 // Handle incoming stream from teacher
283282 peerConnection . ontrack = ( event ) => {
284283 console . log ( '🎥 ✅ STUDENT RECEIVED REMOTE STREAM!' , event . streams [ 0 ] ) ;
@@ -331,37 +330,16 @@ export default function ClassroomContent({ classId }: { classId: string }) {
331330 const setupTeacherPeerConnection = useCallback ( ( studentId : number ) => {
332331 const configuration : RTCConfiguration = {
333332 iceServers : [
334- { urls : 'stun:stun.l.google.com:19302' } ,
335- { urls : 'stun:stun1.l.google.com:19302' } ,
336- { urls : 'stun:stun2.l.google.com:19302' }
337- ] ,
338- iceCandidatePoolSize : 10 ,
339- bundlePolicy : 'max-bundle' ,
340- rtcpMuxPolicy : 'require'
333+ { urls : 'stun:stun.l.google.com:19302' }
334+ ]
341335 } ;
342336
343337 const peerConnection = new RTCPeerConnection ( configuration ) ;
344338
345- // Optimize for Chrome
346- peerConnection . addEventListener ( 'connectionstatechange' , ( ) => {
347- if ( peerConnection . connectionState === 'failed' ) {
348- peerConnection . restartIce ( ) ;
349- }
350- } ) ;
351-
352- // Add current stream to this peer connection with optimized settings
339+ // Add current stream to this peer connection
353340 if ( streamRef . current ) {
354341 streamRef . current . getTracks ( ) . forEach ( track => {
355- const sender = peerConnection . addTrack ( track , streamRef . current ! ) ;
356- // Optimize encoding for better performance
357- if ( track . kind === 'video' ) {
358- const params = sender . getParameters ( ) ;
359- if ( params . encodings && params . encodings . length > 0 ) {
360- params . encodings [ 0 ] . maxBitrate = 1000000 ; // 1Mbps
361- params . encodings [ 0 ] . maxFramerate = 30 ;
362- sender . setParameters ( params ) ;
363- }
364- }
342+ peerConnection . addTrack ( track , streamRef . current ! ) ;
365343 } ) ;
366344 }
367345
@@ -540,32 +518,23 @@ export default function ClassroomContent({ classId }: { classId: string }) {
540518 console . log ( '📺 Starting screen share...' ) ;
541519 // Screen sharing with Chrome compatibility
542520 stream = await navigator . mediaDevices . getDisplayMedia ( {
543- video : {
544- mediaSource : 'screen' ,
545- width : { max : 1920 } ,
546- height : { max : 1080 } ,
547- frameRate : { max : 30 }
548- } ,
521+ video : true ,
549522 audio : false
550523 } ) ;
551524 setMediaState ( prev => ( { ...prev , isScreenSharing : true } ) ) ;
552525 } else {
553526 console . log ( '📹 Starting camera stream...' ) ;
554- // Regular camera/microphone with optimized constraints
527+ // Regular camera/microphone with simple constraints
555528 const constraints : MediaStreamConstraints = { } ;
556529 if ( mediaState . hasVideo && mediaState . videoEnabled ) {
557530 constraints . video = {
558- width : { ideal : 1280 , max : 1920 } ,
559- height : { ideal : 720 , max : 1080 } ,
560- frameRate : { ideal : 30 , max : 30 }
531+ width : { ideal : 640 } ,
532+ height : { ideal : 480 } ,
533+ frameRate : { ideal : 15 }
561534 } ;
562535 }
563536 if ( mediaState . hasAudio && mediaState . audioEnabled ) {
564- constraints . audio = {
565- echoCancellation : true ,
566- noiseSuppression : true ,
567- autoGainControl : true
568- } ;
537+ constraints . audio = true ;
569538 }
570539
571540 console . log ( '📋 Media constraints:' , constraints ) ;
@@ -602,9 +571,9 @@ export default function ClassroomContent({ classId }: { classId: string }) {
602571 } catch ( error ) {
603572 console . error ( '❌ Error accessing media:' , error ) ;
604573 console . error ( 'Error details:' , {
605- name : error . name ,
606- message : error . message ,
607- constraint : error . constraint
574+ name : ( error as Error ) . name ,
575+ message : ( error as Error ) . message ,
576+ constraint : ( error as unknown as { constraint ?: string } ) . constraint
608577 } ) ;
609578
610579 // Fallback to screen sharing if camera fails
@@ -670,12 +639,45 @@ export default function ClassroomContent({ classId }: { classId: string }) {
670639 }
671640 } , [ mediaState . videoEnabled ] ) ;
672641
673- const startScreenShare = useCallback ( ( ) => {
674- if ( streamRef . current ) {
675- streamRef . current . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
642+ const startScreenShare = useCallback ( async ( ) => {
643+ try {
644+ console . log ( '🖥️ Starting screen share...' ) ;
645+
646+ // Stop current stream
647+ if ( streamRef . current ) {
648+ streamRef . current . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
649+ }
650+
651+ // Get screen share with minimal constraints
652+ const stream = await navigator . mediaDevices . getDisplayMedia ( {
653+ video : true ,
654+ audio : false
655+ } ) ;
656+
657+ streamRef . current = stream ;
658+ if ( videoRef . current ) {
659+ videoRef . current . srcObject = stream ;
660+ }
661+ setMediaState ( prev => ( { ...prev , isScreenSharing : true } ) ) ;
662+
663+ // Handle stream end
664+ stream . getVideoTracks ( ) [ 0 ] . onended = ( ) => {
665+ setMediaState ( prev => ( { ...prev , isScreenSharing : false } ) ) ;
666+ } ;
667+
668+ // Restart WebRTC connections with new stream
669+ if ( role === 'teacher' ) {
670+ // Clear existing connections
671+ peerConnectionsRef . current . clear ( ) ;
672+
673+ // Students will reconnect automatically
674+ console . log ( '🔄 Screen share ready - students will reconnect' ) ;
675+ }
676+
677+ } catch ( error ) {
678+ console . error ( '❌ Screen sharing failed:' , error ) ;
676679 }
677- startMediaStream ( true ) ;
678- } , [ startMediaStream ] ) ;
680+ } , [ role ] ) ;
679681
680682 const stopStreaming = useCallback ( ( ) => {
681683 if ( streamRef . current ) {
@@ -719,16 +721,16 @@ export default function ClassroomContent({ classId }: { classId: string }) {
719721 } , [ ] ) ;
720722
721723 // Handle whiteboard drawing updates
722- const handleWhiteboardUpdate = useCallback ( ( drawingData : any ) => {
724+ const handleWhiteboardUpdate = useCallback ( ( drawingData : {
725+ x : number ;
726+ y : number ;
727+ tool : string ;
728+ size : number ;
729+ color : string ;
730+ timestamp : number ;
731+ } ) => {
723732 console . log ( '📝 Sending whiteboard update:' , drawingData ) ;
724733 if ( wsRef . current && role === 'teacher' ) {
725- // Send test message first
726- wsRef . current . send ( JSON . stringify ( {
727- type : 'test_message' ,
728- data : 'Testing WebSocket connection'
729- } ) ) ;
730-
731- // Then send whiteboard data
732734 wsRef . current . send ( JSON . stringify ( {
733735 type : 'whiteboard_draw' ,
734736 drawing_data : drawingData
0 commit comments