update
This commit is contained in:
@@ -5,9 +5,11 @@ declare(strict_types=1);
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\ReactionType;
|
||||
use App\Models\ActivityEvent;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\ArtworkComment;
|
||||
use App\Models\CommentReaction;
|
||||
use App\Models\Story;
|
||||
use App\Models\User;
|
||||
use App\Models\UserMention;
|
||||
use App\Services\ThumbnailPresenter;
|
||||
@@ -74,13 +76,15 @@ final class CommunityActivityService
|
||||
$commentModels = $this->fetchCommentModels($sourceLimit, repliesOnly: false);
|
||||
$replyModels = $this->fetchCommentModels($sourceLimit, repliesOnly: true);
|
||||
$reactionModels = $this->fetchReactionModels($sourceLimit);
|
||||
$recordedActivities = $this->fetchRecordedActivities($sourceLimit);
|
||||
|
||||
$commentActivities = $commentModels->map(fn (ArtworkComment $comment) => $this->mapCommentActivity($comment, 'comment'));
|
||||
$replyActivities = $replyModels->map(fn (ArtworkComment $comment) => $this->mapCommentActivity($comment, 'reply'));
|
||||
$reactionActivities = $reactionModels->map(fn (CommentReaction $reaction) => $this->mapReactionActivity($reaction));
|
||||
$mentionActivities = $this->fetchMentionActivities($sourceLimit);
|
||||
|
||||
$merged = $commentActivities
|
||||
$merged = $recordedActivities
|
||||
->concat($commentActivities)
|
||||
->concat($replyActivities)
|
||||
->concat($reactionActivities)
|
||||
->concat($mentionActivities)
|
||||
@@ -136,6 +140,89 @@ final class CommunityActivityService
|
||||
];
|
||||
}
|
||||
|
||||
private function fetchRecordedActivities(int $limit): Collection
|
||||
{
|
||||
$events = ActivityEvent::query()
|
||||
->select(['id', 'actor_id', 'type', 'target_type', 'target_id', 'meta', 'created_at'])
|
||||
->with([
|
||||
'actor' => function ($query) {
|
||||
$query
|
||||
->select('id', 'name', 'username', 'role', 'is_active', 'created_at')
|
||||
->with('profile:user_id,avatar_hash')
|
||||
->withCount('artworks');
|
||||
},
|
||||
])
|
||||
->whereHas('actor', fn ($query) => $query->where('is_active', true)->whereNull('deleted_at'))
|
||||
->latest('created_at')
|
||||
->limit($limit)
|
||||
->get();
|
||||
|
||||
if ($events->isEmpty()) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
$artworkIds = $events
|
||||
->where('target_type', ActivityEvent::TARGET_ARTWORK)
|
||||
->pluck('target_id')
|
||||
->map(fn ($id) => (int) $id)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$storyIds = $events
|
||||
->where('target_type', ActivityEvent::TARGET_STORY)
|
||||
->pluck('target_id')
|
||||
->map(fn ($id) => (int) $id)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$targetUserIds = $events
|
||||
->where('target_type', ActivityEvent::TARGET_USER)
|
||||
->pluck('target_id')
|
||||
->map(fn ($id) => (int) $id)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$artworks = empty($artworkIds)
|
||||
? collect()
|
||||
: Artwork::query()
|
||||
->select('id', 'user_id', 'title', 'slug', 'hash', 'thumb_ext', 'published_at', 'deleted_at', 'is_public', 'is_approved')
|
||||
->whereIn('id', $artworkIds)
|
||||
->public()
|
||||
->published()
|
||||
->whereNull('deleted_at')
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
$stories = empty($storyIds)
|
||||
? collect()
|
||||
: Story::query()
|
||||
->select('id', 'creator_id', 'title', 'slug', 'cover_image', 'published_at', 'status')
|
||||
->whereIn('id', $storyIds)
|
||||
->published()
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
$targetUsers = empty($targetUserIds)
|
||||
? collect()
|
||||
: User::query()
|
||||
->select('id', 'name', 'username', 'role', 'is_active', 'created_at')
|
||||
->with('profile:user_id,avatar_hash')
|
||||
->withCount('artworks')
|
||||
->whereIn('id', $targetUserIds)
|
||||
->where('is_active', true)
|
||||
->whereNull('deleted_at')
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
return $events
|
||||
->map(fn (ActivityEvent $event) => $this->mapRecordedActivity($event, $artworks, $stories, $targetUsers))
|
||||
->filter()
|
||||
->values();
|
||||
}
|
||||
|
||||
private function fetchCommentModels(int $limit, bool $repliesOnly): Collection
|
||||
{
|
||||
return ArtworkComment::query()
|
||||
@@ -262,6 +349,52 @@ final class CommunityActivityService
|
||||
];
|
||||
}
|
||||
|
||||
private function mapRecordedActivity(ActivityEvent $event, Collection $artworks, Collection $stories, Collection $targetUsers): ?array
|
||||
{
|
||||
if ($event->type === ActivityEvent::TYPE_COMMENT && $event->target_type === ActivityEvent::TARGET_ARTWORK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$artwork = $event->target_type === ActivityEvent::TARGET_ARTWORK
|
||||
? $artworks->get((int) $event->target_id)
|
||||
: null;
|
||||
|
||||
$story = $event->target_type === ActivityEvent::TARGET_STORY
|
||||
? $stories->get((int) $event->target_id)
|
||||
: null;
|
||||
|
||||
$targetUser = $event->target_type === ActivityEvent::TARGET_USER
|
||||
? $targetUsers->get((int) $event->target_id)
|
||||
: null;
|
||||
|
||||
if ($event->target_type === ActivityEvent::TARGET_ARTWORK && ! $artwork) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($event->target_type === ActivityEvent::TARGET_STORY && ! $story) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($event->target_type === ActivityEvent::TARGET_USER && ! $targetUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$iso = $event->created_at?->toIso8601String();
|
||||
|
||||
return [
|
||||
'id' => 'event:' . $event->id,
|
||||
'type' => (string) $event->type,
|
||||
'user' => $this->buildUserPayload($event->actor),
|
||||
'artwork' => $this->buildArtworkPayload($artwork),
|
||||
'story' => $this->buildStoryPayload($story),
|
||||
'target_user' => $this->buildUserPayload($targetUser),
|
||||
'meta' => is_array($event->meta) ? $event->meta : [],
|
||||
'created_at' => $iso,
|
||||
'time_ago' => $event->created_at?->diffForHumans(),
|
||||
'sort_timestamp' => $iso,
|
||||
];
|
||||
}
|
||||
|
||||
private function fetchMentionActivities(int $limit): Collection
|
||||
{
|
||||
if (! Schema::hasTable('user_mentions')) {
|
||||
@@ -384,6 +517,20 @@ final class CommunityActivityService
|
||||
];
|
||||
}
|
||||
|
||||
private function buildStoryPayload(?Story $story): ?array
|
||||
{
|
||||
if (! $story) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int) $story->id,
|
||||
'title' => html_entity_decode((string) ($story->title ?? 'Story'), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'url' => route('stories.show', ['slug' => $story->slug]),
|
||||
'cover_url' => $story->cover_url,
|
||||
];
|
||||
}
|
||||
|
||||
private function buildCommentPayload(ArtworkComment $comment): array
|
||||
{
|
||||
$artwork = $this->buildArtworkPayload($comment->artwork);
|
||||
|
||||
Reference in New Issue
Block a user