Files
SkinbaseNova/app/Services/GroupActivityService.php

159 lines
6.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Artwork;
use App\Models\Group;
use App\Models\GroupActivityItem;
use App\Models\GroupAsset;
use App\Models\GroupChallenge;
use App\Models\GroupEvent;
use App\Models\GroupPost;
use App\Models\GroupProject;
use App\Models\GroupRelease;
use App\Models\User;
use Illuminate\Support\Collection;
class GroupActivityService
{
public function record(
Group $group,
?User $actor,
string $type,
string $subjectType,
?int $subjectId,
string $headline,
?string $summary = null,
string $visibility = GroupActivityItem::VISIBILITY_PUBLIC,
): GroupActivityItem {
return GroupActivityItem::query()->create([
'group_id' => (int) $group->id,
'type' => $type,
'visibility' => $visibility,
'actor_user_id' => $actor?->id,
'subject_type' => $subjectType,
'subject_id' => $subjectId,
'headline' => $headline,
'summary' => $summary,
'is_pinned' => false,
'occurred_at' => now(),
]);
}
public function pin(GroupActivityItem $item, User $actor, bool $isPinned = true): GroupActivityItem
{
$item->forceFill([
'is_pinned' => $isPinned,
])->save();
app(GroupHistoryService::class)->record(
$item->group,
$actor,
$isPinned ? 'activity_pinned' : 'activity_unpinned',
sprintf('%s group activity item.', $isPinned ? 'Pinned' : 'Unpinned'),
'group_activity_item',
(int) $item->id,
['is_pinned' => ! $isPinned],
['is_pinned' => $isPinned],
);
return $item->fresh(['actor']);
}
public function publicFeed(Group $group, int $limit = 8): array
{
return $this->mapItems(
GroupActivityItem::query()
->with('actor:id,name,username')
->where('group_id', $group->id)
->where('visibility', GroupActivityItem::VISIBILITY_PUBLIC)
->orderByDesc('is_pinned')
->orderByDesc('occurred_at')
->limit(max(1, min(24, $limit)))
->get(),
$group
);
}
public function studioFeed(Group $group, User $viewer, int $limit = 20): array
{
if (! $group->canViewStudio($viewer)) {
return [];
}
return $this->mapItems(
GroupActivityItem::query()
->with('actor:id,name,username')
->where('group_id', $group->id)
->orderByDesc('is_pinned')
->orderByDesc('occurred_at')
->limit(max(1, min(50, $limit)))
->get(),
$group
);
}
private function mapItems(Collection $items, Group $group): array
{
$subjects = $this->loadSubjects($items);
return $items->map(function (GroupActivityItem $item) use ($group, $subjects): array {
$subject = $subjects[$item->subject_type][$item->subject_id] ?? null;
return [
'id' => (int) $item->id,
'type' => (string) $item->type,
'visibility' => (string) $item->visibility,
'headline' => (string) $item->headline,
'summary' => $item->summary,
'is_pinned' => (bool) $item->is_pinned,
'occurred_at' => $item->occurred_at?->toISOString(),
'actor' => $item->actor ? [
'id' => (int) $item->actor->id,
'name' => $item->actor->name,
'username' => $item->actor->username,
] : null,
'subject' => $subject ? [
'type' => (string) $item->subject_type,
'id' => (int) $item->subject_id,
'title' => $subject->title ?? null,
'url' => $this->subjectUrl($group, (string) $item->subject_type, $subject),
] : null,
];
})->values()->all();
}
private function loadSubjects(Collection $items): array
{
$grouped = $items
->filter(fn (GroupActivityItem $item): bool => $item->subject_id !== null)
->groupBy('subject_type')
->map(fn (Collection $chunk): array => $chunk->pluck('subject_id')->map(fn ($id): int => (int) $id)->unique()->values()->all());
return [
'artwork' => Artwork::query()->whereIn('id', $grouped->get('artwork', []))->get()->keyBy('id')->all(),
'group_post' => GroupPost::query()->whereIn('id', $grouped->get('group_post', []))->get()->keyBy('id')->all(),
'group_project' => GroupProject::query()->whereIn('id', $grouped->get('group_project', []))->get()->keyBy('id')->all(),
'group_release' => GroupRelease::query()->whereIn('id', $grouped->get('group_release', []))->get()->keyBy('id')->all(),
'group_challenge' => GroupChallenge::query()->whereIn('id', $grouped->get('group_challenge', []))->get()->keyBy('id')->all(),
'group_event' => GroupEvent::query()->whereIn('id', $grouped->get('group_event', []))->get()->keyBy('id')->all(),
'group_asset' => GroupAsset::query()->whereIn('id', $grouped->get('group_asset', []))->get()->keyBy('id')->all(),
];
}
private function subjectUrl(Group $group, string $subjectType, object $subject): ?string
{
return match ($subjectType) {
'artwork' => route('art.show', ['id' => $subject->id, 'slug' => $subject->slug ?: $subject->id]),
'group_post' => route('groups.posts.show', ['group' => $group, 'post' => $subject]),
'group_project' => route('groups.projects.show', ['group' => $group, 'project' => $subject]),
'group_release' => route('groups.releases.show', ['group' => $group, 'release' => $subject]),
'group_challenge' => route('groups.challenges.show', ['group' => $group, 'challenge' => $subject]),
'group_event' => route('groups.events.show', ['group' => $group, 'event' => $subject]),
'group_asset' => route('groups.assets.download', ['group' => $group, 'asset' => $subject]),
default => null,
};
}
}