Fix participant card layout and add meeting video pop-out.
Deploy Ladill Meet / deploy (push) Successful in 56s
Deploy Ladill Meet / deploy (push) Successful in 56s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+108
-3
@@ -177,6 +177,8 @@ function meetRoom() {
|
||||
activeSpeakerIds: [],
|
||||
floatingReactions: [],
|
||||
reactionsInitialized: false,
|
||||
pictureInPictureActive: false,
|
||||
pictureInPictureSupported: false,
|
||||
pollTimer: null,
|
||||
config: {},
|
||||
|
||||
@@ -313,6 +315,8 @@ function meetRoom() {
|
||||
this.syncToolbarConditionalVisibility();
|
||||
document.querySelector('.meet-toolbar__track')?.classList.add('is-ready');
|
||||
});
|
||||
|
||||
this.setupPictureInPicture();
|
||||
},
|
||||
|
||||
connectionErrorMessage(error) {
|
||||
@@ -1054,8 +1058,8 @@ function meetRoom() {
|
||||
|
||||
participantTileShellHtml() {
|
||||
return `
|
||||
<div data-tile-body class="relative min-h-0 flex-1">
|
||||
<div data-tile-stage class="absolute inset-0 flex items-center justify-center"></div>
|
||||
<div data-tile-body class="relative min-h-0 flex-1 overflow-hidden">
|
||||
<div data-tile-stage class="absolute inset-0 overflow-hidden"></div>
|
||||
<div data-tile-avatar class="absolute inset-0 flex items-center justify-center">
|
||||
<div data-tile-avatar-circle class="flex h-20 w-20 shrink-0 items-center justify-center rounded-full text-2xl font-semibold text-white shadow-lg"></div>
|
||||
</div>
|
||||
@@ -1682,7 +1686,7 @@ function meetRoom() {
|
||||
stage.innerHTML = '';
|
||||
|
||||
const el = track.attach();
|
||||
el.className = 'h-full w-full object-cover';
|
||||
el.className = 'meet-tile-video';
|
||||
el.dataset.participant = participant.identity;
|
||||
stage.appendChild(el);
|
||||
},
|
||||
@@ -2488,6 +2492,107 @@ function meetRoom() {
|
||||
}
|
||||
},
|
||||
|
||||
setupPictureInPicture() {
|
||||
this.pictureInPictureSupported = typeof document !== 'undefined'
|
||||
&& 'pictureInPictureEnabled' in document
|
||||
&& document.pictureInPictureEnabled !== false;
|
||||
|
||||
if (!this.pictureInPictureSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.hidden) {
|
||||
this.maybeAutoEnterPictureInPicture();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('leavepictureinpicture', () => {
|
||||
this.pictureInPictureActive = false;
|
||||
});
|
||||
},
|
||||
|
||||
showPictureInPictureControl() {
|
||||
return this.pictureInPictureSupported && !this.audioOnly;
|
||||
},
|
||||
|
||||
findPictureInPictureVideo() {
|
||||
if (this.isScreenSharing) {
|
||||
const screenVideo = document.querySelector('#screen-share video');
|
||||
if (screenVideo) {
|
||||
return screenVideo;
|
||||
}
|
||||
}
|
||||
|
||||
const localId = this.config.participantUuid;
|
||||
const priorities = [];
|
||||
|
||||
if (room?.localParticipant?.isCameraEnabled) {
|
||||
priorities.push(localId);
|
||||
}
|
||||
|
||||
this.activeSpeakerIds.forEach((id) => {
|
||||
if (!priorities.includes(id)) {
|
||||
priorities.push(id);
|
||||
}
|
||||
});
|
||||
|
||||
for (const id of priorities) {
|
||||
const video = this.findParticipantTile(id)?.querySelector('[data-tile-stage] video');
|
||||
if (video) {
|
||||
return video;
|
||||
}
|
||||
}
|
||||
|
||||
return document.querySelector('[data-participant-tile] [data-tile-stage] video');
|
||||
},
|
||||
|
||||
async maybeAutoEnterPictureInPicture() {
|
||||
if (!this.pictureInPictureSupported || this.pictureInPictureActive || this.audioOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.pictureInPictureElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.enterPictureInPicture({ silent: true });
|
||||
},
|
||||
|
||||
async togglePictureInPicture() {
|
||||
if (!this.pictureInPictureSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.pictureInPictureElement) {
|
||||
await document.exitPictureInPicture();
|
||||
this.pictureInPictureActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await this.enterPictureInPicture();
|
||||
},
|
||||
|
||||
async enterPictureInPicture({ silent = false } = {}) {
|
||||
const video = this.findPictureInPictureVideo();
|
||||
if (!video) {
|
||||
if (!silent) {
|
||||
this.connectionStatus = 'No video available to pop out';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await video.requestPictureInPicture();
|
||||
this.pictureInPictureActive = true;
|
||||
} catch {
|
||||
if (!silent) {
|
||||
this.connectionStatus = 'Pop-out blocked — try again from the toolbar';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async raiseHand() {
|
||||
await fetch(this.config.raiseHandUrl, {
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user