classroom isActive Participant
This commit is contained in:
parent
6eea5f8880
commit
08f5d11f28
10 changed files with 61 additions and 19 deletions
|
|
@ -13,4 +13,5 @@ public class ClassroomParticipantDto
|
|||
public bool IsVideoMuted { get; set; }
|
||||
public bool IsHandRaised { get; set; }
|
||||
public DateTime JoinTime { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,6 +187,12 @@ public class ClassroomAppService : PlatformAppService, IClassroomAppService
|
|||
public async Task<ClassroomDto> JoinClassAsync(Guid id)
|
||||
{
|
||||
var classSession = await _classSessionRepository.GetAsync(id);
|
||||
if (classSession == null)
|
||||
{
|
||||
throw new InvalidOperationException("Class not found");
|
||||
}
|
||||
|
||||
var classroomSettings = JsonSerializer.Deserialize<ClassroomSettingsDto>(classSession.SettingsJson);
|
||||
|
||||
if (classSession.ParticipantCount >= classSession.MaxParticipants)
|
||||
{
|
||||
|
|
@ -206,7 +212,11 @@ public class ClassroomAppService : PlatformAppService, IClassroomAppService
|
|||
id,
|
||||
CurrentUser.Id,
|
||||
CurrentUser.Name,
|
||||
false // isTeacher
|
||||
false, // isTeacher
|
||||
classroomSettings?.DefaultMicrophoneState == "muted",
|
||||
classroomSettings?.DefaultCameraState == "off",
|
||||
false, // HandRaised
|
||||
true // isActive
|
||||
);
|
||||
|
||||
await _participantRepository.InsertAsync(participant);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public class ClassroomParticipant : FullAuditedEntity<Guid>
|
|||
public bool IsAudioMuted { get; set; } = false;
|
||||
public bool IsVideoMuted { get; set; } = false;
|
||||
public bool IsHandRaised { get; set; } = false;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime JoinTime { get; set; }
|
||||
public string ConnectionId { get; set; }
|
||||
|
||||
|
|
@ -30,7 +31,8 @@ public class ClassroomParticipant : FullAuditedEntity<Guid>
|
|||
bool isTeacher,
|
||||
bool isAudioMuted,
|
||||
bool isVideoMuted,
|
||||
bool isHandRaised
|
||||
bool isHandRaised,
|
||||
bool isActive
|
||||
) : base(id)
|
||||
{
|
||||
SessionId = sessionId;
|
||||
|
|
@ -40,6 +42,7 @@ public class ClassroomParticipant : FullAuditedEntity<Guid>
|
|||
IsAudioMuted = isAudioMuted;
|
||||
IsVideoMuted = isVideoMuted;
|
||||
IsHandRaised = isHandRaised;
|
||||
IsActive = isActive;
|
||||
JoinTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Kurs.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20250829093104_Initial")]
|
||||
[Migration("20250830114522_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -1816,6 +1816,9 @@ namespace Kurs.Platform.Migrations
|
|||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsAudioMuted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
|
|
@ -1996,6 +1996,7 @@ namespace Kurs.Platform.Migrations
|
|||
IsAudioMuted = table.Column<bool>(type: "bit", nullable: false),
|
||||
IsVideoMuted = table.Column<bool>(type: "bit", nullable: false),
|
||||
IsHandRaised = table.Column<bool>(type: "bit", nullable: false),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
JoinTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
ConnectionId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
|
|
@ -1813,6 +1813,9 @@ namespace Kurs.Platform.Migrations
|
|||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsAudioMuted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ public class ClassroomHub : Hub
|
|||
isTeacher,
|
||||
classroomSettings.DefaultMicrophoneState == "muted",
|
||||
classroomSettings.DefaultCameraState == "off",
|
||||
false
|
||||
false,
|
||||
true
|
||||
);
|
||||
participant.UpdateConnectionId(Context.ConnectionId);
|
||||
await _participantRepository.InsertAsync(participant, autoSave: true);
|
||||
|
|
@ -96,14 +97,19 @@ public class ClassroomHub : Hub
|
|||
await Groups.AddToGroupAsync(Context.ConnectionId, sessionId.ToString());
|
||||
|
||||
// 🔑 Yeni katılana mevcut katılımcıları gönder
|
||||
var existingParticipants = await _participantRepository.GetListAsync(x => x.SessionId == sessionId);
|
||||
// 🔑 Yeni katılana mevcut aktif katılımcıları gönder
|
||||
var existingParticipants = await _participantRepository.GetListAsync(
|
||||
x => x.SessionId == sessionId && x.IsActive
|
||||
);
|
||||
|
||||
var others = existingParticipants
|
||||
.Where(x => x.ConnectionId != Context.ConnectionId)
|
||||
.Select(x => new
|
||||
{
|
||||
UserId = x.UserId,
|
||||
UserName = x.UserName,
|
||||
IsTeacher = x.IsTeacher
|
||||
IsTeacher = x.IsTeacher,
|
||||
IsActive = x.IsActive // ✅ aktiflik bilgisini de gönder
|
||||
})
|
||||
.ToList();
|
||||
|
||||
|
|
@ -111,7 +117,7 @@ public class ClassroomHub : Hub
|
|||
|
||||
// 🔑 Grup üyelerine yeni katılanı öğretmen bilgisiyle bildir
|
||||
await Clients.Group(sessionId.ToString())
|
||||
.SendAsync("ParticipantJoined", userId, userName, isTeacher);
|
||||
.SendAsync("ParticipantJoined", userId, userName, isTeacher, true);
|
||||
}
|
||||
|
||||
[HubMethodName("LeaveClass")]
|
||||
|
|
@ -138,6 +144,17 @@ public class ClassroomHub : Hub
|
|||
await Clients.Group(sessionId.ToString())
|
||||
.SendAsync("AttendanceUpdated", attendance);
|
||||
}
|
||||
|
||||
//Kullanıcıyı Pasife aldım.
|
||||
var participant = await _participantRepository.FirstOrDefaultAsync(
|
||||
x => x.SessionId == sessionId && x.UserId == userId
|
||||
);
|
||||
|
||||
if (participant == null)
|
||||
{
|
||||
participant.IsActive = false;
|
||||
await _participantRepository.UpdateAsync(participant, autoSave: true);
|
||||
}
|
||||
}
|
||||
|
||||
await Clients.Group(sessionId.ToString())
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export interface ClassroomParticipantDto {
|
|||
isAudioMuted?: boolean
|
||||
isVideoMuted?: boolean
|
||||
isHandRaised?: boolean
|
||||
isActive?: boolean
|
||||
stream?: MediaStream
|
||||
screenStream?: MediaStream
|
||||
isScreenSharing?: boolean
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export class SignalRService {
|
|||
private connection!: signalR.HubConnection
|
||||
private isConnected: boolean = false
|
||||
private onAttendanceUpdate?: (record: ClassroomAttendanceDto) => void
|
||||
private onParticipantJoined?: (userId: string, name: string, isTeacher: boolean) => void
|
||||
private onParticipantJoined?: (userId: string, name: string, isTeacher: boolean, isActive: boolean) => void
|
||||
private onParticipantLeft?: (userId: string) => void
|
||||
private onChatMessage?: (message: ClassroomChatDto) => void
|
||||
private onParticipantMuted?: (userId: string, isMuted: boolean) => void
|
||||
|
|
@ -39,8 +39,8 @@ export class SignalRService {
|
|||
this.onAttendanceUpdate?.(record)
|
||||
})
|
||||
|
||||
this.connection.on('ParticipantJoined', (userId: string, name: string, isTeacher: boolean) => {
|
||||
this.onParticipantJoined?.(userId, name, isTeacher)
|
||||
this.connection.on('ParticipantJoined', (userId: string, name: string, isTeacher: boolean, isActive: boolean) => {
|
||||
this.onParticipantJoined?.(userId, name, isTeacher, isActive)
|
||||
})
|
||||
|
||||
this.connection.on('ParticipantLeft', (userId: string) => {
|
||||
|
|
@ -391,7 +391,7 @@ export class SignalRService {
|
|||
this.onAttendanceUpdate = callback
|
||||
}
|
||||
|
||||
setParticipantJoinHandler(callback: (userId: string, name: string, isTeacher: boolean) => void) {
|
||||
setParticipantJoinHandler(callback: (userId: string, name: string, isTeacher: boolean, isActive: boolean) => void) {
|
||||
this.onParticipantJoined = callback
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -262,8 +262,9 @@ const RoomDetail: React.FC = () => {
|
|||
})
|
||||
|
||||
signalRServiceRef.current.setParticipantJoinHandler(
|
||||
async (userId: string, name: string, isTeacher: boolean) => {
|
||||
async (userId: string, name: string, isTeacher: boolean, isActive: boolean) => {
|
||||
if (userId === user.id) return
|
||||
if (!isActive) return // ❌ pasif kullanıcıyı ekleme
|
||||
|
||||
console.log(`Participant joined: ${name}, isTeacher: ${isTeacher}`)
|
||||
|
||||
|
|
@ -276,6 +277,7 @@ const RoomDetail: React.FC = () => {
|
|||
isTeacher,
|
||||
isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
|
||||
isVideoMuted: classSettings.defaultCameraState === 'off',
|
||||
isActive: isActive, // ✅ state’de tut
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -291,8 +293,6 @@ const RoomDetail: React.FC = () => {
|
|||
await signalRServiceRef.current?.sendOffer(classSession.id, userId, offer)
|
||||
}
|
||||
})()
|
||||
} else {
|
||||
console.log('Teacher yok, offer başlatılmadı.')
|
||||
}
|
||||
|
||||
return updated
|
||||
|
|
@ -302,11 +302,14 @@ const RoomDetail: React.FC = () => {
|
|||
|
||||
// 🔑 ExistingParticipants handler
|
||||
signalRServiceRef.current.setExistingParticipantsHandler(
|
||||
async (existing: { userId: string; userName: string; isTeacher: boolean }[]) => {
|
||||
async (
|
||||
existing: { userId: string; userName: string; isTeacher: boolean; isActive: boolean }[],
|
||||
) => {
|
||||
setParticipants((prev) => {
|
||||
let updated = [...prev]
|
||||
|
||||
for (const participant of existing) {
|
||||
if (!participant.isActive) continue // ❌ pasif kullanıcıyı alma
|
||||
if (participant.userId === user.id) continue
|
||||
if (!updated.find((p) => p.id === participant.userId)) {
|
||||
updated.push({
|
||||
|
|
@ -315,6 +318,7 @@ const RoomDetail: React.FC = () => {
|
|||
isTeacher: participant.isTeacher,
|
||||
isAudioMuted: classSettings.defaultMicrophoneState === 'muted',
|
||||
isVideoMuted: classSettings.defaultCameraState === 'off',
|
||||
isActive: participant.isActive, // ✅ state’de tut
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -324,6 +328,7 @@ const RoomDetail: React.FC = () => {
|
|||
if (teacherExists) {
|
||||
;(async () => {
|
||||
for (const participant of existing) {
|
||||
if (!participant.isActive) continue // ❌ pasifleri bağlama
|
||||
if (participant.userId === user.id) continue
|
||||
if (!webRTCServiceRef.current?.getPeerConnection(participant.userId)) {
|
||||
await webRTCServiceRef.current?.createPeerConnection(participant.userId)
|
||||
|
|
@ -338,8 +343,6 @@ const RoomDetail: React.FC = () => {
|
|||
}
|
||||
}
|
||||
})()
|
||||
} else {
|
||||
console.log('Teacher yok, offer başlatılmadı (existing).')
|
||||
}
|
||||
|
||||
return updated
|
||||
|
|
@ -764,7 +767,7 @@ const RoomDetail: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
window.addEventListener('beforeunload', handleLeaveCall)
|
||||
window.addEventListener('pagehide', handleLeaveCall)
|
||||
window.addEventListener('pagehide', handleLeaveCall)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleLeaveCall)
|
||||
|
|
@ -812,7 +815,7 @@ const RoomDetail: React.FC = () => {
|
|||
{/* Video Grid */}
|
||||
<div className="flex-1 relative overflow-hidden min-h-0 h-full">
|
||||
<ParticipantGrid
|
||||
participants={participants}
|
||||
participants={participants.filter((p) => p.isActive)}
|
||||
localStream={user.role === 'observer' ? undefined : localStream}
|
||||
currentUserId={user.id}
|
||||
currentUserName={user.name}
|
||||
|
|
|
|||
Loading…
Reference in a new issue