Upgrade all dependencies

(1) react-native-track-player to v2
(2) react-navigation to v6
(3) react-native to v0.66.4
This commit is contained in:
Lei Nelissen
2021-12-31 15:04:37 +01:00
parent 6440c1ac7b
commit 9668ba9c7c
28 changed files with 21769 additions and 5659 deletions

View File

@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { Provider } from 'react-redux';
import TrackPlayer from 'react-native-track-player';
import TrackPlayer, { Capability } from 'react-native-track-player';
import { PersistGate } from 'redux-persist/integration/react';
import Routes from '../screens';
import store, { persistedStore } from 'store';
@@ -12,7 +12,6 @@ import {
import { useColorScheme } from 'react-native';
import { ColorSchemeContext, themes } from './Colors';
// import ErrorReportingAlert from 'utility/ErrorReportingAlert';
import PlayerStateUpdater from './PlayerStateUpdater';
export default function App(): JSX.Element {
const colorScheme = useColorScheme();
@@ -24,12 +23,12 @@ export default function App(): JSX.Element {
await TrackPlayer.setupPlayer();
await TrackPlayer.updateOptions({
capabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_SKIP_TO_NEXT,
TrackPlayer.CAPABILITY_SKIP_TO_PREVIOUS,
TrackPlayer.CAPABILITY_STOP,
TrackPlayer.CAPABILITY_SEEK_TO,
Capability.Play,
Capability.Pause,
Capability.SkipToNext,
Capability.SkipToPrevious,
Capability.Stop,
Capability.SeekTo,
]
});
}
@@ -42,7 +41,6 @@ export default function App(): JSX.Element {
<ColorSchemeContext.Provider value={theme}>
<NavigationContainer theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Routes />
<PlayerStateUpdater />
</NavigationContainer>
</ColorSchemeContext.Provider>
</PersistGate>

View File

@@ -1,54 +0,0 @@
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;

View File

@@ -1,10 +1,10 @@
import React, { useCallback } from 'react';
import React, { PropsWithChildren, useCallback } from 'react';
import { Pressable, ViewStyle } from 'react-native';
interface TouchableHandlerProps {
id: string;
onPress: (id: string) => void;
onLongPress?: (id: string) => void;
interface TouchableHandlerProps<T = number> {
id: T;
onPress: (id: T) => void;
onLongPress?: (id: T) => void;
}
function TouchableStyles({ pressed }: { pressed: boolean }): ViewStyle {
@@ -19,7 +19,12 @@ function TouchableStyles({ pressed }: { pressed: boolean }): ViewStyle {
* This is a generic handler that accepts id as a prop, and return it when it is
* pressed. This comes in handy with lists in which albums / tracks need to be selected.
*/
const TouchableHandler: React.FC<TouchableHandlerProps> = ({ id, onPress, onLongPress, children }) => {
function TouchableHandler<T>({
id,
onPress,
onLongPress,
children
}: PropsWithChildren<TouchableHandlerProps<T>>): JSX.Element {
const handlePress = useCallback(() => {
return onPress(id);
}, [id, onPress]);
@@ -37,6 +42,6 @@ const TouchableHandler: React.FC<TouchableHandlerProps> = ({ id, onPress, onLon
{children}
</Pressable>
);
};
}
export default TouchableHandler;

View File

@@ -72,27 +72,20 @@ const Album: React.FC = () => {
const dispatch = useDispatch();
const getImage = useGetImage();
const playAlbum = usePlayAlbum();
const currentTrack = useCurrentTrack();
const { track: currentTrack } = useCurrentTrack();
const navigation = useNavigation();
// Setup callbacks
const selectAlbum = useCallback(() => { playAlbum(id); }, [playAlbum, id]);
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));
if (track) {
await TrackPlayer.skip(track.id);
await TrackPlayer.play();
}
}
const selectTrack = useCallback(async (index: number) => {
await playAlbum(id, false);
await TrackPlayer.skip(index);
await TrackPlayer.play();
}, [playAlbum, id]);
const longPressTrack = useCallback((trackId: string) => {
navigation.navigate('TrackPopupMenu', { trackId });
}, [navigation]);
const longPressTrack = useCallback((index: number) => {
navigation.navigate('TrackPopupMenu', { trackId: album?.Tracks?.[index] });
}, [navigation, album]);
// Retrieve album tracks on load
useEffect(() => {
@@ -118,14 +111,14 @@ const Album: React.FC = () => {
<Text style={[ defaultStyles.text, styles.artist ]}>{album?.AlbumArtist}</Text>
<Button title={t('play-album')} icon={Play} onPress={selectAlbum} />
<View style={{ marginTop: 15 }}>
{album?.Tracks?.length ? album.Tracks.map((trackId) =>
{album?.Tracks?.length ? album.Tracks.map((trackId, i) =>
<TouchableHandler
key={trackId}
id={trackId}
id={i}
onPress={selectTrack}
onLongPress={longPressTrack}
>
<TrackContainer isPlaying={currentTrack?.id.startsWith(trackId) || false} style={defaultStyles.border}>
<TrackContainer isPlaying={currentTrack?.backendId === trackId || false} style={defaultStyles.border}>
<Text style={[ defaultStyles.text, styles.index ]}>
{tracks[trackId]?.IndexNumber}
</Text>

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useEffect } from 'react';
import { useGetImage } from 'utility/JellyfinApi';
import { Album, NavigationProp } from '../types';
import { NavigationProp } from '../types';
import { Text, SafeAreaView, FlatList, StyleSheet } from 'react-native';
import { useDispatch } from 'react-redux';
import { useNavigation } from '@react-navigation/native';
@@ -14,6 +14,7 @@ import { Header } from 'components/Typography';
import ListButton from 'components/ListButton';
import { t } from '@localisation';
import useDefaultStyles from 'components/Colors';
import { Album } from 'store/music/types';
const styles = StyleSheet.create({
columnWrapper: {

View File

@@ -226,7 +226,7 @@ export default function Search() {
}
return (
<TouchableHandler id={album.Id} onPress={selectAlbum}>
<TouchableHandler<string> id={album.Id} onPress={selectAlbum}>
<SearchResult style={defaultStyles.border}>
<AlbumImage source={{ uri: getImage(album.Id) }} />
<View>

View File

@@ -2,6 +2,7 @@ import { StackNavigationProp } from '@react-navigation/stack';
import { Album } from 'store/music/types';
export type StackParams = {
[key: string]: Record<string, unknown> | undefined;
Albums: undefined;
Album: { id: string, album: Album };
RecentAlbums: undefined;

View File

@@ -1,5 +1,5 @@
import React, { useState, useCallback, useEffect, useRef } from 'react';
import TrackPlayer, { usePlaybackState, STATE_PLAYING, STATE_PAUSED } from 'react-native-track-player';
import React, { useState, useCallback } from 'react';
import TrackPlayer, { Event, State, usePlaybackState, useTrackPlayerEvents } from 'react-native-track-player';
import { TouchableOpacity, useColorScheme } from 'react-native';
import styled from 'styled-components/native';
import { useHasQueue } from 'utility/useQueue';
@@ -84,16 +84,12 @@ export function NextButton({ fill }: { fill: string }) {
export function RepeatButton({ fill }: { fill: string}) {
const [isRepeating, setRepeating] = useState(false);
const handlePress = useCallback(() => setRepeating(!isRepeating), [isRepeating, setRepeating]);
const listener = useRef<TrackPlayer.EmitterSubscription | null>(null);
// The callback that should determine whether we need to repeeat or not
const handleEndEvent = useCallback(async () => {
useTrackPlayerEvents([Event.PlaybackQueueEnded], async () => {
if (isRepeating) {
// Retrieve all current tracks
const tracks = await TrackPlayer.getQueue();
// Then skip to the first track
await TrackPlayer.skip(tracks[0].id);
// Skip to the first track
await TrackPlayer.skip(0);
// Cautiously reset the seek time, as there might only be a single
// item in queue.
@@ -102,19 +98,7 @@ export function RepeatButton({ fill }: { fill: string}) {
// Then play the item
await TrackPlayer.play();
}
}, [isRepeating]);
// Subscribe to ended event handler so that we can restart the queue from
// the start if looping is enabled
useEffect(() => {
// Set the event listener
listener.current = TrackPlayer.addEventListener('playback-queue-ended', handleEndEvent);
// Then clean up after
return function cleanup() {
listener?.current?.remove();
};
}, [handleEndEvent]);
});
return (
<TouchableOpacity onPress={handlePress} style={{ opacity: isRepeating ? 1 : 0.5 }}>
@@ -146,13 +130,13 @@ export function MainButton({ fill }: { fill: string }) {
const state = usePlaybackState();
switch (state) {
case STATE_PLAYING:
case State.Playing:
return (
<TouchableOpacity onPress={pause}>
<PauseIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill={fill} />
</TouchableOpacity>
);
case STATE_PAUSED:
case State.Paused:
return (
<TouchableOpacity onPress={play}>
<PlayIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill={fill} />

View File

@@ -32,7 +32,7 @@ const styles = StyleSheet.create({
export default function NowPlaying() {
const track = useCurrentTrack();
const { track } = useCurrentTrack();
const defaultStyles = useDefaultStyles();
return (
@@ -40,7 +40,7 @@ export default function NowPlaying() {
<Artwork
style={defaultStyles.imageBackground}
source={{
uri: track?.artwork,
uri: track?.artwork as string | undefined,
priority: FastImage.priority.high,
}}
/>

View File

@@ -38,10 +38,9 @@ const styles = StyleSheet.create({
export default function Queue() {
const defaultStyles = useDefaultStyles();
const queue = useQueue();
const currentTrack = useCurrentTrack();
const currentIndex = queue.findIndex(d => d.id === currentTrack?.id);
const playTrack = useCallback(async (trackId: string) => {
await TrackPlayer.skip(trackId);
const { index: currentIndex } = useCurrentTrack();
const playTrack = useCallback(async (index: number) => {
await TrackPlayer.skip(index);
await TrackPlayer.play();
}, []);
const clearQueue = useCallback(async () => {
@@ -52,14 +51,14 @@ export default function Queue() {
<View>
<Text style={{ marginTop: 20, marginBottom: 20 }}>{t('queue')}</Text>
{queue.map((track, i) => (
<TouchableHandler id={track.id} onPress={playTrack} key={i}>
<TouchableHandler id={i} onPress={playTrack} key={i}>
<QueueItem
active={currentTrack?.id === track.id}
active={currentIndex === i}
key={i}
alreadyPlayed={i < currentIndex}
alreadyPlayed={currentIndex ? i < currentIndex : false}
style={[
defaultStyles.border,
currentTrack?.id === track.id ? defaultStyles.activeBackground : {},
currentIndex === i ? defaultStyles.activeBackground : {},
]}
>
<Text style={styles.trackTitle}>{track.title}</Text>

View File

@@ -62,12 +62,11 @@ function Screens() {
}
return <Icon fill={color} width={size} height={size} />;
}
},
tabBarActiveTintColor: THEME_COLOR,
tabBarInactiveTintColor: 'gray',
headerShown: false,
})}
tabBarOptions={{
activeTintColor: THEME_COLOR,
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="NowPlaying" component={Player} options={{ tabBarLabel: t('now-playing') }} />
<Tab.Screen name="Music" component={Music} options={{ tabBarLabel: t('music') }} />
@@ -85,10 +84,12 @@ type Routes = {
export default function Routes() {
return (
<Stack.Navigator mode="modal" headerMode="none" screenOptions={{
<Stack.Navigator screenOptions={{
cardStyle: {
backgroundColor: 'transparent'
}
},
presentation: 'modal',
headerShown: false,
}}>
<Stack.Screen name="Screens" component={Screens} />
<Stack.Screen name="SetJellyfinServer" component={SetJellyfinServer} />

View File

@@ -1,4 +1,5 @@
export interface ModalStackParams {
[key: string]: Record<string, unknown> | undefined;
SetJellyfinServer: undefined;
TrackPopupMenu: { trackId: string };
}

View File

@@ -13,11 +13,9 @@ const persistConfig: PersistConfig<AppState> = {
import settings from './settings';
import music from './music';
import player from './player';
const reducers = combineReducers({
settings,
player: player.reducer,
music: music.reducer,
});

View File

@@ -1,27 +0,0 @@
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,
reducers: {
addNewTrackToPlayer: (state) => {
state.addedTrackCount += 1;
},
setCurrentTrack: (state, action: PayloadAction<Track | undefined>) => {
state.currentTrack = action.payload;
},
}
});
export default player;

View File

@@ -0,0 +1,43 @@
import TrackPlayer from 'react-native-track-player';
import { useEffect } from 'react';
import EventEmitter from 'events';
const eventName = 'track-added';
const addedTrackEmitter = new EventEmitter();
/**
* Emit the event that a track has been added
*/
export function emitTrackAdded() {
addedTrackEmitter.emit(eventName);
}
/**
* Call the callback whenever a track has been added to the queue
*/
export function onTrackAdded(callback: () => void) {
addedTrackEmitter.addListener(eventName, callback);
}
/**
* A hook to manage the listeners for the added track function
*/
export function useOnTrackAdded(callback: () => void) {
useEffect(() => {
addedTrackEmitter.addListener(eventName, callback);
return () => {
addedTrackEmitter.removeListener(eventName, callback);
};
});
}
/**
* Monkey-patch the track-player to also emit track added events
*/
export function patchTrackPlayer() {
const oldAddFunction = TrackPlayer.add;
TrackPlayer.add = (...args: Parameters<typeof oldAddFunction>) => {
emitTrackAdded();
return oldAddFunction(...args);
};
}

View File

@@ -44,8 +44,8 @@ export function generateTrack(track: AlbumTrack, credentials: Credentials): Trac
const url = encodeURI(`${credentials?.uri}/Audio/${track.Id}/universal?${trackParams}`);
return {
id: track.Id,
url,
backendId: track.Id,
title: track.Name,
artist: track.Artists.join(', '),
album: track.Album,

View File

@@ -7,30 +7,30 @@
* such as processing media buttons or analytics
*/
import TrackPlayer from 'react-native-track-player';
import TrackPlayer, { Event } from 'react-native-track-player';
export default async function() {
TrackPlayer.addEventListener('remote-play', () => {
TrackPlayer.addEventListener(Event.RemotePlay, () => {
TrackPlayer.play();
});
TrackPlayer.addEventListener('remote-pause', () => {
TrackPlayer.addEventListener(Event.RemotePause, () => {
TrackPlayer.pause();
});
TrackPlayer.addEventListener('remote-next', () => {
TrackPlayer.addEventListener(Event.RemoteNext, () => {
TrackPlayer.skipToNext();
});
TrackPlayer.addEventListener('remote-previous', () => {
TrackPlayer.addEventListener(Event.RemotePrevious, () => {
TrackPlayer.skipToPrevious();
});
TrackPlayer.addEventListener('remote-stop', () => {
TrackPlayer.addEventListener(Event.RemoteStop, () => {
TrackPlayer.destroy();
});
TrackPlayer.addEventListener('remote-seek', (event) => {
TrackPlayer.addEventListener(Event.RemoteSeek, (event) => {
TrackPlayer.seekTo(event.position);
});

View File

@@ -1,15 +1,29 @@
import { Track } from 'react-native-track-player';
import { useTypedSelector } from 'store';
import { useCallback, useEffect, useState } from 'react';
import TrackPlayer, { Event, Track, useTrackPlayerEvents } from 'react-native-track-player';
const idEqual = (left: Track | undefined, right: Track | undefined) => {
return left?.id === right?.id;
};
interface CurrentTrackResponse {
track: Track | undefined;
index: number | undefined;
}
/**
* This hook retrieves the current playing track from TrackPlayer
*/
export default function useCurrentTrack(): Track | undefined {
const track = useTypedSelector(state => state.player.currentTrack, idEqual);
export default function useCurrentTrack(): CurrentTrackResponse {
const [track, setTrack] = useState<Track | undefined>();
const [index, setIndex] = useState<number | undefined>();
// Retrieve the current track from the queue using the index
const retrieveCurrentTrack = useCallback(async () => {
const queue = await TrackPlayer.getQueue();
const currentTrackIndex = await TrackPlayer.getCurrentTrack();
setTrack(queue[currentTrackIndex]);
setIndex(currentTrackIndex);
}, [setTrack, setIndex]);
// Then execute the function on component mount and track changes
useEffect(() => { retrieveCurrentTrack(); }, [retrieveCurrentTrack]);
useTrackPlayerEvents([ Event.PlaybackTrackChanged ], retrieveCurrentTrack);
return track;
return { track, index };
}

View File

@@ -2,55 +2,27 @@ import { useTypedSelector } from 'store';
import { useCallback } from 'react';
import TrackPlayer, { Track } from 'react-native-track-player';
import { generateTrack } from './JellyfinApi';
import useQueue from './useQueue';
import player from 'store/player';
import { useDispatch } from 'react-redux';
/**
* Generate a callback function that starts playing a full album given its
* supplied id.
*/
export default function usePlayAlbum() {
const dispatch = useDispatch();
const credentials = useTypedSelector(state => state.settings.jellyfin);
const albums = useTypedSelector(state => state.music.albums.entities);
const tracks = useTypedSelector(state => state.music.tracks.entities);
const queue = useQueue();
return useCallback(async function playAlbum(albumId: string, play = true): Promise<TrackPlayer.Track[] | undefined> {
return useCallback(async function playAlbum(albumId: string, play: boolean = true): Promise<Track[] | undefined> {
const album = albums[albumId];
const trackIds = album?.Tracks;
const backendTrackIds = album?.Tracks;
// GUARD: Check that the album actually has tracks
if (!album || !trackIds?.length) {
// GUARD: Check if the album has songs
if (!backendTrackIds?.length) {
return;
}
// Check if the queue already contains the consecutive track listing
// that is described as part of the album
const queuedAlbum = queue.reduce<TrackPlayer.Track[]>((sum, track) => {
if (track.id.startsWith(trackIds[sum.length])) {
sum.push(track);
} else {
sum = [];
}
return sum;
}, []);
// If the entire album is already in the queue, we can just return those
// tracks, rather than adding it to the queue again.
if (queuedAlbum.length === trackIds.length) {
if (play) {
await TrackPlayer.skip(trackIds[0]);
await TrackPlayer.play();
}
return queuedAlbum;
}
// Convert all trackIds to the relevant format for react-native-track-player
const newTracks = trackIds.map((trackId) => {
// Convert all backendTrackIds to the relevant format for react-native-track-player
const newTracks = backendTrackIds.map((trackId) => {
const track = tracks[trackId];
if (!trackId || !track) {
return;
@@ -60,17 +32,14 @@ export default function usePlayAlbum() {
}).filter((t): t is Track => typeof t !== 'undefined');
// Clear the queue and add all tracks
await TrackPlayer.removeUpcomingTracks();
await TrackPlayer.reset();
await TrackPlayer.add(newTracks);
// Then, we'll dispatch the added track event
dispatch(player.actions.addNewTrackToPlayer());
// Play the queue
if (play) {
await TrackPlayer.skip(trackIds[0]);
await TrackPlayer.play();
}
return newTracks;
}, [credentials, albums, tracks, queue, dispatch]);
}, [credentials, albums, tracks]);
}

View File

@@ -3,20 +3,17 @@ import TrackPlayer from 'react-native-track-player';
import { useTypedSelector } from 'store';
import { generateTrack } from './JellyfinApi';
import useQueue from './useQueue';
import { useDispatch } from 'react-redux';
import player from 'store/player';
/**
* A hook that generates a callback that can setup and start playing a
* particular trackId in the player.
*/
export default function usePlayTrack() {
const dispatch = useDispatch();
const credentials = useTypedSelector(state => state.settings.jellyfin);
const tracks = useTypedSelector(state => state.music.tracks.entities);
const queue = useQueue();
return useCallback(async function playTrack(trackId: string, play = true, addToEnd = true) {
return useCallback(async function playTrack(trackId: string, play: boolean = true, addToEnd: boolean = true) {
// Get the relevant track
const track = tracks[trackId];
@@ -25,22 +22,21 @@ export default function usePlayTrack() {
return;
}
// GUARD: Check if the track is already in the queue
const trackInstances = queue.filter((t) => t.id.startsWith(trackId));
// Generate the new track for the queue
const newTrack = {
...(trackInstances.length ? trackInstances[0] : generateTrack(track, credentials)),
id: `${trackId}_${trackInstances.length}`
};
const newTrack = generateTrack(track, credentials);
// Then, we'll need to check where to add the track
if (addToEnd) {
await TrackPlayer.add([ newTrack ]);
// Then we'll skip to it and play it
if (play) {
await TrackPlayer.skip(await (await TrackPlayer.getQueue()).length);
await TrackPlayer.play();
}
} else {
// Try and locate the current track
const currentTrackId = await TrackPlayer.getCurrentTrack();
const currentTrackIndex = queue.findIndex(track => track.id === currentTrackId);
const currentTrackIndex = await TrackPlayer.getCurrentTrack();
// Since the argument is the id to insert the track BEFORE, we need
// to get the current track + 1
@@ -51,17 +47,13 @@ export default function usePlayTrack() {
// Depending on whether this track exists, we either add it there,
// or at the end of the queue.
await TrackPlayer.add([ newTrack ], targetTrack);
}
// Then, we'll dispatch the added track event
dispatch(player.actions.addNewTrackToPlayer());
// Then we'll skip to it and play it
if (play) {
await TrackPlayer.skip(newTrack.id);
await TrackPlayer.play();
if (play) {
await TrackPlayer.skip(currentTrackIndex + 1);
await TrackPlayer.play();
}
}
return newTrack;
}, [credentials, tracks, queue, dispatch]);
}, [credentials, tracks, queue]);
}

View File

@@ -1,18 +1,22 @@
import { useEffect, useState } from 'react';
import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player';
import { useTypedSelector } from 'store';
import { useCallback, useEffect, useState } from 'react';
import TrackPlayer, { Event, Track, useTrackPlayerEvents } from 'react-native-track-player';
import { useOnTrackAdded } from './AddedTrackEvents';
/**
* This hook retrieves the current playing track from TrackPlayer
*/
export default function useQueue(): Track[] {
const state = usePlaybackState();
const [queue, setQueue] = useState<Track[]>([]);
const addedTrackCount = useTypedSelector(state => state.player.addedTrackCount);
useEffect(() => {
TrackPlayer.getQueue().then(setQueue);
}, [state, addedTrackCount]);
// Define function that fetches the current queue
const updateQueue = useCallback(() => TrackPlayer.getQueue().then(setQueue), [setQueue]);
// Then define the triggers for updating it
useEffect(() => { updateQueue(); }, [updateQueue]);
useTrackPlayerEvents([
Event.PlaybackState,
], updateQueue);
useOnTrackAdded(updateQueue);
return queue;
}