UI Refactoring and Fix of image issue when adding an image to a quiz

This commit is contained in:
Juhn-Vitus Saß
2025-04-06 19:49:46 +02:00
parent 3f07ce4ec7
commit cf7a2fb8bb
7 changed files with 473 additions and 278 deletions

View File

@@ -4,8 +4,9 @@ import asyncio
from ..models import QuizGame, QuizGameParticipant
from channels.db import database_sync_to_async
# Dictionary to track inactive check tasks per game
# Dictionary to track inactive check tasks and active connections per game
game_check_tasks = {}
game_connections = {}
class LobbyConsumer(AsyncWebsocketConsumer):
@classmethod
@@ -16,39 +17,20 @@ class LobbyConsumer(AsyncWebsocketConsumer):
room_group_name = f'lobby_{join_code}'
while True:
try:
await asyncio.sleep(30) # Check every 30 seconds
await asyncio.sleep(10) # Check every 10 seconds to match frontend heartbeat
# Get participants list and broadcast update
try:
from django.utils import timezone
from datetime import timedelta
game = await database_sync_to_async(QuizGame.objects.get)(join_code=join_code)
cutoff_time = timezone.now() - timedelta(minutes=1)
# Get previous active participants
prev_active = await database_sync_to_async(lambda: set(
game.participants.values_list('display_name', flat=True)
))()
# Get current active participants
cutoff_time = timezone.now() - timedelta(seconds=20) # Consider inactive after 20 seconds (2 missed heartbeats)
# Get current active participants with full info
active_participants = await database_sync_to_async(lambda: list(
game.participants.filter(last_heartbeat__gte=cutoff_time)
.values_list('display_name', flat=True)
.values('participant_id', 'display_name')
))()
# Find removed participants
removed = set(prev_active) - set(active_participants)
# Send notifications for removed participants
for name in removed:
await channel_layer.group_send(
room_group_name,
{
'type': 'player_left',
'player_name': name,
'was_kicked': True
}
)
# Broadcast update to all clients
# Send update to all clients
await channel_layer.group_send(
room_group_name,
{
@@ -76,6 +58,14 @@ class LobbyConsumer(AsyncWebsocketConsumer):
# Get participant ID from session
self.participant_id = self.scope['session'].get('participant_id')
# Track connection
if self.join_code not in game_connections:
game_connections[self.join_code] = set()
game_connections[self.join_code].add(self.channel_name)
# Start inactive check task if not already running
await self.start_inactive_check(self.join_code, self.channel_layer)
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
@@ -87,9 +77,6 @@ class LobbyConsumer(AsyncWebsocketConsumer):
# Send initial participants list
await self.update_participants()
# Ensure inactive check is running for this game
await self.start_inactive_check(self.join_code, self.channel_layer)
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
@@ -97,6 +84,17 @@ class LobbyConsumer(AsyncWebsocketConsumer):
self.channel_name
)
# Remove from connection tracking
if self.join_code in game_connections:
game_connections[self.join_code].discard(self.channel_name)
if not game_connections[self.join_code]:
# Last connection closed
del game_connections[self.join_code]
if self.join_code in game_check_tasks:
# Cancel the inactive check task
game_check_tasks[self.join_code].cancel()
del game_check_tasks[self.join_code]
async def receive(self, text_data):
data = json.loads(text_data)
message_type = data.get('type')
@@ -113,8 +111,7 @@ class LobbyConsumer(AsyncWebsocketConsumer):
participant_id = data.get('participant_id')
if participant_id:
await self.update_participant_heartbeat(participant_id)
# Update participants list after heartbeat
await self.update_participants()
# Don't update participants list after heartbeat - let the background task handle it
elif message_type in ['leave_game', 'kick_player']:
participant_id = data.get('participant_id')
if participant_id:
@@ -200,7 +197,7 @@ class LobbyConsumer(AsyncWebsocketConsumer):
from datetime import timedelta
game = await database_sync_to_async(QuizGame.objects.get)(join_code=self.join_code)
# Only return participants that have been seen in the last minute
cutoff_time = timezone.now() - timedelta(minutes=1)
cutoff_time = timezone.now() - timedelta(seconds=20) # Consider inactive after 20 seconds (2 missed heartbeats)
participants = await database_sync_to_async(lambda: list(
game.participants.filter(last_heartbeat__gte=cutoff_time)
.values('participant_id', 'display_name')