diff --git a/resources/js/meet-room.js b/resources/js/meet-room.js index f27f1ae..37eda69 100644 --- a/resources/js/meet-room.js +++ b/resources/js/meet-room.js @@ -175,9 +175,6 @@ 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(); @@ -191,6 +188,7 @@ function meetRoom() { this.updateParticipantTile(participant); } if (publication.kind === Track.Kind.Audio) { + publication.track?.detach(); this.updateParticipantTile(participant); } if (participant === room.localParticipant) { @@ -201,7 +199,8 @@ function meetRoom() { if (publication.kind === Track.Kind.Video && publication.track) { this.attachTrack(publication.track, publication, participant); } - if (publication.kind === Track.Kind.Audio) { + if (publication.kind === Track.Kind.Audio && publication.track) { + this.attachTrack(publication.track, publication, participant); this.updateParticipantTile(participant); } if (participant === room.localParticipant) { @@ -221,8 +220,14 @@ function meetRoom() { } this.syncLocalMediaState(); }); - room.on(RoomEvent.ParticipantConnected, () => this.syncParticipants()); - room.on(RoomEvent.ParticipantDisconnected, () => this.syncParticipants()); + room.on(RoomEvent.ParticipantConnected, (participant) => { + this.attachExistingTracks(participant); + this.syncParticipants(); + }); + room.on(RoomEvent.ParticipantDisconnected, (participant) => { + this.removeParticipantTile(participant.identity); + this.syncParticipants(); + }); room.on(RoomEvent.Disconnected, () => { this.connectionStatus = 'Disconnected'; }); @@ -261,12 +266,9 @@ function meetRoom() { this.setupCaptionPipeline(); - this.updateParticipantTile(room.localParticipant); - room.localParticipant.videoTrackPublications.forEach((pub) => { - if (pub.track && !pub.isMuted && pub.source !== Track.Source.ScreenShare) { - this.attachTrack(pub.track, pub, room.localParticipant); - } - }); + this.attachExistingTracks(room.localParticipant); + room.remoteParticipants.forEach((participant) => this.attachExistingTracks(participant)); + this.syncParticipants(); } catch (e) { console.error(e); this.connectionStatus = this.connectionErrorMessage(e); @@ -472,6 +474,13 @@ function meetRoom() { await this.poll(); }, + removeParticipantTile(identity) { + const grid = document.getElementById('video-grid'); + if (!grid || !identity) return; + + grid.querySelector(`[data-participant-tile="${identity}"]`)?.remove(); + }, + getOrCreateParticipantTile(participant) { const grid = document.getElementById('video-grid'); if (!grid) return null; @@ -686,7 +695,40 @@ function meetRoom() { }); }, + attachExistingTracks(participant) { + if (!participant) return; + + const isLocal = room && participant === room.localParticipant; + + if (!isLocal) { + participant.audioTrackPublications.forEach((pub) => { + if (pub.track && !pub.isMuted) { + this.attachTrack(pub.track, pub, participant); + } + }); + } + + participant.videoTrackPublications.forEach((pub) => { + if (pub.track && !pub.isMuted && pub.source !== Track.Source.ScreenShare) { + this.attachTrack(pub.track, pub, participant); + } + }); + + this.updateParticipantTile(participant); + }, + attachTrack(track, publication, participant) { + if (track.kind === Track.Kind.Audio) { + if (participant === room?.localParticipant) return; + if (publication?.isMuted) return; + + const element = track.attach(); + element.className = 'meet-remote-audio'; + this.getAudioContainer().appendChild(element); + this.ensureAudioPlayback(); + return; + } + if (track.kind !== Track.Kind.Video) return; const isScreen = publication?.source === Track.Source.ScreenShare @@ -711,11 +753,55 @@ function meetRoom() { this.attachVideoToTile(participant, track); }, + getAudioContainer() { + let container = document.getElementById('meet-audio-elements'); + if (!container) { + container = document.createElement('div'); + container.id = 'meet-audio-elements'; + container.className = 'sr-only'; + container.setAttribute('aria-hidden', 'true'); + document.body.appendChild(container); + } + + return container; + }, + syncParticipants() { if (!room) return; + const all = [room.localParticipant, ...room.remoteParticipants.values()]; + const activeIdentities = new Set(all.map((p) => p.identity)); + this.participants = all.map((p) => ({ identity: p.identity, name: p.name })); all.forEach((participant) => this.updateParticipantTile(participant)); + + const grid = document.getElementById('video-grid'); + if (!grid) return; + + grid.querySelectorAll('[data-participant-tile]').forEach((tile) => { + if (!activeIdentities.has(tile.dataset.participantTile)) { + tile.remove(); + } + }); + }, + + pruneTilesFromSession() { + const joinedIds = new Set( + this.sessionParticipants + .filter((p) => p.status === 'joined') + .map((p) => p.uuid), + ); + + const grid = document.getElementById('video-grid'); + if (!grid) return; + + grid.querySelectorAll('[data-participant-tile]').forEach((tile) => { + const identity = tile.dataset.participantTile; + if (room?.localParticipant?.identity === identity) return; + if (!joinedIds.has(identity)) { + tile.remove(); + } + }); }, async toggleMute() { @@ -862,6 +948,7 @@ function meetRoom() { const data = await res.json(); if (data.participants) { this.sessionParticipants = data.participants; + this.pruneTilesFromSession(); } this.updateChatFromPoll(data.messages || []); this.qaItems = data.qa || []; diff --git a/resources/views/meet/join/cancelled.blade.php b/resources/views/meet/join/cancelled.blade.php index 54074da..646a4dd 100644 --- a/resources/views/meet/join/cancelled.blade.php +++ b/resources/views/meet/join/cancelled.blade.php @@ -1,4 +1,4 @@ - +

{{ $room->title }}

diff --git a/resources/views/meet/join/denied.blade.php b/resources/views/meet/join/denied.blade.php index 635351a..e413d00 100644 --- a/resources/views/meet/join/denied.blade.php +++ b/resources/views/meet/join/denied.blade.php @@ -1,4 +1,4 @@ - +
diff --git a/resources/views/meet/join/show.blade.php b/resources/views/meet/join/show.blade.php index 2d63d2d..7b7545d 100644 --- a/resources/views/meet/join/show.blade.php +++ b/resources/views/meet/join/show.blade.php @@ -1,4 +1,4 @@ - +

{{ $room->title }}

diff --git a/resources/views/meet/left/thanks.blade.php b/resources/views/meet/left/thanks.blade.php index 29cde7d..820ed4d 100644 --- a/resources/views/meet/left/thanks.blade.php +++ b/resources/views/meet/left/thanks.blade.php @@ -1,4 +1,4 @@ - +