diff --git a/resources/css/app.css b/resources/css/app.css index 3d2f3b0..674431b 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -201,6 +201,10 @@ html.meet-room-page { white-space: nowrap; } +.meet-room-page .meet-audience-avatar--speaker { + border-color: rgb(139 92 246 / 0.55); +} + .meet-room-page .meet-audio-grid { place-content: center; place-items: center; diff --git a/resources/js/meet-room.js b/resources/js/meet-room.js index 5d26ee1..04c0ef6 100644 --- a/resources/js/meet-room.js +++ b/resources/js/meet-room.js @@ -550,24 +550,30 @@ function meetRoom() { return person?.display_name || participant.name || participant.identity; }, - avatarUrlForParticipant(participant) { - const person = this.participantForIdentity(participant.identity); + avatarUrlForPerson(person) { + if (!person) { + return null; + } - if (person?.avatar_url) { + if (person.avatar_url) { return person.avatar_url; } - if (person && ['host', 'co_host'].includes(person.role) && this.roomHost?.avatar_url) { + if (['host', 'co_host'].includes(person.role) && this.roomHost?.avatar_url) { return this.roomHost.avatar_url; } - if (this.roomHost?.user_ref && person?.user_ref === this.roomHost.user_ref && this.roomHost.avatar_url) { + if (this.roomHost?.user_ref && person.user_ref === this.roomHost.user_ref && this.roomHost.avatar_url) { return this.roomHost.avatar_url; } return null; }, + avatarUrlForParticipant(participant) { + return this.avatarUrlForPerson(this.participantForIdentity(participant.identity)); + }, + updateTileAvatarCircle(tile, participant) { const circle = tile.querySelector('[data-tile-avatar-circle]'); if (!circle) { @@ -662,6 +668,71 @@ function meetRoom() { return [...this.audienceParticipants()].sort((a, b) => a.display_name.localeCompare(b.display_name)); }, + resolveSpotlightParticipantUuid() { + const spotlightRef = this.resolveSpotlightSpeakerRef(); + if (spotlightRef) { + const match = this.speakerParticipants().find((person) => person.user_ref === spotlightRef); + if (match) { + return match.uuid; + } + } + + return this.speakerParticipantsSorted()[0]?.uuid || null; + }, + + plenaryStripParticipants() { + const spotlightUuid = this.resolveSpotlightParticipantUuid(); + + return this.inCallParticipants() + .filter((person) => person.uuid !== spotlightUuid) + .sort((a, b) => { + const aSpeaker = this.isSpeakerPerson(a); + const bSpeaker = this.isSpeakerPerson(b); + + if (aSpeaker !== bSpeaker) { + return aSpeaker ? -1 : 1; + } + + return a.display_name.localeCompare(b.display_name); + }); + }, + + createStripInitials(person) { + const initials = document.createElement('span'); + initials.className = 'meet-audience-avatar__initials'; + initials.style.background = this.avatarStyle(person.display_name).background; + initials.textContent = this.participantInitials(person.display_name); + + return initials; + }, + + appendStripAvatarItem(strip, person, index, total) { + const item = document.createElement('div'); + item.className = 'meet-audience-avatar'; + if (this.isSpeakerPerson(person)) { + item.classList.add('meet-audience-avatar--speaker'); + } + item.style.zIndex = String(total - index); + item.title = person.display_name; + + const avatarUrl = this.avatarUrlForPerson(person); + + if (avatarUrl) { + const img = document.createElement('img'); + img.src = avatarUrl; + img.alt = ''; + img.className = 'meet-audience-avatar__image'; + img.addEventListener('error', () => { + img.replaceWith(this.createStripInitials(person)); + }, { once: true }); + item.appendChild(img); + } else { + item.appendChild(this.createStripInitials(person)); + } + + strip.appendChild(item); + }, + resolveSpotlightSpeakerRef() { const speakers = this.speakerParticipants(); @@ -773,7 +844,7 @@ function meetRoom() { return; } - const audience = this.audienceParticipantsSorted(); + const audience = this.plenaryStripParticipants(); if (audience.length === 0) { return; } @@ -783,26 +854,7 @@ function meetRoom() { const overflow = audience.length - visible.length; visible.forEach((person, index) => { - const item = document.createElement('div'); - item.className = 'meet-audience-avatar'; - item.style.zIndex = String(visible.length - index); - item.title = person.display_name; - - if (person.avatar_url) { - const img = document.createElement('img'); - img.src = person.avatar_url; - img.alt = ''; - img.className = 'meet-audience-avatar__image'; - item.appendChild(img); - } else { - const initials = document.createElement('span'); - initials.className = 'meet-audience-avatar__initials'; - initials.style.background = this.avatarStyle(person.display_name).background; - initials.textContent = this.participantInitials(person.display_name); - item.appendChild(initials); - } - - strip.appendChild(item); + this.appendStripAvatarItem(strip, person, index, visible.length); }); if (overflow > 0) { @@ -1811,6 +1863,7 @@ function meetRoom() { this.sessionParticipants = data.participants; this.pruneTilesFromSession(); this.refreshParticipantTileAvatars(); + this.renderAudienceStrip(); } if (typeof data.uses_stage_layout === 'boolean' || typeof data.stage_layout === 'string') { this.mergeStageConfig(data);