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
@@ -252,14 +252,70 @@ class MeetingRoomController extends Controller
]);
}
public function promoteSpaceSpeaker(Request $request, Session $session, Participant $participant): JsonResponse
public function inviteSpaceSpeaker(Request $request, Session $session, Participant $participant): JsonResponse
{
$actor = $this->currentParticipant($request, $session);
abort_unless($participant->session_id === $session->id, 404);
abort_unless($session->room->isSpace(), 404);
abort_unless($actor->isHost() || ($request->user() && $request->user()->ownerRef() === $session->room->host_user_ref), 403);
$updated = app(SpaceService::class)->promoteToSpeaker($session->room, $participant);
$updated = app(SpaceService::class)->inviteToSpeak($session->room, $participant);
$avatars = ParticipantPresenter::avatarMap($session->participants()->get(), $session->room->host_user_ref);
return response()->json([
'participant' => ParticipantPresenter::toArray($updated, $avatars, $session->room->host_user_ref),
]);
}
public function acceptSpaceSpeakInvitation(Request $request, Session $session): JsonResponse
{
$participant = $this->currentParticipant($request, $session);
abort_unless($session->room->isSpace(), 404);
$updated = app(SpaceService::class)->acceptSpeakInvitation($session->room, $participant);
$avatars = ParticipantPresenter::avatarMap($session->participants()->get(), $session->room->host_user_ref);
return response()->json([
'participant' => ParticipantPresenter::toArray($updated, $avatars, $session->room->host_user_ref),
]);
}
public function declineSpaceSpeakInvitation(Request $request, Session $session): JsonResponse
{
$participant = $this->currentParticipant($request, $session);
abort_unless($session->room->isSpace(), 404);
$updated = app(SpaceService::class)->declineSpeakInvitation($session->room, $participant);
$avatars = ParticipantPresenter::avatarMap($session->participants()->get(), $session->room->host_user_ref);
return response()->json([
'participant' => ParticipantPresenter::toArray($updated, $avatars, $session->room->host_user_ref),
]);
}
public function dismissSpaceSpeakInvitation(Request $request, Session $session, Participant $participant): JsonResponse
{
$actor = $this->currentParticipant($request, $session);
abort_unless($participant->session_id === $session->id, 404);
abort_unless($session->room->isSpace(), 404);
abort_unless($actor->isHost() || ($request->user() && $request->user()->ownerRef() === $session->room->host_user_ref), 403);
$updated = app(SpaceService::class)->dismissSpeakInvitation($session->room, $participant);
$avatars = ParticipantPresenter::avatarMap($session->participants()->get(), $session->room->host_user_ref);
return response()->json([
'participant' => ParticipantPresenter::toArray($updated, $avatars, $session->room->host_user_ref),
]);
}
public function promoteSpaceCoHost(Request $request, Session $session, Participant $participant): JsonResponse
{
$actor = $this->currentParticipant($request, $session);
abort_unless($participant->session_id === $session->id, 404);
abort_unless($session->room->isSpace(), 404);
abort_unless($actor->isHost() || ($request->user() && $request->user()->ownerRef() === $session->room->host_user_ref), 403);
$updated = app(SpaceService::class)->promoteToCoHost($session->room, $participant);
$avatars = ParticipantPresenter::avatarMap($session->participants()->get(), $session->room->host_user_ref);
return response()->json([
+59 -2
View File
@@ -72,6 +72,49 @@ class SpaceService
], true);
}
public function inviteToSpeak(Room $room, Participant $participant): Participant
{
abort_unless($room->isSpace(), 422);
abort_unless(in_array($participant->role, ['attendee', 'guest', 'participant'], true), 422);
abort_unless(! $participant->speak_requested, 422, 'An invitation is already pending.');
$participant->update([
'speak_requested' => true,
'hand_raised' => false,
]);
return $participant->fresh();
}
public function acceptSpeakInvitation(Room $room, Participant $participant): Participant
{
abort_unless($room->isSpace(), 422);
abort_unless(in_array($participant->role, ['attendee', 'guest', 'participant'], true), 422);
abort_unless($participant->speak_requested, 422, 'No speak invitation to accept.');
return $this->promoteToSpeaker($room, $participant);
}
public function declineSpeakInvitation(Room $room, Participant $participant): Participant
{
abort_unless($room->isSpace(), 422);
abort_unless($participant->speak_requested, 422);
$participant->update(['speak_requested' => false]);
return $participant->fresh();
}
public function dismissSpeakInvitation(Room $room, Participant $participant): Participant
{
abort_unless($room->isSpace(), 422);
abort_unless($participant->speak_requested, 422);
$participant->update(['speak_requested' => false]);
return $participant->fresh();
}
public function promoteToSpeaker(Room $room, Participant $participant): Participant
{
abort_unless($room->isSpace(), 422);
@@ -80,6 +123,7 @@ class SpaceService
$participant->update([
'role' => 'panelist',
'hand_raised' => false,
'speak_requested' => false,
]);
if ($participant->user_ref) {
@@ -99,12 +143,25 @@ class SpaceService
return $participant->fresh();
}
public function demoteFromSpeaker(Room $room, Participant $participant): Participant
public function promoteToCoHost(Room $room, Participant $participant): Participant
{
abort_unless($room->isSpace(), 422);
abort_unless($participant->role === 'panelist', 422);
$participant->update(['role' => 'attendee']);
$participant->update(['role' => 'co_host']);
return $participant->fresh();
}
public function demoteFromSpeaker(Room $room, Participant $participant): Participant
{
abort_unless($room->isSpace(), 422);
abort_unless(in_array($participant->role, ['panelist', 'co_host'], true), 422);
$participant->update([
'role' => 'attendee',
'speak_requested' => false,
]);
if ($participant->user_ref) {
$speakerRefs = collect((array) $room->setting('speaker_refs', []))