Files
QueueCube/frontend/src/components/App.tsx

150 lines
4.1 KiB
TypeScript
Raw Normal View History

2025-02-15 21:16:04 -08:00
import React, { useState, useEffect, useCallback } from 'react';
2025-02-15 14:56:58 -08:00
import SongTable from './SongTable';
2025-02-15 15:22:07 -08:00
import NowPlaying from './NowPlaying';
import AddSongPanel from './AddSongPanel';
2025-02-15 16:28:47 -08:00
import { API, PlaylistItem } from '../api/player';
2025-02-15 15:22:07 -08:00
const App: React.FC = () => {
const [isPlaying, setIsPlaying] = useState(false);
const [nowPlayingSong, setNowPlayingSong] = useState<string | null>(null);
const [nowPlayingFileName, setNowPlayingFileName] = useState<string | null>(null);
2025-02-15 21:16:04 -08:00
const [volume, setVolume] = useState(100);
const [volumeSettingIsLocked, setVolumeSettingIsLocked] = useState(false);
2025-02-15 16:28:47 -08:00
const [songs, setSongs] = useState<PlaylistItem[]>([]);
2025-02-15 21:16:04 -08:00
const [_, setWs] = useState<WebSocket | null>(null);
2025-02-15 16:28:47 -08:00
2025-02-15 21:16:04 -08:00
const fetchPlaylist = useCallback(async () => {
2025-02-15 16:28:47 -08:00
const playlist = await API.getPlaylist();
setSongs(playlist);
2025-02-15 21:16:04 -08:00
}, []);
2025-02-15 16:28:47 -08:00
2025-02-15 21:16:04 -08:00
const fetchNowPlaying = useCallback(async () => {
2025-02-15 16:28:47 -08:00
const nowPlaying = await API.getNowPlaying();
setNowPlayingSong(nowPlaying.nowPlaying);
setNowPlayingFileName(nowPlaying.currentFile);
setIsPlaying(!nowPlaying.isPaused);
2025-02-15 21:16:04 -08:00
if (!volumeSettingIsLocked) {
setVolume(nowPlaying.volume);
}
}, [volumeSettingIsLocked]);
2025-02-15 12:15:41 -08:00
2025-02-15 15:22:07 -08:00
const handleAddURL = (url: string) => {
const urlToAdd = url.trim();
if (urlToAdd) {
2025-02-15 16:28:47 -08:00
API.addToPlaylist(urlToAdd);
fetchPlaylist();
2025-02-15 15:22:07 -08:00
}
};
2025-02-15 12:15:41 -08:00
2025-02-15 15:22:07 -08:00
const handleDelete = (index: number) => {
setSongs(songs.filter((_, i) => i !== index));
2025-02-15 16:28:47 -08:00
API.removeFromPlaylist(index);
fetchPlaylist();
fetchNowPlaying();
};
const handleSkipTo = async (index: number) => {
const song = songs[index];
if (song.playing) {
togglePlayPause();
} else {
await API.skipTo(index);
await API.play();
}
fetchNowPlaying();
fetchPlaylist();
};
const togglePlayPause = async () => {
if (isPlaying) {
await API.pause();
} else {
await API.play();
}
fetchNowPlaying();
};
const handleSkip = async () => {
await API.skip();
fetchNowPlaying();
2025-02-15 15:22:07 -08:00
};
2025-02-15 12:15:41 -08:00
2025-02-15 16:28:47 -08:00
const handlePrevious = async () => {
await API.previous();
fetchNowPlaying();
2025-02-15 15:22:07 -08:00
};
2025-02-15 12:15:41 -08:00
2025-02-15 21:16:04 -08:00
const handleVolumeSettingChange = async (volume: number) => {
setVolume(volume);
await API.setVolume(volume);
2025-02-15 16:28:47 -08:00
};
2025-02-15 21:16:04 -08:00
const handleWebSocketEvent = useCallback((event: any) => {
switch (event.event) {
case 'user_modify':
case 'end-file':
case 'playback-restart':
fetchPlaylist();
fetchNowPlaying();
break;
}
}, [fetchPlaylist, fetchNowPlaying]);
// Initial data fetch
2025-02-15 16:28:47 -08:00
useEffect(() => {
fetchPlaylist();
fetchNowPlaying();
2025-02-15 21:16:04 -08:00
}, [fetchPlaylist, fetchNowPlaying]);
// WebSocket connection
useEffect(() => {
const ws = API.subscribeToEvents(handleWebSocketEvent);
setWs(ws);
2025-02-15 16:28:47 -08:00
return () => {
if (ws) {
ws.close();
}
};
2025-02-15 21:16:04 -08:00
}, [handleWebSocketEvent]);
2025-02-15 16:28:47 -08:00
2025-02-15 12:15:41 -08:00
return (
2025-02-15 21:16:04 -08:00
<div className="flex items-center justify-center h-screen w-screen bg-black md:py-10">
<div className="bg-violet-900 w-full md:max-w-2xl h-full md:max-h-xl md:border md:rounded-2xl flex flex-col">
2025-02-15 12:15:41 -08:00
<NowPlaying
className="flex flex-row md:rounded-t-2xl"
2025-02-15 15:22:07 -08:00
songName={nowPlayingSong || "(Not Playing)"}
fileName={nowPlayingFileName || ""}
2025-02-15 12:15:41 -08:00
isPlaying={isPlaying}
2025-02-15 16:28:47 -08:00
onPlayPause={togglePlayPause}
onSkip={handleSkip}
onPrevious={handlePrevious}
2025-02-15 21:16:04 -08:00
volume={volume}
onVolumeSettingChange={handleVolumeSettingChange}
onVolumeWillChange={() => setVolumeSettingIsLocked(true)}
onVolumeDidChange={() => setVolumeSettingIsLocked(false)}
2025-02-15 12:15:41 -08:00
/>
2025-02-15 15:22:07 -08:00
{songs.length > 0 ? (
<SongTable
songs={songs}
2025-02-15 16:28:47 -08:00
isPlaying={isPlaying}
2025-02-15 15:22:07 -08:00
onDelete={handleDelete}
onSkipTo={handleSkipTo}
/>
) : (
<div className="flex items-center justify-center h-full">
<div className="text-white text-2xl font-bold">Playlist is empty</div>
</div>
)}
<AddSongPanel onAddURL={handleAddURL} />
2025-02-15 12:15:41 -08:00
</div>
</div>
);
};
export default App;