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; } }