'use client'; import React, { useRef, useState, useEffect } from 'react'; import Image from 'next/image'; import { motion, PanInfo, AnimatePresence } from 'framer-motion'; import { useAudioPlayer, Track } from './AudioPlayerContext'; import { FaPlay, FaPause, FaExpand, FaForward, FaBackward } from 'react-icons/fa6'; import { Heart } from 'lucide-react'; import { constrain } from '@/lib/utils'; interface DraggableMiniPlayerProps { onExpand: () => void; } export const DraggableMiniPlayer: React.FC = ({ onExpand }) => { const { currentTrack, playPreviousTrack, playNextTrack, toggleCurrentTrackStar, isPlaying, togglePlayPause } = useAudioPlayer(); const [position, setPosition] = useState({ x: 0, y: 0 }); const [isDragging, setIsDragging] = useState(false); const containerRef = useRef(null); const dragStartRef = useRef({ x: 0, y: 0 }); // Save position to localStorage when it changes useEffect(() => { if (!isDragging) { localStorage.setItem('mini-player-position', JSON.stringify(position)); } }, [position, isDragging]); // Keyboard controls for the mini player useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Only handle keyboard shortcuts if the mini player is focused if (document.activeElement?.tagName === 'INPUT') return; const step = e.shiftKey ? 100 : 10; // Larger steps with shift key switch (e.key) { case 'ArrowLeft': setPosition(prev => ({ ...prev, x: constrain( prev.x - step, -(window.innerWidth - (containerRef.current?.offsetWidth || 0)) / 2 + 16, (window.innerWidth - (containerRef.current?.offsetWidth || 0)) / 2 - 16 ) })); break; case 'ArrowRight': setPosition(prev => ({ ...prev, x: constrain( prev.x + step, -(window.innerWidth - (containerRef.current?.offsetWidth || 0)) / 2 + 16, (window.innerWidth - (containerRef.current?.offsetWidth || 0)) / 2 - 16 ) })); break; case 'ArrowUp': setPosition(prev => ({ ...prev, y: constrain( prev.y - step, -(window.innerHeight - (containerRef.current?.offsetHeight || 0)) / 2 + 16, (window.innerHeight - (containerRef.current?.offsetHeight || 0)) / 2 - 16 ) })); break; case 'ArrowDown': setPosition(prev => ({ ...prev, y: constrain( prev.y + step, -(window.innerHeight - (containerRef.current?.offsetHeight || 0)) / 2 + 16, (window.innerHeight - (containerRef.current?.offsetHeight || 0)) / 2 - 16 ) })); break; case 'Escape': onExpand(); break; } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [onExpand]); // Load saved position on mount useEffect(() => { const savedPosition = localStorage.getItem('mini-player-position'); if (savedPosition) { try { const pos = JSON.parse(savedPosition); setPosition(pos); } catch (error) { console.error('Failed to parse saved mini player position:', error); } } }, []); // Ensure player stays within viewport bounds useEffect(() => { const constrainToViewport = () => { if (!containerRef.current || isDragging) return; const rect = containerRef.current.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; // Add some padding from edges const padding = 16; const newX = constrain( position.x, -(viewportWidth - rect.width) / 2 + padding, (viewportWidth - rect.width) / 2 - padding ); const newY = constrain( position.y, -(viewportHeight - rect.height) / 2 + padding, (viewportHeight - rect.height) / 2 - padding ); if (newX !== position.x || newY !== position.y) { setPosition({ x: newX, y: newY }); } }; constrainToViewport(); window.addEventListener('resize', constrainToViewport); return () => window.removeEventListener('resize', constrainToViewport); }, [position, isDragging]); const handleDragStart = () => { setIsDragging(true); dragStartRef.current = position; }; const handleDrag = (_: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => { setPosition({ x: dragStartRef.current.x + info.offset.x, y: dragStartRef.current.y + info.offset.y }); }; const handleDragEnd = () => { setIsDragging(false); }; if (!currentTrack) return null; return (
{/* Album Art */}
{currentTrack.name}
{/* Track Info */}

{currentTrack.name}

{currentTrack.artist}

{/* Controls */} {/* Keyboard shortcut hint */}
Arrow keys to move • Hold Shift for larger steps • Esc to expand
); };