feat: Remove Player tab and use persistent player across app
- Remove Player tab from Music Storage page - Update play buttons to use persistent player instead of embedded player - Add persistent player to Music Storage page for consistent experience - Convert MusicFile objects to Song objects for persistent player compatibility - Ensure music playback works seamlessly across both main view and Music Storage - Create unified music playback experience throughout the application Now users have a consistent music player experience across all pages with the floating persistent player at the bottom of the screen.
This commit is contained in:
parent
a3d1b4d211
commit
1f235d8fa8
@ -24,8 +24,9 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FiPlay, FiTrash2, FiMusic, FiRefreshCw } from 'react-icons/fi';
|
import { FiPlay, FiTrash2, FiMusic, FiRefreshCw } from 'react-icons/fi';
|
||||||
import { MusicUpload } from '../components/MusicUpload';
|
import { MusicUpload } from '../components/MusicUpload';
|
||||||
import { MusicPlayer } from '../components/MusicPlayer';
|
|
||||||
import { SongMatching } from '../components/SongMatching';
|
import { SongMatching } from '../components/SongMatching';
|
||||||
|
import { PersistentMusicPlayer } from '../components/PersistentMusicPlayer';
|
||||||
|
import type { Song } from '../types/interfaces';
|
||||||
|
|
||||||
interface MusicFile {
|
interface MusicFile {
|
||||||
_id: string;
|
_id: string;
|
||||||
@ -42,7 +43,7 @@ interface MusicFile {
|
|||||||
|
|
||||||
export const MusicStorage: React.FC = () => {
|
export const MusicStorage: React.FC = () => {
|
||||||
const [musicFiles, setMusicFiles] = useState<MusicFile[]>([]);
|
const [musicFiles, setMusicFiles] = useState<MusicFile[]>([]);
|
||||||
const [selectedFile, setSelectedFile] = useState<MusicFile | null>(null);
|
const [currentPlayingSong, setCurrentPlayingSong] = useState<Song | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isSyncing, setIsSyncing] = useState(false);
|
const [isSyncing, setIsSyncing] = useState(false);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@ -131,8 +132,8 @@ export const MusicStorage: React.FC = () => {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setMusicFiles(prev => prev.filter(file => file._id !== fileId));
|
setMusicFiles(prev => prev.filter(file => file._id !== fileId));
|
||||||
if (selectedFile?._id === fileId) {
|
if (currentPlayingSong?.s3File?.musicFileId === fileId) {
|
||||||
setSelectedFile(null);
|
setCurrentPlayingSong(null);
|
||||||
}
|
}
|
||||||
toast({
|
toast({
|
||||||
title: 'File Deleted',
|
title: 'File Deleted',
|
||||||
@ -156,6 +157,44 @@ export const MusicStorage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle playing a music file from the Music Storage page
|
||||||
|
const handlePlayMusicFile = async (musicFile: MusicFile) => {
|
||||||
|
try {
|
||||||
|
// Create a Song object from the music file for the persistent player
|
||||||
|
const song: Song = {
|
||||||
|
id: musicFile._id,
|
||||||
|
title: musicFile.title || musicFile.originalName,
|
||||||
|
artist: musicFile.artist || 'Unknown Artist',
|
||||||
|
album: musicFile.album || '',
|
||||||
|
totalTime: musicFile.duration?.toString() || '0',
|
||||||
|
location: '',
|
||||||
|
s3File: {
|
||||||
|
musicFileId: musicFile._id,
|
||||||
|
s3Key: '', // This will be fetched by the persistent player
|
||||||
|
s3Url: '',
|
||||||
|
streamingUrl: '',
|
||||||
|
hasS3File: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
setCurrentPlayingSong(song);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error playing music file:', error);
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'Failed to play music file',
|
||||||
|
status: 'error',
|
||||||
|
duration: 3000,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle closing the music player
|
||||||
|
const handleCloseMusicPlayer = () => {
|
||||||
|
setCurrentPlayingSong(null);
|
||||||
|
};
|
||||||
|
|
||||||
const formatFileSize = (bytes: number): string => {
|
const formatFileSize = (bytes: number): string => {
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||||
if (bytes === 0) return '0 Bytes';
|
if (bytes === 0) return '0 Bytes';
|
||||||
@ -208,9 +247,6 @@ export const MusicStorage: React.FC = () => {
|
|||||||
<Tab color="gray.300" _selected={{ bg: "gray.700", color: "white", borderColor: "gray.600" }}>
|
<Tab color="gray.300" _selected={{ bg: "gray.700", color: "white", borderColor: "gray.600" }}>
|
||||||
Song Matching
|
Song Matching
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab color="gray.300" _selected={{ bg: "gray.700", color: "white", borderColor: "gray.600" }}>
|
|
||||||
Player
|
|
||||||
</Tab>
|
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanels flex={1} overflow="hidden">
|
<TabPanels flex={1} overflow="hidden">
|
||||||
@ -324,7 +360,7 @@ export const MusicStorage: React.FC = () => {
|
|||||||
icon={<FiPlay />}
|
icon={<FiPlay />}
|
||||||
size="sm"
|
size="sm"
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
onClick={() => setSelectedFile(file)}
|
onClick={() => handlePlayMusicFile(file)}
|
||||||
_hover={{ bg: "blue.700" }}
|
_hover={{ bg: "blue.700" }}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -349,42 +385,14 @@ export const MusicStorage: React.FC = () => {
|
|||||||
<TabPanel bg="gray.900" height="100%" overflowY="auto">
|
<TabPanel bg="gray.900" height="100%" overflowY="auto">
|
||||||
<SongMatching />
|
<SongMatching />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
{/* Player Tab */}
|
|
||||||
<TabPanel bg="gray.900" height="100%" overflowY="auto">
|
|
||||||
<VStack spacing={4} align="stretch">
|
|
||||||
<Heading size="md" color="white">Music Player</Heading>
|
|
||||||
{selectedFile ? (
|
|
||||||
<MusicPlayer
|
|
||||||
musicFile={selectedFile}
|
|
||||||
onEnded={() => {
|
|
||||||
// Auto-play next song or stop
|
|
||||||
const currentIndex = musicFiles.findIndex(f => f._id === selectedFile._id);
|
|
||||||
const nextFile = musicFiles[currentIndex + 1];
|
|
||||||
if (nextFile) {
|
|
||||||
setSelectedFile(nextFile);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
p={8}
|
|
||||||
textAlign="center"
|
|
||||||
border="2px dashed"
|
|
||||||
borderColor="gray.600"
|
|
||||||
borderRadius="lg"
|
|
||||||
color="gray.500"
|
|
||||||
bg="gray.800"
|
|
||||||
>
|
|
||||||
<FiMusic size={48} style={{ margin: '0 auto 16px' }} />
|
|
||||||
<Text>Select a music file from the library to start playing</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</VStack>
|
|
||||||
</TabPanel>
|
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
{/* Persistent Music Player */}
|
||||||
|
<PersistentMusicPlayer
|
||||||
|
currentSong={currentPlayingSong}
|
||||||
|
onClose={handleCloseMusicPlayer}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user