fix: make call join/leave sounds audible to all participants + server-side hard voice limit docs
CI / Build & Quality Checks (push) Successful in 10m29s
Trigger Desktop Build / trigger (push) Successful in 5s

Sounds (P5-16): browsers block the Web Audio context until a user gesture
starts it, so join/leave sounds — which fire later with no gesture — were
silent. unlockCallSounds() now primes/resumes the shared AudioContext inside
the Join click (centralized in useCallStart so every join path is covered),
making the per-client sounds reliably audible to everyone in the call.

Voice limit (P5-10): the limit is now a hard, cross-client server-side cap
enforced by the voice-limit-guard sidecar (matrix repo) that fronts
lk-jwt-service and refuses LiveKit tokens when a room is full. Updated
LOTUS_FEATURES.md / README.md / LOTUS_TODO.md to reflect that the client
'Channel Full' check is UX only and the server is authoritative.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 23:45:31 -04:00
parent 702e2e00eb
commit 2c5f0b8b28
5 changed files with 30 additions and 8 deletions
+5
View File
@@ -16,6 +16,7 @@ import { useCallMembersChange, useCallSession } from './useCall';
import { CallPreferences } from '../state/callPreferences';
import { useSetting } from '../state/hooks/settings';
import { settingsAtom } from '../state/settings';
import { unlockCallSounds } from '../utils/callSounds';
const CallEmbedContext = createContext<CallEmbed | undefined>(undefined);
@@ -84,6 +85,10 @@ export const useCallStart = (dm = false) => {
if (!container) {
throw new Error('Failed to start call, No embed container element found!');
}
// startCall is always invoked from a click/tap handler — unlock the Web
// Audio context now (within the gesture) so join/leave sounds that fire
// later, without any gesture, are audible to everyone in the call.
unlockCallSounds();
const callEmbed = createCallEmbed(
mx,
room,
+11
View File
@@ -12,6 +12,17 @@ const getAudioContext = (): AudioContext | undefined => {
}
};
/**
* Create and resume the shared AudioContext from within a user gesture
* (e.g. clicking "Join"). Browsers block AudioContext playback until it has
* been started by a gesture, so join/leave sounds — which fire later without
* any gesture — would otherwise be silent. Call this on call entry so every
* participant's later membership-change sounds are actually audible.
*/
export const unlockCallSounds = (): void => {
getAudioContext();
};
type Note = {
freq: number;
/** Offset from now, in seconds */