Redirect participants to meeting ended screen instead of 404.
Deploy Ladill Meet / deploy (push) Successful in 49s
Deploy Ladill Meet / deploy (push) Successful in 49s
Poll and waiting-room clients follow the same redirect when the host ends the call; chat and participants use mobile bottom sheets. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -181,8 +181,15 @@ class JoinController extends Controller
|
||||
{
|
||||
$room->loadMissing('organization');
|
||||
|
||||
$session = $room->activeSession();
|
||||
abort_unless($session?->isLive(), 404);
|
||||
$session = $room->activeSession() ?? $this->participantSessionForRoom($request, $room);
|
||||
|
||||
if (! $session || ! $session->isLive()) {
|
||||
if ($session) {
|
||||
return redirect()->route('meet.ended', $session);
|
||||
}
|
||||
|
||||
return redirect()->route('meet.join', $room);
|
||||
}
|
||||
|
||||
$participant = $this->participantFromSession($request, $session);
|
||||
|
||||
@@ -204,8 +211,18 @@ class JoinController extends Controller
|
||||
|
||||
public function waitingStatus(Request $request, Room $room): JsonResponse
|
||||
{
|
||||
$session = $room->activeSession();
|
||||
abort_unless($session, 404);
|
||||
$session = $room->activeSession() ?? $this->participantSessionForRoom($request, $room);
|
||||
|
||||
if (! $session || ! $session->isLive()) {
|
||||
if ($session) {
|
||||
return response()->json([
|
||||
'ended' => true,
|
||||
'redirect' => route('meet.ended', $session),
|
||||
]);
|
||||
}
|
||||
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$participant = $this->participantFromSession($request, $session);
|
||||
|
||||
@@ -230,4 +247,15 @@ class JoinController extends Controller
|
||||
->where('session_id', $session->id)
|
||||
->first();
|
||||
}
|
||||
|
||||
protected function participantSessionForRoom(Request $request, Room $room): ?Session
|
||||
{
|
||||
foreach ($room->sessions()->orderByDesc('started_at')->get() as $session) {
|
||||
if ($request->session()->has("meet.participant.{$session->uuid}")) {
|
||||
return $session;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,4 +77,50 @@ class LeaveController extends Controller
|
||||
'organization' => $session->room->organization,
|
||||
]);
|
||||
}
|
||||
|
||||
public function ended(Request $request, Session $session): View|RedirectResponse
|
||||
{
|
||||
$session->loadMissing('room.organization');
|
||||
abort_unless($session->room, 404);
|
||||
|
||||
if ($session->isLive()) {
|
||||
return redirect()->route('meet.room', $session);
|
||||
}
|
||||
|
||||
$canGiveFeedback = $this->prepareFeedbackSession($request, $session);
|
||||
|
||||
return view('meet.ended.show', [
|
||||
'session' => $session,
|
||||
'room' => $session->room,
|
||||
'organization' => $session->room->organization,
|
||||
'canGiveFeedback' => $canGiveFeedback,
|
||||
]);
|
||||
}
|
||||
|
||||
public function prepareFeedbackSession(Request $request, Session $session): bool
|
||||
{
|
||||
$participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
|
||||
|
||||
if ($participantUuid) {
|
||||
$participant = Participant::where('uuid', $participantUuid)
|
||||
->where('session_id', $session->id)
|
||||
->first();
|
||||
|
||||
if ($participant) {
|
||||
$request->session()->put('meet.left.feedback', [
|
||||
'session_uuid' => $session->uuid,
|
||||
'participant_uuid' => $participant->uuid,
|
||||
'display_name' => $participant->display_name,
|
||||
'user_ref' => $participant->user_ref,
|
||||
]);
|
||||
$request->session()->forget("meet.participant.{$session->uuid}");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$feedback = (array) $request->session()->get('meet.left.feedback', []);
|
||||
|
||||
return ($feedback['session_uuid'] ?? null) === $session->uuid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@ class MeetingRoomController extends Controller
|
||||
|
||||
public function show(Request $request, Session $session): View|RedirectResponse
|
||||
{
|
||||
abort_unless($session->isLive(), 404, 'Meeting has ended.');
|
||||
if (! $session->isLive()) {
|
||||
return redirect()->route('meet.ended', $session);
|
||||
}
|
||||
|
||||
$participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
|
||||
abort_unless($participantUuid, 403, 'Please join the meeting first.');
|
||||
@@ -156,6 +158,13 @@ class MeetingRoomController extends Controller
|
||||
|
||||
public function poll(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
if (! $session->isLive()) {
|
||||
return response()->json([
|
||||
'ended' => true,
|
||||
'redirect' => route('meet.ended', $session),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->currentParticipant($request, $session);
|
||||
|
||||
$session->load(['participants' => fn ($q) => $q->whereIn('status', ['joined', 'waiting'])]);
|
||||
|
||||
@@ -72,6 +72,101 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Meeting room chat / participants: bottom sheet on mobile, sidebar on desktop */
|
||||
.meet-room-panel {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.meet-room-panel__backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: rgb(2 6 23 / 0.72);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.meet-room-panel__sheet {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-height: min(85vh, 100%);
|
||||
flex-direction: column;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
background-color: rgb(2 6 23);
|
||||
padding: 0 0.75rem max(0.75rem, env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
|
||||
.meet-room-panel__handle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 0.75rem 0 0.25rem;
|
||||
}
|
||||
|
||||
.meet-room-panel__handle span {
|
||||
display: block;
|
||||
height: 0.25rem;
|
||||
width: 2.5rem;
|
||||
border-radius: 9999px;
|
||||
background-color: rgb(51 65 85);
|
||||
}
|
||||
|
||||
.meet-room-panel__header {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 0 0.25rem 0.75rem;
|
||||
}
|
||||
|
||||
.meet-room-panel__body {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border-radius: 1rem;
|
||||
background-color: rgb(15 23 42 / 0.9);
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.meet-room-panel {
|
||||
position: relative;
|
||||
inset: auto;
|
||||
z-index: auto;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 20rem;
|
||||
flex-shrink: 0;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.meet-room-panel__backdrop,
|
||||
.meet-room-panel__handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.meet-room-panel__sheet {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
max-height: none;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
border-radius: 0;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.meet-room-panel__body {
|
||||
border-radius: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* QR create/show: flush mobile action bar to the physical screen bottom (mobile only) */
|
||||
@media (max-width: 1023px) {
|
||||
.mobile-action-bar {
|
||||
|
||||
@@ -24,6 +24,11 @@ Alpine.data('waitingRoom', () => ({
|
||||
const res = await fetch(url, { headers: { Accept: 'application/json' } });
|
||||
const data = await res.json();
|
||||
|
||||
if (data.ended && data.redirect) {
|
||||
window.location.href = data.redirect;
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.redirect) {
|
||||
window.location.href = data.redirect;
|
||||
return;
|
||||
|
||||
@@ -73,6 +73,7 @@ function meetRoom() {
|
||||
url: el.dataset.url,
|
||||
configured: el.dataset.configured === '1',
|
||||
pollUrl: el.dataset.pollUrl,
|
||||
endedUrl: el.dataset.endedUrl,
|
||||
chatUrl: el.dataset.chatUrl,
|
||||
reactionUrl: el.dataset.reactionUrl,
|
||||
raiseHandUrl: el.dataset.raiseHandUrl,
|
||||
@@ -1044,7 +1045,18 @@ function meetRoom() {
|
||||
async poll() {
|
||||
try {
|
||||
const res = await fetch(this.config.pollUrl, { headers: { Accept: 'application/json' } });
|
||||
if (!res.ok) {
|
||||
if (res.status === 404 && this.config.endedUrl) {
|
||||
this.redirectToMeetingEnded(this.config.endedUrl);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
if (data.ended && data.redirect) {
|
||||
this.redirectToMeetingEnded(data.redirect);
|
||||
return;
|
||||
}
|
||||
if (data.participants) {
|
||||
this.sessionParticipants = data.participants;
|
||||
this.pruneTilesFromSession();
|
||||
@@ -1067,6 +1079,19 @@ function meetRoom() {
|
||||
}
|
||||
},
|
||||
|
||||
redirectToMeetingEnded(url) {
|
||||
if (this.pollTimer) {
|
||||
clearInterval(this.pollTimer);
|
||||
this.pollTimer = null;
|
||||
}
|
||||
|
||||
if (room) {
|
||||
room.disconnect().catch(() => {});
|
||||
}
|
||||
|
||||
window.location.href = url;
|
||||
},
|
||||
|
||||
updateChatFromPoll(messages) {
|
||||
const container = document.getElementById('chat-messages');
|
||||
if (!container) return;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
@props([
|
||||
'show',
|
||||
'title',
|
||||
])
|
||||
|
||||
<aside x-show="{{ $show }}" x-cloak
|
||||
class="meet-room-panel"
|
||||
@keydown.escape.window="{{ $show }} = false">
|
||||
<div x-show="{{ $show }}"
|
||||
x-transition:enter="transition-opacity ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition-opacity ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="meet-room-panel__backdrop"
|
||||
@click="{{ $show }} = false"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<div x-show="{{ $show }}"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="translate-y-full sm:translate-y-0"
|
||||
x-transition:enter-end="translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="translate-y-0"
|
||||
x-transition:leave-end="translate-y-full sm:translate-y-0"
|
||||
class="meet-room-panel__sheet">
|
||||
<div class="meet-room-panel__handle" aria-hidden="true"><span></span></div>
|
||||
|
||||
<div class="meet-room-panel__header">
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-sm font-semibold text-white">{{ $title }}</h2>
|
||||
@isset($subtitle)
|
||||
<div class="text-xs text-slate-400">{{ $subtitle }}</div>
|
||||
@endisset
|
||||
</div>
|
||||
<button @click="{{ $show }} = false" type="button"
|
||||
class="shrink-0 rounded-lg p-2 text-slate-400 transition-colors hover:bg-slate-800 hover:text-white"
|
||||
title="Close">
|
||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="meet-room-panel__body">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -0,0 +1,33 @@
|
||||
<x-meet-kiosk-layout :title="'Meeting ended · '.$room->title" :organization="$organization">
|
||||
<div class="flex min-h-[calc(100vh-8rem)] flex-col items-center justify-center text-center">
|
||||
<div class="w-full max-w-md rounded-3xl border border-slate-200 bg-white p-8 shadow-sm">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-full bg-slate-100 text-slate-600">
|
||||
<svg class="h-7 w-7" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="mt-6 text-2xl font-bold text-slate-900">Meeting ended</h1>
|
||||
<p class="mt-3 text-sm text-slate-600">The host has ended this meeting.</p>
|
||||
<p class="mt-1 text-sm font-medium text-slate-700">{{ $room->title }}</p>
|
||||
|
||||
<div class="mt-8 flex flex-col gap-3">
|
||||
@if ($canGiveFeedback)
|
||||
<a href="{{ route('meet.left', $session) }}" class="btn-primary btn-primary-lg py-3.5 font-semibold">
|
||||
Share feedback
|
||||
</a>
|
||||
@endif
|
||||
@auth
|
||||
<a href="{{ route('meet.dashboard') }}"
|
||||
class="{{ $canGiveFeedback ? 'btn-secondary py-3.5 font-semibold' : 'btn-primary btn-primary-lg py-3.5 font-semibold' }}">
|
||||
Back to Meet
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('meet.join', $room) }}"
|
||||
class="{{ $canGiveFeedback ? 'btn-secondary py-3.5 font-semibold' : 'btn-primary btn-primary-lg py-3.5 font-semibold' }}">
|
||||
Return to join page
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-meet-kiosk-layout>
|
||||
@@ -33,6 +33,7 @@
|
||||
data-display-name="{{ $participant->display_name }}"
|
||||
data-is-host="{{ $participant->isHost() ? '1' : '0' }}"
|
||||
data-poll-url="{{ route('meet.room.poll', $session) }}"
|
||||
data-ended-url="{{ route('meet.ended', $session) }}"
|
||||
data-chat-url="{{ route('meet.room.chat', $session) }}"
|
||||
data-reaction-url="{{ route('meet.room.reaction', $session) }}"
|
||||
data-raise-hand-url="{{ route('meet.room.raise-hand', $session) }}"
|
||||
@@ -389,20 +390,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside x-show="chatOpen" x-cloak
|
||||
class="flex h-full w-80 shrink-0 flex-col bg-slate-950 px-3 py-3">
|
||||
<div class="flex shrink-0 items-center justify-between px-1 pb-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-white">Meeting chat</h2>
|
||||
<p class="text-xs text-slate-400">Messages are visible to everyone in the call</p>
|
||||
</div>
|
||||
<button @click="chatOpen = false" type="button"
|
||||
class="rounded-lg p-2 text-slate-400 transition-colors hover:bg-slate-800 hover:text-white"
|
||||
title="Close chat">
|
||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex min-h-0 flex-1 flex-col rounded-2xl bg-slate-900/90 p-3">
|
||||
<x-meet.room.panel show="chatOpen" title="Meeting chat">
|
||||
<x-slot:subtitle>
|
||||
<p>Messages are visible to everyone in the call</p>
|
||||
</x-slot:subtitle>
|
||||
|
||||
<div class="flex min-h-0 flex-1 flex-col">
|
||||
<div class="flex-1 space-y-3 overflow-y-auto" id="chat-messages">
|
||||
@foreach ($messages as $message)
|
||||
@php $isOwn = $message->sender_name === $participant->display_name; @endphp
|
||||
@@ -427,22 +420,14 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</aside>
|
||||
</x-meet.room.panel>
|
||||
|
||||
<aside x-show="participantsOpen" x-cloak
|
||||
class="flex h-full w-80 shrink-0 flex-col bg-slate-950 px-3 py-3">
|
||||
<div class="flex shrink-0 items-center justify-between px-1 pb-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-white">Participants</h2>
|
||||
<p class="text-xs text-slate-400" x-text="participantCount() + (participantCount() === 1 ? ' person' : ' people') + ' in this call'"></p>
|
||||
</div>
|
||||
<button @click="participantsOpen = false" type="button"
|
||||
class="rounded-lg p-2 text-slate-400 transition-colors hover:bg-slate-800 hover:text-white"
|
||||
title="Close participants">
|
||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex min-h-0 flex-1 flex-col overflow-y-auto rounded-2xl bg-slate-900/90 p-2">
|
||||
<x-meet.room.panel show="participantsOpen" title="Participants">
|
||||
<x-slot:subtitle>
|
||||
<p x-text="participantCount() + (participantCount() === 1 ? ' person' : ' people') + ' in this call'"></p>
|
||||
</x-slot:subtitle>
|
||||
|
||||
<div class="flex min-h-0 flex-1 flex-col overflow-y-auto">
|
||||
<template x-if="isHost && waitingParticipants().length">
|
||||
<div class="mb-3 border-b border-slate-800 pb-3">
|
||||
<p class="px-2 pb-2 text-xs font-semibold uppercase tracking-wide text-amber-300">
|
||||
@@ -499,7 +484,7 @@
|
||||
</template>
|
||||
<p x-show="inCallParticipants().length === 0 && waitingParticipants().length === 0" class="px-2 py-4 text-center text-xs text-slate-500">No participants yet.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</x-meet.room.panel>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -49,6 +49,7 @@ Route::post('/r/{room}/enter', [JoinController::class, 'enter'])->name('meet.joi
|
||||
Route::get('/room/{session}/left', [LeaveController::class, 'show'])->name('meet.left');
|
||||
Route::post('/room/{session}/left', [LeaveController::class, 'store'])->name('meet.left.feedback');
|
||||
Route::get('/room/{session}/thanks', [LeaveController::class, 'thanks'])->name('meet.left.thanks');
|
||||
Route::get('/room/{session}/ended', [LeaveController::class, 'ended'])->name('meet.ended');
|
||||
|
||||
Route::match(['get', 'post'], '/invite/{invitation}/rsvp', [InvitationController::class, 'rsvp'])->name('meet.invitations.rsvp');
|
||||
|
||||
|
||||
@@ -251,6 +251,94 @@ class MeetWebTest extends TestCase
|
||||
->assertRedirect(route('meet.left', $session));
|
||||
}
|
||||
|
||||
public function test_participant_redirected_to_ended_page_when_meeting_ended(): void
|
||||
{
|
||||
$room = $this->createRoom(['settings' => array_merge(config('meet.default_settings'), ['waiting_room' => false])]);
|
||||
$session = Session::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'room_id' => $room->id,
|
||||
'media_room_name' => 'room-'.$room->uuid,
|
||||
'status' => 'ended',
|
||||
'started_at' => now()->subHour(),
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
$room->update(['status' => 'ended']);
|
||||
$participant = \App\Models\Participant::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'session_id' => $session->id,
|
||||
'display_name' => 'Guest',
|
||||
'role' => 'guest',
|
||||
'status' => 'left',
|
||||
'joined_at' => now()->subHour(),
|
||||
'left_at' => now(),
|
||||
]);
|
||||
|
||||
$this->withSession(["meet.participant.{$session->uuid}" => $participant->uuid])
|
||||
->get('/room/'.$session->uuid)
|
||||
->assertRedirect(route('meet.ended', $session));
|
||||
}
|
||||
|
||||
public function test_poll_returns_ended_payload_when_meeting_ended(): void
|
||||
{
|
||||
$room = $this->createRoom();
|
||||
$session = Session::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'room_id' => $room->id,
|
||||
'media_room_name' => 'room-'.$room->uuid,
|
||||
'status' => 'ended',
|
||||
'started_at' => now()->subHour(),
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
$participant = \App\Models\Participant::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'session_id' => $session->id,
|
||||
'display_name' => 'Guest',
|
||||
'role' => 'guest',
|
||||
'status' => 'left',
|
||||
'joined_at' => now()->subHour(),
|
||||
'left_at' => now(),
|
||||
]);
|
||||
|
||||
$this->withSession(["meet.participant.{$session->uuid}" => $participant->uuid])
|
||||
->getJson('/room/'.$session->uuid.'/poll')
|
||||
->assertOk()
|
||||
->assertJson([
|
||||
'ended' => true,
|
||||
'redirect' => route('meet.ended', $session),
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_waiting_guest_redirected_when_host_ends_meeting(): void
|
||||
{
|
||||
$room = $this->createRoom(['settings' => array_merge(config('meet.default_settings'), ['waiting_room' => true])]);
|
||||
$session = Session::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'room_id' => $room->id,
|
||||
'media_room_name' => 'room-'.$room->uuid,
|
||||
'status' => 'ended',
|
||||
'started_at' => now()->subHour(),
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
$room->update(['status' => 'ended']);
|
||||
$participant = \App\Models\Participant::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'session_id' => $session->id,
|
||||
'display_name' => 'Waiting Guest',
|
||||
'role' => 'guest',
|
||||
'status' => 'left',
|
||||
'joined_at' => now()->subHour(),
|
||||
'left_at' => now(),
|
||||
]);
|
||||
|
||||
$this->withSession(["meet.participant.{$session->uuid}" => $participant->uuid])
|
||||
->getJson('/r/'.$room->uuid.'/waiting/status')
|
||||
->assertOk()
|
||||
->assertJson([
|
||||
'ended' => true,
|
||||
'redirect' => route('meet.ended', $session),
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_authenticated_user_can_join_live_meeting_without_display_name(): void
|
||||
{
|
||||
$room = $this->createRoom();
|
||||
|
||||
Reference in New Issue
Block a user