diff --git a/resources/js/meet-room.js b/resources/js/meet-room.js index 093f871..c331916 100644 --- a/resources/js/meet-room.js +++ b/resources/js/meet-room.js @@ -735,6 +735,11 @@ function meetRoom() { }, refreshParticipantTileAvatars() { + if (this.isSpace && !this.usesStageLayout) { + this.renderSpaceSpeakerTiles(); + return; + } + if (!room) { return; } @@ -972,19 +977,121 @@ function meetRoom() { } }, + liveKitParticipantForIdentity(identity) { + if (!room) { + return null; + } + + if (room.localParticipant.identity === identity) { + return room.localParticipant; + } + + return room.remoteParticipants.get(identity) || null; + }, + + participantTileShellHtml() { + return ` +
+
+
+
+
+
+
+ +
+ + + +
+
`; + }, + + ensureParticipantTileShell(identity) { + let tile = this.findParticipantTile(identity); + + if (!tile) { + tile = document.createElement('div'); + tile.dataset.participantTile = identity; + tile.className = this.audioOnly + ? 'meet-audio-tile relative flex aspect-square w-full flex-col overflow-hidden rounded-2xl bg-slate-900' + : 'relative flex aspect-video w-full flex-col overflow-hidden rounded-xl bg-slate-900 min-h-0'; + tile.innerHTML = this.participantTileShellHtml(); + } + + return tile; + }, + + updateSessionParticipantTile(person, liveParticipant = null, tile = null) { + tile = tile || this.findParticipantTile(person.uuid); + if (!tile || !person) { + return; + } + + const pseudoParticipant = { identity: person.uuid, name: person.display_name }; + this.updateTileAvatarCircle(tile, pseudoParticipant); + tile.querySelector('[data-tile-name]').textContent = person.display_name; + + const speaking = this.isSpeaking(person.uuid); + tile.classList.toggle('meet-tile-speaking', speaking); + tile.querySelector('[data-tile-speaking]')?.classList.toggle('hidden', !speaking); + + const muted = liveParticipant + ? !liveParticipant.isMicrophoneEnabled + : Boolean(person.is_muted); + tile.querySelector('[data-tile-mic]')?.classList.toggle('hidden', !muted); + tile.querySelector('[data-tile-cam]')?.classList.add('hidden'); + }, + + renderSpaceSpeakerTiles() { + if (!this.isSpace || this.usesStageLayout) { + return; + } + + const grid = document.getElementById('video-grid'); + if (!grid) { + return; + } + + const speakers = this.speakerParticipantsSorted(); + const speakerIds = new Set(speakers.map((person) => person.uuid)); + + speakers.forEach((person) => { + const liveParticipant = this.liveKitParticipantForIdentity(person.uuid); + const tile = this.ensureParticipantTileShell(person.uuid); + + if (tile.parentElement !== grid) { + grid.appendChild(tile); + } + + this.updateSessionParticipantTile(person, liveParticipant, tile); + }); + + grid.querySelectorAll('[data-participant-tile]').forEach((tile) => { + if (!speakerIds.has(tile.dataset.participantTile)) { + tile.remove(); + } + }); + + this.updateAudioGridLayout(); + }, + findParticipantTile(identity) { return document.querySelector(`[data-participant-tile="${identity}"]`); }, getTileContainerForIdentity(identity) { if (!this.usesStageLayout) { - if (this.isSpace) { - const person = this.sessionParticipants.find((entry) => entry.uuid === identity); - if (!person || !['host', 'co_host', 'panelist'].includes(person.role)) { - return null; - } - } - return document.getElementById('video-grid'); } @@ -1063,7 +1170,9 @@ function meetRoom() { document.getElementById('meet-stage-spotlight')?.replaceChildren(); document.getElementById('meet-audience-strip')?.replaceChildren(); - if (room) { + if (this.isSpace) { + this.renderSpaceSpeakerTiles(); + } else if (room) { const all = [room.localParticipant, ...room.remoteParticipants.values()]; all.forEach((participant) => { const target = this.getTileContainerForIdentity(participant.identity); @@ -1085,7 +1194,9 @@ function meetRoom() { }); } - this.updateAudioGridLayout(); + if (!this.isSpace) { + this.updateAudioGridLayout(); + } return; } @@ -1395,36 +1506,7 @@ function meetRoom() { let tile = existing; if (!tile) { - tile = document.createElement('div'); - tile.dataset.participantTile = identity; - tile.className = this.audioOnly - ? 'meet-audio-tile relative flex aspect-square w-full flex-col overflow-hidden rounded-2xl bg-slate-900' - : 'relative flex aspect-video w-full flex-col overflow-hidden rounded-xl bg-slate-900 min-h-0'; - tile.innerHTML = ` -
-
-
-
-
-
-
- -
- - - -
-
`; + tile = this.ensureParticipantTileShell(identity); } if (tile.parentElement !== container) { @@ -2119,6 +2201,12 @@ function meetRoom() { syncParticipants() { if (!room) return; + if (this.isSpace && !this.usesStageLayout) { + this.renderSpaceSpeakerTiles(); + this.syncActiveSpeakers(room.activeSpeakers ?? []); + return; + } + const all = [room.localParticipant, ...room.remoteParticipants.values()]; const activeIdentities = new Set(all.map((p) => p.identity)); @@ -2179,12 +2267,21 @@ function meetRoom() { if (room) { [room.localParticipant, ...room.remoteParticipants.values()].forEach((participant) => { + if (this.isSpace && !this.usesStageLayout) { + return; + } + if (!this.getTileContainerForIdentity(participant.identity)) { this.removeParticipantTile(participant.identity); } }); } + if (this.isSpace && !this.usesStageLayout) { + this.renderSpaceSpeakerTiles(); + return; + } + this.applyStageLayout(); }, @@ -2336,6 +2433,10 @@ function meetRoom() { this.canPublish = ['host', 'co_host', 'panelist'].includes(participant.role); this.speakRequested = Boolean(participant.speak_requested); } + + if (this.isSpace && !this.usesStageLayout) { + this.renderSpaceSpeakerTiles(); + } }, async refreshMediaAccess() {