Fix meeting audio playback and restyle room detail actions.
Deploy Ladill Meet / deploy (push) Successful in 39s

Unlock LiveKit audio after user gesture, respect join mute/video settings,
and replace plain links with btn-secondary pills on the meeting details page.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-01 13:38:01 +00:00
co-authored by Cursor
parent 7ae26a467c
commit dc3c6dee2d
4 changed files with 121 additions and 10 deletions
+71 -2
View File
@@ -30,6 +30,9 @@ function meetRoom() {
captionText: '',
displayName: '',
canPublish: true,
muteOnJoin: true,
videoOffOnJoin: false,
audioPlaybackBlocked: false,
isWebinar: false,
qaItems: [],
activePolls: [],
@@ -95,6 +98,8 @@ function meetRoom() {
this.isHost = el.dataset.isHost === '1';
this.canPublish = el.dataset.canPublish !== '0';
this.isWebinar = el.dataset.isWebinar === '1';
this.muteOnJoin = el.dataset.muteOnJoin === '1';
this.videoOffOnJoin = el.dataset.videoOffOnJoin === '1';
this.isRecording = el.dataset.recordingActive === '1';
this.isLocked = el.dataset.locked === '1';
this.watermark = el.dataset.watermark === '1';
@@ -170,6 +175,9 @@ function meetRoom() {
room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
this.attachTrack(track, publication, participant);
if (track.kind === Track.Kind.Audio) {
this.ensureAudioPlayback();
}
});
room.on(RoomEvent.TrackUnsubscribed, (track, publication, participant) => {
track.detach();
@@ -218,6 +226,15 @@ function meetRoom() {
room.on(RoomEvent.Disconnected, () => {
this.connectionStatus = 'Disconnected';
});
room.on(RoomEvent.AudioPlaybackStatusChanged, () => {
this.audioPlaybackBlocked = room ? !room.canPlaybackAudio : false;
if (this.audioPlaybackBlocked) {
this.connectionStatus = 'Connected — click anywhere to enable audio';
this.registerAudioUnlockGesture();
} else if (this.connectionStatus.includes('enable audio')) {
this.connectionStatus = 'Connected';
}
});
await room.prepareConnection(this.config.url, this.config.token);
@@ -240,6 +257,8 @@ function meetRoom() {
this.isVideoOff = true;
}
await this.ensureAudioPlayback();
this.setupCaptionPipeline();
this.updateParticipantTile(room.localParticipant);
@@ -275,12 +294,24 @@ function meetRoom() {
},
async enableLocalMedia(activeRoom, attempts = 3) {
const wantMic = !this.muteOnJoin || this.isHost;
const wantCamera = !this.videoOffOnJoin;
for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
await activeRoom.localParticipant.setMicrophoneEnabled(true);
await activeRoom.localParticipant.setCameraEnabled(false);
await activeRoom.localParticipant.setMicrophoneEnabled(wantMic);
await activeRoom.localParticipant.setCameraEnabled(wantCamera);
this.syncLocalMediaState();
this.updateParticipantTile(activeRoom.localParticipant);
if (wantCamera) {
activeRoom.localParticipant.videoTrackPublications.forEach((pub) => {
if (pub.track && !pub.isMuted && pub.source !== Track.Source.ScreenShare) {
this.attachTrack(pub.track, pub, activeRoom.localParticipant);
}
});
}
this.connectionStatus = 'Connected';
return;
} catch (mediaError) {
@@ -299,6 +330,40 @@ function meetRoom() {
}
},
async ensureAudioPlayback() {
if (!room || room.canPlaybackAudio) {
this.audioPlaybackBlocked = false;
return;
}
try {
await room.startAudio();
this.audioPlaybackBlocked = false;
if (this.connectionStatus.includes('enable audio')) {
this.connectionStatus = 'Connected';
}
} catch (e) {
this.audioPlaybackBlocked = true;
this.registerAudioUnlockGesture();
}
},
registerAudioUnlockGesture() {
if (this._audioUnlockRegistered) return;
this._audioUnlockRegistered = true;
const unlock = async () => {
await this.ensureAudioPlayback();
if (!this.audioPlaybackBlocked) {
document.removeEventListener('click', unlock, true);
document.removeEventListener('keydown', unlock, true);
}
};
document.addEventListener('click', unlock, true);
document.addEventListener('keydown', unlock, true);
},
syncLocalMediaState() {
if (!room) return;
@@ -502,6 +567,8 @@ function meetRoom() {
setupCaptionPipeline() {
if (!this.liveCaptions || !this.config.captionUrl) return;
if (room?.localParticipant?.isMicrophoneEnabled) return;
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) return;
@@ -653,6 +720,7 @@ function meetRoom() {
async toggleMute() {
if (!room) return;
await this.ensureAudioPlayback();
const enabled = room.localParticipant.isMicrophoneEnabled;
await room.localParticipant.setMicrophoneEnabled(!enabled);
this.syncLocalMediaState();
@@ -661,6 +729,7 @@ function meetRoom() {
async toggleVideo() {
if (!room) return;
await this.ensureAudioPlayback();
const enabled = room.localParticipant.isCameraEnabled;
await room.localParticipant.setCameraEnabled(!enabled);
this.syncLocalMediaState();