153 lines
6.0 KiB
PHP
153 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Messaging;
|
|
|
|
use App\Models\Conversation;
|
|
use App\Models\ConversationParticipant;
|
|
use App\Models\Message;
|
|
use App\Models\MessageAttachment;
|
|
use App\Models\User;
|
|
|
|
class MessagingPayloadFactory
|
|
{
|
|
public function message(Message $message, ?int $viewerId = null): array
|
|
{
|
|
$message->loadMissing([
|
|
'sender:id,username,name',
|
|
'attachments',
|
|
'reactions',
|
|
]);
|
|
|
|
return [
|
|
'id' => (int) $message->id,
|
|
'uuid' => (string) $message->uuid,
|
|
'client_temp_id' => $message->client_temp_id,
|
|
'conversation_id' => (int) $message->conversation_id,
|
|
'sender_id' => (int) $message->sender_id,
|
|
'sender' => $this->userSummary($message->sender),
|
|
'message_type' => (string) ($message->message_type ?? 'text'),
|
|
'body' => (string) ($message->body ?? ''),
|
|
'reply_to_message_id' => $message->reply_to_message_id ? (int) $message->reply_to_message_id : null,
|
|
'attachments' => $message->attachments->map(fn (MessageAttachment $attachment) => $this->attachment($attachment))->values()->all(),
|
|
'reaction_summary' => $this->reactionSummary($message, $viewerId),
|
|
'edited_at' => optional($message->edited_at)?->toIso8601String(),
|
|
'deleted_at' => optional($message->deleted_at)?->toIso8601String(),
|
|
'created_at' => optional($message->created_at)?->toIso8601String(),
|
|
'updated_at' => optional($message->updated_at)?->toIso8601String(),
|
|
];
|
|
}
|
|
|
|
public function conversationSummary(Conversation $conversation, int $viewerId): array
|
|
{
|
|
$conversation->loadMissing([
|
|
'allParticipants.user:id,username,name',
|
|
'latestMessage.sender:id,username,name',
|
|
'latestMessage.attachments',
|
|
'latestMessage.reactions',
|
|
]);
|
|
|
|
/** @var ConversationParticipant|null $myParticipant */
|
|
$myParticipant = $conversation->allParticipants->firstWhere('user_id', $viewerId);
|
|
|
|
return [
|
|
'id' => (int) $conversation->id,
|
|
'uuid' => (string) $conversation->uuid,
|
|
'type' => (string) $conversation->type,
|
|
'title' => $conversation->title,
|
|
'is_active' => (bool) ($conversation->is_active ?? true),
|
|
'last_message_at' => optional($conversation->last_message_at)?->toIso8601String(),
|
|
'unread_count' => app(UnreadCounterService::class)->unreadCountForConversation($conversation, $viewerId),
|
|
'my_participant' => $myParticipant ? $this->participant($myParticipant) : null,
|
|
'all_participants' => $conversation->allParticipants
|
|
->whereNull('left_at')
|
|
->map(fn (ConversationParticipant $participant) => $this->participant($participant))
|
|
->values()
|
|
->all(),
|
|
'latest_message' => $conversation->latestMessage
|
|
? $this->message($conversation->latestMessage, $viewerId)
|
|
: null,
|
|
];
|
|
}
|
|
|
|
public function presenceUser(User $user): array
|
|
{
|
|
return [
|
|
'id' => (int) $user->id,
|
|
'username' => (string) $user->username,
|
|
'display_name' => (string) ($user->name ?: $user->username),
|
|
'avatar_thumb_url' => null,
|
|
];
|
|
}
|
|
|
|
public function userSummary(?User $user): array
|
|
{
|
|
if (! $user) {
|
|
return [
|
|
'id' => null,
|
|
'username' => null,
|
|
'display_name' => null,
|
|
'avatar_thumb_url' => null,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'id' => (int) $user->id,
|
|
'username' => (string) $user->username,
|
|
'display_name' => (string) ($user->name ?: $user->username),
|
|
'avatar_thumb_url' => null,
|
|
];
|
|
}
|
|
|
|
private function participant(ConversationParticipant $participant): array
|
|
{
|
|
return [
|
|
'id' => (int) $participant->id,
|
|
'user_id' => (int) $participant->user_id,
|
|
'role' => (string) $participant->role,
|
|
'last_read_at' => optional($participant->last_read_at)?->toIso8601String(),
|
|
'last_read_message_id' => $participant->last_read_message_id ? (int) $participant->last_read_message_id : null,
|
|
'is_muted' => (bool) $participant->is_muted,
|
|
'is_archived' => (bool) $participant->is_archived,
|
|
'is_pinned' => (bool) $participant->is_pinned,
|
|
'is_hidden' => (bool) ($participant->is_hidden ?? false),
|
|
'pinned_at' => optional($participant->pinned_at)?->toIso8601String(),
|
|
'joined_at' => optional($participant->joined_at)?->toIso8601String(),
|
|
'left_at' => optional($participant->left_at)?->toIso8601String(),
|
|
'user' => $this->userSummary($participant->user),
|
|
];
|
|
}
|
|
|
|
private function attachment(MessageAttachment $attachment): array
|
|
{
|
|
return [
|
|
'id' => (int) $attachment->id,
|
|
'disk' => (string) ($attachment->disk ?: config('messaging.attachments.disk', 'local')),
|
|
'type' => (string) $attachment->type,
|
|
'mime' => (string) $attachment->mime,
|
|
'size_bytes' => (int) $attachment->size_bytes,
|
|
'width' => $attachment->width ? (int) $attachment->width : null,
|
|
'height' => $attachment->height ? (int) $attachment->height : null,
|
|
'original_name' => (string) $attachment->original_name,
|
|
];
|
|
}
|
|
|
|
private function reactionSummary(Message $message, ?int $viewerId = null): array
|
|
{
|
|
$counts = [];
|
|
$mine = [];
|
|
|
|
foreach ($message->reactions as $reaction) {
|
|
$emoji = (string) $reaction->reaction;
|
|
$counts[$emoji] = ($counts[$emoji] ?? 0) + 1;
|
|
|
|
if ($viewerId !== null && (int) $reaction->user_id === $viewerId) {
|
|
$mine[] = $emoji;
|
|
}
|
|
}
|
|
|
|
$counts['me'] = array_values(array_unique($mine));
|
|
|
|
return $counts;
|
|
}
|
|
}
|