diff --git a/frontend/src/components/AddSongPanel.tsx b/frontend/src/components/AddSongPanel.tsx index a33541d..9b53bcb 100644 --- a/frontend/src/components/AddSongPanel.tsx +++ b/frontend/src/components/AddSongPanel.tsx @@ -13,7 +13,7 @@ const AddSongPanel: React.FC = ({ onAddURL }) => { } return ( -
+
{ const [isPlaying, setIsPlaying] = useState(false); const [nowPlayingSong, setNowPlayingSong] = useState(null); const [nowPlayingFileName, setNowPlayingFileName] = useState(null); + const [volume, setVolume] = useState(100); + const [volumeSettingIsLocked, setVolumeSettingIsLocked] = useState(false); const [songs, setSongs] = useState([]); - const [ws, setWs] = useState(null); + const [_, setWs] = useState(null); - const fetchPlaylist = async () => { + const fetchPlaylist = useCallback(async () => { const playlist = await API.getPlaylist(); setSongs(playlist); - }; + }, []); - const fetchNowPlaying = async () => { + const fetchNowPlaying = useCallback(async () => { const nowPlaying = await API.getNowPlaying(); setNowPlayingSong(nowPlaying.nowPlaying); setNowPlayingFileName(nowPlaying.currentFile); setIsPlaying(!nowPlaying.isPaused); - }; + + if (!volumeSettingIsLocked) { + setVolume(nowPlaying.volume); + } + }, [volumeSettingIsLocked]); const handleAddURL = (url: string) => { const urlToAdd = url.trim(); @@ -71,36 +77,43 @@ const App: React.FC = () => { fetchNowPlaying(); }; - const watchForEvents = () => { - const ws = API.subscribeToEvents((event) => { - switch (event.event) { - case 'user_modify': - case 'end-file': - case 'playback-restart': - fetchPlaylist(); - fetchNowPlaying(); - break; - } - }); - - return ws; + const handleVolumeSettingChange = async (volume: number) => { + setVolume(volume); + await API.setVolume(volume); }; + 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 useEffect(() => { fetchPlaylist(); fetchNowPlaying(); - setWs(watchForEvents()); + }, [fetchPlaylist, fetchNowPlaying]); + + // WebSocket connection + useEffect(() => { + const ws = API.subscribeToEvents(handleWebSocketEvent); + setWs(ws); return () => { if (ws) { ws.close(); } }; - }, []); + }, [handleWebSocketEvent]); return ( -
-
+
+
{ onPlayPause={togglePlayPause} onSkip={handleSkip} onPrevious={handlePrevious} + volume={volume} + onVolumeSettingChange={handleVolumeSettingChange} + onVolumeWillChange={() => setVolumeSettingIsLocked(true)} + onVolumeDidChange={() => setVolumeSettingIsLocked(false)} /> {songs.length > 0 ? ( diff --git a/frontend/src/components/NowPlaying.tsx b/frontend/src/components/NowPlaying.tsx index 69fe70f..5973cd6 100644 --- a/frontend/src/components/NowPlaying.tsx +++ b/frontend/src/components/NowPlaying.tsx @@ -1,14 +1,24 @@ import React, { HTMLAttributes } from 'react'; import classNames from 'classnames'; -import { FaPlay, FaPause, FaStepForward, FaStepBackward } from 'react-icons/fa'; +import { FaPlay, FaPause, FaStepForward, FaStepBackward, FaVolumeUp } from 'react-icons/fa'; interface NowPlayingProps extends HTMLAttributes { songName: string; fileName: string; isPlaying: boolean; + volume: number; onPlayPause: () => void; onSkip: () => void; onPrevious: () => void; + + // Sent when the volume setting actually changes value + onVolumeSettingChange: (volume: number) => void; + + // Sent when the volume is about to start changing + onVolumeWillChange: (volume: number) => void; + + // Sent when the volume has changed + onVolumeDidChange: (volume: number) => void; } const NowPlaying: React.FC = (props) => { @@ -20,13 +30,30 @@ const NowPlaying: React.FC = (props) => {
{props.songName}
{props.fileName}
-
+
+ +
+ + props.onVolumeWillChange(props.volume)} + onMouseUp={() => props.onVolumeDidChange(props.volume)} + onChange={(e) => props.onVolumeSettingChange(Number(e.target.value))} + className="w-24 h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:rounded-full hover:[&::-webkit-slider-thumb]:bg-violet-300" + /> +
+ + +