@@ -32,37 +32,97 @@ export function AudioProvider({ children, allTracks }: { children: React.ReactNo
3232 const [ progress , setProgress ] = useState ( 0 ) ;
3333 const audioRef = useRef < HTMLAudioElement | null > ( null ) ;
3434
35+ // Refs for state to avoid closure issues in event listeners
36+ const currentTrackRef = useRef < Track | null > ( null ) ;
37+ const allTracksRef = useRef < Track [ ] > ( allTracks ) ;
38+
39+ useEffect ( ( ) => {
40+ currentTrackRef . current = currentTrack ;
41+ } , [ currentTrack ] ) ;
42+
43+ useEffect ( ( ) => {
44+ allTracksRef . current = allTracks ;
45+ } , [ allTracks ] ) ;
46+
47+ const playTrack = ( track : Track ) => {
48+ if ( audioRef . current ) {
49+ setIsLoading ( true ) ;
50+ audioRef . current . src = track . url ;
51+ audioRef . current . play ( ) . catch ( e => {
52+ console . error ( "Playback failed:" , e ) ;
53+ setIsLoading ( false ) ;
54+ } ) ;
55+ setCurrentTrack ( track ) ;
56+ setIsPlaying ( true ) ;
57+ }
58+ } ;
59+
60+ const playNext = ( ) => {
61+ const track = currentTrackRef . current ;
62+ const tracks = allTracksRef . current ;
63+ if ( ! track || tracks . length === 0 ) return ;
64+ const currentIndex = tracks . findIndex ( t => t . id === track . id ) ;
65+ const nextIndex = ( currentIndex + 1 ) % tracks . length ;
66+ playTrack ( tracks [ nextIndex ] ) ;
67+ } ;
68+
69+ const playPrev = ( ) => {
70+ const track = currentTrackRef . current ;
71+ const tracks = allTracksRef . current ;
72+ if ( ! track || tracks . length === 0 ) return ;
73+ const currentIndex = tracks . findIndex ( t => t . id === track . id ) ;
74+ const prevIndex = ( currentIndex - 1 + tracks . length ) % tracks . length ;
75+ playTrack ( tracks [ prevIndex ] ) ;
76+ } ;
77+
3578 // Initialize Audio
3679 useEffect ( ( ) => {
37- audioRef . current = new Audio ( ) ;
80+ if ( ! audioRef . current ) {
81+ audioRef . current = new Audio ( ) ;
82+ }
83+ const audio = audioRef . current ;
3884
3985 const handleEnded = ( ) => playNext ( ) ;
4086 const handleTimeUpdate = ( ) => {
41- if ( audioRef . current ) {
42- const p = ( audioRef . current . currentTime / audioRef . current . duration ) * 100 ;
43- setProgress ( isNaN ( p ) ? 0 : p ) ;
44- }
87+ const p = ( audio . currentTime / audio . duration ) * 100 ;
88+ setProgress ( isNaN ( p ) ? 0 : p ) ;
4589 } ;
4690
4791 const handleWaiting = ( ) => setIsLoading ( true ) ;
4892 const handleCanPlay = ( ) => setIsLoading ( false ) ;
49- const handlePlaying = ( ) => setIsLoading ( false ) ;
93+ const handlePlaying = ( ) => {
94+ setIsLoading ( false ) ;
95+ setIsPlaying ( true ) ;
96+ } ;
97+ const handlePause = ( ) => setIsPlaying ( false ) ;
98+ const handlePlay = ( ) => setIsPlaying ( true ) ;
5099 const handleLoadStart = ( ) => setIsLoading ( true ) ;
100+ const handleError = ( e : any ) => {
101+ console . error ( "Audio error:" , e ) ;
102+ setIsLoading ( false ) ;
103+ setIsPlaying ( false ) ;
104+ } ;
51105
52- audioRef . current . addEventListener ( 'ended' , handleEnded ) ;
53- audioRef . current . addEventListener ( 'timeupdate' , handleTimeUpdate ) ;
54- audioRef . current . addEventListener ( 'waiting' , handleWaiting ) ;
55- audioRef . current . addEventListener ( 'canplay' , handleCanPlay ) ;
56- audioRef . current . addEventListener ( 'playing' , handlePlaying ) ;
57- audioRef . current . addEventListener ( 'loadstart' , handleLoadStart ) ;
106+ audio . addEventListener ( 'ended' , handleEnded ) ;
107+ audio . addEventListener ( 'timeupdate' , handleTimeUpdate ) ;
108+ audio . addEventListener ( 'waiting' , handleWaiting ) ;
109+ audio . addEventListener ( 'canplay' , handleCanPlay ) ;
110+ audio . addEventListener ( 'playing' , handlePlaying ) ;
111+ audio . addEventListener ( 'pause' , handlePause ) ;
112+ audio . addEventListener ( 'play' , handlePlay ) ;
113+ audio . addEventListener ( 'loadstart' , handleLoadStart ) ;
114+ audio . addEventListener ( 'error' , handleError ) ;
58115
59116 return ( ) => {
60- audioRef . current ?. removeEventListener ( 'ended' , handleEnded ) ;
61- audioRef . current ?. removeEventListener ( 'timeupdate' , handleTimeUpdate ) ;
62- audioRef . current ?. removeEventListener ( 'waiting' , handleWaiting ) ;
63- audioRef . current ?. removeEventListener ( 'canplay' , handleCanPlay ) ;
64- audioRef . current ?. removeEventListener ( 'playing' , handlePlaying ) ;
65- audioRef . current ?. removeEventListener ( 'loadstart' , handleLoadStart ) ;
117+ audio . removeEventListener ( 'ended' , handleEnded ) ;
118+ audio . removeEventListener ( 'timeupdate' , handleTimeUpdate ) ;
119+ audio . removeEventListener ( 'waiting' , handleWaiting ) ;
120+ audio . removeEventListener ( 'canplay' , handleCanPlay ) ;
121+ audio . removeEventListener ( 'playing' , handlePlaying ) ;
122+ audio . removeEventListener ( 'pause' , handlePause ) ;
123+ audio . removeEventListener ( 'play' , handlePlay ) ;
124+ audio . removeEventListener ( 'loadstart' , handleLoadStart ) ;
125+ audio . removeEventListener ( 'error' , handleError ) ;
66126 } ;
67127 } , [ ] ) ;
68128
@@ -84,8 +144,16 @@ export function AudioProvider({ children, allTracks }: { children: React.ReactNo
84144 ]
85145 } ) ;
86146
87- navigator . mediaSession . setActionHandler ( 'play' , ( ) => togglePlay ( ) ) ;
88- navigator . mediaSession . setActionHandler ( 'pause' , ( ) => togglePlay ( ) ) ;
147+ navigator . mediaSession . setActionHandler ( 'play' , ( ) => {
148+ if ( audioRef . current && ! isPlaying ) {
149+ audioRef . current . play ( ) . catch ( console . error ) ;
150+ }
151+ } ) ;
152+ navigator . mediaSession . setActionHandler ( 'pause' , ( ) => {
153+ if ( audioRef . current && isPlaying ) {
154+ audioRef . current . pause ( ) ;
155+ }
156+ } ) ;
89157 navigator . mediaSession . setActionHandler ( 'previoustrack' , ( ) => playPrev ( ) ) ;
90158 navigator . mediaSession . setActionHandler ( 'nexttrack' , ( ) => playNext ( ) ) ;
91159
@@ -98,45 +166,16 @@ export function AudioProvider({ children, allTracks }: { children: React.ReactNo
98166 } , [ currentTrack , isPlaying ] ) ; // Keep in sync with track and state
99167
100168
101- const playTrack = ( track : Track ) => {
102- if ( audioRef . current ) {
103- setIsLoading ( true ) ; // Set loading immediately
104- audioRef . current . src = track . url ;
105- audioRef . current . play ( ) . catch ( e => {
106- console . error ( "Playback failed:" , e ) ;
107- setIsLoading ( false ) ;
108- } ) ;
109- setCurrentTrack ( track ) ;
110- setIsPlaying ( true ) ;
111- }
112- } ;
113-
114169 const togglePlay = ( ) => {
115170 if ( audioRef . current ) {
116171 if ( isPlaying ) {
117172 audioRef . current . pause ( ) ;
118- setIsPlaying ( false ) ;
119173 } else {
120174 audioRef . current . play ( ) . catch ( console . error ) ;
121- setIsPlaying ( true ) ;
122175 }
123176 }
124177 } ;
125178
126- const playNext = ( ) => {
127- if ( ! currentTrack ) return ;
128- const currentIndex = allTracks . findIndex ( t => t . id === currentTrack . id ) ;
129- const nextIndex = ( currentIndex + 1 ) % allTracks . length ;
130- playTrack ( allTracks [ nextIndex ] ) ;
131- } ;
132-
133- const playPrev = ( ) => {
134- if ( ! currentTrack ) return ;
135- const currentIndex = allTracks . findIndex ( t => t . id === currentTrack . id ) ;
136- const prevIndex = ( currentIndex - 1 + allTracks . length ) % allTracks . length ;
137- playTrack ( allTracks [ prevIndex ] ) ;
138- } ;
139-
140179 const closePlayer = ( ) => {
141180 if ( audioRef . current ) {
142181 audioRef . current . pause ( ) ;
0 commit comments