(fullstack) support everyone_can_mute room configuration

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.
This commit is contained in:
lebaudantoine
2026-03-24 23:04:35 +01:00
committed by aleb_the_flash
parent 9846a61bd0
commit c20daafd81
8 changed files with 100 additions and 1 deletions
+1
View File
@@ -329,6 +329,7 @@ class RoomConfiguration(BaseModel):
"""
can_publish_sources: list[RoomConfigurationTrackSource] | None = None
everyone_can_mute: bool | None = None
model_config = {"extra": "forbid"}
@@ -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
@@ -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 (
<Div
display="flex"
@@ -130,6 +133,17 @@ export const Admin = () => {
fullWidth: true,
}}
/>
<Field
type="switch"
label={t('moderation.mute.label')}
description={t('moderation.mute.description')}
isSelected={isMutingEnabled}
onChange={toggleMuting}
wrapperProps={{
noMargin: true,
fullWidth: true,
}}
/>
</div>
</div>
<div
@@ -0,0 +1,46 @@
import { usePatchRoom } from '@/features/rooms/api/patchRoom'
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
import { useCallback } from 'react'
import { queryClient } from '@/api/queryClient'
import { keys } from '@/api/queryKeys'
export const usePermissionsManager = () => {
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,
}
}
+4
View File
@@ -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."
}
}
},
+4
View File
@@ -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."
}
}
},
+4
View File
@@ -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."
}
}
},
+4
View File
@@ -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."
}
}
},