Implement conference breakouts with meeting mode and programme lineup UI.
Deploy Ladill Meet / deploy (push) Successful in 1m3s

Attendees reconnect to breakout rooms with publish access while hosts stay on stage; poll and media-token endpoints drive LiveKit reconnection, and the live room shows Events-linked programme when available.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-02 00:06:45 +00:00
co-authored by Cursor
parent e698e75f17
commit fa9b4138af
8 changed files with 545 additions and 22 deletions
+134
View File
@@ -1032,6 +1032,140 @@ class MeetWebTest extends TestCase
]);
}
public function test_breakouts_exclude_host_and_enable_meeting_mode_for_attendees(): void
{
config([
'meet.media.livekit.api_key' => 'APIxxxxxxxxxxxxxxxx',
'meet.media.livekit.api_secret' => 'secretsecretsecretsecretsecret12',
]);
$room = Room::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'host_user_ref' => $this->user->public_id,
'title' => 'Summit',
'type' => 'town_hall',
'status' => 'live',
'scheduled_at' => now()->addHour(),
'timezone' => 'UTC',
'settings' => config('meet.default_settings'),
]);
$session = Session::create([
'owner_ref' => $this->user->public_id,
'room_id' => $room->id,
'media_room_name' => 'meet-'.$room->uuid,
'status' => 'live',
'session_mode' => 'live',
'started_at' => now(),
]);
$host = \App\Models\Participant::create([
'owner_ref' => $this->user->public_id,
'session_id' => $session->id,
'user_ref' => $this->user->public_id,
'display_name' => 'Host',
'role' => 'host',
'status' => 'joined',
'joined_at' => now(),
]);
$attendee = \App\Models\Participant::create([
'owner_ref' => $this->user->public_id,
'session_id' => $session->id,
'display_name' => 'Attendee',
'role' => 'attendee',
'status' => 'joined',
'joined_at' => now(),
]);
$sessions = app(\App\Services\Meet\SessionService::class);
$this->assertFalse($sessions->canParticipantPublish($attendee));
$this->actingAs($this->user)
->withSession(["meet.participant.{$session->uuid}" => $host->uuid])
->postJson('/room/'.$session->uuid.'/breakouts', ['count' => 2])
->assertOk();
$host->refresh();
$attendee->refresh();
$this->assertNull($host->breakout_room_id);
$this->assertNotNull($attendee->breakout_room_id);
$this->assertTrue($sessions->canParticipantPublish($attendee));
$this->actingAs($this->user)
->withSession(["meet.participant.{$session->uuid}" => $attendee->uuid])
->getJson('/room/'.$session->uuid.'/media-token')
->assertOk()
->assertJsonPath('in_breakout', true)
->assertJsonPath('can_publish', true)
->assertJsonPath('uses_stage_layout', false);
}
public function test_live_room_shows_programme_when_linked_from_events(): void
{
config([
'meet.media.livekit.api_key' => 'APIxxxxxxxxxxxxxxxx',
'meet.media.livekit.api_secret' => 'secretsecretsecretsecretsecret12',
]);
$programme = [
'title' => 'Summit programme',
'days' => [
[
'label' => 'Day 1',
'items' => [
['time' => '09:00', 'title' => 'Opening keynote', 'host' => 'Alex'],
],
],
],
];
$room = Room::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'host_user_ref' => $this->user->public_id,
'title' => 'Summit',
'type' => 'town_hall',
'status' => 'live',
'scheduled_at' => now()->addHour(),
'timezone' => 'UTC',
'settings' => array_merge(config('meet.default_settings'), [
'programme_snapshot' => $programme,
'linked_programme_items' => [
['time' => '09:00', 'title' => 'Opening keynote', 'host' => 'Alex'],
],
]),
]);
$session = Session::create([
'owner_ref' => $this->user->public_id,
'room_id' => $room->id,
'media_room_name' => 'meet-'.$room->uuid,
'status' => 'live',
'session_mode' => 'live',
'started_at' => now(),
]);
$participant = \App\Models\Participant::create([
'owner_ref' => $this->user->public_id,
'session_id' => $session->id,
'user_ref' => $this->user->public_id,
'display_name' => $this->user->name,
'role' => 'host',
'status' => 'joined',
'joined_at' => now(),
]);
$this->actingAs($this->user)
->withSession(["meet.participant.{$session->uuid}" => $participant->uuid])
->get('/room/'.$session->uuid)
->assertOk()
->assertSee('Opening keynote', false)
->assertSee('Programme', false);
}
public function test_afia_chat_returns_reply_when_ai_configured(): void
{
config([