From 9db84f137e61d28ee4a699f01a0bc8d66d85920c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Mon, 1 Sep 2025 00:52:24 +0300 Subject: [PATCH] =?UTF-8?q?Uyar=C4=B1lar=20de=C4=9Fi=C5=9Ftirildi=20kullan?= =?UTF-8?q?=C4=B1c=C4=B1ya=20g=C3=B6sterildi.=20toast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Seeds/SeederData.json | 6 +- .../Classroom/ClassroomHub.cs | 39 ++--- .../components/classroom/panels/ChatPanel.tsx | 2 +- ui/src/services/classroom/signalr.tsx | 159 +++++++++--------- ui/src/services/classroom/webrtc.tsx | 132 ++++++++------- ui/src/views/classroom/RoomDetail.tsx | 146 +++++++++------- 6 files changed, 258 insertions(+), 226 deletions(-) diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json index 730161bd..94adabae 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json @@ -17712,7 +17712,7 @@ "isActive": false, "isScheduled": true, "participantCount": 0, - "settingsJson": null + "settingsJson": "{\"AllowHandRaise\":true,\"AllowStudentChat\":true,\"AllowPrivateMessages\":true,\"AllowStudentScreenShare\":true,\"DefaultMicrophoneState\":\"muted\",\"DefaultCameraState\":\"off\",\"DefaultLayout\":\"grid\",\"AutoMuteNewParticipants\":true}" }, { "name": "Fizik 201 - Kuantum Mekaniği", @@ -17729,7 +17729,7 @@ "isActive": false, "isScheduled": true, "participantCount": 0, - "settingsJson": null + "settingsJson": "{\"AllowHandRaise\":true,\"AllowStudentChat\":true,\"AllowPrivateMessages\":true,\"AllowStudentScreenShare\":true,\"DefaultMicrophoneState\":\"muted\",\"DefaultCameraState\":\"off\",\"DefaultLayout\":\"grid\",\"AutoMuteNewParticipants\":true}" }, { "name": "Kimya 301 - Organik Kimya", @@ -17746,7 +17746,7 @@ "isActive": false, "isScheduled": true, "participantCount": 0, - "settingsJson": null + "settingsJson": "{\"AllowHandRaise\":true,\"AllowStudentChat\":true,\"AllowPrivateMessages\":true,\"AllowStudentScreenShare\":true,\"DefaultMicrophoneState\":\"muted\",\"DefaultCameraState\":\"off\",\"DefaultLayout\":\"grid\",\"AutoMuteNewParticipants\":true}" } ] } diff --git a/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs b/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs index 89ed806c..2ba6a4e6 100644 --- a/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs +++ b/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs @@ -121,8 +121,6 @@ public class ClassroomHub : Hub [HubMethodName("JoinClass")] public async Task JoinClassAsync(Guid sessionId, Guid userId, string userName, bool isTeacher, bool isActive) { - _logger.LogInformation("🔵 JoinClass çağrıldı: User={UserId}, Session={SessionId}, IsTeacher={IsTeacher}", userId, sessionId, isTeacher); - var classroom = await _classSessionRepository.GetAsync(sessionId); if (classroom == null) { @@ -235,9 +233,6 @@ public class ClassroomHub : Hub await Clients.Group(sessionId.ToString()) .SendAsync("ParticipantLeft", new { UserId = userId.Value, SessionId = sessionId }); - - - _logger.LogInformation("User {UserId} left class {SessionId}", userId, sessionId); } [HubMethodName("MuteParticipant")] @@ -412,17 +407,13 @@ public class ClassroomHub : Hub x => x.SessionId == sessionId && x.UserId == participantId ); - _logger.LogInformation("👢 KickParticipant çağrıldı: Session={SessionId}, Target={ParticipantId}", sessionId, participantId); - if (participant == null) { - _logger.LogWarning("⚠️ KickParticipant: participant bulunamadı (Session={SessionId}, Target={ParticipantId})", sessionId, participantId); + _logger.LogWarning($"⚠️ KickParticipant: participant bulunamadı Session={sessionId}, Target={participantId})"); + await Clients.Caller.SendAsync("Warning", "Participant bulunamadı"); return; } - _logger.LogInformation("✅ Kick öncesi durum: IsActive={IsActive}, IsKicked={IsKicked}, ConnId={ConnId}", participant.IsActive, participant.IsKicked, participant.ConnectionId); - - // ConnectionId'yi cache et (null yazmadan önce) var connectionId = participant.ConnectionId; @@ -454,15 +445,12 @@ public class ClassroomHub : Hub }); // 6. Log - _logger.LogInformation("👢 Participant {ParticipantId} kicked from session {SessionId}", - participantId, sessionId); + _logger.LogInformation($"👢 Participant {participantId} kicked from session {sessionId}"); + await Clients.Caller.SendAsync("Info", "Kick işlemi başarısız oldu."); } catch (Exception ex) { - _logger.LogError(ex, - "❌ KickParticipant hata verdi (Session={SessionId}, Participant={ParticipantId})", - sessionId, participantId); - + _logger.LogError(ex, $"❌ KickParticipant hata verdi (Session={sessionId}, Participant={participantId})"); await Clients.Caller.SendAsync("Error", "Kick işlemi başarısız oldu."); } } @@ -505,7 +493,6 @@ public class ClassroomHub : Hub [HubMethodName("SendOffer")] public async Task SendOfferAsync(Guid sessionId, Guid targetUserId, object offer) { - _logger.LogInformation("➡️ SendOffer to {TargetUserId}, from {CurrentUser}", targetUserId, _currentUser.Id); await Clients.User(targetUserId.ToString()) .SendAsync("ReceiveOffer", _currentUser.Id?.ToString(), offer); } @@ -532,13 +519,11 @@ public class ClassroomHub : Hub var userId = _currentUser.Id; if (!userId.HasValue) { - _logger.LogWarning("OnDisconnectedAsync çağrıldı fakat UserId bulunamadı. ConnId={ConnectionId}", Context.ConnectionId); + _logger.LogWarning($"OnDisconnectedAsync çağrıldı fakat UserId bulunamadı. ConnId={Context.ConnectionId}"); + await Clients.Caller.SendAsync("Warning", "User bulunamadı"); return; } - _logger.LogInformation("🔴 OnDisconnectedAsync: User={UserId}, ConnId={ConnId}, Exception={Exception}", - userId, Context.ConnectionId, exception?.Message); - // 🔑 Yeni scope aç (her disconnect için ayrı UoW) using var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true); var participants = await _participantRepository @@ -546,15 +531,15 @@ public class ClassroomHub : Hub if (!participants.Any()) { - _logger.LogInformation("OnDisconnectedAsync: User {UserId} için aktif participant bulunamadı. ConnId={ConnectionId}", userId, Context.ConnectionId); + _logger.LogInformation($"OnDisconnectedAsync: User {userId} için aktif participant bulunamadı. ConnId={Context.ConnectionId}"); + await Clients.Caller.SendAsync("Warning", "Aktif participant bulunamadı"); } foreach (var participant in participants) { - _logger.LogInformation("OnDisconnectedAsync: User {UserId}, Session {SessionId} bağlantısı koptu.", - userId, participant.SessionId); + _logger.LogInformation($"OnDisconnectedAsync: User {userId}, Session {participant.SessionId} bağlantısı koptu."); + await Clients.Caller.SendAsync("Info", $"Bağlantı koptu: User {userId}"); - // 🔑 Attendance güncelle var attendances = await _attendanceRepository.GetListAsync( x => x.SessionId == participant.SessionId && x.StudentId == userId.Value && @@ -606,10 +591,12 @@ public class ClassroomHub : Hub catch (TaskCanceledException) { _logger.LogDebug("OnDisconnectedAsync iptal edildi (connection aborted). ConnId={ConnectionId}", Context.ConnectionId); + await Clients.Caller.SendAsync("Error", "OnDisconnectedAsync iptal edildi"); } catch (Exception ex) { _logger.LogError(ex, "OnDisconnectedAsync hata. ConnId={ConnectionId}", Context.ConnectionId); + await Clients.Caller.SendAsync("Error", "OnDisconnectedAsync hata oluştu"); } await base.OnDisconnectedAsync(exception); diff --git a/ui/src/components/classroom/panels/ChatPanel.tsx b/ui/src/components/classroom/panels/ChatPanel.tsx index fae3a009..1698202a 100644 --- a/ui/src/components/classroom/panels/ChatPanel.tsx +++ b/ui/src/components/classroom/panels/ChatPanel.tsx @@ -51,7 +51,7 @@ const ChatPanel: React.FC = ({ {/* Header */}

