🚸(frontend) mute participants by default when joining a large meeting

When joining a meeting that already has many participants, new
participants are now muted by default.

This is an empirical change, not directly requested by users but
informed by user experience: a lot of people joining large meetings
arrive with their microphone and/or camera open, which can be
painful for the host, who otherwise has to mute everyone or ask
everyone to mute. Many of these participants are inattentive but
still noisy.

If you are joining a room that is already at a certain size, you are
probably not the one expected to speak; presenters typically join
among the very first participants.
This commit is contained in:
lebaudantoine
2026-06-15 15:42:47 +02:00
committed by aleb_the_flash
parent 00e197b216
commit 040df0e15a
18 changed files with 144 additions and 2 deletions
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import {
@@ -33,6 +33,9 @@ import { useIsMobile } from '@/utils/useIsMobile'
import { navigateTo } from '@/navigation/navigateTo'
import { connectionObserverStore } from '@/stores/connectionObserver'
import { PictureInPictureConference } from '@/features/pip/components/PictureInPictureConference'
import { notifyAutoMutedOnJoin } from '@/features/notifications/utils'
import { useSnapshot } from 'valtio'
import { userPreferencesStore } from '@/stores/userPreferences'
export const Conference = ({
roomId,
@@ -57,6 +60,8 @@ export const Conference = ({
const [isConnectionWarmedUp, setIsConnectionWarmedUp] = useState(false)
const userPreferencesSnap = useSnapshot(userPreferencesStore)
const {
mutateAsync: createRoom,
status: createStatus,
@@ -173,6 +178,8 @@ export const Conference = ({
const isMobile = useIsMobile()
const hasAutoMutedRef = useRef(false)
/*
* Ensure stable WebSocket connection URL. This is critical for legacy browser compatibility
* (Firefox <124, Chrome <125, Edge <125) where HTTPS URLs in WebSocket() constructor
@@ -228,6 +235,19 @@ export const Conference = ({
onError={(e) => {
posthog.captureException(e)
}}
onConnected={async () => {
if (!apiConfig) return
if (
userPreferencesSnap.is_auto_mute_large_room_enabled &&
!hasAutoMutedRef.current &&
userConfig.audioEnabled &&
room.numParticipants > apiConfig.auto_mute_on_join_threshold
) {
hasAutoMutedRef.current = true
await room.localParticipant.setMicrophoneEnabled(false)
notifyAutoMutedOnJoin()
}
}}
onDisconnected={(e) => {
const metadata = {
room_id: roomId,