Replace meet toolbar icons and move host tools into More menu.
Deploy Ladill Meet / deploy (push) Successful in 1m53s
Deploy Ladill Meet / deploy (push) Successful in 1m53s
Use custom meet-icons assets, custom breakout/end modals instead of native prompts, and relocate share files, recording, and lock controls under the More dropdown. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -31,6 +31,9 @@ function meetRoom() {
|
||||
chatInput: '',
|
||||
shareOpen: false,
|
||||
shareCopied: false,
|
||||
breakoutModalOpen: false,
|
||||
breakoutCount: '4',
|
||||
endMeetingConfirmOpen: false,
|
||||
joinUrl: '',
|
||||
passcode: '',
|
||||
roomTitle: '',
|
||||
@@ -446,18 +449,54 @@ function meetRoom() {
|
||||
|
||||
async createBreakouts() {
|
||||
if (!this.isHost || !this.config.breakoutsCreateUrl) return;
|
||||
const count = parseInt(prompt('Number of breakout rooms?', '4') || '0', 10);
|
||||
if (!count) return;
|
||||
this.breakoutCount = '4';
|
||||
this.breakoutModalOpen = true;
|
||||
this.featuresOpen = false;
|
||||
},
|
||||
|
||||
async submitBreakouts() {
|
||||
if (!this.isHost || !this.config.breakoutsCreateUrl) return;
|
||||
|
||||
const count = parseInt(this.breakoutCount, 10);
|
||||
if (!count || count < 1) return;
|
||||
|
||||
await fetch(this.config.breakoutsCreateUrl, {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify({ count }),
|
||||
});
|
||||
|
||||
this.breakoutModalOpen = false;
|
||||
},
|
||||
|
||||
async closeBreakouts() {
|
||||
if (!this.isHost || !this.config.breakoutsCloseUrl) return;
|
||||
await fetch(this.config.breakoutsCloseUrl, { method: 'POST', headers: this.headers() });
|
||||
this.featuresOpen = false;
|
||||
},
|
||||
|
||||
openUploadPicker() {
|
||||
document.getElementById('meet-file-input')?.click();
|
||||
this.featuresOpen = false;
|
||||
},
|
||||
|
||||
async toggleRecordingFromMenu() {
|
||||
await this.toggleRecording();
|
||||
this.featuresOpen = false;
|
||||
},
|
||||
|
||||
async toggleLockFromMenu() {
|
||||
await this.toggleLock();
|
||||
this.featuresOpen = false;
|
||||
},
|
||||
|
||||
openEndMeetingConfirm() {
|
||||
this.endMeetingConfirmOpen = true;
|
||||
},
|
||||
|
||||
submitEndMeeting() {
|
||||
document.getElementById('meet-end-form')?.requestSubmit();
|
||||
this.endMeetingConfirmOpen = false;
|
||||
},
|
||||
|
||||
async uploadFile(event) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
@props(['icon' => null, 'title', 'subtitle' => null])
|
||||
|
||||
<button type="button" {{ $attributes->merge(['class' => 'flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left text-sm text-slate-200 transition-colors hover:bg-slate-800']) }}>
|
||||
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-slate-800/80">
|
||||
@if ($icon)
|
||||
@include('meet.room.partials.meet-icon', ['icon' => $icon, 'class' => 'h-5 w-5'])
|
||||
@else
|
||||
{{ $slot }}
|
||||
@endif
|
||||
</span>
|
||||
<span class="min-w-0">
|
||||
<span class="block font-medium">{{ $title }}</span>
|
||||
@if ($subtitle)
|
||||
<span class="block text-xs text-slate-400">{{ $subtitle }}</span>
|
||||
@endif
|
||||
</span>
|
||||
</button>
|
||||
@@ -63,7 +63,7 @@
|
||||
<button @click="openShare()" type="button"
|
||||
class="inline-flex items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-900 px-2.5 py-1.5 text-xs font-medium text-slate-200 transition-colors hover:bg-slate-800"
|
||||
title="Share meeting link">
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.935-2.186 2.25 2.25 0 0 0-3.935 2.186Z"/></svg>
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'share', 'class' => 'h-4 w-4'])
|
||||
Share
|
||||
</button>
|
||||
<span x-show="isRecording" class="rounded bg-red-600 px-2 py-0.5 font-medium text-white">REC</span>
|
||||
@@ -110,7 +110,7 @@
|
||||
<template x-for="reaction in floatingReactions" :key="reaction.id">
|
||||
<span class="flex h-10 w-10 animate-bounce items-center justify-center rounded-full bg-slate-800/90 ring-1 ring-white/20">
|
||||
<img x-show="reaction.kind === 'thumbs'" src="{{ asset('images/meet-icons/applause.svg') }}" alt="" class="meet-toolbar-icon h-5 w-5">
|
||||
<svg x-show="reaction.kind === 'heart'" class="h-5 w-5 text-rose-400" fill="currentColor" viewBox="0 0 24 24"><path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z"/></svg>
|
||||
<img x-show="reaction.kind === 'heart'" src="{{ asset('images/meet-icons/react.svg') }}" alt="" class="meet-toolbar-icon h-5 w-5">
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
@@ -187,7 +187,7 @@
|
||||
<button @click="openShare()" type="button"
|
||||
class="rounded-full bg-slate-700 p-3 hover:bg-slate-600"
|
||||
title="Share meeting link">
|
||||
<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="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.935-2.186 2.25 2.25 0 0 0-3.935 2.186Z"/></svg>
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'share'])
|
||||
</button>
|
||||
<button @click="raiseHand()" type="button" class="rounded-full bg-slate-700 p-3 hover:bg-slate-600" title="Raise hand">
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'raise-hand'])
|
||||
@@ -196,33 +196,15 @@
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'applause'])
|
||||
</button>
|
||||
<button @click="sendReaction('heart')" type="button" class="rounded-full bg-slate-700 p-3 hover:bg-slate-600" title="React">
|
||||
<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="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z"/></svg>
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'react'])
|
||||
</button>
|
||||
<button @click="toggleChat()" type="button"
|
||||
class="rounded-full p-3 transition-colors"
|
||||
:class="chatOpen ? 'bg-indigo-600 hover:bg-indigo-500' : 'bg-slate-700 hover:bg-slate-600'"
|
||||
title="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="M8.625 9.75a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-8.25 3.75a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.76c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 0 1 .778-.332 48.294 48.294 0 0 0 5.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z"/></svg>
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'chat'])
|
||||
</button>
|
||||
<input type="file" id="meet-file-input" class="hidden" @change="uploadFile">
|
||||
<button @click="document.getElementById('meet-file-input').click()" type="button" class="rounded-full bg-slate-700 p-3 hover:bg-slate-600" title="Upload file">
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'upload-file'])
|
||||
</button>
|
||||
@if ($participant->isHost())
|
||||
<button @click="toggleRecording()" type="button"
|
||||
class="rounded-full p-3 transition-colors"
|
||||
:class="isRecording ? 'bg-red-600 hover:bg-red-500' : 'bg-slate-700 hover:bg-slate-600'"
|
||||
:title="isRecording ? 'Stop recording' : 'Start recording'">
|
||||
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="5"/></svg>
|
||||
</button>
|
||||
<button @click="toggleLock()" type="button"
|
||||
class="rounded-full p-3 transition-colors"
|
||||
:class="isLocked ? 'bg-amber-600 hover:bg-amber-500' : 'bg-slate-700 hover:bg-slate-600'"
|
||||
:title="isLocked ? 'Unlock meeting' : 'Lock meeting'">
|
||||
<svg x-show="!isLocked" 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="M16.5 10.5V6.75a4.5 4.5 0 0 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"/></svg>
|
||||
<svg x-show="isLocked" x-cloak 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="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75M3.75 21h16.5A2.25 2.25 0 0 0 22.5 18.75v-6.75A2.25 2.25 0 0 0 20.25 9.75H3.75A2.25 2.25 0 0 0 1.5 12.75v6.75A2.25 2.25 0 0 0 3.75 21Z"/></svg>
|
||||
</button>
|
||||
@endif
|
||||
<button @click="toggleFeatures()" type="button"
|
||||
class="inline-flex items-center gap-1.5 rounded-full px-3.5 py-3 text-xs font-medium transition-colors"
|
||||
:class="featuresOpen ? 'bg-indigo-600 hover:bg-indigo-500' : 'bg-slate-700 hover:bg-slate-600'"
|
||||
@@ -235,9 +217,10 @@
|
||||
<button type="submit" class="rounded-full bg-red-600 px-5 py-3 text-sm font-medium hover:bg-red-500">Leave</button>
|
||||
</form>
|
||||
@if ($participant->isHost())
|
||||
<form method="POST" action="{{ route('meet.room.end', $session) }}" class="inline" onsubmit="return confirm('End meeting for everyone?')">
|
||||
<button @click="openEndMeetingConfirm()" type="button"
|
||||
class="rounded-full border border-red-500 px-4 py-3 text-sm text-red-400 hover:bg-red-950">End</button>
|
||||
<form id="meet-end-form" method="POST" action="{{ route('meet.room.end', $session) }}" class="hidden">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-full border border-red-500 px-4 py-3 text-sm text-red-400 hover:bg-red-950">End</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
@@ -261,27 +244,37 @@
|
||||
</div>
|
||||
|
||||
<div class="space-y-1 p-2">
|
||||
<x-meet.room.menu-item icon="upload-file" title="Share files" subtitle="Upload a file for everyone in the call"
|
||||
@click="openUploadPicker()" />
|
||||
|
||||
@if ($participant->isHost())
|
||||
<button @click="createBreakouts(); featuresOpen = false" type="button"
|
||||
<button @click="toggleRecordingFromMenu()" type="button"
|
||||
class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left text-sm text-slate-200 transition-colors hover:bg-slate-800">
|
||||
<span class="flex h-9 w-9 items-center justify-center rounded-lg bg-indigo-500/15 text-indigo-300">
|
||||
<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="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z"/></svg>
|
||||
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-red-500/15">
|
||||
<span class="h-3 w-3 rounded-full" :class="isRecording ? 'bg-red-500 animate-pulse' : 'bg-red-400'"></span>
|
||||
</span>
|
||||
<span>
|
||||
<span class="block font-medium">Start breakouts</span>
|
||||
<span class="block text-xs text-slate-400">Split participants into smaller rooms</span>
|
||||
<span class="min-w-0">
|
||||
<span class="block font-medium" x-text="isRecording ? 'Stop recording' : 'Start recording'"></span>
|
||||
<span class="block text-xs text-slate-400">Save this meeting to the cloud</span>
|
||||
</span>
|
||||
</button>
|
||||
<button @click="closeBreakouts(); featuresOpen = false" type="button"
|
||||
|
||||
<button @click="toggleLockFromMenu()" type="button"
|
||||
class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left text-sm text-slate-200 transition-colors hover:bg-slate-800">
|
||||
<span class="flex h-9 w-9 items-center justify-center rounded-lg bg-slate-700 text-slate-300">
|
||||
<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="M9 9V4.5M9 9H4.5M9 9 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5 5.25 5.25"/></svg>
|
||||
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-slate-800/80">
|
||||
@include('meet.room.partials.meet-icon', ['icon' => 'lock', 'class' => 'h-5 w-5'])
|
||||
</span>
|
||||
<span>
|
||||
<span class="block font-medium">Close breakouts</span>
|
||||
<span class="block text-xs text-slate-400">Bring everyone back to the main room</span>
|
||||
<span class="min-w-0">
|
||||
<span class="block font-medium" x-text="isLocked ? 'Unlock meeting' : 'Lock meeting'"></span>
|
||||
<span class="block text-xs text-slate-400">Control who can join</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<x-meet.room.menu-item icon="start-breakout" title="Start breakouts" subtitle="Split participants into smaller rooms"
|
||||
@click="createBreakouts()" />
|
||||
|
||||
<x-meet.room.menu-item icon="close-breakout" title="Close breakouts" subtitle="Bring everyone back to the main room"
|
||||
@click="closeBreakouts()" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -308,6 +301,43 @@
|
||||
<p x-show="breakoutBroadcast" class="border-t border-slate-800 bg-amber-950/40 px-4 py-3 text-sm text-amber-100" x-text="breakoutBroadcast"></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div x-show="breakoutModalOpen" x-cloak
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm"
|
||||
@keydown.escape.window="breakoutModalOpen = false">
|
||||
<div class="w-full max-w-sm rounded-2xl border border-slate-700 bg-slate-900 p-5 shadow-2xl" @click.outside="breakoutModalOpen = false">
|
||||
<h3 class="text-base font-semibold text-white">Start breakout rooms</h3>
|
||||
<p class="mt-1 text-sm text-slate-400">How many breakout rooms should be created?</p>
|
||||
<form @submit.prevent="submitBreakouts()" class="mt-4 space-y-4">
|
||||
<div>
|
||||
<label for="breakout-count" class="sr-only">Number of rooms</label>
|
||||
<input id="breakout-count" type="number" min="1" max="20" x-model="breakoutCount"
|
||||
class="w-full rounded-xl border-slate-700 bg-slate-950 px-3 py-2.5 text-sm text-white focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
<div class="flex justify-end gap-2">
|
||||
<button type="button" @click="breakoutModalOpen = false"
|
||||
class="rounded-xl px-4 py-2 text-sm font-medium text-slate-300 hover:bg-slate-800">Cancel</button>
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-500">Create rooms</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="endMeetingConfirmOpen" x-cloak
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm"
|
||||
@keydown.escape.window="endMeetingConfirmOpen = false">
|
||||
<div class="w-full max-w-sm rounded-2xl border border-slate-700 bg-slate-900 p-5 shadow-2xl" @click.outside="endMeetingConfirmOpen = false">
|
||||
<h3 class="text-base font-semibold text-white">End meeting for everyone?</h3>
|
||||
<p class="mt-2 text-sm text-slate-400">All participants will be disconnected and the session will close.</p>
|
||||
<div class="mt-5 flex justify-end gap-2">
|
||||
<button type="button" @click="endMeetingConfirmOpen = false"
|
||||
class="rounded-xl px-4 py-2 text-sm font-medium text-slate-300 hover:bg-slate-800">Cancel</button>
|
||||
<button type="button" @click="submitEndMeeting()"
|
||||
class="rounded-xl bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-500">End meeting</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user