Classroom güncellemesi

This commit is contained in:
Sedat Öztürk 2025-08-31 11:06:03 +03:00
parent b3415a4222
commit 6cc2c0f5db
4 changed files with 31 additions and 17 deletions

View file

@ -221,7 +221,8 @@ public class ClassroomHub : Hub
} }
await Clients.Group(sessionId.ToString()) 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); _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 Groups.RemoveFromGroupAsync(participant.ConnectionId, sessionId.ToString());
await DeactivateParticipantAsync(participant); 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 // 3. Diğerlerine duyur
_logger.LogInformation("👢 Participant {ParticipantId} kicked from session {SessionId}", participantId, sessionId); _logger.LogInformation("👢 Participant {ParticipantId} kicked from session {SessionId}", participantId, sessionId);
await Clients.Group(sessionId.ToString()).SendAsync("ParticipantLeft", participantId);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -528,7 +529,8 @@ public class ClassroomHub : Hub
// 🔑 Frontende bildir // 🔑 Frontende bildir
await Clients.Group(participant.SessionId.ToString()) await Clients.Group(participant.SessionId.ToString())
.SendAsync("ParticipantLeft", userId.Value); .SendAsync("ParticipantLeft", new { UserId = userId.Value, SessionId = participant.SessionId });
} }
} }
catch (TaskCanceledException) catch (TaskCanceledException)

View file

@ -56,6 +56,7 @@ export interface ClassroomAttendanceDto {
export interface ClassroomParticipantDto { export interface ClassroomParticipantDto {
id: string id: string
name: string name: string
sessionId: string
isTeacher: boolean isTeacher: boolean
isObserver?: boolean isObserver?: boolean
isAudioMuted?: boolean isAudioMuted?: boolean

View file

@ -21,7 +21,7 @@ export class SignalRService {
isTeacher: boolean, isTeacher: boolean,
isActive: boolean, isActive: boolean,
) => void ) => void
private onParticipantLeft?: (userId: string) => void private onParticipantLeft?: (payload: { userId: string; sessionId: string }) => void
private onChatMessage?: (message: ClassroomChatDto) => void private onChatMessage?: (message: ClassroomChatDto) => void
private onParticipantMuted?: (userId: string, isMuted: boolean) => void private onParticipantMuted?: (userId: string, isMuted: boolean) => void
private onHandRaiseReceived?: (studentId: string) => void private onHandRaiseReceived?: (studentId: string) => void
@ -60,8 +60,8 @@ export class SignalRService {
}, },
) )
this.connection.on('ParticipantLeft', (userId: string) => { this.connection.on('ParticipantLeft', (payload: { userId: string; sessionId: string }) => {
this.onParticipantLeft?.(userId) this.onParticipantLeft?.(payload)
}) })
this.connection.on('ChatMessage', (message: any) => { this.connection.on('ChatMessage', (message: any) => {
@ -101,16 +101,23 @@ export class SignalRService {
this.isConnected = true this.isConnected = true
}) })
this.connection.onclose(async () => { this.connection.onclose(async (err) => {
this.isConnected = false this.isConnected = false
// Eğer kick sebebiyle kapandıysa tekrar LeaveClass deneme
if (this.isKicked) {
this.currentSessionId = undefined
return
}
try { try {
if (this.currentSessionId) { if (this.currentSessionId) {
await this.connection.invoke('LeaveClass', this.currentSessionId) await this.connection.invoke('LeaveClass', this.currentSessionId)
} }
} catch (err) { } 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 { } 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) console.log('Error starting SignalR connection simulating leave class for user', auth.user.id)
// Simulate successful leave in demo mode // Simulate successful leave in demo mode
setTimeout(() => { setTimeout(() => {
this.onParticipantLeft?.(auth.user.id) this.onParticipantLeft?.({ userId: auth.user.id, sessionId })
}, 100) }, 100)
return return
} }
@ -364,7 +371,7 @@ export class SignalRService {
async kickParticipant(sessionId: string, participantId: string): Promise<void> { async kickParticipant(sessionId: string, participantId: string): Promise<void> {
if (!this.isConnected) { if (!this.isConnected) {
setTimeout(() => { setTimeout(() => {
this.onParticipantLeft?.(participantId) this.onParticipantLeft?.({ userId: participantId, sessionId })
}, 100) }, 100)
return return
} }
@ -435,7 +442,7 @@ export class SignalRService {
this.onParticipantJoined = callback this.onParticipantJoined = callback
} }
setParticipantLeaveHandler(callback: (userId: string) => void) { setParticipantLeaveHandler(callback: (payload: { userId: string; sessionId: string }) => void) {
this.onParticipantLeft = callback this.onParticipantLeft = callback
} }

View file

@ -290,6 +290,7 @@ const RoomDetail: React.FC = () => {
updated.push({ updated.push({
id: userId, id: userId,
name, name,
sessionId: classSession.id,
isTeacher, isTeacher,
isAudioMuted: classSettings.defaultMicrophoneState === 'muted', isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
isVideoMuted: classSettings.defaultCameraState === 'off', isVideoMuted: classSettings.defaultCameraState === 'off',
@ -334,6 +335,7 @@ const RoomDetail: React.FC = () => {
updated.push({ updated.push({
id: participant.userId, id: participant.userId,
name: participant.userName, name: participant.userName,
sessionId: classSession.id,
isTeacher: participant.isTeacher, isTeacher: participant.isTeacher,
isAudioMuted: classSettings.defaultMicrophoneState === 'muted', isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
isVideoMuted: classSettings.defaultCameraState === 'off', isVideoMuted: classSettings.defaultCameraState === 'off',
@ -373,14 +375,16 @@ const RoomDetail: React.FC = () => {
}, },
) )
signalRServiceRef.current.setParticipantLeaveHandler((userId) => { signalRServiceRef.current.setParticipantLeaveHandler(({ userId, sessionId }) => {
console.log(`Participant left: ${userId}`) console.log(`Participant left: ${userId} from session ${sessionId}`)
// peer connectionı kapat // peer connectionı kapat
webRTCServiceRef.current?.closePeerConnection(userId) webRTCServiceRef.current?.closePeerConnection(userId)
// katılımcıyı stateden sil // katılımcıyı stateden tamamen sil
setParticipants((prev) => prev.filter((p) => p.id !== userId)) setParticipants((prev) =>
prev.filter((p) => !(p.id === userId && p.sessionId === sessionId)),
)
}) })
signalRServiceRef.current.setAttendanceUpdatedHandler((record) => { signalRServiceRef.current.setAttendanceUpdatedHandler((record) => {