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>
148 lines
4.6 KiB
PHP
148 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Meet;
|
|
|
|
use App\Models\Participant;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class ParticipantPresenter
|
|
{
|
|
/**
|
|
* @param Collection<int, Participant> $participants
|
|
*/
|
|
public static function avatarMap(Collection $participants, ?string $roomHostUserRef = null): Collection
|
|
{
|
|
$refs = $participants->pluck('user_ref')->filter();
|
|
|
|
if ($roomHostUserRef) {
|
|
$refs = $refs->push($roomHostUserRef)->unique();
|
|
}
|
|
|
|
if ($refs->isEmpty()) {
|
|
return collect();
|
|
}
|
|
|
|
return User::query()
|
|
->whereIn('public_id', $refs->all())
|
|
->get()
|
|
->keyBy('public_id');
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function toArray(Participant $participant, Collection $avatars, ?string $roomHostUserRef = null): array
|
|
{
|
|
return [
|
|
'uuid' => $participant->uuid,
|
|
'user_ref' => $participant->user_ref,
|
|
'display_name' => $participant->display_name,
|
|
'role' => $participant->role,
|
|
'status' => $participant->status,
|
|
'hand_raised' => $participant->hand_raised,
|
|
'speak_requested' => (bool) $participant->speak_requested,
|
|
'speak_granted' => (bool) $participant->speak_granted,
|
|
'is_muted' => $participant->is_muted,
|
|
'is_video_off' => $participant->is_video_off,
|
|
'breakout_room_id' => $participant->breakout_room_id,
|
|
'avatar_url' => self::avatarUrl($participant, $avatars, $roomHostUserRef),
|
|
];
|
|
}
|
|
|
|
public static function avatarUrl(Participant $participant, Collection $avatars, ?string $roomHostUserRef = null): ?string
|
|
{
|
|
if ($participant->user_ref) {
|
|
$url = $avatars->get($participant->user_ref)?->avatarUrl();
|
|
if ($url) {
|
|
return $url;
|
|
}
|
|
}
|
|
|
|
if (in_array($participant->role, ['host', 'co_host'], true) && $roomHostUserRef) {
|
|
return $avatars->get($roomHostUserRef)?->avatarUrl();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param list<array<string, mixed>> $participants
|
|
* @param list<string> $presenterRefs
|
|
*/
|
|
public static function isSpeakerPerson(array $person, array $presenterRefs = []): bool
|
|
{
|
|
if (in_array($person['role'] ?? '', ['host', 'co_host', 'panelist'], true)) {
|
|
return true;
|
|
}
|
|
|
|
$userRef = $person['user_ref'] ?? null;
|
|
|
|
return is_string($userRef) && $userRef !== '' && in_array($userRef, $presenterRefs, true);
|
|
}
|
|
|
|
/**
|
|
* @param list<array<string, mixed>> $participants
|
|
* @param list<string> $presenterRefs
|
|
*/
|
|
public static function headerCountLabel(array $participants, bool $usesStageLayout, array $presenterRefs = []): string
|
|
{
|
|
$joined = array_values(array_filter($participants, fn ($p) => ($p['status'] ?? '') === 'joined'));
|
|
|
|
if ($usesStageLayout) {
|
|
$count = count(array_filter($joined, fn ($p) => self::isSpeakerPerson($p, $presenterRefs)));
|
|
|
|
return $count === 1 ? '1 speaker' : "{$count} speakers";
|
|
}
|
|
|
|
$count = count($joined);
|
|
|
|
return "{$count} in call";
|
|
}
|
|
|
|
/**
|
|
* @param list<array<string, mixed>> $participants
|
|
* @return array{display_name: string, avatar_url: ?string}|null
|
|
*/
|
|
public static function headerHost(array $participants, array $roomHost): ?array
|
|
{
|
|
foreach ($participants as $person) {
|
|
if (($person['status'] ?? '') !== 'joined') {
|
|
continue;
|
|
}
|
|
|
|
if (in_array($person['role'] ?? '', ['host', 'co_host'], true)) {
|
|
return [
|
|
'display_name' => (string) ($person['display_name'] ?? 'Host'),
|
|
'avatar_url' => $person['avatar_url'] ?? null,
|
|
];
|
|
}
|
|
}
|
|
|
|
if (($roomHost['display_name'] ?? '') !== '') {
|
|
return [
|
|
'display_name' => (string) $roomHost['display_name'],
|
|
'avatar_url' => $roomHost['avatar_url'] ?? null,
|
|
];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static function initials(?string $name): string
|
|
{
|
|
$parts = preg_split('/\s+/', trim($name ?? '')) ?: [];
|
|
$parts = array_values(array_filter($parts, fn ($part) => $part !== ''));
|
|
|
|
if ($parts === []) {
|
|
return '?';
|
|
}
|
|
|
|
if (count($parts) === 1) {
|
|
return strtoupper(substr($parts[0], 0, 2));
|
|
}
|
|
|
|
return strtoupper(substr($parts[0], 0, 1).substr($parts[count($parts) - 1], 0, 1));
|
|
}
|
|
}
|