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