159 lines
6.2 KiB
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,
|
|
};
|
|
}
|
|
} |