Add speak access for webinar/conference attendees and fix Events linking UX.
Deploy Ladill Meet / deploy (push) Successful in 33s
Deploy Ladill Meet / deploy (push) Successful in 33s
Let attendees request host-granted microphone access with LiveKit reconnect, participant panel controls, and toolbar UI that only shows mic when allowed. Fix Events create URL, surface API key setup guidance, and align the Webinars sidebar icon with other nav items using currentColor strokes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1368,6 +1368,132 @@ class MeetWebTest extends TestCase
|
||||
->assertSee('data-session-noun="webinar"', false);
|
||||
}
|
||||
|
||||
public function test_webinar_attendee_can_request_and_receive_speak_access(): 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' => 'Town hall',
|
||||
'type' => 'webinar',
|
||||
'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->withSession(["meet.participant.{$session->uuid}" => $attendee->uuid])
|
||||
->get('/room/'.$session->uuid)
|
||||
->assertOk()
|
||||
->assertSee('Request to speak', false)
|
||||
->assertSee('meet-icons/speak.svg', false)
|
||||
->assertSee('data-uses-speak-access="1"', false);
|
||||
|
||||
$this->withSession(["meet.participant.{$session->uuid}" => $attendee->uuid])
|
||||
->postJson(route('meet.room.speak.request', $session))
|
||||
->assertOk()
|
||||
->assertJsonPath('participant.speak_requested', true);
|
||||
|
||||
$attendee->refresh();
|
||||
$this->assertTrue($attendee->speak_requested);
|
||||
$this->assertFalse($attendee->speak_granted);
|
||||
|
||||
$this->actingAs($this->user)
|
||||
->withSession(["meet.participant.{$session->uuid}" => $host->uuid])
|
||||
->postJson(route('meet.room.speak.grant', [$session, $attendee]))
|
||||
->assertOk()
|
||||
->assertJsonPath('participant.speak_granted', true)
|
||||
->assertJsonPath('participant.speak_requested', false);
|
||||
|
||||
$attendee->refresh();
|
||||
$this->assertTrue($attendee->speak_granted);
|
||||
$this->assertTrue($sessions->canParticipantPublish($attendee));
|
||||
|
||||
$mediaState = $sessions->mediaSessionState($attendee);
|
||||
$this->assertTrue($mediaState['can_publish']);
|
||||
$this->assertTrue($mediaState['audio_only_publish']);
|
||||
$this->assertTrue($mediaState['speak_granted']);
|
||||
|
||||
$this->withSession(["meet.participant.{$session->uuid}" => $attendee->uuid])
|
||||
->get('/room/'.$session->uuid)
|
||||
->assertOk()
|
||||
->assertSee('You can speak', false)
|
||||
->assertSee('data-speak-granted="1"', false);
|
||||
|
||||
$this->actingAs($this->user)
|
||||
->withSession(["meet.participant.{$session->uuid}" => $host->uuid])
|
||||
->postJson(route('meet.room.speak.revoke', [$session, $attendee]))
|
||||
->assertOk()
|
||||
->assertJsonPath('participant.speak_granted', false);
|
||||
|
||||
$attendee->refresh();
|
||||
$this->assertFalse($sessions->canParticipantPublish($attendee));
|
||||
}
|
||||
|
||||
public function test_webinar_show_events_create_link_uses_events_create_path(): void
|
||||
{
|
||||
config([
|
||||
'meet.events_app_url' => 'https://events.test',
|
||||
'meet.events_api_url' => 'https://events.test/api/service/v1',
|
||||
'meet.events_api_key' => '',
|
||||
]);
|
||||
|
||||
$room = Room::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'host_user_ref' => $this->user->public_id,
|
||||
'title' => 'Sales webinar',
|
||||
'type' => 'webinar',
|
||||
'status' => 'ended',
|
||||
'scheduled_at' => now()->subDay(),
|
||||
'timezone' => 'UTC',
|
||||
'settings' => config('meet.default_settings'),
|
||||
]);
|
||||
|
||||
$this->actingAs($this->user)
|
||||
->get(route('meet.webinars.show', $room))
|
||||
->assertOk()
|
||||
->assertSee('https://events.test/events/create', false)
|
||||
->assertSee('Events linking is not configured on this Meet instance.', false)
|
||||
->assertDontSee('/qr-codes/create', false);
|
||||
}
|
||||
|
||||
public function test_afia_chat_returns_reply_when_ai_configured(): void
|
||||
{
|
||||
config([
|
||||
|
||||
Reference in New Issue
Block a user