Files
muzick/frontend/src/components/LyricsOverlay.tsx
T

73 lines
3.0 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { X } from 'lucide-react';
import { usePlaybackStore } from '../store/usePlaybackStore';
import { trackService } from '../services/trackService';
import { albumService } from '../services/albumService';
import { Artwork } from './Artwork';
import { ArtistLinks } from './ArtistLinks';
import { SyncedLyrics } from './SyncedLyrics';
/**
* Dedicated, roomy lyrics view — overlays the content area (not the playback
* bar, so transport stays usable). Separated from the queue sidebar so the
* karaoke lyrics get the space they deserve.
*/
export function LyricsOverlay({ onClose }: { onClose: () => void }) {
const currentTrack = usePlaybackStore((s) => s.currentTrack);
const albumQ = useQuery({
queryKey: ['album', currentTrack?.album_id],
queryFn: () => albumService.getAlbum(currentTrack!.album_id),
enabled: !!currentTrack?.album_id,
staleTime: 5 * 60 * 1000,
});
const lyricsQ = useQuery({
queryKey: ['lyrics', currentTrack?.id],
queryFn: () => trackService.getLyrics(currentTrack!.id),
enabled: !!currentTrack,
retry: false,
});
return (
<div className="absolute inset-0 z-40 glass flex flex-col animate-rise">
{/* Header */}
<div className="flex items-center gap-4 px-6 py-4 border-b border-border/70">
{currentTrack && (
<div className="w-12 h-12 flex-none rounded-lg overflow-hidden shadow-md shadow-black/40">
<Artwork seed={`${currentTrack.title} ${currentTrack.artist}`} src={albumQ.data?.artwork_id} className="w-full h-full" rounded="lg" />
</div>
)}
<div className="min-w-0 flex-1">
<div className="text-lg font-bold text-text truncate">{currentTrack?.title ?? 'Lyrics'}</div>
{currentTrack && (
<ArtistLinks artists={currentTrack.artists} fallback={currentTrack.artist} className="block text-sm text-muted truncate" />
)}
</div>
<button onClick={onClose} aria-label="Close lyrics" className="rounded-md p-2 text-muted hover:text-text hover:bg-surface1 transition-colors">
<X size={18} />
</button>
</div>
{/* Lyrics */}
<div
className="flex-1 overflow-y-auto mx-auto w-full max-w-2xl"
style={{
WebkitMaskImage: 'linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%)',
maskImage: 'linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%)',
}}
>
{!currentTrack ? (
<p className="px-6 py-10 text-center text-sm text-muted italic">Nothing playing.</p>
) : lyricsQ.isLoading ? (
<p className="px-6 py-10 text-center text-sm text-muted">Loading</p>
) : lyricsQ.isError || !lyricsQ.data ? (
<p className="px-6 py-10 text-center text-sm text-muted italic">No lyrics available.</p>
) : (
<SyncedLyrics synced={lyricsQ.data.synced_lyrics} plain={lyricsQ.data.lyrics_text} />
)}
</div>
</div>
);
}