From 6cc2c0f5db1a3f30c8f8e99390dc2d150db750b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Sun, 31 Aug 2025 11:06:03 +0300 Subject: [PATCH] =?UTF-8?q?Classroom=20g=C3=BCncellemesi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Classroom/ClassroomHub.cs | 10 +++++--- ui/src/proxy/classroom/models.ts | 1 + ui/src/services/classroom/signalr.ts | 25 ++++++++++++------- ui/src/views/classroom/RoomDetail.tsx | 12 ++++++--- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs b/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs index b4680bc9..eaf73ec3 100644 --- a/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs +++ b/api/src/Kurs.Platform.HttpApi.Host/Classroom/ClassroomHub.cs @@ -221,7 +221,8 @@ public class ClassroomHub : Hub } await Clients.Group(sessionId.ToString()) - .SendAsync("ParticipantLeft", userId.Value); + .SendAsync("ParticipantLeft", new { UserId = userId.Value, SessionId = sessionId }); + _logger.LogInformation("User {UserId} left class {SessionId}", userId, sessionId); } @@ -393,12 +394,12 @@ public class ClassroomHub : Hub await Groups.RemoveFromGroupAsync(participant.ConnectionId, sessionId.ToString()); await DeactivateParticipantAsync(participant); - await Clients.Group(sessionId.ToString()).SendAsync("ParticipantLeft", participantId); + await Clients.Group(sessionId.ToString()) + .SendAsync("ParticipantLeft", new { UserId = participantId, SessionId = sessionId }); } // 3. Diğerlerine duyur _logger.LogInformation("👢 Participant {ParticipantId} kicked from session {SessionId}", participantId, sessionId); - await Clients.Group(sessionId.ToString()).SendAsync("ParticipantLeft", participantId); } catch (Exception ex) { @@ -528,7 +529,8 @@ public class ClassroomHub : Hub // 🔑 Frontend’e bildir await Clients.Group(participant.SessionId.ToString()) - .SendAsync("ParticipantLeft", userId.Value); + .SendAsync("ParticipantLeft", new { UserId = userId.Value, SessionId = participant.SessionId }); + } } catch (TaskCanceledException) diff --git a/ui/src/proxy/classroom/models.ts b/ui/src/proxy/classroom/models.ts index 9f4820d4..d6da1abd 100644 --- a/ui/src/proxy/classroom/models.ts +++ b/ui/src/proxy/classroom/models.ts @@ -56,6 +56,7 @@ export interface ClassroomAttendanceDto { export interface ClassroomParticipantDto { id: string name: string + sessionId: string isTeacher: boolean isObserver?: boolean isAudioMuted?: boolean diff --git a/ui/src/services/classroom/signalr.ts b/ui/src/services/classroom/signalr.ts index 7028c59b..d0092134 100644 --- a/ui/src/services/classroom/signalr.ts +++ b/ui/src/services/classroom/signalr.ts @@ -21,7 +21,7 @@ export class SignalRService { isTeacher: boolean, isActive: boolean, ) => void - private onParticipantLeft?: (userId: string) => void + private onParticipantLeft?: (payload: { userId: string; sessionId: string }) => void private onChatMessage?: (message: ClassroomChatDto) => void private onParticipantMuted?: (userId: string, isMuted: boolean) => void private onHandRaiseReceived?: (studentId: string) => void @@ -60,8 +60,8 @@ export class SignalRService { }, ) - this.connection.on('ParticipantLeft', (userId: string) => { - this.onParticipantLeft?.(userId) + this.connection.on('ParticipantLeft', (payload: { userId: string; sessionId: string }) => { + this.onParticipantLeft?.(payload) }) this.connection.on('ChatMessage', (message: any) => { @@ -101,16 +101,23 @@ export class SignalRService { this.isConnected = true }) - this.connection.onclose(async () => { + this.connection.onclose(async (err) => { this.isConnected = false + + // Eğer kick sebebiyle kapandıysa tekrar LeaveClass deneme + if (this.isKicked) { + this.currentSessionId = undefined + return + } + try { if (this.currentSessionId) { await this.connection.invoke('LeaveClass', this.currentSessionId) } } catch (err) { - console.warn('LeaveClass could not be sent, maybe server already closed', err) + console.warn('LeaveClass could not be sent, connection was already closed') } finally { - this.currentSessionId = undefined // ✅ garanti sıfırlama + this.currentSessionId = undefined } }) @@ -182,7 +189,7 @@ export class SignalRService { console.log('Error starting SignalR connection simulating leave class for user', auth.user.id) // Simulate successful leave in demo mode setTimeout(() => { - this.onParticipantLeft?.(auth.user.id) + this.onParticipantLeft?.({ userId: auth.user.id, sessionId }) }, 100) return } @@ -364,7 +371,7 @@ export class SignalRService { async kickParticipant(sessionId: string, participantId: string): Promise { if (!this.isConnected) { setTimeout(() => { - this.onParticipantLeft?.(participantId) + this.onParticipantLeft?.({ userId: participantId, sessionId }) }, 100) return } @@ -435,7 +442,7 @@ export class SignalRService { this.onParticipantJoined = callback } - setParticipantLeaveHandler(callback: (userId: string) => void) { + setParticipantLeaveHandler(callback: (payload: { userId: string; sessionId: string }) => void) { this.onParticipantLeft = callback } diff --git a/ui/src/views/classroom/RoomDetail.tsx b/ui/src/views/classroom/RoomDetail.tsx index 545b1ec0..274d2591 100644 --- a/ui/src/views/classroom/RoomDetail.tsx +++ b/ui/src/views/classroom/RoomDetail.tsx @@ -290,6 +290,7 @@ const RoomDetail: React.FC = () => { updated.push({ id: userId, name, + sessionId: classSession.id, isTeacher, isAudioMuted: classSettings.defaultMicrophoneState === 'muted', isVideoMuted: classSettings.defaultCameraState === 'off', @@ -334,6 +335,7 @@ const RoomDetail: React.FC = () => { updated.push({ id: participant.userId, name: participant.userName, + sessionId: classSession.id, isTeacher: participant.isTeacher, isAudioMuted: classSettings.defaultMicrophoneState === 'muted', isVideoMuted: classSettings.defaultCameraState === 'off', @@ -373,14 +375,16 @@ const RoomDetail: React.FC = () => { }, ) - signalRServiceRef.current.setParticipantLeaveHandler((userId) => { - console.log(`Participant left: ${userId}`) + signalRServiceRef.current.setParticipantLeaveHandler(({ userId, sessionId }) => { + console.log(`Participant left: ${userId} from session ${sessionId}`) // peer connection’ı kapat webRTCServiceRef.current?.closePeerConnection(userId) - // katılımcıyı state’den sil - setParticipants((prev) => prev.filter((p) => p.id !== userId)) + // katılımcıyı state’den tamamen sil + setParticipants((prev) => + prev.filter((p) => !(p.id === userId && p.sessionId === sessionId)), + ) }) signalRServiceRef.current.setAttendanceUpdatedHandler((record) => {