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])); } }