feat: refactor player component structure and introduce new player controls, progress, track info, and volume components

This commit is contained in:
JSC
2025-09-22 19:47:13 +02:00
parent b77dff03c1
commit 9784d259ba
8 changed files with 692 additions and 419 deletions

View File

@@ -0,0 +1,248 @@
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import type { PlayerMode, PlayerState } from '@/lib/api/services/player'
import {
ArrowRight,
ArrowRightToLine,
List,
Pause,
Play,
Repeat,
Repeat1,
Shuffle,
SkipBack,
SkipForward,
Square,
} from 'lucide-react'
import { memo, useMemo } from 'react'
import { useRenderFlash } from '@/hooks/useRenderFlash'
interface PlayerControlsProps {
status: PlayerState['status']
mode: PlayerMode
isLoading: boolean
showPlaylistButton?: boolean
onPlayPause: () => void
onStop: () => void
onPrevious: () => void
onNext: () => void
onModeChange: () => void
onTogglePlaylist?: () => void
variant?: 'normal' | 'maximized' | 'minimized'
}
export const PlayerControls = memo(function PlayerControls({
status,
mode,
isLoading,
showPlaylistButton = false,
onPlayPause,
onStop,
onPrevious,
onNext,
onModeChange,
onTogglePlaylist,
variant = 'normal',
}: PlayerControlsProps) {
const isMinimized = variant === 'minimized'
const isMaximized = variant === 'maximized'
const flashClass = useRenderFlash([status, mode], 'green')
const modeIcon = useMemo(() => {
switch (mode) {
case 'continuous':
return <ArrowRight className="h-4 w-4" />
case 'loop':
return <Repeat className="h-4 w-4" />
case 'loop_one':
return <Repeat1 className="h-4 w-4" />
case 'random':
return <Shuffle className="h-4 w-4" />
default:
return <ArrowRightToLine className="h-4 w-4" />
}
}, [mode])
const modeLabel = useMemo(() =>
mode.replace('_', ' '),
[mode]
)
if (isMinimized) {
return (
<div className={`${flashClass} flex items-center gap-1`}>
{/* DEBUG: PlayerControls Minimized - GREEN FLASH */}
<Button
size="sm"
variant="ghost"
onClick={onPrevious}
disabled={isLoading}
className="h-8 w-8 p-0"
>
<SkipBack className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="ghost"
onClick={onPlayPause}
disabled={isLoading}
className="h-8 w-8 p-0"
>
{status === 'playing' ? (
<Pause className="h-4 w-4" />
) : (
<Play className="h-4 w-4" />
)}
</Button>
<Button
size="sm"
variant="ghost"
onClick={onStop}
disabled={isLoading}
className="h-8 w-8 p-0"
>
<Square className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="ghost"
onClick={onNext}
disabled={isLoading}
className="h-8 w-8 p-0"
>
<SkipForward className="h-4 w-4" />
</Button>
</div>
)
}
if (isMaximized) {
return (
<div className={flashClass}>
{/* DEBUG: PlayerControls Maximized - GREEN FLASH */}
{/* Large Controls */}
<div className="flex items-center gap-4 mb-8">
<Button
size="lg"
variant="ghost"
onClick={onPrevious}
disabled={isLoading}
>
<SkipBack className="h-6 w-6" />
</Button>
<Button
size="lg"
onClick={onPlayPause}
disabled={isLoading}
className="h-16 w-16 rounded-full"
>
{status === 'playing' ? (
<Pause className="h-8 w-8" />
) : (
<Play className="h-8 w-8" />
)}
</Button>
<Button
size="lg"
variant="ghost"
onClick={onStop}
disabled={isLoading}
>
<Square className="h-6 w-6" />
</Button>
<Button
size="lg"
variant="ghost"
onClick={onNext}
disabled={isLoading}
>
<SkipForward className="h-6 w-6" />
</Button>
</div>
{/* Secondary Controls */}
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<Button size="sm" variant="ghost" onClick={onModeChange}>
{modeIcon}
</Button>
<Badge variant="secondary">{modeLabel}</Badge>
</div>
</div>
</div>
)
}
// Normal variant
return (
<div className={flashClass}>
{/* DEBUG: PlayerControls Normal - GREEN FLASH */}
{/* Main Controls */}
<div className="flex items-center justify-center gap-2 mb-4">
<Button
size="sm"
variant="ghost"
onClick={onModeChange}
className="h-8 w-8 p-0"
title={`Mode: ${modeLabel}`}
>
{modeIcon}
</Button>
<Button
size="sm"
variant="ghost"
onClick={onPrevious}
disabled={isLoading}
>
<SkipBack className="h-4 w-4" />
</Button>
<Button
size="sm"
onClick={onPlayPause}
disabled={isLoading}
className="h-10 w-10 rounded-full"
>
{status === 'playing' ? (
<Pause className="h-5 w-5" />
) : (
<Play className="h-5 w-5" />
)}
</Button>
<Button
size="sm"
variant="ghost"
onClick={onStop}
disabled={isLoading}
>
<Square className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="ghost"
onClick={onNext}
disabled={isLoading}
>
<SkipForward className="h-4 w-4" />
</Button>
{showPlaylistButton && onTogglePlaylist && (
<Button
size="sm"
variant="ghost"
onClick={onTogglePlaylist}
className="h-8 w-8 p-0"
title="Toggle Playlist"
>
<List className="h-4 w-4" />
</Button>
)}
</div>
{/* Secondary Controls */}
<div className="flex items-center justify-between">
<Badge variant="secondary" className="text-xs">
{modeLabel}
</Badge>
</div>
</div>
)
})