diff --git a/app/Services/Meet/SessionService.php b/app/Services/Meet/SessionService.php index dc748e2..f5f8220 100644 --- a/app/Services/Meet/SessionService.php +++ b/app/Services/Meet/SessionService.php @@ -91,12 +91,7 @@ class SessionService ?string $email = null, ): Participant { $userRef = $user?->ownerRef(); - - $existing = $session->participants() - ->when($userRef, fn ($q) => $q->where('user_ref', $userRef)) - ->when(! $userRef && $email, fn ($q) => $q->where('email', $email)) - ->whereIn('status', ['invited', 'waiting', 'joined']) - ->first(); + $existing = $this->findExistingParticipant($session, $userRef, $email); if ($existing) { $existing->update([ @@ -136,6 +131,37 @@ class SessionService return $participant; } + protected function findExistingParticipant(Session $session, ?string $userRef, ?string $email): ?Participant + { + $active = fn ($q) => $q->whereIn('status', ['invited', 'waiting', 'joined']); + + if ($userRef) { + return $session->participants() + ->where('user_ref', $userRef) + ->where($active) + ->first(); + } + + if ($email) { + return $session->participants() + ->whereNull('user_ref') + ->where('email', $email) + ->where($active) + ->first(); + } + + $guestUuid = (string) request()?->session()?->get("meet.participant.{$session->uuid}", ''); + if ($guestUuid === '') { + return null; + } + + return $session->participants() + ->where('uuid', $guestUuid) + ->whereNull('user_ref') + ->where($active) + ->first(); + } + public function rememberParticipantSession(Request $request, Participant $participant, ?Session $session = null): void { $session ??= $participant->session; diff --git a/resources/js/meet-room.js b/resources/js/meet-room.js index 6c7ac14..2c1e3d2 100644 --- a/resources/js/meet-room.js +++ b/resources/js/meet-room.js @@ -41,6 +41,7 @@ function meetRoom() { connectionStatus: 'Connecting…', participants: [], sessionParticipants: [], + roomHost: null, floatingReactions: [], pollTimer: null, config: {}, @@ -95,6 +96,12 @@ function meetRoom() { this.sessionParticipants = []; } + try { + this.roomHost = JSON.parse(el.dataset.roomHost || 'null'); + } catch { + this.roomHost = null; + } + if (this.config.configured && this.config.token) { this.connectLiveKit(); } else { @@ -318,8 +325,20 @@ function meetRoom() { hostParticipant() { const hosts = this.sessionParticipants.filter((p) => p.role === 'host' || p.role === 'co_host'); + if (hosts.length) { + return hosts[0]; + } - return hosts[0] || this.sessionParticipants[0] || null; + if (this.roomHost?.user_ref) { + const inCall = this.sessionParticipants.find((p) => p.user_ref === this.roomHost.user_ref); + if (inCall) { + return inCall; + } + + return this.roomHost; + } + + return null; }, participantCount() { diff --git a/resources/views/meet/room/show.blade.php b/resources/views/meet/room/show.blade.php index e9f1283..406f6ee 100644 --- a/resources/views/meet/room/show.blade.php +++ b/resources/views/meet/room/show.blade.php @@ -10,6 +10,7 @@
@php + use App\Models\User; use App\Services\Meet\ParticipantPresenter; $joined = $session->participants->where('status', 'joined'); @@ -17,6 +18,12 @@ $joinedParticipants = $joined ->map(fn ($p) => ParticipantPresenter::toArray($p, $participantAvatars, $room->host_user_ref)) ->values(); + $roomHostUser = User::query()->where('public_id', $room->host_user_ref)->first(); + $roomHost = [ + 'user_ref' => $room->host_user_ref, + 'display_name' => $roomHostUser?->name ?? 'Host', + 'avatar_url' => $roomHostUser?->avatarUrl(), + ]; @endphp @if (! $mediaConfigured) diff --git a/tests/Feature/MeetWebTest.php b/tests/Feature/MeetWebTest.php index 7ff770a..b6221f5 100644 --- a/tests/Feature/MeetWebTest.php +++ b/tests/Feature/MeetWebTest.php @@ -152,6 +152,50 @@ class MeetWebTest extends TestCase ->assertSee('data-display-name="Guest User"', false); } + public function test_anonymous_guest_does_not_take_over_host_participant(): void + { + $room = $this->createRoom([ + 'settings' => array_merge(config('meet.default_settings'), [ + 'join_before_host' => true, + ]), + ]); + $session = Session::create([ + 'owner_ref' => $this->user->public_id, + 'room_id' => $room->id, + 'media_room_name' => 'room-'.$room->uuid, + 'status' => 'live', + 'started_at' => now(), + ]); + $room->update(['status' => 'live']); + + $hostParticipant = \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->post('/r/'.$room->uuid.'/enter', [ + 'display_name' => 'Guest User', + ])->assertRedirect(route('meet.room', $session)); + + $hostParticipant->refresh(); + $this->assertSame('host', $hostParticipant->role); + $this->assertSame($this->user->name, $hostParticipant->display_name); + + $guest = \App\Models\Participant::query() + ->where('session_id', $session->id) + ->where('display_name', 'Guest User') + ->first(); + + $this->assertNotNull($guest); + $this->assertSame('guest', $guest->role); + $this->assertNotSame($hostParticipant->uuid, $guest->uuid); + } + public function test_authenticated_user_can_join_live_meeting_without_display_name(): void { $room = $this->createRoom();