Require acceptance for space speak invites and add instant co-host promotion.
Deploy Ladill Meet / deploy (push) Successful in 40s

Remove duplicate host record button on desktop and split invite-to-speak from make co-host so listeners must accept before speaking while co-host is applied immediately.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-04 00:50:58 +00:00
co-authored by Cursor
parent 46af4f93b6
commit 2fae2e7ed8
6 changed files with 338 additions and 14 deletions
+101 -2
View File
@@ -124,6 +124,10 @@ function meetRoom() {
speakDismissUrl: el.dataset.speakDismissUrl,
spacePromoteUrl: el.dataset.spacePromoteUrl,
spaceDemoteUrl: el.dataset.spaceDemoteUrl,
spaceSpeakAcceptUrl: el.dataset.spaceSpeakAcceptUrl,
spaceSpeakDeclineUrl: el.dataset.spaceSpeakDeclineUrl,
spaceSpeakDismissUrl: el.dataset.spaceSpeakDismissUrl,
spaceCoHostUrl: el.dataset.spaceCoHostUrl,
recordingStartUrl: el.dataset.recordingStartUrl,
recordingStopUrl: el.dataset.recordingStopUrl,
lockUrl: el.dataset.lockUrl,
@@ -2175,7 +2179,18 @@ function meetRoom() {
},
handRaisedParticipants() {
return this.inCallParticipants().filter((person) => person.hand_raised && person.role === 'attendee');
return this.inCallParticipants().filter((person) => (
person.hand_raised
&& person.role === 'attendee'
&& !person.speak_requested
));
},
spaceSpeakInvitePendingParticipants() {
return this.inCallParticipants().filter((person) => (
person.role === 'attendee'
&& person.speak_requested
));
},
spaceSpeakerUrl(template, participantUuid) {
@@ -2194,10 +2209,19 @@ function meetRoom() {
if (participant.uuid === this.config.participantUuid) {
this.canPublish = ['host', 'co_host', 'panelist'].includes(participant.role);
this.speakRequested = Boolean(participant.speak_requested);
}
},
async promoteSpaceSpeaker(participantUuid) {
async refreshMediaAccess() {
if (!this.config.configured) {
return;
}
await this.reconnectLiveKit();
},
async inviteSpaceSpeaker(participantUuid) {
const url = this.spaceSpeakerUrl(this.config.spacePromoteUrl, participantUuid);
if (!this.isSpace || !this.isHost || !url) {
return;
@@ -2216,6 +2240,81 @@ function meetRoom() {
this.mergeSessionParticipant(data.participant);
},
async acceptSpaceSpeakInvitation() {
if (!this.isSpace || !this.config.spaceSpeakAcceptUrl) {
return;
}
const res = await fetch(this.config.spaceSpeakAcceptUrl, {
method: 'POST',
headers: this.headers(),
});
if (!res.ok) {
return;
}
const data = await res.json();
this.mergeSessionParticipant(data.participant);
await this.refreshMediaAccess();
},
async declineSpaceSpeakInvitation() {
if (!this.isSpace || !this.config.spaceSpeakDeclineUrl) {
return;
}
const res = await fetch(this.config.spaceSpeakDeclineUrl, {
method: 'POST',
headers: this.headers(),
});
if (!res.ok) {
return;
}
const data = await res.json();
this.mergeSessionParticipant(data.participant);
},
async dismissSpaceSpeakInvitation(participantUuid) {
const url = this.spaceSpeakerUrl(this.config.spaceSpeakDismissUrl, participantUuid);
if (!this.isSpace || !this.isHost || !url) {
return;
}
const res = await fetch(url, {
method: 'POST',
headers: this.headers(),
});
if (!res.ok) {
return;
}
const data = await res.json();
this.mergeSessionParticipant(data.participant);
},
async promoteSpaceCoHost(participantUuid) {
const url = this.spaceSpeakerUrl(this.config.spaceCoHostUrl, participantUuid);
if (!this.isSpace || !this.isHost || !url) {
return;
}
const res = await fetch(url, {
method: 'POST',
headers: this.headers(),
});
if (!res.ok) {
return;
}
const data = await res.json();
this.mergeSessionParticipant(data.participant);
},
async demoteSpaceSpeaker(participantUuid) {
const url = this.spaceSpeakerUrl(this.config.spaceDemoteUrl, participantUuid);
if (!this.isSpace || !this.isHost || !url) {