183 lines
5.4 KiB
PHP
183 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Models\Episode;
|
|
use App\Models\Playlist;
|
|
use App\Models\PlaylistEpisode;
|
|
use App\Services\PlaylistService;
|
|
use Illuminate\Contracts\View\View;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Livewire\Component;
|
|
|
|
class PlaylistSidebar extends Component
|
|
{
|
|
public const PAGE_SIZE = 20;
|
|
|
|
public const MAX_WINDOW = 60;
|
|
|
|
protected PlaylistService $playlistService;
|
|
|
|
public Playlist $playlist;
|
|
|
|
public Episode $currentEpisode;
|
|
|
|
public int $currentEpisodeId;
|
|
|
|
public int $total = 0;
|
|
|
|
public int $windowStart = 1;
|
|
|
|
public int $windowEnd = 0;
|
|
|
|
public bool $isOwner = false;
|
|
|
|
public bool $collapsible = false;
|
|
|
|
public ?string $previousEpisodeSlug = null;
|
|
|
|
public ?string $nextEpisodeSlug = null;
|
|
|
|
public function boot(PlaylistService $playlistService): void
|
|
{
|
|
$this->playlistService = $playlistService;
|
|
}
|
|
|
|
public function mount(int $playlistId, int $currentEpisodeId, bool $collapsible = false): void
|
|
{
|
|
$this->playlist = Playlist::with('user')->withCount('episodes')->findOrFail($playlistId);
|
|
$this->currentEpisodeId = $currentEpisodeId;
|
|
$this->currentEpisode = Episode::with(['gallery' => fn ($query) => $query->orderBy('id')->limit(1)])
|
|
->findOrFail($currentEpisodeId);
|
|
$this->total = $this->playlist->episodes_count;
|
|
$this->collapsible = $collapsible;
|
|
$this->isOwner = Auth::check() && Auth::user()->id === $this->playlist->user_id;
|
|
|
|
$this->repairNullPositions();
|
|
|
|
$position = $this->currentPosition() ?? 1;
|
|
|
|
$this->setWindowAround($position);
|
|
$this->resolveAdjacentSlugs($position);
|
|
}
|
|
|
|
public function getEpisodesProperty(): Collection
|
|
{
|
|
return PlaylistEpisode::query()
|
|
->where('playlist_id', $this->playlist->id)
|
|
->whereBetween('position', [$this->windowStart, $this->windowEnd])
|
|
->orderBy('position')
|
|
->with([
|
|
'episode.gallery' => fn ($gallery) => $gallery->orderBy('id')->limit(1),
|
|
'episode.studio',
|
|
])
|
|
->get();
|
|
}
|
|
|
|
public function appendChunk(): void
|
|
{
|
|
if ($this->windowEnd >= $this->total) {
|
|
return;
|
|
}
|
|
|
|
$this->windowEnd = min($this->total, $this->windowEnd + self::PAGE_SIZE);
|
|
$this->windowStart = max(1, $this->windowEnd - self::MAX_WINDOW + 1);
|
|
}
|
|
|
|
public function prependChunk(): void
|
|
{
|
|
if ($this->windowStart <= 1) {
|
|
return;
|
|
}
|
|
|
|
$this->windowStart = max(1, $this->windowStart - self::PAGE_SIZE);
|
|
$this->windowEnd = min($this->total, $this->windowStart + self::MAX_WINDOW - 1);
|
|
}
|
|
|
|
public function remove(int $playlistEpisodeId): void
|
|
{
|
|
if (! $this->isOwner) {
|
|
return;
|
|
}
|
|
|
|
$playlistEpisode = PlaylistEpisode::find($playlistEpisodeId);
|
|
|
|
if (! $playlistEpisode) {
|
|
return;
|
|
}
|
|
|
|
$playlistEpisode->delete();
|
|
$this->playlistService->reorderPositions($this->playlist);
|
|
$this->playlist->loadCount('episodes');
|
|
|
|
$this->total = $this->playlist->episodes_count;
|
|
|
|
$this->windowEnd = min($this->windowEnd, max(1, $this->total));
|
|
$this->windowStart = min($this->windowStart, max(1, $this->total));
|
|
|
|
$position = $this->currentPosition();
|
|
|
|
if ($position) {
|
|
$this->resolveAdjacentSlugs($position);
|
|
}
|
|
}
|
|
|
|
public function render(): View
|
|
{
|
|
$activePlaylistEpisode = $this->activePlaylistEpisode();
|
|
|
|
return view('livewire.playlist-sidebar', [
|
|
'episodes' => $this->episodes,
|
|
'currentPosition' => $activePlaylistEpisode?->position ?? 1,
|
|
'currentPlaylistEpisodeId' => $activePlaylistEpisode?->id,
|
|
'isOwner' => $this->isOwner,
|
|
]);
|
|
}
|
|
|
|
private function activePlaylistEpisode(): ?PlaylistEpisode
|
|
{
|
|
return PlaylistEpisode::where('playlist_id', $this->playlist->id)
|
|
->where('episode_id', $this->currentEpisodeId)
|
|
->first();
|
|
}
|
|
|
|
private function currentPosition(): ?int
|
|
{
|
|
return $this->activePlaylistEpisode()?->position;
|
|
}
|
|
|
|
private function setWindowAround(int $position): void
|
|
{
|
|
$this->windowStart = max(1, $position - self::PAGE_SIZE);
|
|
$this->windowEnd = min($this->total, $position + self::PAGE_SIZE);
|
|
}
|
|
|
|
private function resolveAdjacentSlugs(int $position): void
|
|
{
|
|
$adjacent = PlaylistEpisode::query()
|
|
->where('playlist_id', $this->playlist->id)
|
|
->whereBetween('position', [$position - 1, $position + 1])
|
|
->orderBy('position')
|
|
->with('episode')
|
|
->get();
|
|
|
|
$slugsByPosition = $adjacent->keyBy('position');
|
|
|
|
$this->previousEpisodeSlug = $slugsByPosition->get($position - 1)?->episode->slug;
|
|
$this->nextEpisodeSlug = $slugsByPosition->get($position + 1)?->episode->slug;
|
|
}
|
|
|
|
private function repairNullPositions(): void
|
|
{
|
|
if (! PlaylistEpisode::where('playlist_id', $this->playlist->id)->whereNull('position')->exists()) {
|
|
return;
|
|
}
|
|
|
|
PlaylistEpisode::where('playlist_id', $this->playlist->id)
|
|
->orderBy('position')->orderBy('id')
|
|
->get()
|
|
->each(fn ($playlistEpisode, $index) => $playlistEpisode->update(['position' => $index + 1]));
|
|
}
|
|
}
|