Update playlist sidebar to use livewire

This commit is contained in:
2026-08-04 18:33:50 +02:00
parent 7f9573fe0f
commit 9dcf13a62e
10 changed files with 472 additions and 205 deletions
+1 -5
View File
@@ -56,14 +56,11 @@ class StreamController extends Controller
// Playlist
if ($request->has('playlist')) {
// Get and check if playlist exists
$playlist = Playlist::where('id', $request->input('playlist'))->firstOrFail();
$playlist = Playlist::withCount('episodes')->where('id', $request->input('playlist'))->firstOrFail();
// Check if episode is in playlist
$inPlaylist = PlaylistEpisode::where('playlist_id', $playlist->id)->where('episode_id', $episode->id)->firstOrFail();
// Get Playlist Episodes and order them
$playlistEpisodes = $playlist->episodes()->orderBy('position')->get();
// Check if authorized
if ($playlist->is_private && (Auth::guest() || (! Auth::guest() && Auth::user()->id != $playlist->user_id))) {
abort(404);
@@ -75,7 +72,6 @@ class StreamController extends Controller
'studioEpisodes' => $studioEpisodes,
'gallery' => $gallery,
'playlist' => $playlist,
'playlistEpisodes' => $playlistEpisodes,
'popularWeekly' => CacheHelper::getPopularWeekly(),
'isMobile' => $isMobile,
]);
+182
View File
@@ -0,0 +1,182 @@
<?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]));
}
}