diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 3fd0e72e..3c1e0c0e 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -309,12 +309,20 @@ class RoomViewSet( """Persist the room update, then sync metadata to LiveKit.""" old_configuration = serializer.instance.configuration + old_access_level = serializer.instance.access_level + room = serializer.save() - if room.configuration == old_configuration: + if ( + room.configuration == old_configuration + and room.access_level == old_access_level + ): return - metadata = {"configuration": room.configuration} + metadata = { + "configuration": room.configuration, + "access_level": room.access_level, + } try: RoomManagement().update_metadata( diff --git a/src/backend/core/tests/rooms/test_api_rooms_update.py b/src/backend/core/tests/rooms/test_api_rooms_update.py index 91cf934e..7d19ff80 100644 --- a/src/backend/core/tests/rooms/test_api_rooms_update.py +++ b/src/backend/core/tests/rooms/test_api_rooms_update.py @@ -117,6 +117,7 @@ def test_api_rooms_update_administrators(mock_update_metadata): mock_update_metadata.assert_called_once_with( room_name=str(room.id), metadata={ + "access_level": "public", "configuration": {"can_publish_sources": ["camera", "microphone"]}, }, ) @@ -153,6 +154,7 @@ def test_api_rooms_update_administrators_configuration_only(mock_update_metadata mock_update_metadata.assert_called_once_with( room_name=str(room.id), metadata={ + "access_level": "restricted", "configuration": {"can_publish_sources": ["camera", "microphone"]}, }, ) @@ -160,7 +162,7 @@ def test_api_rooms_update_administrators_configuration_only(mock_update_metadata @patch.object(RoomManagement, "update_metadata") def test_api_rooms_update_administrators_access_level_only(mock_update_metadata): - """Should not sync LiveKit metadata when only access level changes.""" + """Should sync LiveKit metadata when only access level changes.""" user = UserFactory() room = RoomFactory( access_level=RoomAccessLevel.RESTRICTED, @@ -185,7 +187,13 @@ def test_api_rooms_update_administrators_access_level_only(mock_update_metadata) assert room.access_level == RoomAccessLevel.PUBLIC assert room.configuration == {"can_publish_sources": ["camera"]} - mock_update_metadata.assert_not_called() + mock_update_metadata.assert_called_once_with( + room_name=str(room.id), + metadata={ + "access_level": "public", + "configuration": {"can_publish_sources": ["camera"]}, + }, + ) @patch.object(RoomManagement, "update_metadata") @@ -395,7 +403,10 @@ def test_api_rooms_update_livekit_room_not_found(mock_update_metadata): mock_update_metadata.assert_called_once_with( room_name=str(room.id), - metadata={"configuration": {"can_publish_sources": ["camera"]}}, + metadata={ + "access_level": room.access_level, + "configuration": {"can_publish_sources": ["camera"]}, + }, ) @@ -421,5 +432,8 @@ def test_api_rooms_update_livekit_sync_failure(mock_update_metadata): mock_update_metadata.assert_called_once_with( room_name=str(room.id), - metadata={"configuration": {"can_publish_sources": ["camera"]}}, + metadata={ + "access_level": room.access_level, + "configuration": {"can_publish_sources": ["camera"]}, + }, ) diff --git a/src/frontend/src/features/rooms/livekit/hooks/useSyncLiveKitMetadata.ts b/src/frontend/src/features/rooms/livekit/hooks/useSyncLiveKitMetadata.ts index 154af313..98856e4c 100644 --- a/src/frontend/src/features/rooms/livekit/hooks/useSyncLiveKitMetadata.ts +++ b/src/frontend/src/features/rooms/livekit/hooks/useSyncLiveKitMetadata.ts @@ -4,7 +4,11 @@ import { useEffect } from 'react' import { RoomEvent } from 'livekit-client' import { queryClient } from '@/api/queryClient' import { keys } from '@/api/queryKeys' -import type { ApiRoom, RoomConfiguration } from '@/features/rooms/api/ApiRoom' +import { + ApiAccessLevel, + ApiRoom, + RoomConfiguration, +} from '@/features/rooms/api/ApiRoom' import { useRoomContext } from '@livekit/components-react' import { useRoomData } from './useRoomData' @@ -14,6 +18,7 @@ import { useRoomData } from './useRoomData' */ type RoomLiveKitMetadata = { configuration?: RoomConfiguration + access_level?: ApiAccessLevel } const parseMetadata = (raw: string | undefined): RoomLiveKitMetadata | null => { @@ -46,19 +51,24 @@ export const useSyncLiveKitMetadata = () => { const applyMetadata = (raw: string | undefined) => { const parsed = parseMetadata(raw) - // No configuration key → leave the cache alone. Don't clobber the - // value we already loaded from the API with `undefined`. - if (!parsed?.configuration) return + if (!parsed) return queryClient.setQueryData([keys.room, roomSlug], (prev) => { if (!prev) return prev + const nextConfiguration = parsed.configuration ?? prev.configuration + const nextAccessLevel = parsed.access_level ?? prev.access_level if ( - JSON.stringify(prev.configuration) === - JSON.stringify(parsed.configuration) + nextConfiguration === prev.configuration && + nextAccessLevel === prev.access_level ) { return prev } - return { ...prev, configuration: parsed.configuration } + + return { + ...prev, + configuration: nextConfiguration, + access_level: nextAccessLevel, + } }) }