Classroom güncellemeleri
This commit is contained in:
parent
09380b1e63
commit
a9d2b33638
2 changed files with 74 additions and 66 deletions
|
|
@ -188,14 +188,17 @@ public class ClassroomHub : Hub
|
|||
var userId = _currentUser.Id;
|
||||
if (!userId.HasValue) return;
|
||||
|
||||
var attendance = await _attendanceRepository.FirstOrDefaultAsync(
|
||||
var attendances = await _attendanceRepository.GetListAsync(
|
||||
x => x.SessionId == sessionId && x.StudentId == userId.Value && x.LeaveTime == null
|
||||
);
|
||||
|
||||
if (attendance != null)
|
||||
if (attendances != null)
|
||||
{
|
||||
await CloseAttendanceAsync(attendance);
|
||||
await Clients.Group(sessionId.ToString()).SendAsync("AttendanceUpdated", attendance);
|
||||
foreach (var attendance in attendances)
|
||||
{
|
||||
await CloseAttendanceAsync(attendance);
|
||||
await Clients.Group(sessionId.ToString()).SendAsync("AttendanceUpdated", attendance);
|
||||
}
|
||||
}
|
||||
|
||||
var participant = await _participantRepository.FirstOrDefaultAsync(
|
||||
|
|
@ -356,18 +359,19 @@ public class ClassroomHub : Hub
|
|||
{
|
||||
try
|
||||
{
|
||||
// 1. Attendance kapat
|
||||
var attendance = await _attendanceRepository.FirstOrDefaultAsync(
|
||||
var attendances = await _attendanceRepository.GetListAsync(
|
||||
x => x.SessionId == sessionId && x.StudentId == participantId && x.LeaveTime == null
|
||||
);
|
||||
|
||||
if (attendance != null)
|
||||
if (attendances != null)
|
||||
{
|
||||
await CloseAttendanceAsync(attendance);
|
||||
await Clients.Group(sessionId.ToString()).SendAsync("AttendanceUpdated", attendance);
|
||||
foreach (var attendance in attendances)
|
||||
{
|
||||
await CloseAttendanceAsync(attendance);
|
||||
await Clients.Group(sessionId.ToString()).SendAsync("AttendanceUpdated", attendance);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Participant bul
|
||||
var participant = await _participantRepository.FirstOrDefaultAsync(
|
||||
x => x.SessionId == sessionId && x.UserId == participantId
|
||||
);
|
||||
|
|
@ -455,40 +459,53 @@ public class ClassroomHub : Hub
|
|||
.SendAsync("ReceiveIceCandidate", _currentUser.Id?.ToString(), candidate);
|
||||
}
|
||||
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Eğer bağlantı zaten iptal edilmişse boşuna uğraşma
|
||||
if (Context.ConnectionAborted.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
var userId = _currentUser.Id;
|
||||
if (userId.HasValue)
|
||||
if (!userId.HasValue)
|
||||
{
|
||||
// 🔑 1. Katılımcı listesi
|
||||
var participants = await _participantRepository
|
||||
.GetListAsync(x => x.UserId == userId.Value && x.ConnectionId == Context.ConnectionId);
|
||||
_logger.LogWarning("OnDisconnectedAsync çağrıldı fakat UserId bulunamadı. ConnId={ConnectionId}", Context.ConnectionId);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var participant in participants)
|
||||
// 🔑 Aynı anda birden fazla session olabilir (tab senaryosu)
|
||||
var participants = await _participantRepository
|
||||
.GetListAsync(x => x.UserId == userId.Value && x.ConnectionId == Context.ConnectionId);
|
||||
|
||||
if (!participants.Any())
|
||||
{
|
||||
_logger.LogInformation("OnDisconnectedAsync: User {UserId} için aktif participant bulunamadı. ConnId={ConnectionId}", userId, Context.ConnectionId);
|
||||
}
|
||||
|
||||
foreach (var participant in participants)
|
||||
{
|
||||
_logger.LogInformation("OnDisconnectedAsync: User {UserId}, Session {SessionId} bağlantısı koptu.", userId, participant.SessionId);
|
||||
|
||||
// 🔑 Attendance kapat
|
||||
var attendances = await _attendanceRepository.GetListAsync(
|
||||
x => x.SessionId == participant.SessionId &&
|
||||
x.StudentId == userId.Value &&
|
||||
x.LeaveTime == null
|
||||
);
|
||||
|
||||
if (attendances != null)
|
||||
{
|
||||
// 🔑 2. Attendance kaydını kapat
|
||||
var attendance = await _attendanceRepository.FirstOrDefaultAsync(
|
||||
x => x.SessionId == participant.SessionId &&
|
||||
x.StudentId == userId.Value &&
|
||||
x.LeaveTime == null
|
||||
);
|
||||
|
||||
if (attendance != null)
|
||||
foreach (var attendance in attendances)
|
||||
{
|
||||
attendance.LeaveTime = DateTime.UtcNow;
|
||||
attendance.TotalDurationMinutes = (int)Math.Max(
|
||||
1,
|
||||
(attendance.LeaveTime.Value - attendance.JoinTime).TotalMinutes
|
||||
);
|
||||
|
||||
await _attendanceRepository.UpdateAsync(attendance, autoSave: true);
|
||||
|
||||
// Frontend’e bildir
|
||||
await Clients.Group(participant.SessionId.ToString())
|
||||
.SendAsync("AttendanceUpdated", new
|
||||
{
|
||||
|
|
@ -501,24 +518,26 @@ public class ClassroomHub : Hub
|
|||
attendance.TotalDurationMinutes
|
||||
});
|
||||
}
|
||||
|
||||
participant.IsActive = false;
|
||||
participant.ConnectionId = null;
|
||||
await _participantRepository.UpdateAsync(participant, autoSave: true);
|
||||
|
||||
// 🔑 3. ParticipantLeft event’i
|
||||
await Clients.Group(participant.SessionId.ToString())
|
||||
.SendAsync("ParticipantLeft", userId.Value);
|
||||
}
|
||||
|
||||
// 🔑 Participant pasifleştir
|
||||
participant.IsActive = false;
|
||||
participant.ConnectionId = null;
|
||||
|
||||
await _participantRepository.UpdateAsync(participant, autoSave: true);
|
||||
|
||||
// 🔑 Frontend’e bildir
|
||||
await Clients.Group(participant.SessionId.ToString())
|
||||
.SendAsync("ParticipantLeft", userId.Value);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
_logger.LogDebug("OnDisconnectedAsync iptal edildi (connection aborted).");
|
||||
_logger.LogDebug("OnDisconnectedAsync iptal edildi (connection aborted). ConnId={ConnectionId}", Context.ConnectionId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "OnDisconnectedAsync hata");
|
||||
_logger.LogError(ex, "OnDisconnectedAsync hata. ConnId={ConnectionId}", Context.ConnectionId);
|
||||
}
|
||||
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ import {
|
|||
HandRaiseDto,
|
||||
MessageType,
|
||||
} from '@/proxy/classroom/models'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { store } from '@/store/store'
|
||||
import * as signalR from '@microsoft/signalr'
|
||||
|
||||
export class SignalRService {
|
||||
private connection!: signalR.HubConnection
|
||||
private isConnected: boolean = false
|
||||
private currentSessionId?: string
|
||||
|
||||
private onAttendanceUpdate?: (record: ClassroomAttendanceDto) => void
|
||||
private onParticipantJoined?: (
|
||||
userId: string,
|
||||
|
|
@ -68,7 +71,6 @@ export class SignalRService {
|
|||
this.onParticipantMuted?.(userId, isMuted)
|
||||
})
|
||||
|
||||
|
||||
this.connection.on('HandRaiseReceived', (payload: any) => {
|
||||
// payload = { handRaiseId, studentId, studentName, ... }
|
||||
this.onHandRaiseReceived?.(payload.studentId)
|
||||
|
|
@ -96,12 +98,19 @@ export class SignalRService {
|
|||
|
||||
this.connection.onreconnected(() => {
|
||||
this.isConnected = true
|
||||
console.log('SignalR reconnected')
|
||||
})
|
||||
|
||||
this.connection.onclose(() => {
|
||||
this.connection.onclose(async () => {
|
||||
this.isConnected = false
|
||||
console.log('SignalR connection closed')
|
||||
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)
|
||||
} finally {
|
||||
this.currentSessionId = undefined // ✅ garanti sıfırlama
|
||||
}
|
||||
})
|
||||
|
||||
this.connection.on('Error', (message: string) => {
|
||||
|
|
@ -112,7 +121,7 @@ export class SignalRService {
|
|||
console.warn('⚠️ ForceDisconnect received:', message)
|
||||
|
||||
await this.disconnect()
|
||||
window.location.href = '/classrooms'
|
||||
window.location.href = ROUTES_ENUM.protected.admin.classroom.classes
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +129,6 @@ export class SignalRService {
|
|||
try {
|
||||
await this.connection.start()
|
||||
this.isConnected = true
|
||||
console.log('SignalR connection started')
|
||||
} catch (error) {
|
||||
console.error('Error starting SignalR connection:', error)
|
||||
// Switch to demo mode if connection fails
|
||||
|
|
@ -140,16 +148,8 @@ export class SignalRService {
|
|||
return
|
||||
}
|
||||
|
||||
console.log(
|
||||
'Joining class session:',
|
||||
sessionId,
|
||||
'as',
|
||||
userName,
|
||||
'isTeacher:',
|
||||
isTeacher,
|
||||
'isActive:',
|
||||
isActive,
|
||||
)
|
||||
//Global değişkene yazılıyor.
|
||||
this.currentSessionId = sessionId
|
||||
|
||||
try {
|
||||
await this.connection.invoke('JoinClass', sessionId, userId, userName, isTeacher, isActive)
|
||||
|
|
@ -172,6 +172,9 @@ export class SignalRService {
|
|||
|
||||
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)
|
||||
}
|
||||
|
|
@ -185,7 +188,6 @@ export class SignalRService {
|
|||
isTeacher: boolean,
|
||||
): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log('Error starting SignalR connection simulating chat message from', senderName)
|
||||
const chatMessage: ClassroomChatDto = {
|
||||
id: crypto.randomUUID(),
|
||||
sessionId,
|
||||
|
|
@ -227,12 +229,6 @@ export class SignalRService {
|
|||
isTeacher: boolean,
|
||||
): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log(
|
||||
'Error starting SignalR connection simulating private message from',
|
||||
senderName,
|
||||
'to',
|
||||
recipientName,
|
||||
)
|
||||
const chatMessage: ClassroomChatDto = {
|
||||
id: crypto.randomUUID(),
|
||||
sessionId,
|
||||
|
|
@ -276,7 +272,6 @@ export class SignalRService {
|
|||
isTeacher: boolean,
|
||||
): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log('Error starting SignalR connection simulating announcement from', senderName)
|
||||
const chatMessage: ClassroomChatDto = {
|
||||
id: crypto.randomUUID(),
|
||||
sessionId,
|
||||
|
|
@ -314,15 +309,12 @@ export class SignalRService {
|
|||
isTeacher: boolean,
|
||||
): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log('Error starting SignalR connection simulating mute participant', userId, isMuted)
|
||||
setTimeout(() => {
|
||||
this.onParticipantMuted?.(userId, isMuted)
|
||||
}, 100)
|
||||
return
|
||||
}
|
||||
|
||||
console.log('Muting participant:', userId, 'Muted:', isMuted, 'isTeacher:', isTeacher)
|
||||
|
||||
try {
|
||||
await this.connection.invoke('MuteParticipant', sessionId, userId, isMuted, isTeacher)
|
||||
} catch (error) {
|
||||
|
|
@ -332,7 +324,6 @@ export class SignalRService {
|
|||
|
||||
async raiseHand(sessionId: string, studentId: string, studentName: string): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log('Error starting SignalR connection simulating hand raise from', studentName)
|
||||
const handRaise: HandRaiseDto = {
|
||||
id: crypto.randomUUID(),
|
||||
studentId,
|
||||
|
|
@ -355,7 +346,6 @@ export class SignalRService {
|
|||
|
||||
async kickParticipant(sessionId: string, participantId: string): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log('Error starting SignalR connection simulating kick participant', participantId)
|
||||
setTimeout(() => {
|
||||
this.onParticipantLeft?.(participantId)
|
||||
}, 100)
|
||||
|
|
@ -371,7 +361,6 @@ export class SignalRService {
|
|||
|
||||
async approveHandRaise(sessionId: string, studentId: string): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log('Simulating hand raise approval for student', studentId)
|
||||
setTimeout(() => {
|
||||
this.onHandRaiseDismissed?.(studentId)
|
||||
}, 100)
|
||||
|
|
@ -387,7 +376,6 @@ export class SignalRService {
|
|||
|
||||
async dismissHandRaise(sessionId: string, studentId: string): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
console.log('Simulating hand raise dismissal for student', studentId)
|
||||
setTimeout(() => {
|
||||
this.onHandRaiseDismissed?.(studentId)
|
||||
}, 100)
|
||||
|
|
@ -472,6 +460,7 @@ export class SignalRService {
|
|||
if (this.isConnected && this.connection) {
|
||||
await this.connection.stop()
|
||||
this.isConnected = false
|
||||
this.currentSessionId = undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue