mirror of
https://github.com/wahyd4/you-music.git
synced 2026-08-08 20:59:47 +10:00
Ui update
This commit is contained in:
@@ -5,6 +5,7 @@ import { X, Play, Pause, SkipBack, SkipForward, Heart, ListPlus, Share2, Message
|
||||
import { formatDuration } from '@/lib/utils'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { musicApi } from '@/api/client'
|
||||
import React from 'react'
|
||||
|
||||
interface FullScreenPlayerProps {
|
||||
currentMusic: Music
|
||||
@@ -43,7 +44,12 @@ export default function FullScreenPlayer({
|
||||
onToggleLyrics,
|
||||
onSeek,
|
||||
}: FullScreenPlayerProps) {
|
||||
// Fetch lyrics when enabled
|
||||
const [userScrolling, setUserScrolling] = React.useState(false)
|
||||
const scrollTimeoutRef = React.useRef<number>()
|
||||
const lyricsContainerRef = React.useRef<HTMLDivElement>(null)
|
||||
const lastScrollTop = React.useRef(0)
|
||||
|
||||
// Fetch lyrics when enabled with caching
|
||||
const { data: lyrics, isLoading: lyricsLoading } = useQuery({
|
||||
queryKey: ['lyrics', currentMusic.id],
|
||||
queryFn: async () => {
|
||||
@@ -51,44 +57,87 @@ export default function FullScreenPlayer({
|
||||
return response.data.lyrics
|
||||
},
|
||||
enabled: showLyrics && !!currentMusic.id,
|
||||
staleTime: Infinity, // Cache lyrics forever
|
||||
gcTime: 1000 * 60 * 60 * 24, // 24 hours
|
||||
})
|
||||
|
||||
// Handle manual scroll
|
||||
const handleScroll = React.useCallback((e: React.UIEvent<HTMLDivElement>) => {
|
||||
const currentScrollTop = e.currentTarget.scrollTop
|
||||
|
||||
// Only set userScrolling if scroll position actually changed (user initiated)
|
||||
if (Math.abs(currentScrollTop - lastScrollTop.current) > 1) {
|
||||
setUserScrolling(true)
|
||||
lastScrollTop.current = currentScrollTop
|
||||
|
||||
// Clear existing timeout
|
||||
if (scrollTimeoutRef.current) {
|
||||
clearTimeout(scrollTimeoutRef.current)
|
||||
}
|
||||
|
||||
// Resume auto-scroll after 5 seconds of no scrolling
|
||||
scrollTimeoutRef.current = window.setTimeout(() => {
|
||||
setUserScrolling(false)
|
||||
}, 5000)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Cleanup timeout on unmount or when lyrics disabled
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (scrollTimeoutRef.current) {
|
||||
clearTimeout(scrollTimeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Reset user scrolling when lyrics change or disabled
|
||||
React.useEffect(() => {
|
||||
if (!showLyrics) {
|
||||
setUserScrolling(false)
|
||||
if (scrollTimeoutRef.current) {
|
||||
clearTimeout(scrollTimeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [showLyrics, currentMusic.id])
|
||||
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-background/95 backdrop-blur-sm flex flex-col">
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-8 overflow-y-auto">
|
||||
{/* Large Album Art */}
|
||||
<div className="w-full max-w-lg mb-8">
|
||||
<div className={`w-full max-w-lg ${showLyrics ? 'mb-4 md:mb-8' : 'mb-8'}`}>
|
||||
{currentMusic.thumbnail ? (
|
||||
<img
|
||||
src={currentMusic.thumbnail.startsWith('http') ? currentMusic.thumbnail : `/music/${currentMusic.thumbnail}`}
|
||||
alt={currentMusic.title}
|
||||
className="w-full aspect-square rounded-2xl object-cover shadow-2xl"
|
||||
className={`w-full aspect-square rounded-2xl object-cover shadow-2xl ${showLyrics ? 'max-w-xs md:max-w-lg mx-auto' : ''}`}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full aspect-square rounded-2xl bg-secondary flex items-center justify-center shadow-2xl">
|
||||
<Play className="h-32 w-32 text-muted-foreground" />
|
||||
<div className={`w-full aspect-square rounded-2xl bg-secondary flex items-center justify-center shadow-2xl ${showLyrics ? 'max-w-xs md:max-w-lg mx-auto' : ''}`}>
|
||||
<Play className={showLyrics ? 'h-24 w-24 md:h-32 md:w-32 text-muted-foreground' : 'h-32 w-32 text-muted-foreground'} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Song Info */}
|
||||
<div className="text-center mb-6 max-w-lg w-full">
|
||||
<h1 className="text-3xl font-bold mb-2">{currentMusic.title}</h1>
|
||||
<div className={`text-center max-w-lg w-full ${showLyrics ? 'mb-3 md:mb-6' : 'mb-6'}`}>
|
||||
<h1 className={showLyrics ? 'text-2xl md:text-3xl font-bold mb-1 md:mb-2' : 'text-3xl font-bold mb-2'}>{currentMusic.title}</h1>
|
||||
{currentMusic.artist && currentMusic.artist !== 'Unknown' ? (
|
||||
<button
|
||||
onClick={onNavigateToArtist}
|
||||
className="text-xl text-muted-foreground hover:underline"
|
||||
className={showLyrics ? 'text-lg md:text-xl text-muted-foreground hover:underline' : 'text-xl text-muted-foreground hover:underline'}
|
||||
>
|
||||
{currentMusic.artist}
|
||||
</button>
|
||||
) : (
|
||||
<p className="text-xl text-muted-foreground">
|
||||
<p className={showLyrics ? 'text-lg md:text-xl text-muted-foreground' : 'text-xl text-muted-foreground'}>
|
||||
{currentMusic.artist || 'Unknown Artist'}
|
||||
</p>
|
||||
)}
|
||||
{currentMusic.album && (
|
||||
<p className="text-sm text-muted-foreground mt-1">{currentMusic.album}</p>
|
||||
<p className={showLyrics ? 'text-xs md:text-sm text-muted-foreground mt-1' : 'text-sm text-muted-foreground mt-1'}>{currentMusic.album}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -98,21 +147,27 @@ export default function FullScreenPlayer({
|
||||
{lyricsLoading ? (
|
||||
<p className="text-center text-foreground py-8 md:py-12 text-lg md:text-xl">Loading lyrics...</p>
|
||||
) : lyrics && lyrics.length > 0 ? (
|
||||
<div className="relative h-64 md:h-96 overflow-hidden p-2 md:p-4">
|
||||
<div
|
||||
ref={lyricsContainerRef}
|
||||
className="relative h-80 md:h-96 overflow-y-auto p-2 md:p-4 scrollbar-hide"
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<div
|
||||
className="lyrics-scroll-content"
|
||||
className={userScrolling ? '' : 'lyrics-scroll-content'}
|
||||
style={{
|
||||
animationDuration: `${Math.max(lyrics.split('\n').length * 1.5, 30)}s`,
|
||||
animationPlayState: isPlaying ? 'running' : 'paused'
|
||||
animationDuration: userScrolling ? undefined : `${Math.max(lyrics.split('\n').length * 1.5, 30)}s`,
|
||||
animationPlayState: userScrolling ? 'paused' : (isPlaying ? 'running' : 'paused'),
|
||||
}}
|
||||
>
|
||||
<div className="whitespace-pre-wrap text-center text-base md:text-xl leading-relaxed font-normal text-foreground py-4 md:py-8 px-2">
|
||||
{lyrics}
|
||||
</div>
|
||||
{/* Duplicate for seamless loop */}
|
||||
<div className="whitespace-pre-wrap text-center text-base md:text-xl leading-relaxed font-normal text-foreground py-4 md:py-8 px-2">
|
||||
{lyrics}
|
||||
</div>
|
||||
{!userScrolling && (
|
||||
<div className="whitespace-pre-wrap text-center text-base md:text-xl leading-relaxed font-normal text-foreground py-4 md:py-8 px-2">
|
||||
{lyrics}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Gradient overlays for fade effect */}
|
||||
<div className="absolute top-0 left-0 right-0 h-16 md:h-20 bg-gradient-to-b from-background to-transparent pointer-events-none" />
|
||||
@@ -144,26 +199,26 @@ export default function FullScreenPlayer({
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="flex items-center gap-6 mb-6">
|
||||
<div className={`flex items-center mb-6 ${showLyrics ? 'gap-4 md:gap-6' : 'gap-6'}`}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onPrevious}
|
||||
className="h-12 w-12"
|
||||
className={showLyrics ? 'h-10 w-10 md:h-12 md:w-12' : 'h-12 w-12'}
|
||||
>
|
||||
<SkipBack className="h-6 w-6" />
|
||||
<SkipBack className={showLyrics ? 'h-5 w-5 md:h-6 md:w-6' : 'h-6 w-6'} />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="default"
|
||||
size="icon"
|
||||
onClick={onTogglePlay}
|
||||
className="h-16 w-16"
|
||||
className={showLyrics ? 'h-12 w-12 md:h-16 md:w-16' : 'h-16 w-16'}
|
||||
>
|
||||
{isPlaying ? (
|
||||
<Pause className="h-8 w-8" />
|
||||
<Pause className={showLyrics ? 'h-6 w-6 md:h-8 md:w-8' : 'h-8 w-8'} />
|
||||
) : (
|
||||
<Play className="h-8 w-8" />
|
||||
<Play className={showLyrics ? 'h-6 w-6 md:h-8 md:w-8' : 'h-8 w-8'} />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@@ -171,9 +226,9 @@ export default function FullScreenPlayer({
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onNext}
|
||||
className="h-12 w-12"
|
||||
className={showLyrics ? 'h-10 w-10 md:h-12 md:w-12' : 'h-12 w-12'}
|
||||
>
|
||||
<SkipForward className="h-6 w-6" />
|
||||
<SkipForward className={showLyrics ? 'h-5 w-5 md:h-6 md:w-6' : 'h-6 w-6'} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user