classroom düzeltmeler

This commit is contained in:
Sedat Öztürk 2025-08-31 21:09:59 +03:00
parent b67080646c
commit 8acbd2e257
8 changed files with 62 additions and 75 deletions

View file

@ -372,6 +372,7 @@ public class ClassroomHub : Hub
{ {
try try
{ {
// 1. Attendance kapat
var attendances = await _attendanceRepository.GetListAsync( var attendances = await _attendanceRepository.GetListAsync(
x => x.SessionId == sessionId && x.StudentId == participantId && x.LeaveTime == null x => x.SessionId == sessionId && x.StudentId == participantId && x.LeaveTime == null
); );
@ -381,42 +382,60 @@ public class ClassroomHub : Hub
foreach (var attendance in attendances) foreach (var attendance in attendances)
{ {
await CloseAttendanceAsync(attendance); await CloseAttendanceAsync(attendance);
await Clients.Group(sessionId.ToString()).SendAsync("AttendanceUpdated", attendance); await Clients.Group(sessionId.ToString())
.SendAsync("AttendanceUpdated", attendance);
} }
} }
// 2. Participant bul
var participant = await _participantRepository.FirstOrDefaultAsync( var participant = await _participantRepository.FirstOrDefaultAsync(
x => x.SessionId == sessionId && x.UserId == participantId x => x.SessionId == sessionId && x.UserId == participantId
); );
if (participant != null && !string.IsNullOrEmpty(participant.ConnectionId)) if (participant == null)
{ {
await Clients.Client(participant.ConnectionId) _logger.LogWarning("KickParticipant: Session {SessionId} için participant {ParticipantId} bulunamadı",
.SendAsync("ForceDisconnect", "You have been removed from the class."); sessionId, participantId);
return;
}
await Groups.RemoveFromGroupAsync(participant.ConnectionId, sessionId.ToString()); // ConnectionId'yi kaydet (DB'ye null yazmadan önce)
var connectionId = participant.ConnectionId;
// ❌ pasif + ✅ kicked işaretle // 3. DB'de kick flag setle
participant.IsActive = false; participant.IsActive = false;
participant.IsKicked = true; participant.IsKicked = true;
participant.ConnectionId = null; participant.ConnectionId = null;
await _participantRepository.UpdateAsync(participant, autoSave: true); await _participantRepository.UpdateAsync(participant, autoSave: true);
await Clients.Group(sessionId.ToString()) // 4. Hedef kullanıcıya bildir
.SendAsync("ParticipantLeft", new { UserId = participantId, SessionId = sessionId }); if (!string.IsNullOrEmpty(connectionId))
{
await Clients.Client(connectionId)
.SendAsync("ForceDisconnect", "You have been removed from the class.");
await Groups.RemoveFromGroupAsync(connectionId, sessionId.ToString());
} }
// 5. Diğer katılımcılara bildir
await Clients.Group(sessionId.ToString())
.SendAsync("ParticipantLeft", new { UserId = participantId, SessionId = sessionId });
// 3. Diğerlerine duyur // 6. Log
_logger.LogInformation("👢 Participant {ParticipantId} kicked from session {SessionId}", participantId, sessionId); _logger.LogInformation("👢 Participant {ParticipantId} kicked from session {SessionId}",
participantId, sessionId);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "❌ KickParticipant hata verdi"); _logger.LogError(ex, "❌ KickParticipant hata verdi (Session={SessionId}, Participant={ParticipantId})",
sessionId, participantId);
await Clients.Caller.SendAsync("Error", "Kick işlemi başarısız oldu."); await Clients.Caller.SendAsync("Error", "Kick işlemi başarısız oldu.");
} }
} }
[HubMethodName("ApproveHandRaise")] [HubMethodName("ApproveHandRaise")]
public async Task ApproveHandRaiseAsync(Guid sessionId, Guid studentId) public async Task ApproveHandRaiseAsync(Guid sessionId, Guid studentId)
{ {

View file

@ -281,8 +281,7 @@ const RoomDetail: React.FC = () => {
signalRServiceRef.current.setParticipantJoinHandler( signalRServiceRef.current.setParticipantJoinHandler(
async (userId: string, name: string, isTeacher: boolean, isActive: boolean) => { async (userId: string, name: string, isTeacher: boolean, isActive: boolean) => {
if (userId === user.id) return if (userId === user.id) return
if (!isActive) return // ❌ pasif kullanıcıyı ekleme if (!isActive) return
if (isTeacher) setTeacherDisconnected(false)
console.log(`Participant joined: ${name}, isTeacher: ${isTeacher}`) console.log(`Participant joined: ${name}, isTeacher: ${isTeacher}`)
@ -296,31 +295,19 @@ const RoomDetail: React.FC = () => {
isTeacher, isTeacher,
isAudioMuted: classSettings.defaultMicrophoneState === 'muted', isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
isVideoMuted: classSettings.defaultCameraState === 'off', isVideoMuted: classSettings.defaultCameraState === 'off',
isActive: isActive, // ✅ statede tut isActive: true,
}) })
} }
return updated
})
// ✅ güncel listedeki öğretmen kontrolü // 🔑 Mesh: Herkes herkese offer gönderir
const teacherExists = updated.some((p) => p.isTeacher && p.isActive)
if (teacherExists) {
;(async () => {
if (!webRTCServiceRef.current?.getPeerConnection(userId)) { if (!webRTCServiceRef.current?.getPeerConnection(userId)) {
await webRTCServiceRef.current?.createPeerConnection(userId) await webRTCServiceRef.current?.createPeerConnection(userId)
} }
// ✅ öğretmen ise her zaman offer başlatır
if (isActive) {
if (user.role === 'teacher') {
const offer = await webRTCServiceRef.current!.createOffer(userId) const offer = await webRTCServiceRef.current!.createOffer(userId)
await signalRServiceRef.current?.sendOffer(classSession.id, userId, offer) await signalRServiceRef.current?.sendOffer(classSession.id, userId, offer)
}
}
})()
}
return updated
})
}, },
) )
@ -329,12 +316,12 @@ const RoomDetail: React.FC = () => {
async ( async (
existing: { userId: string; userName: string; isTeacher: boolean; isActive: boolean }[], existing: { userId: string; userName: string; isTeacher: boolean; isActive: boolean }[],
) => { ) => {
setParticipants((prev) => {
let updated = [...prev]
for (const participant of existing) { for (const participant of existing) {
if (!participant.isActive) continue // ❌ pasif kullanıcıyı alma if (!participant.isActive) continue
if (participant.userId === user.id) continue if (participant.userId === user.id) continue
setParticipants((prev) => {
const updated = [...prev]
if (!updated.find((p) => p.id === participant.userId)) { if (!updated.find((p) => p.id === participant.userId)) {
updated.push({ updated.push({
id: participant.userId, id: participant.userId,
@ -343,39 +330,20 @@ const RoomDetail: React.FC = () => {
isTeacher: participant.isTeacher, isTeacher: participant.isTeacher,
isAudioMuted: classSettings.defaultMicrophoneState === 'muted', isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
isVideoMuted: classSettings.defaultCameraState === 'off', isVideoMuted: classSettings.defaultCameraState === 'off',
isActive: participant.isActive, // ✅ statede tut isActive: true,
}) })
} }
} return updated
})
// ✅ güncel listede öğretmen var mı?
const teacherExists = updated.some((p) => p.isTeacher && p.isActive)
if (teacherExists) {
;(async () => {
for (const participant of existing) {
if (!participant.isActive) continue
if (participant.userId === user.id) continue
if (!webRTCServiceRef.current?.getPeerConnection(participant.userId)) { if (!webRTCServiceRef.current?.getPeerConnection(participant.userId)) {
await webRTCServiceRef.current?.createPeerConnection(participant.userId) await webRTCServiceRef.current?.createPeerConnection(participant.userId)
} }
// ✅ sadece öğretmen offer başlatır // 🔑 Mesh: yeni gelen user da karşıya offer yollar
if (user.role === 'teacher') {
const offer = await webRTCServiceRef.current!.createOffer(participant.userId) const offer = await webRTCServiceRef.current!.createOffer(participant.userId)
await signalRServiceRef.current?.sendOffer( await signalRServiceRef.current?.sendOffer(classSession.id, participant.userId, offer)
classSession.id,
participant.userId,
offer,
)
} }
}
})()
}
return updated
})
}, },
) )