Limit audio rooms to Twitter Spaces features and fix live toolbar UX.
Deploy Ladill Meet / deploy (push) Successful in 1m3s
Deploy Ladill Meet / deploy (push) Successful in 1m3s
Block breakouts, chat, Q&A, and other meeting-only tools in spaces, hide video controls for audio-only rooms, and stabilize mobile nav and toolbar layout. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -16,6 +16,7 @@ use App\Services\Meet\LiveStreamService;
|
||||
use App\Services\Meet\PollService;
|
||||
use App\Services\Meet\QaService;
|
||||
use App\Services\Meet\SessionService;
|
||||
use App\Services\Meet\SpaceService;
|
||||
use App\Services\Meet\StageLayoutService;
|
||||
use App\Services\Meet\TownHallService;
|
||||
use App\Services\Meet\WhiteboardService;
|
||||
@@ -42,6 +43,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function submitQuestion(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'qa');
|
||||
$participant = $this->participant($request, $session);
|
||||
$validated = $request->validate(['question' => ['required', 'string', 'max:1000']]);
|
||||
$question = $this->qa->submit($session, $participant, $validated['question']);
|
||||
@@ -51,6 +53,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function approveQuestion(Request $request, Session $session, QaQuestion $question): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'qa');
|
||||
$this->hostOnly($request, $session);
|
||||
abort_unless($question->session_id === $session->id, 404);
|
||||
|
||||
@@ -59,6 +62,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function answerQuestion(Request $request, Session $session, QaQuestion $question): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'qa');
|
||||
$participant = $this->hostOnly($request, $session);
|
||||
abort_unless($question->session_id === $session->id, 404);
|
||||
$validated = $request->validate(['answer' => ['required', 'string', 'max:2000']]);
|
||||
@@ -70,6 +74,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function upvoteQuestion(Request $request, Session $session, QaQuestion $question): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'qa');
|
||||
$this->participant($request, $session);
|
||||
abort_unless($question->session_id === $session->id, 404);
|
||||
|
||||
@@ -80,6 +85,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function createPoll(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'polls');
|
||||
$this->hostOnly($request, $session);
|
||||
$validated = $request->validate([
|
||||
'question' => ['required', 'string', 'max:500'],
|
||||
@@ -94,6 +100,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function votePoll(Request $request, Session $session, MeetPoll $poll): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'polls');
|
||||
$participant = $this->participant($request, $session);
|
||||
abort_unless($poll->session_id === $session->id, 404);
|
||||
$validated = $request->validate(['option_index' => ['required', 'integer', 'min:0']]);
|
||||
@@ -104,6 +111,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function closePoll(Request $request, Session $session, MeetPoll $poll): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'polls');
|
||||
$this->hostOnly($request, $session);
|
||||
abort_unless($poll->session_id === $session->id, 404);
|
||||
|
||||
@@ -114,6 +122,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function createBreakouts(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'breakouts');
|
||||
$this->hostOnly($request, $session);
|
||||
$validated = $request->validate([
|
||||
'count' => ['required', 'integer', 'min:1', 'max:20'],
|
||||
@@ -131,6 +140,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function moveToBreakout(Request $request, Session $session, BreakoutRoom $breakout): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'breakouts');
|
||||
$participant = $this->participant($request, $session);
|
||||
abort_unless($breakout->session_id === $session->id, 404);
|
||||
$this->breakouts->moveParticipant($participant, $breakout);
|
||||
@@ -140,6 +150,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function returnFromBreakout(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'breakouts');
|
||||
$participant = $this->participant($request, $session);
|
||||
$this->breakouts->returnToMain($participant);
|
||||
|
||||
@@ -148,6 +159,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function broadcastBreakout(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'breakouts');
|
||||
$participant = $this->hostOnly($request, $session);
|
||||
$validated = $request->validate(['message' => ['required', 'string', 'max:1000']]);
|
||||
$message = $this->breakouts->broadcast($session, $validated['message'], $participant->display_name);
|
||||
@@ -157,6 +169,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function closeBreakouts(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'breakouts');
|
||||
$this->hostOnly($request, $session);
|
||||
$this->breakouts->closeAll($session);
|
||||
|
||||
@@ -167,6 +180,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function uploadFile(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'files');
|
||||
$participant = $this->participant($request, $session);
|
||||
$validated = $request->validate(['file' => ['required', 'file', 'max:51200']]);
|
||||
|
||||
@@ -199,6 +213,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function showWhiteboard(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'whiteboard');
|
||||
$this->participant($request, $session);
|
||||
$board = $this->whiteboards->ensureForSession($session);
|
||||
|
||||
@@ -207,6 +222,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function saveWhiteboard(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'whiteboard');
|
||||
$this->participant($request, $session);
|
||||
$validated = $request->validate(['state' => ['required', 'array']]);
|
||||
$board = $this->whiteboards->ensureForSession($session);
|
||||
@@ -216,6 +232,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function exportWhiteboard(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'whiteboard');
|
||||
$this->participant($request, $session);
|
||||
$validated = $request->validate(['image_data' => ['required', 'string']]);
|
||||
$data = preg_replace('#^data:image/\w+;base64,#i', '', $validated['image_data']);
|
||||
@@ -232,6 +249,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function configureLiveStream(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'live_stream');
|
||||
$this->hostOnly($request, $session);
|
||||
$validated = $request->validate([
|
||||
'platform' => ['required', 'string', 'in:'.implode(',', config('meet.live_stream.platforms'))],
|
||||
@@ -247,6 +265,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function startLiveStream(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'live_stream');
|
||||
$this->hostOnly($request, $session);
|
||||
$validated = $request->validate([
|
||||
'platform' => ['required', 'string', 'in:'.implode(',', config('meet.live_stream.platforms'))],
|
||||
@@ -260,6 +279,7 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
public function stopLiveStream(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
$this->abortUnlessRoomFeature($session, 'live_stream');
|
||||
$this->hostOnly($request, $session);
|
||||
$validated = $request->validate([
|
||||
'platform' => ['required', 'string', 'in:'.implode(',', config('meet.live_stream.platforms'))],
|
||||
@@ -382,4 +402,13 @@ class MeetingFeaturesController extends Controller
|
||||
|
||||
return $participant;
|
||||
}
|
||||
|
||||
protected function abortUnlessRoomFeature(Session $session, string $feature): void
|
||||
{
|
||||
abort_unless(
|
||||
app(SpaceService::class)->allowsRoomFeature($session->room, $feature),
|
||||
422,
|
||||
'This feature is not available in audio rooms.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class MeetingRoomController extends Controller
|
||||
'messages' => $this->chat->recentMessages($session),
|
||||
'activeRecording' => $session->recordings()->where('status', 'recording')->first(),
|
||||
'watermark' => $room->organization?->securitySetting('watermark_enabled', false),
|
||||
'isWebinar' => $room->isWebinar() || $room->isConference() || $room->isSpace(),
|
||||
'isWebinar' => $room->isWebinar() || $room->isConference(),
|
||||
'isConference' => $room->isConference(),
|
||||
'isGreenRoom' => $session->session_mode === 'green_room',
|
||||
'sessionMode' => $session->session_mode,
|
||||
@@ -254,6 +254,8 @@ class MeetingRoomController extends Controller
|
||||
|
||||
public function sendMessage(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
abort_unless(app(SpaceService::class)->allowsRoomFeature($session->room, 'chat'), 422);
|
||||
|
||||
$participant = $this->currentParticipant($request, $session);
|
||||
|
||||
$validated = $request->validate(['body' => ['required', 'string', 'max:2000']]);
|
||||
@@ -493,6 +495,8 @@ class MeetingRoomController extends Controller
|
||||
|
||||
public function lock(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
abort_unless(app(SpaceService::class)->allowsRoomFeature($session->room, 'lock'), 422);
|
||||
|
||||
$participant = $this->currentParticipant($request, $session);
|
||||
abort_unless($participant->isHost(), 403);
|
||||
$this->sessions->lock($session, $participant->user_ref ?? $participant->uuid);
|
||||
@@ -502,6 +506,8 @@ class MeetingRoomController extends Controller
|
||||
|
||||
public function unlock(Request $request, Session $session): JsonResponse
|
||||
{
|
||||
abort_unless(app(SpaceService::class)->allowsRoomFeature($session->room, 'lock'), 422);
|
||||
|
||||
$participant = $this->currentParticipant($request, $session);
|
||||
abort_unless($participant->isHost(), 403);
|
||||
$this->sessions->unlock($session, $participant->user_ref ?? $participant->uuid);
|
||||
|
||||
Reference in New Issue
Block a user