"use client"; import { useEffect, useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import Image from "next/image"; import Link from "next/link"; import { usePlaylistsQuery } from "@/hooks/useQueries"; import { useQueryClient } from "@tanstack/react-query"; import { queryKeys } from "@/hooks/useQueries"; import { useAuth } from "@/lib/auth-context"; import { useAudio } from "@/lib/audio-context"; import { Play, Music, Eye, EyeOff } from "lucide-react"; import { GradientSpinner } from "@/components/ui/GradientSpinner"; import { api } from "@/lib/api"; import { cn, isLocalUrl } from "@/utils/cn"; // Lidify brand yellow const LIDIFY_YELLOW = "#ecb200"; interface PlaylistItem { id: string; track: { album?: { coverArt?: string; }; }; } interface Playlist { id: string; name: string; trackCount?: number; items?: PlaylistItem[]; isOwner?: boolean; isHidden?: boolean; user?: { username: string; }; } // Generate mosaic cover from playlist tracks function PlaylistMosaic({ items, size = 4, greyed = false, }: { items?: PlaylistItem[]; size?: number; greyed?: boolean; }) { const coverUrls = useMemo(() => { if (!items || items.length === 0) return []; const tracksWithCovers = items.filter( (item) => item.track?.album?.coverArt ); if (tracksWithCovers.length === 0) return []; // Get unique cover arts (up to 4) const uniqueCovers = Array.from( new Set(tracksWithCovers.map((item) => item.track.album!.coverArt)) ).slice(0, size); return uniqueCovers.map((cover) => api.getCoverArtUrl(cover!, 200)); }, [items, size]); if (coverUrls.length === 0) { return (
); } if (coverUrls.length === 1) { return ( ); } return (
{coverUrls.slice(0, 4).map((url, index) => (
))} {Array.from({ length: Math.max(0, 4 - coverUrls.length) }).map( (_, index) => (
) )}
); } function PlaylistCard({ playlist, index, onPlay, onToggleHide, isHiddenView = false, }: { playlist: Playlist; index: number; onPlay: (playlistId: string) => void; onToggleHide: (playlistId: string, hide: boolean) => void; isHiddenView?: boolean; }) { const isShared = playlist.isOwner === false; const [isHiding, setIsHiding] = useState(false); const handleToggleHide = async (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); setIsHiding(true); try { await onToggleHide(playlist.id, !playlist.isHidden); } finally { setIsHiding(false); } }; return (
{/* Cover Image */}
{/* Hide/Unhide button for shared playlists */} {isShared && ( )} {/* Play button overlay */}
{/* Title and info */}

{playlist.name}

{isShared && playlist.user?.username ? ( By {playlist.user.username} ยท{" "} ) : null} {playlist.trackCount || 0}{" "} {playlist.trackCount === 1 ? "song" : "songs"}

); } export default function PlaylistsPage() { const router = useRouter(); const { isAuthenticated } = useAuth(); const { playTracks } = useAudio(); const queryClient = useQueryClient(); const [showHiddenTab, setShowHiddenTab] = useState(false); // Use React Query hook for playlists const { data: playlists = [], isLoading } = usePlaylistsQuery(); // Separate visible and hidden playlists const { visiblePlaylists, hiddenPlaylists } = useMemo(() => { const visible: Playlist[] = []; const hidden: Playlist[] = []; playlists.forEach((p: Playlist) => { if (p.isHidden) { hidden.push(p); } else { visible.push(p); } }); return { visiblePlaylists: visible, hiddenPlaylists: hidden }; }, [playlists]); // Listen for playlist events and invalidate cache useEffect(() => { const handlePlaylistEvent = () => { queryClient.invalidateQueries({ queryKey: queryKeys.playlists() }); }; window.addEventListener("playlist-created", handlePlaylistEvent); window.addEventListener("playlist-updated", handlePlaylistEvent); window.addEventListener("playlist-deleted", handlePlaylistEvent); return () => { window.removeEventListener("playlist-created", handlePlaylistEvent); window.removeEventListener("playlist-updated", handlePlaylistEvent); window.removeEventListener("playlist-deleted", handlePlaylistEvent); }; }, [queryClient]); const handlePlayPlaylist = async (playlistId: string) => { try { const playlist = await api.getPlaylist(playlistId); if (playlist?.items && playlist.items.length > 0) { const tracks = playlist.items.map((item: any) => ({ id: item.track.id, title: item.track.title, artist: { name: item.track.album?.artist?.name || "Unknown", id: item.track.album?.artist?.id, }, album: { title: item.track.album?.title || "Unknown", coverArt: item.track.album?.coverArt, id: item.track.album?.id, }, duration: item.track.duration, })); playTracks(tracks, 0); } } catch (error) { console.error("Failed to play playlist:", error); } }; const handleToggleHide = async (playlistId: string, hide: boolean) => { try { if (hide) { await api.hidePlaylist(playlistId); } else { await api.unhidePlaylist(playlistId); } // Invalidate and refetch playlists queryClient.invalidateQueries({ queryKey: queryKeys.playlists() }); } catch (error) { console.error("Failed to toggle playlist visibility:", error); } }; if (isLoading) { return (
); } const displayedPlaylists = showHiddenTab ? hiddenPlaylists : visiblePlaylists; return (
{/* Quick gradient fade - yellow to purple */}
{/* Header */}

Playlists

{visiblePlaylists.length}{" "} {visiblePlaylists.length === 1 ? "playlist" : "playlists"}

{/* Browse Public Playlists */} Browse Playlists {/* Hidden Playlists Toggle */} {hiddenPlaylists.length > 0 && ( )}
{/* Content */}
{/* Hidden playlists notice */} {showHiddenTab && (

Hidden playlists won't appear in your library. Hover and click the eye icon to restore.

)} {displayedPlaylists.length > 0 ? (
{displayedPlaylists.map( (playlist: Playlist, index: number) => ( ) )}
) : (

{showHiddenTab ? "No hidden playlists" : "No playlists yet"}

{showHiddenTab ? "You haven't hidden any playlists" : "Create your first playlist by adding songs from albums or artists"}

{!showHiddenTab && ( Browse Playlists )}
)}
); }