232 lines
6.6 KiB
PHP
232 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Enums\ActivityTypeEnum;
|
|
use App\Enums\ApprovalStatusEnum;
|
|
use App\Events\ActivityLogged;
|
|
use App\Jobs\ArticleDiscoveryJob;
|
|
use App\Models\Feed;
|
|
use App\Models\RouteArticle;
|
|
use App\Support\PendingFeedGroup;
|
|
use Illuminate\Contracts\View\View;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
|
use Illuminate\Support\Collection;
|
|
use Livewire\Component;
|
|
use Livewire\WithPagination;
|
|
|
|
class Articles extends Component
|
|
{
|
|
use WithPagination;
|
|
|
|
public string $tab = 'pending';
|
|
|
|
public string $search = '';
|
|
|
|
public ?int $feedId = null;
|
|
|
|
public bool $isRefreshing = false;
|
|
|
|
/** @var array<int, bool> */
|
|
public array $expandedFeeds = [];
|
|
|
|
public function setTab(string $tab): void
|
|
{
|
|
$this->tab = $tab;
|
|
$this->search = '';
|
|
$this->expandedFeeds = [];
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function updatedFeedId(): void
|
|
{
|
|
$this->expandedFeeds = [];
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function toggleFeed(int $feedId): void
|
|
{
|
|
if (isset($this->expandedFeeds[$feedId])) {
|
|
unset($this->expandedFeeds[$feedId]);
|
|
|
|
return;
|
|
}
|
|
|
|
$this->expandedFeeds[$feedId] = true;
|
|
}
|
|
|
|
public function updatedSearch(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function approve(int $routeArticleId): void
|
|
{
|
|
RouteArticle::findOrFail($routeArticleId)->approve();
|
|
}
|
|
|
|
public function reject(int $routeArticleId): void
|
|
{
|
|
RouteArticle::findOrFail($routeArticleId)->reject();
|
|
}
|
|
|
|
public function restore(int $routeArticleId): void
|
|
{
|
|
$routeArticle = RouteArticle::findOrFail($routeArticleId);
|
|
$routeArticle->update([
|
|
'approval_status' => ApprovalStatusEnum::PENDING,
|
|
'decided_at' => null,
|
|
]);
|
|
}
|
|
|
|
public function clear(): void
|
|
{
|
|
$feed = $this->feedId !== null ? Feed::find($this->feedId) : null;
|
|
$cleared = $this->clearableQuery()->update([
|
|
'approval_status' => ApprovalStatusEnum::REJECTED,
|
|
'decided_at' => now(),
|
|
]);
|
|
|
|
if ($cleared > 0) {
|
|
ActivityLogged::dispatch(
|
|
ActivityTypeEnum::REJECT,
|
|
$feed !== null
|
|
? "Cleared {$cleared} pending articles from {$feed->name}"
|
|
: "Cleared {$cleared} pending articles",
|
|
['cleared' => $cleared],
|
|
$feed,
|
|
);
|
|
}
|
|
|
|
$this->expandedFeeds = [];
|
|
}
|
|
|
|
/**
|
|
* @return Builder<RouteArticle>
|
|
*/
|
|
private function clearableQuery(): Builder
|
|
{
|
|
return RouteArticle::query()
|
|
->where('approval_status', ApprovalStatusEnum::PENDING)
|
|
->when($this->feedId !== null, fn (Builder $q) => $q->where('feed_id', $this->feedId));
|
|
}
|
|
|
|
public function refresh(): void
|
|
{
|
|
$this->isRefreshing = true;
|
|
|
|
ArticleDiscoveryJob::dispatch();
|
|
|
|
$this->dispatch('refresh-started');
|
|
}
|
|
|
|
public function retryPublish(int $routeArticleId): void
|
|
{
|
|
$routeArticle = RouteArticle::failed()->find($routeArticleId);
|
|
|
|
if (! $routeArticle instanceof RouteArticle) {
|
|
return;
|
|
}
|
|
|
|
$routeArticle->clearPublishFailure();
|
|
|
|
ActivityLogged::dispatch(
|
|
ActivityTypeEnum::PUBLISH,
|
|
"Queued \"{$routeArticle->article->title}\" for another publish attempt",
|
|
['route_article_id' => $routeArticle->id],
|
|
$routeArticle->article,
|
|
);
|
|
}
|
|
|
|
public function render(): View
|
|
{
|
|
$pendingCount = RouteArticle::where('approval_status', ApprovalStatusEnum::PENDING)->count();
|
|
$failedCount = RouteArticle::failed()->count();
|
|
|
|
if ($this->tab === 'pending') {
|
|
return view('livewire.articles', [
|
|
'routeArticles' => null,
|
|
'pendingFeeds' => $this->pendingFeeds(),
|
|
'feedOptions' => $this->feedOptions(),
|
|
'pendingCount' => $pendingCount,
|
|
'failedCount' => $failedCount,
|
|
'clearableCount' => $this->clearableQuery()->count(),
|
|
])->layout('layouts.app');
|
|
}
|
|
|
|
$query = RouteArticle::with(['article.feed', 'feed', 'platformChannel'])
|
|
->orderBy('created_at', 'desc');
|
|
|
|
if ($this->tab === 'failed') {
|
|
$query->failed();
|
|
}
|
|
|
|
if ($this->feedId !== null) {
|
|
$query->where('feed_id', $this->feedId);
|
|
}
|
|
|
|
if ($this->search !== '') {
|
|
$search = $this->search;
|
|
$query->whereHas('article', function ($q) use ($search) {
|
|
$q->where('title', 'like', "%{$search}%")
|
|
->orWhere('description', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
return view('livewire.articles', [
|
|
'routeArticles' => $query->paginate(15),
|
|
'pendingFeeds' => null,
|
|
'feedOptions' => $this->feedOptions(),
|
|
'pendingCount' => $pendingCount,
|
|
'failedCount' => $failedCount,
|
|
'clearableCount' => 0,
|
|
])->layout('layouts.app');
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, PendingFeedGroup>
|
|
*/
|
|
private function pendingFeeds(): Collection
|
|
{
|
|
$counts = RouteArticle::query()
|
|
->selectRaw('feed_id, COUNT(*) as aggregate')
|
|
->where('approval_status', ApprovalStatusEnum::PENDING)
|
|
->when($this->feedId !== null, fn ($q) => $q->where('feed_id', $this->feedId))
|
|
->groupBy('feed_id')
|
|
->pluck('aggregate', 'feed_id');
|
|
|
|
return Feed::whereIn('id', $counts->keys())->get()
|
|
->map(fn (Feed $feed): PendingFeedGroup => new PendingFeedGroup(
|
|
$feed,
|
|
(int) $counts->get($feed->id, 0),
|
|
isset($this->expandedFeeds[$feed->id])
|
|
? $this->routeArticlesForFeed($feed->id)
|
|
: null,
|
|
))
|
|
->sortByDesc('count')
|
|
->values();
|
|
}
|
|
|
|
/**
|
|
* @return EloquentCollection<int, Feed>
|
|
*/
|
|
private function feedOptions(): EloquentCollection
|
|
{
|
|
return Feed::whereIn('id', RouteArticle::query()->select('feed_id')->distinct())
|
|
->orderBy('name')
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* @return EloquentCollection<int, RouteArticle>
|
|
*/
|
|
private function routeArticlesForFeed(int $feedId): EloquentCollection
|
|
{
|
|
return RouteArticle::with(['article.feed', 'feed', 'platformChannel'])
|
|
->where('approval_status', ApprovalStatusEnum::PENDING)
|
|
->where('feed_id', $feedId)
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
}
|
|
}
|