From c20daafd81914cce29bc7478b7226bf3030fd809 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Tue, 24 Mar 2026 23:04:35 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(fullstack)=20support=20`everyone=5Fca?= =?UTF-8?q?n=5Fmute`=20room=20configuration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new room setting controlling whether all participants, including non-privileged users, can mute others. Update API validation accordingly and add the frontend controls allowing administrators to toggle the option and persist the configuration through the API. --- src/backend/core/api/serializers.py | 1 + .../core/tests/rooms/test_api_rooms_update.py | 22 +++++++++ .../rooms/livekit/components/Admin.tsx | 16 ++++++- .../livekit/hooks/usePermissionsManager.ts | 46 +++++++++++++++++++ src/frontend/src/locales/de/rooms.json | 4 ++ src/frontend/src/locales/en/rooms.json | 4 ++ src/frontend/src/locales/fr/rooms.json | 4 ++ src/frontend/src/locales/nl/rooms.json | 4 ++ 8 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/frontend/src/features/rooms/livekit/hooks/usePermissionsManager.ts diff --git a/src/backend/core/api/serializers.py b/src/backend/core/api/serializers.py index 77a9b278..bd40c632 100644 --- a/src/backend/core/api/serializers.py +++ b/src/backend/core/api/serializers.py @@ -329,6 +329,7 @@ class RoomConfiguration(BaseModel): """ can_publish_sources: list[RoomConfigurationTrackSource] | None = None + everyone_can_mute: bool | None = None model_config = {"extra": "forbid"} 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 258be673..f9f86eec 100644 --- a/src/backend/core/tests/rooms/test_api_rooms_update.py +++ b/src/backend/core/tests/rooms/test_api_rooms_update.py @@ -122,6 +122,10 @@ def test_api_rooms_update_administrators(): }, {"can_publish_sources": []}, {"can_publish_sources": None}, + {"can_publish_sources": None, "everyone_can_mute": True}, + {"can_publish_sources": None, "everyone_can_mute": False}, + {"can_publish_sources": None, "everyone_can_mute": "yes"}, + {"can_publish_sources": None, "everyone_can_mute": "1"}, ], ) def test_api_rooms_update_configuration_valid(configuration): @@ -198,6 +202,24 @@ def test_api_rooms_update_configuration_wrong_type(): assert room.configuration == {} +@pytest.mark.parametrize("invalid_value", ["test", [], {}]) +def test_api_rooms_update_configuration_everyone_can_mute_wrong_type(invalid_value): + """everyone_can_mute values with wrong types should be rejected.""" + user = UserFactory() + room = RoomFactory(users=[(user, "owner")]) + client = APIClient() + client.force_login(user) + + response = client.patch( + f"/api/v1.0/rooms/{room.id!s}/", + {"configuration": {"everyone_can_mute": invalid_value}}, + format="json", + ) + assert response.status_code == 400 + room.refresh_from_db() + assert room.configuration == {} + + def test_api_rooms_update_administrators_of_another(): """ Being administrator or owner of a room should not grant authorization to update diff --git a/src/frontend/src/features/rooms/livekit/components/Admin.tsx b/src/frontend/src/features/rooms/livekit/components/Admin.tsx index efe1090a..1ebbacda 100644 --- a/src/frontend/src/features/rooms/livekit/components/Admin.tsx +++ b/src/frontend/src/features/rooms/livekit/components/Admin.tsx @@ -9,7 +9,8 @@ import { queryClient } from '@/api/queryClient' import { keys } from '@/api/queryKeys' import { useQuery } from '@tanstack/react-query' import { useParams } from 'wouter' -import { usePublishSourcesManager } from '@/features/rooms/livekit/hooks/usePublishSourcesManager' +import { usePublishSourcesManager } from '../hooks/usePublishSourcesManager' +import { usePermissionsManager } from '../hooks/usePermissionsManager' export const Admin = () => { const { t } = useTranslation('rooms', { keyPrefix: 'admin' }) @@ -38,6 +39,8 @@ export const Admin = () => { isScreenShareEnabled, } = usePublishSourcesManager() + const { toggleMuting, isMutingEnabled } = usePermissionsManager() + return (
{ fullWidth: true, }} /> +
{ + const { mutateAsync: patchRoom } = usePatchRoom() + + const data = useRoomData() + const configuration = data?.configuration + const roomId = data?.slug + + const isMutingEnabled = configuration?.everyone_can_mute ?? true + + const toggleMuting = useCallback( + async (enabled: boolean) => { + if (!roomId) return + + try { + const newConfiguration = { + ...configuration, + everyone_can_mute: enabled, + } + + const room = await patchRoom({ + roomId, + room: { configuration: newConfiguration }, + }) + + queryClient.setQueryData([keys.room, roomId], room) + + return { configuration: newConfiguration } + } catch (error) { + console.error('Failed to update muting permission:', error) + return { success: false, error } + } + }, + [configuration, roomId, patchRoom] + ) + + return { + toggleMuting, + isMutingEnabled, + } +} diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index 2582c353..f9c81637 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -528,6 +528,10 @@ "screenshare": { "label": "Bildschirm teilen", "description": "Wenn du diese Option deaktivierst, können Teilnehmende ihren Bildschirm nicht mehr teilen. Laufende Bildschirmfreigaben werden sofort beendet." + }, + "mute": { + "label": "Andere stummschalten", + "description": "Wenn deaktiviert, können Teilnehmer andere Teilnehmer nicht mehr stummschalten." } } }, diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index c09beded..af3b1947 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -527,6 +527,10 @@ "screenshare": { "label": "Share their screen", "description": "Disabling this option will prevent participants from sharing their screen, and any ongoing screen sharing will be stopped immediately." + }, + "mute": { + "label": "Mute others", + "description": "When disabled, participants will no longer be able to mute other participants." } } }, diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index d0a64e42..5421a207 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -527,6 +527,10 @@ "screenshare": { "label": "Partager leur écran", "description": "En désactivant cette option, les participants ne pourront plus partager leur écran et tout partage en cours sera immédiatement interrompu." + }, + "mute": { + "label": "Muter les autres", + "description": "En désactivant cette option, les participants ne pourront plus muter d'autres participants." } } }, diff --git a/src/frontend/src/locales/nl/rooms.json b/src/frontend/src/locales/nl/rooms.json index 57896e25..3579b2ab 100644 --- a/src/frontend/src/locales/nl/rooms.json +++ b/src/frontend/src/locales/nl/rooms.json @@ -527,6 +527,10 @@ "screenshare": { "label": "Hun scherm delen", "description": "Als u deze optie uitschakelt, kunnen deelnemers hun scherm niet meer delen en wordt elke lopende schermdeling onmiddellijk gestopt." + }, + "mute": { + "label": "Anderen dempen", + "description": "Wanneer uitgeschakeld, kunnen deelnemers andere deelnemers niet meer dempen." } } },