import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { getTrack } from '../services/trackService'; import { TrackServiceError as TrackUploadError } from '../errors/trackErrors'; import { usePlayerStore } from '@/features/player/store/playerStore'; import type { Track as PlayerTrack } from '@/features/player/types'; import toast from '@/utils/toast'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { LoadingSpinner } from '@/components/ui/loading-spinner'; import { ErrorDisplay } from '@/components/ui/ErrorDisplay'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'; import { Play, Pause, ArrowLeft, Share2, Plus, BarChart3, Music, Heart, Clock } from 'lucide-react'; import type { Track } from '../types/track'; import { CommentSection } from '../components/CommentSection'; import { ShareDialog } from '../components/ShareDialog'; import { TrackHistory } from '../components/TrackHistory'; import { TrackStatsDisplay } from '../components/TrackStatsDisplay'; function formatDuration(seconds: number): string { const minutes = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${minutes}:${secs.toString().padStart(2, '0')}`; } export function TrackDetailPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [track, setTrack] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [isShareDialogOpen, setIsShareDialogOpen] = useState(false); const { play, pause, currentTrack, isPlaying, addToQueue } = usePlayerStore(); const loadTrack = async () => { if (!id) { setError(new Error('Track ID is required')); setIsLoading(false); return; } try { setIsLoading(true); setError(null); const loadedTrack = await getTrack(id); setTrack(loadedTrack); } catch (err) { const errorMessage = err instanceof TrackUploadError ? err.message : err instanceof Error ? err.message : 'Failed to load track'; setError(new Error(errorMessage)); } finally { setIsLoading(false); } }; useEffect(() => { loadTrack(); }, [id]); const mapToPlayerTrack = (t: Track): PlayerTrack => ({ id: t.id, title: t.title, artist: t.artist, album: t.album, duration: t.duration, url: t.stream_manifest_url || t.file_path, cover: t.cover_art_path, genre: t.genre, }); const handlePlay = () => { if (track) play(mapToPlayerTrack(track)); }; const handlePause = () => { pause(); }; const handleAddToQueue = () => { if (track) { addToQueue([mapToPlayerTrack(track)]); toast.success("Added to queue"); } }; const handleShare = () => { setIsShareDialogOpen(true); }; const isCurrentTrack = currentTrack?.id === track?.id; const isCurrentlyPlaying = isCurrentTrack && isPlaying; if (isLoading) { return
; } if (error || !track) { return (
navigate(-1), variant: 'outline' }]} />
); } return (
{/* 1. Immersive Blur Background */}
{/* Left Column: Cover & Main Actions */}
{track.cover_art_path ? ( {track.title} ) : (
)} {/* Vinyl Shine Effect */}
{isCurrentlyPlaying ? ( ) : ( )}
{/* Quick Stats Grid */}
{track.like_count} Likes {track.play_count} Plays
{/* Right Column: Details & Content */}
{/* Header Info */}

{track.title}

{track.artist} {track.album && • {track.album}} {track.year && • {track.year}}
{track.genre && ( {track.genre} )} {formatDuration(track.duration)}
{/* Waveform */} {track.waveform_path && (
Waveform
)} {/* Tabs & Content */} Discussion 24 Analytics History

Performance Data

Version History

{track && setIsShareDialogOpen(false)} trackId={track.id} trackTitle={track.title} />}
); }