Sohbet

-
diff --git a/ui/src/services/classroom/signalr.tsx b/ui/src/services/classroom/signalr.tsx index fa8f7765..4672324b 100644 --- a/ui/src/services/classroom/signalr.tsx +++ b/ui/src/services/classroom/signalr.tsx @@ -1,13 +1,12 @@ -import { toast } from '@/components/ui' import { ClassroomAttendanceDto, ClassroomChatDto, HandRaiseDto, - MessageType, } from '@/proxy/classroom/models' import { ROUTES_ENUM } from '@/routes/route.constant' import { store } from '@/store/store' import * as signalR from '@microsoft/signalr' +import { toast } from '@/components/ui' import Notification from '@/components/ui/Notification' export class SignalRService { @@ -36,13 +35,10 @@ export class SignalRService { constructor() { const { auth } = store.getState() - // Only initialize connection if not in demo mode - // In production, replace with your actual SignalR hub URL this.connection = new signalR.HubConnectionBuilder() .withUrl(`${import.meta.env.VITE_API_URL}/classroomhub`, { accessTokenFactory: () => auth.session.token || '', }) - //.withAutomaticReconnect() .configureLogging(signalR.LogLevel.Information) .build() @@ -76,12 +72,10 @@ export class SignalRService { }) this.connection.on('HandRaiseReceived', (payload: any) => { - // payload = { handRaiseId, studentId, studentName, ... } this.onHandRaiseReceived?.(payload.studentId) }) this.connection.on('HandRaiseDismissed', (payload: any) => { - // payload = { handRaiseId, studentId } this.onHandRaiseDismissed?.(payload.studentId) }) @@ -102,33 +96,25 @@ export class SignalRService { this.connection.onreconnected(async () => { this.isConnected = true - console.log("🔄 SignalR reconnected. currentSessionId=", this.currentSessionId) - toast.push(, { placement: 'top-center', }) - // Eğer sınıftayken bağlantı koptuysa → tekrar join et if (this.currentSessionId && store.getState().auth.user) { const u = store.getState().auth.user await this.joinClass(this.currentSessionId, u.id, u.name, u.role === 'teacher', true) } }) - this.connection.onclose(async (err) => { - console.warn("🔥 onclose triggered", { isKicked: this.isKicked, error: err }) - + this.connection.onclose(async () => { if (this.isKicked) { toast.push( , - { - placement: 'top-center', - }, + { placement: 'top-center' }, ) - this.isConnected = false this.currentSessionId = undefined - return // ❗ Kick durumunda kesinlikle LeaveClass çağırma + return } this.isConnected = false @@ -136,46 +122,55 @@ export class SignalRService { if (this.currentSessionId) { await this.connection.invoke('LeaveClass', this.currentSessionId) } - } catch { - console.warn('LeaveClass could not be sent, connection was already closed') } finally { this.currentSessionId = undefined } }) this.connection.on('Error', (message: string) => { - console.error('Hub error:', message) toast.push(, { placement: 'top-center', }) }) - this.connection.onreconnecting((err) => { + this.connection.on('Warning', (message: string) => { + toast.push(, { + placement: 'top-center', + }) + }) + + this.connection.on('Info', (message: string) => { + toast.push(, { + placement: 'top-center', + }) + }) + + this.connection.onreconnecting(() => { if (this.isKicked) { - console.warn('Reconnect blocked because user was kicked') + toast.push( + , + ) this.connection.stop() throw new Error('Reconnect blocked after kick') } }) this.connection.on('ForceDisconnect', async (message: string) => { - console.warn("🚨 ForceDisconnect event alındı", message) - this.isKicked = true toast.push(, { placement: 'top-center', }) if (this.onForceCleanup) { - console.warn('⚡ ForceCleanup callback çağrılıyor') this.onForceCleanup() } try { await this.connection.stop() - } catch (e) { - console.warn('connection.stop hata:', e) - } + } catch {} this.isConnected = false @@ -193,8 +188,6 @@ export class SignalRService { async start(): Promise { try { - console.log('🔌 SignalR start() çağrıldı') - const startPromise = this.connection.start() const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Bağlantı zaman aşımına uğradı')), 10000), @@ -205,10 +198,13 @@ export class SignalRService { toast.push(, { placement: 'top-center', }) - } catch (error) { - console.error('Error starting SignalR connection:', error) - alert( - '⚠️ Sunucuya bağlanılamadı. Lütfen sayfayı yenileyin veya internet bağlantınızı kontrol edin.', + } catch { + toast.push( + , + { placement: 'top-center' }, ) this.isConnected = false } @@ -222,18 +218,22 @@ export class SignalRService { isActive: boolean, ): Promise { if (!this.isConnected) { - console.log('Error starting SignalR connection join class for', userName) + toast.push( + , + ) return } - console.log(`📡 joinClass: sessionId=${sessionId}, userId=${userId}, isTeacher=${isTeacher}`) - //Global değişkene yazılıyor. this.currentSessionId = sessionId - try { await this.connection.invoke('JoinClass', sessionId, userId, userName, isTeacher, isActive) - } catch (error) { - console.error('Error joining class:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -241,21 +241,17 @@ export class SignalRService { const { auth } = store.getState() if (!this.isConnected) { - console.log('Error starting SignalR connection simulating leave class for user', auth.user.id) - // Simulate successful leave in demo mode - setTimeout(() => { - this.onParticipantLeft?.({ userId: auth.user.id, sessionId }) - }, 100) + this.onParticipantLeft?.({ userId: auth.user.id, sessionId }) return } try { await this.connection.invoke('LeaveClass', sessionId) - - //Global değişkene null atanıyor. this.currentSessionId = undefined - } catch (error) { - console.error('Error leaving class:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -293,8 +289,10 @@ export class SignalRService { isTeacher, 'public', ) - } catch (error) { - console.error('Error sending chat message:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -338,8 +336,10 @@ export class SignalRService { isTeacher, 'private', ) - } catch (error) { - console.error('Error sending private message:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -376,8 +376,10 @@ export class SignalRService { message, isTeacher, ) - } catch (error) { - console.error('Error sending chat message:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -396,20 +398,15 @@ export class SignalRService { try { await this.connection.invoke('MuteParticipant', sessionId, userId, isMuted, isTeacher) - } catch (error) { - console.error('Error muting participant:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } async raiseHand(sessionId: string, studentId: string, studentName: string): Promise { if (!this.isConnected) { - const handRaise: HandRaiseDto = { - id: crypto.randomUUID(), - studentId, - studentName, - timestamp: new Date().toISOString(), - isActive: true, - } setTimeout(() => { this.onHandRaiseReceived?.(studentId) }, 100) @@ -418,8 +415,10 @@ export class SignalRService { try { await this.connection.invoke('RaiseHand', sessionId, studentId, studentName) - } catch (error) { - console.error('Error raising hand:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -431,12 +430,12 @@ export class SignalRService { return } - console.log(`👢 kickParticipant çağrıldı: sessionId=${sessionId}, participantId=${participantId}`) - try { await this.connection.invoke('KickParticipant', sessionId, participantId) - } catch (error) { - console.error('Error kicking participant:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -450,8 +449,10 @@ export class SignalRService { try { await this.connection.invoke('ApproveHandRaise', sessionId, studentId) - } catch (error) { - console.error('Error approving hand raise:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -465,8 +466,10 @@ export class SignalRService { try { await this.connection.invoke('DismissHandRaise', sessionId, studentId) - } catch (error) { - console.error('Error dismissing hand raise:', error) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -541,8 +544,10 @@ export class SignalRService { if (this.isConnected && this.currentSessionId) { try { await this.connection.invoke('LeaveClass', this.currentSessionId) - } catch (err) { - console.warn('LeaveClass gönderilemedi:', err) + } catch { + toast.push(, { + placement: 'top-center', + }) } } if (this.connection) { diff --git a/ui/src/services/classroom/webrtc.tsx b/ui/src/services/classroom/webrtc.tsx index 05b4cdf4..6b33dc05 100644 --- a/ui/src/services/classroom/webrtc.tsx +++ b/ui/src/services/classroom/webrtc.tsx @@ -1,9 +1,12 @@ +import { toast } from '@/components/ui' +import Notification from '@/components/ui/Notification' + export class WebRTCService { private peerConnections: Map = new Map() - private retryCounts: Map = new Map() // 🔑 her kullanıcı için retry sayacı - private maxRetries = 3 // 🔑 maksimum yeniden deneme sayısı - private signalRService: any // 👈 dışarıdan set edilecek SignalR servisi - private sessionId: string = '' // oturum için de lazım olabilir + private retryCounts: Map = new Map() + private maxRetries = 3 + private signalRService: any + private sessionId: string = '' private localStream: MediaStream | null = null private onRemoteStream?: (userId: string, stream: MediaStream) => void @@ -17,23 +20,8 @@ export class WebRTCService { ], } - // private rtcConfiguration: RTCConfiguration = { - // iceServers: [ - // { urls: 'stun:stun.l.google.com:19302' }, // STUN - // { - // urls: ['turn:your-server-ip:3478?transport=udp', 'turn:your-server-ip:3478?transport=tcp'], - // username: 'kurs', // static user/pass kullanmak istersen - // credential: 'kurs12345', - // }, - // ], - // } - - /** - * Local stream'i başlatır. Kamera/mikrofon ayarlarını parametreden alır. - */ async initializeLocalStream(enableAudio: boolean, enableVideo: boolean): Promise { try { - // her zaman hem ses hem video al this.localStream = await navigator.mediaDevices.getUserMedia({ video: { width: { ideal: 1280 }, @@ -47,23 +35,26 @@ export class WebRTCService { }, }) - // başlangıç ayarlarını uygula this.localStream.getAudioTracks().forEach((track) => (track.enabled = enableAudio)) this.localStream.getVideoTracks().forEach((track) => (track.enabled = enableVideo)) return this.localStream - } catch (error) { - console.error('Error accessing media devices:', error) - throw error + } catch { + toast.push( + , + ) + throw new Error('Media devices access failed') } } async createPeerConnection(userId: string): Promise { const peerConnection = new RTCPeerConnection(this.rtcConfiguration) this.peerConnections.set(userId, peerConnection) - this.retryCounts.set(userId, 0) // bağlantı başında sıfırla + this.retryCounts.set(userId, 0) - // Eğer local stream varsa track'leri ekle if (this.localStream) { this.localStream.getTracks().forEach((track) => { peerConnection.addTrack(track, this.localStream!) @@ -83,7 +74,6 @@ export class WebRTCService { peerConnection.onconnectionstatechange = async () => { const state = peerConnection.connectionState - console.log(`Bağlantı durumu [${userId}]: ${state}`) if (state === 'closed') { this.closePeerConnection(userId) @@ -92,28 +82,38 @@ export class WebRTCService { if (state === 'failed') { let retries = this.retryCounts.get(userId) ?? 0 if (retries < this.maxRetries) { - console.warn( - `⚠️ Bağlantı failed oldu, ICE restart deneniyor [${userId}] (Deneme ${retries + 1})`, + toast.push( + , ) this.retryCounts.set(userId, retries + 1) await this.restartIce(peerConnection, userId) } else { - console.error( - `❌ Bağlantı ${this.maxRetries} denemede başarısız [${userId}], peer kapatılıyor.`, + toast.push( + , + { placement: 'top-center' }, ) this.closePeerConnection(userId) } } } - // En sona ekle if (this.candidateBuffer.has(userId)) { for (const cand of this.candidateBuffer.get(userId)!) { try { await peerConnection.addIceCandidate(cand) - console.log(`Buffered ICE candidate eklendi [${userId}]`) - } catch (err) { - console.warn(`Buffered candidate eklenemedi [${userId}]:`, err) + } catch { + toast.push( + , + ) } } this.candidateBuffer.delete(userId) @@ -139,9 +139,11 @@ export class WebRTCService { const offer = await pc.createOffer() await pc.setLocalDescription(offer) return offer - } catch (err) { - console.error('Offer oluşturulurken hata:', err) - throw err + } catch { + toast.push(, { + placement: 'top-center', + }) + throw new Error('Offer creation failed') } } @@ -157,44 +159,46 @@ export class WebRTCService { const answer = await pc.createAnswer() await pc.setLocalDescription(answer) return answer - } catch (err) { - console.error('Answer oluşturulurken hata:', err) - throw err + } catch { + toast.push(, { + placement: 'top-center', + }) + throw new Error('Answer creation failed') } } async handleAnswer(userId: string, answer: RTCSessionDescriptionInit): Promise { const peerConnection = this.peerConnections.get(userId) if (!peerConnection) throw new Error('Peer connection not found') - await peerConnection.setRemoteDescription(answer) } async addIceCandidate(userId: string, candidate: RTCIceCandidateInit): Promise { const pc = this.peerConnections.get(userId) if (!pc) { - // Peer yoksa buffer’a at if (!this.candidateBuffer.has(userId)) { this.candidateBuffer.set(userId, []) } this.candidateBuffer.get(userId)!.push(candidate) - console.warn(`ICE candidate bufferlandı [${userId}]`) return } if (pc.signalingState === 'stable' || pc.signalingState === 'have-remote-offer') { try { await pc.addIceCandidate(candidate) - } catch (err) { - console.warn(`ICE candidate eklenemedi [${userId}]:`, err) + } catch { + toast.push( + , + ) } } else { - // signalling hazır değilse → buffer’a at if (!this.candidateBuffer.has(userId)) { this.candidateBuffer.set(userId, []) } this.candidateBuffer.get(userId)!.push(candidate) - console.warn(`ICE candidate bufferlandı [${userId}], state=${pc.signalingState}`) } } @@ -223,8 +227,10 @@ export class WebRTCService { } }) } - } catch (err) { - console.error('Video açılırken hata:', err) + } catch { + toast.push(, { + placement: 'top-center', + }) } } } @@ -250,8 +256,10 @@ export class WebRTCService { } }) } - } catch (err) { - console.error('Audio açılırken hata:', err) + } catch { + toast.push(, { + placement: 'top-center', + }) } } } @@ -264,17 +272,18 @@ export class WebRTCService { try { const offer = await peerConnection.createOffer({ iceRestart: true }) await peerConnection.setLocalDescription(offer) - console.log(`ICE restart başlatıldı [${userId}]`) - // 🔑 SignalR üzerinden karşı tarafa gönder if (this.signalRService) { await this.signalRService.sendOffer(this.sessionId, userId, offer) - console.log(`ICE restart offer karşıya gönderildi [${userId}]`) } else { - console.warn('⚠️ SignalR servisi bağlı değil, offer gönderilemedi') + toast.push(, { + placement: 'top-center', + }) } - } catch (err) { - console.error(`ICE restart başarısız [${userId}]:`, err) + } catch { + toast.push(, { + placement: 'top-center', + }) } } @@ -284,7 +293,7 @@ export class WebRTCService { peerConnection.getSenders().forEach((sender) => sender.track?.stop()) peerConnection.close() this.peerConnections.delete(userId) - this.retryCounts.delete(userId) // sayaç temizle + this.retryCounts.delete(userId) } } @@ -311,7 +320,6 @@ export class WebRTCService { const alreadyHas = pc.getSenders().some((s) => s.track?.id === track.id) if (!alreadyHas) { pc.addTrack(track, stream) - // 🔑 track bittiğinde otomatik sil track.onended = () => { this.removeTrackFromPeers(track) } @@ -326,8 +334,10 @@ export class WebRTCService { if (sender.track === track) { try { pc.removeTrack(sender) - } catch (err) { - console.warn('removeTrack hata verdi:', err) + } catch { + toast.push(, { + placement: 'top-center', + }) } if (sender.track?.readyState !== 'ended') { sender.track?.stop() diff --git a/ui/src/views/classroom/RoomDetail.tsx b/ui/src/views/classroom/RoomDetail.tsx index 0c7ba91c..bea817a6 100644 --- a/ui/src/views/classroom/RoomDetail.tsx +++ b/ui/src/views/classroom/RoomDetail.tsx @@ -241,7 +241,7 @@ const RoomDetail: React.FC = () => { // WebRTC başlat webRTCServiceRef.current = new WebRTCService() webRTCServiceRef.current.setSignalRService(signalRServiceRef.current, classSession.id) - + const stream = await webRTCServiceRef.current.initializeLocalStream(micEnabled, camEnabled) if (stream) { setLocalStream(stream) @@ -251,7 +251,6 @@ const RoomDetail: React.FC = () => { // Setup WebRTC remote stream handler webRTCServiceRef.current.onRemoteStreamReceived((userId, stream) => { - console.log('Received remote stream from:', userId) setParticipants((prev) => prev.map((p) => (p.id === userId ? { ...p, stream } : p))) }) @@ -285,7 +284,6 @@ const RoomDetail: React.FC = () => { if (remoteUserId === user.id) return if (!isActive) return - console.log(`Participant joined: ${name}, isTeacher: ${isTeacher}`) toast.push(, { placement: 'top-center', }) @@ -366,11 +364,14 @@ const RoomDetail: React.FC = () => { }) signalRServiceRef.current.setParticipantLeaveHandler(({ userId, sessionId }) => { - console.log(`👋 Participant left handler: ${userId}, sessionId=${sessionId}`) + if (userId !== user.id) { + const leftUser = participants.find((p) => p.id === userId) + const leftName = leftUser ? leftUser.name : 'Bilinmeyen' - toast.push( p.id === userId)?.name}`} type="warning" />, { - placement: 'top-center', - }) + toast.push(, { + placement: 'top-center', + }) + } // peer connection’ı kapat webRTCServiceRef.current?.closePeerConnection(userId) @@ -438,7 +439,13 @@ const RoomDetail: React.FC = () => { true, ) } catch (error) { - console.error('Failed to initialize services:', error) + toast.push( + , + { placement: 'top-center' }, + ) } } @@ -476,7 +483,7 @@ const RoomDetail: React.FC = () => { // Başka sayfaya yönlendir navigate(ROUTES_ENUM.protected.admin.classroom.classes) } catch (err) { - console.error('Leave işlemi sırasında hata:', err) + toast.push() navigate(ROUTES_ENUM.protected.admin.classroom.classes) } } @@ -485,31 +492,43 @@ const RoomDetail: React.FC = () => { e.preventDefault() if (newMessage.trim() && signalRServiceRef.current) { if (messageMode === 'private' && selectedRecipient) { - await signalRServiceRef.current.sendPrivateMessage( - classSession.id, - user.id, - user.name, - newMessage.trim(), - selectedRecipient.id, - selectedRecipient.name, - user.role === 'teacher', - ) + try { + await signalRServiceRef.current.sendPrivateMessage( + classSession.id, + user.id, + user.name, + newMessage.trim(), + selectedRecipient.id, + selectedRecipient.name, + user.role === 'teacher', + ) + } catch (error) { + toast.push() + } } else if (messageMode === 'announcement' && user.role === 'teacher') { - await signalRServiceRef.current.sendAnnouncement( - classSession.id, - user.id, - user.name, - newMessage.trim(), - user.role === 'teacher', - ) + try { + await signalRServiceRef.current.sendAnnouncement( + classSession.id, + user.id, + user.name, + newMessage.trim(), + user.role === 'teacher', + ) + } catch (error) { + toast.push() + } } else { - await signalRServiceRef.current.sendChatMessage( - classSession.id, - user.id, - user.name, - newMessage.trim(), - user.role === 'teacher', - ) + try { + await signalRServiceRef.current.sendChatMessage( + classSession.id, + user.id, + user.name, + newMessage.trim(), + user.role === 'teacher', + ) + } catch (error) { + toast.push() + } } setNewMessage('') } @@ -521,12 +540,16 @@ const RoomDetail: React.FC = () => { isTeacher: boolean, ) => { if (signalRServiceRef.current && user.role === 'teacher') { - await signalRServiceRef.current.muteParticipant( - classSession.id, - participantId, - isMuted, - isTeacher, - ) + try { + await signalRServiceRef.current.muteParticipant( + classSession.id, + participantId, + isMuted, + isTeacher, + ) + } catch (err) { + toast.push() + } } } @@ -563,24 +586,26 @@ const RoomDetail: React.FC = () => { const handleKickParticipant = async (participantId: string) => { if (signalRServiceRef.current && user.role === 'teacher') { - console.log(`👢 handleKickParticipant UI’den çağrıldı: ${participantId}`) - - await signalRServiceRef.current.kickParticipant(classSession.id, participantId) - setAttendanceRecords((prev) => - prev.map((r) => { - if (r.studentId === participantId && !r.leaveTime) { - const leaveTime = new Date().toISOString() - const join = new Date(r.joinTime) - const leave = new Date(leaveTime) - const totalDurationMinutes = Math.max( - 1, - Math.round((leave.getTime() - join.getTime()) / 60000), - ) - return { ...r, leaveTime, totalDurationMinutes } - } - return r - }), - ) + try { + await signalRServiceRef.current.kickParticipant(classSession.id, participantId) + setAttendanceRecords((prev) => + prev.map((r) => { + if (r.studentId === participantId && !r.leaveTime) { + const leaveTime = new Date().toISOString() + const join = new Date(r.joinTime) + const leave = new Date(leaveTime) + const totalDurationMinutes = Math.max( + 1, + Math.round((leave.getTime() - join.getTime()) / 60000), + ) + return { ...r, leaveTime, totalDurationMinutes } + } + return r + }), + ) + } catch (error) { + toast.push() + } } } @@ -619,7 +644,12 @@ const RoomDetail: React.FC = () => { try { mic = await navigator.mediaDevices.getUserMedia({ audio: true }) } catch (err) { - console.warn('Mic alınamadı, sadece ekran paylaşılacak', err) + toast.push( + , + ) } // 3. merge et @@ -637,7 +667,7 @@ const RoomDetail: React.FC = () => { handleStopScreenShare() } } catch (error) { - console.error('Error starting screen share:', error) + toast.push() } }