diff --git a/src/components/App.tsx b/src/components/App.tsx index 70d49b6..4568b0d 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -11,7 +11,8 @@ import { } from '@react-navigation/native'; import { useColorScheme } from 'react-native'; import { ColorSchemeContext, themes } from './Colors'; -import ErrorReportingAlert from 'utility/ErrorReportingAlert'; +// import ErrorReportingAlert from 'utility/ErrorReportingAlert'; +import PlayerStateUpdater from './PlayerStateUpdater'; export default function App(): JSX.Element { const colorScheme = useColorScheme(); @@ -41,6 +42,7 @@ export default function App(): JSX.Element { + diff --git a/src/components/PlayerStateUpdater.ts b/src/components/PlayerStateUpdater.ts new file mode 100644 index 0000000..4030a17 --- /dev/null +++ b/src/components/PlayerStateUpdater.ts @@ -0,0 +1,54 @@ +import { useCallback, useEffect } from 'react'; +import TrackPlayer, { TrackPlayerEvents } from 'react-native-track-player'; +import { shallowEqual, useDispatch } from 'react-redux'; +import { useTypedSelector } from 'store'; +import player from 'store/player'; + +function PlayerStateUpdater() { + const dispatch = useDispatch(); + const trackId = useTypedSelector(state => state.player.currentTrack?.id, shallowEqual); + + const handleUpdate = useCallback(async () => { + const currentTrackId = await TrackPlayer.getCurrentTrack(); + + // GUARD: Only retrieve new track if it is different from the one we + // have currently in state. + if (currentTrackId === trackId){ + return; + } + + // GUARD: Only fetch current track if there is a current track + if (!currentTrackId) { + dispatch(player.actions.setCurrentTrack(undefined)); + } + + // If it is different, retrieve the track and save it + try { + const currentTrack = await TrackPlayer.getTrack(currentTrackId); + dispatch(player.actions.setCurrentTrack(currentTrack)); + } catch { + // Due to the async nature, a track might be removed at the + // point when we try to retrieve it. If this happens, we'll just + // smother the error and wait for a new track update to + // finish. + } + }, [trackId, dispatch]); + + useEffect(() => { + function handler() { + handleUpdate(); + } + + handler(); + + const subscription = TrackPlayer.addEventListener(TrackPlayerEvents.PLAYBACK_TRACK_CHANGED, handler); + + return () => { + subscription.remove(); + }; + }, []); + + return null; +} + +export default PlayerStateUpdater; \ No newline at end of file diff --git a/src/screens/Music/stacks/Album.tsx b/src/screens/Music/stacks/Album.tsx index 348f6a0..4cdf0bb 100644 --- a/src/screens/Music/stacks/Album.tsx +++ b/src/screens/Music/stacks/Album.tsx @@ -80,7 +80,7 @@ const Album: React.FC = () => { const refresh = useCallback(() => { dispatch(fetchTracksByAlbum(id)); }, [id, dispatch]); const selectTrack = useCallback(async (trackId) => { const tracks = await playAlbum(id, false); - + if (tracks) { const track = tracks.find((t) => t.id.startsWith(trackId)); diff --git a/src/store/player/index.ts b/src/store/player/index.ts index 53a2d1e..79a5298 100644 --- a/src/store/player/index.ts +++ b/src/store/player/index.ts @@ -1,10 +1,26 @@ -import { createSlice } from '@reduxjs/toolkit'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { Track } from 'react-native-track-player'; + +interface State { + addedTrackCount: number, + currentTrack: Track | undefined, +} + +const initialState: State = { + addedTrackCount: 0, + currentTrack: undefined, +}; const player = createSlice({ name: 'player', - initialState: 0, + initialState, reducers: { - addNewTrackToPlayer: (state) => state + 1, + addNewTrackToPlayer: (state) => { + state.addedTrackCount += 1; + }, + setCurrentTrack: (state, action: PayloadAction) => { + state.currentTrack = action.payload; + }, } }); diff --git a/src/utility/useCurrentTrack.ts b/src/utility/useCurrentTrack.ts index df412b3..aea4cb4 100644 --- a/src/utility/useCurrentTrack.ts +++ b/src/utility/useCurrentTrack.ts @@ -1,42 +1,15 @@ -import { useEffect, useState } from 'react'; -import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player'; +import { Track } from 'react-native-track-player'; +import { useTypedSelector } from 'store'; + +const idEqual = (left: Track | undefined, right: Track | undefined) => { + return left?.id === right?.id; +}; /** * This hook retrieves the current playing track from TrackPlayer */ export default function useCurrentTrack(): Track | undefined { - const state = usePlaybackState(); - const [track, setTrack] = useState(); - - useEffect(() => { - const fetchTrack = async () => { - const currentTrackId = await TrackPlayer.getCurrentTrack(); - - // GUARD: Only fetch current track if there is a current track - if (!currentTrackId) { - setTrack(undefined); - } - - // GUARD: Only retrieve new track if it is different from the one we - // have currently in state. - if (currentTrackId === track?.id){ - return; - } - - // If it is different, retrieve the track and save it - try { - const currentTrack = await TrackPlayer.getTrack(currentTrackId); - setTrack(currentTrack); - } catch { - // Due to the async nature, a track might be removed at the - // point when we try to retrieve it. If this happens, we'll just - // smother the error and wait for a new track update to - // finish. - } - }; - - fetchTrack(); - }, [state, track, setTrack]); - + const track = useTypedSelector(state => state.player.currentTrack, idEqual); + return track; } \ No newline at end of file diff --git a/src/utility/useQueue.ts b/src/utility/useQueue.ts index 8c4440d..85fcac6 100644 --- a/src/utility/useQueue.ts +++ b/src/utility/useQueue.ts @@ -8,7 +8,7 @@ import { useTypedSelector } from 'store'; export default function useQueue(): Track[] { const state = usePlaybackState(); const [queue, setQueue] = useState([]); - const addedTrackCount = useTypedSelector(state => state.player); + const addedTrackCount = useTypedSelector(state => state.player.addedTrackCount); useEffect(() => { TrackPlayer.getQueue().then(setQueue);