classroom düzeltmeler
This commit is contained in:
parent
8acbd2e257
commit
dfd05cbf57
3 changed files with 77 additions and 59 deletions
|
|
@ -117,7 +117,6 @@ public class ClassroomHub : Hub
|
|||
[HubMethodName("JoinClass")]
|
||||
public async Task JoinClassAsync(Guid sessionId, Guid userId, string userName, bool isTeacher, bool isActive)
|
||||
{
|
||||
bool initialMuteState;
|
||||
var classroom = await _classSessionRepository.GetAsync(sessionId);
|
||||
if (classroom == null)
|
||||
{
|
||||
|
|
@ -129,7 +128,7 @@ public class ClassroomHub : Hub
|
|||
? new ClassroomSettingsDto()
|
||||
: JsonSerializer.Deserialize<ClassroomSettingsDto>(classroom.SettingsJson);
|
||||
|
||||
initialMuteState = !isTeacher && classroomSettings.AutoMuteNewParticipants
|
||||
bool initialMuteState = !isTeacher && classroomSettings.AutoMuteNewParticipants
|
||||
? true
|
||||
: classroomSettings.DefaultMicrophoneState == "muted";
|
||||
|
||||
|
|
@ -137,15 +136,14 @@ public class ClassroomHub : Hub
|
|||
x => x.SessionId == sessionId && x.UserId == userId
|
||||
);
|
||||
|
||||
// 🚨 Kick edilmiş kullanıcı tekrar giriş yapamaz
|
||||
// ❌ Kicklenmiş kullanıcı tekrar giremez
|
||||
if (participant != null && participant.IsKicked)
|
||||
{
|
||||
await Clients.Caller.SendAsync("Error", "You are not allowed to rejoin this class.");
|
||||
Context.Abort(); // bağlantıyı anında kapat
|
||||
Context.Abort();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (participant == null)
|
||||
{
|
||||
participant = new ClassroomParticipant(
|
||||
|
|
@ -156,20 +154,25 @@ public class ClassroomHub : Hub
|
|||
isTeacher,
|
||||
initialMuteState,
|
||||
classroomSettings.DefaultCameraState == "off",
|
||||
false, //isHandRaised
|
||||
false, //isKicked
|
||||
false, // isHandRaised
|
||||
false, // isKicked
|
||||
isActive
|
||||
);
|
||||
await _participantRepository.InsertAsync(participant, autoSave: true);
|
||||
await UpdateParticipantConnectionAsync(participant, Context.ConnectionId, isActive);
|
||||
await UpdateParticipantCountAsync(sessionId, classroom);
|
||||
}
|
||||
else
|
||||
{
|
||||
await UpdateParticipantConnectionAsync(participant, Context.ConnectionId, isActive);
|
||||
}
|
||||
|
||||
await UpdateParticipantConnectionAsync(participant, Context.ConnectionId, isActive);
|
||||
|
||||
// ✅ Attendance sadece yoksa aç
|
||||
var existingOpenAttendance = await _attendanceRepository.FirstOrDefaultAsync(
|
||||
x => x.SessionId == sessionId && x.StudentId == userId && x.LeaveTime == null
|
||||
);
|
||||
|
||||
if (existingOpenAttendance == null)
|
||||
{
|
||||
await CreateAttendanceAsync(sessionId, userId, userName);
|
||||
}
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, sessionId.ToString());
|
||||
|
||||
|
|
@ -189,9 +192,11 @@ public class ClassroomHub : Hub
|
|||
.ToList();
|
||||
|
||||
await Clients.Caller.SendAsync("ExistingParticipants", others);
|
||||
|
||||
await Clients.Group(sessionId.ToString())
|
||||
.SendAsync("ParticipantJoined", userId, userName, isTeacher, isActive);
|
||||
}
|
||||
|
||||
[HubMethodName("LeaveClass")]
|
||||
public async Task LeaveClassAsync(Guid sessionId)
|
||||
{
|
||||
|
|
@ -372,20 +377,21 @@ public class ClassroomHub : Hub
|
|||
{
|
||||
try
|
||||
{
|
||||
// 1. Attendance kapat
|
||||
// 1. Attendance kapat (sadece kicklenen için)
|
||||
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
|
||||
);
|
||||
|
||||
if (attendances != null)
|
||||
{
|
||||
foreach (var attendance in attendances)
|
||||
{
|
||||
await CloseAttendanceAsync(attendance);
|
||||
|
||||
// yalnızca diğer katılımcılar görebilsin
|
||||
await Clients.Group(sessionId.ToString())
|
||||
.SendAsync("AttendanceUpdated", attendance);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Participant bul
|
||||
var participant = await _participantRepository.FirstOrDefaultAsync(
|
||||
|
|
@ -394,22 +400,23 @@ public class ClassroomHub : Hub
|
|||
|
||||
if (participant == null)
|
||||
{
|
||||
_logger.LogWarning("KickParticipant: Session {SessionId} için participant {ParticipantId} bulunamadı",
|
||||
_logger.LogWarning(
|
||||
"KickParticipant: Session {SessionId} için participant {ParticipantId} bulunamadı",
|
||||
sessionId, participantId);
|
||||
return;
|
||||
}
|
||||
|
||||
// ConnectionId'yi kaydet (DB'ye null yazmadan önce)
|
||||
// ConnectionId'yi cache et (null yazmadan önce)
|
||||
var connectionId = participant.ConnectionId;
|
||||
|
||||
// 3. DB'de kick flag setle
|
||||
// 3. DB güncelle
|
||||
participant.IsActive = false;
|
||||
participant.IsKicked = true;
|
||||
participant.ConnectionId = null;
|
||||
|
||||
await _participantRepository.UpdateAsync(participant, autoSave: true);
|
||||
|
||||
// 4. Hedef kullanıcıya bildir
|
||||
// 4. Hedef kullanıcıya bildir (ForceDisconnect)
|
||||
if (!string.IsNullOrEmpty(connectionId))
|
||||
{
|
||||
await Clients.Client(connectionId)
|
||||
|
|
@ -418,9 +425,13 @@ public class ClassroomHub : Hub
|
|||
await Groups.RemoveFromGroupAsync(connectionId, sessionId.ToString());
|
||||
}
|
||||
|
||||
// 5. Diğer katılımcılara bildir
|
||||
// 5. Diğer katılımcılara duyur
|
||||
await Clients.Group(sessionId.ToString())
|
||||
.SendAsync("ParticipantLeft", new { UserId = participantId, SessionId = sessionId });
|
||||
.SendAsync("ParticipantLeft", new
|
||||
{
|
||||
UserId = participantId,
|
||||
SessionId = sessionId
|
||||
});
|
||||
|
||||
// 6. Log
|
||||
_logger.LogInformation("👢 Participant {ParticipantId} kicked from session {SessionId}",
|
||||
|
|
@ -428,7 +439,8 @@ public class ClassroomHub : Hub
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "❌ KickParticipant hata verdi (Session={SessionId}, Participant={ParticipantId})",
|
||||
_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.");
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export class SignalRService {
|
|||
.withUrl(`${import.meta.env.VITE_API_URL}/classroomhub`, {
|
||||
accessTokenFactory: () => auth.session.token || '',
|
||||
})
|
||||
.withAutomaticReconnect()
|
||||
//.withAutomaticReconnect()
|
||||
.configureLogging(signalR.LogLevel.Information)
|
||||
.build()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import {
|
||||
FaUsers,
|
||||
FaComments,
|
||||
FaUserPlus,
|
||||
FaExpand,
|
||||
FaHandPaper,
|
||||
FaVolumeMute,
|
||||
|
|
@ -19,7 +17,6 @@ import {
|
|||
FaCompress,
|
||||
FaUserFriends,
|
||||
FaLayerGroup,
|
||||
FaWrench,
|
||||
FaFilePdf,
|
||||
FaFileWord,
|
||||
FaFileImage,
|
||||
|
|
@ -278,40 +275,45 @@ const RoomDetail: React.FC = () => {
|
|||
await webRTCServiceRef.current?.addIceCandidate(fromUserId, candidate)
|
||||
})
|
||||
|
||||
// 🔑 Yeni katılan birini gördüğünde
|
||||
signalRServiceRef.current.setParticipantJoinHandler(
|
||||
async (userId: string, name: string, isTeacher: boolean, isActive: boolean) => {
|
||||
if (userId === user.id) return
|
||||
async (remoteUserId: string, name: string, isTeacher: boolean, isActive: boolean) => {
|
||||
if (remoteUserId === user.id) return
|
||||
if (!isActive) return
|
||||
|
||||
console.log(`Participant joined: ${name}, isTeacher: ${isTeacher}`)
|
||||
|
||||
// State’e ekle
|
||||
setParticipants((prev) => {
|
||||
const updated = [...prev]
|
||||
if (!updated.find((p) => p.id === userId)) {
|
||||
updated.push({
|
||||
id: userId,
|
||||
if (prev.find((p) => p.id === remoteUserId)) return prev
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
id: remoteUserId,
|
||||
name,
|
||||
sessionId: classSession.id,
|
||||
isTeacher,
|
||||
isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
|
||||
isVideoMuted: classSettings.defaultCameraState === 'off',
|
||||
isActive: true,
|
||||
})
|
||||
}
|
||||
return updated
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
// 🔑 Mesh: Herkes herkese offer gönderir
|
||||
if (!webRTCServiceRef.current?.getPeerConnection(userId)) {
|
||||
await webRTCServiceRef.current?.createPeerConnection(userId)
|
||||
// PeerConnection hazırla
|
||||
if (!webRTCServiceRef.current?.getPeerConnection(remoteUserId)) {
|
||||
await webRTCServiceRef.current?.createPeerConnection(remoteUserId)
|
||||
}
|
||||
|
||||
const offer = await webRTCServiceRef.current!.createOffer(userId)
|
||||
await signalRServiceRef.current?.sendOffer(classSession.id, userId, offer)
|
||||
// 🔑 Çakışmayı önle: sadece id’si küçük olan offer başlatır
|
||||
if (user.id < remoteUserId) {
|
||||
const offer = await webRTCServiceRef.current!.createOffer(remoteUserId)
|
||||
await signalRServiceRef.current?.sendOffer(classSession.id, remoteUserId, offer)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// 🔑 ExistingParticipants handler
|
||||
// 🔑 Odaya girdiğinde var olan katılımcılar
|
||||
signalRServiceRef.current.setExistingParticipantsHandler(
|
||||
async (
|
||||
existing: { userId: string; userName: string; isTeacher: boolean; isActive: boolean }[],
|
||||
|
|
@ -320,10 +322,12 @@ const RoomDetail: React.FC = () => {
|
|||
if (!participant.isActive) continue
|
||||
if (participant.userId === user.id) continue
|
||||
|
||||
// State’e ekle
|
||||
setParticipants((prev) => {
|
||||
const updated = [...prev]
|
||||
if (!updated.find((p) => p.id === participant.userId)) {
|
||||
updated.push({
|
||||
if (prev.find((p) => p.id === participant.userId)) return prev
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
id: participant.userId,
|
||||
name: participant.userName,
|
||||
sessionId: classSession.id,
|
||||
|
|
@ -331,19 +335,21 @@ const RoomDetail: React.FC = () => {
|
|||
isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
|
||||
isVideoMuted: classSettings.defaultCameraState === 'off',
|
||||
isActive: true,
|
||||
})
|
||||
}
|
||||
return updated
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
// PeerConnection hazırla
|
||||
if (!webRTCServiceRef.current?.getPeerConnection(participant.userId)) {
|
||||
await webRTCServiceRef.current?.createPeerConnection(participant.userId)
|
||||
}
|
||||
|
||||
// 🔑 Mesh: yeni gelen user da karşıya offer yollar
|
||||
// 🔑 Çakışmayı önle
|
||||
if (user.id < participant.userId) {
|
||||
const offer = await webRTCServiceRef.current!.createOffer(participant.userId)
|
||||
await signalRServiceRef.current?.sendOffer(classSession.id, participant.userId, offer)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue