From 2e57f54bb2a350a5ac8fef7f07dda9a8b436dc16 Mon Sep 17 00:00:00 2001 From: Frank Stellmacher <171614930+Psychotoxical@users.noreply.github.com> Date: Wed, 13 May 2026 12:57:20 +0200 Subject: [PATCH] =?UTF-8?q?refactor(playlist):=20G.64=20=E2=80=94=20extrac?= =?UTF-8?q?t=20PlaylistHero=20(#631)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pages/PlaylistDetail.tsx` 1199 → 1051 LOC. The full hero section (blurred background, back button, cover with click-to-edit, title with smart-playlist sparkle, meta line, every action button: play / shuffle / enqueue / add-songs toggle / CSV import / ZIP download with inline progress, offline cache toggle with downloading-progress state) moves to `components/playlist/PlaylistHero.tsx`. Props cover the data + every callback the buttons fire; the component subscribes to themeStore for the two cover-art feature flags (enableCoverArtBackground / enablePlaylistCoverPhoto) and uses useTranslation + useNavigate directly so the page doesn't pass them in. Pure code move — same JSX, same handlers, same fragment quirk in album-detail-meta. --- src/components/playlist/PlaylistHero.tsx | 231 +++++++++++++++++++++++ src/pages/PlaylistDetail.tsx | 198 ++++--------------- 2 files changed, 263 insertions(+), 166 deletions(-) create mode 100644 src/components/playlist/PlaylistHero.tsx diff --git a/src/components/playlist/PlaylistHero.tsx b/src/components/playlist/PlaylistHero.tsx new file mode 100644 index 00000000..3b0bc30a --- /dev/null +++ b/src/components/playlist/PlaylistHero.tsx @@ -0,0 +1,231 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import { + Camera, ChevronLeft, Download, FileUp, Globe, HardDriveDownload, ListPlus, + Loader2, Lock, Pencil, Play, Search, Shuffle, Sparkles, Trash2, +} from 'lucide-react'; +import type { SubsonicPlaylist, SubsonicSong } from '../../api/subsonicTypes'; +import type { ZipDownload } from '../../store/zipDownloadStore'; +import { useThemeStore } from '../../store/themeStore'; +import { + displayPlaylistName, formatSize, isSmartPlaylistName, totalDurationLabel, +} from '../../utils/playlistDetailHelpers'; +import CachedImage from '../CachedImage'; + +interface Props { + playlist: SubsonicPlaylist; + songs: SubsonicSong[]; + id: string | undefined; + customCoverId: string | null; + customCoverFetchUrl: string | null; + customCoverCacheKey: string | null; + coverQuadUrls: ({ src: string; cacheKey: string } | null)[]; + resolvedBgUrl: string | null; + saving: boolean; + searchOpen: boolean; + csvImporting: boolean; + activeZip: ZipDownload | undefined; + isCached: boolean; + isDownloading: boolean; + offlineProgress: { done: number; total: number } | null; + activeServerId: string; + setEditingMeta: React.Dispatch>; + setSearchOpen: React.Dispatch>; + setSearchQuery: React.Dispatch>; + setSearchResults: React.Dispatch>; + setSelectedSearchIds: React.Dispatch>>; + setSearchPlPickerOpen: React.Dispatch>; + handlePlayAll: () => void; + handleShuffleAll: () => void; + handleEnqueueAll: () => void; + handleImportCsv: () => void; + handleDownload: () => void; + deleteAlbum: (id: string, serverId: string) => void; + downloadPlaylist: (id: string, name: string, coverArt: string | undefined, songs: SubsonicSong[], serverId: string) => void; +} + +export default function PlaylistHero({ + playlist, songs, id, + customCoverId, customCoverFetchUrl, customCoverCacheKey, coverQuadUrls, + resolvedBgUrl, saving, searchOpen, csvImporting, activeZip, + isCached, isDownloading, offlineProgress, activeServerId, + setEditingMeta, setSearchOpen, setSearchQuery, setSearchResults, + setSelectedSearchIds, setSearchPlPickerOpen, + handlePlayAll, handleShuffleAll, handleEnqueueAll, handleImportCsv, handleDownload, + deleteAlbum, downloadPlaylist, +}: Props) { + const { t } = useTranslation(); + const navigate = useNavigate(); + const enableCoverArtBackground = useThemeStore(s => s.enableCoverArtBackground); + const enablePlaylistCoverPhoto = useThemeStore(s => s.enablePlaylistCoverPhoto); + + return ( +
+ {resolvedBgUrl && enableCoverArtBackground && ( + <> +