(1) Play whole album when selecting a single track

(2) Create popup window on track long-press in which the track can be added to the end or front of the queue
(3) Add Redux counter for added tracks so that the queue is properly updated
This commit is contained in:
Lei Nelissen
2020-08-28 16:28:49 +02:00
parent 3026fdf4da
commit c88158cf16
11 changed files with 226 additions and 29 deletions

View File

@@ -2,17 +2,22 @@ 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) {
return useCallback(async function playAlbum(albumId: string, play = true): Promise<TrackPlayer.Track[] | undefined> {
const album = albums[albumId];
const trackIds = album?.Tracks;
@@ -21,6 +26,29 @@ export default function usePlayAlbum() {
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) => {
const track = tracks[trackId];
@@ -34,7 +62,15 @@ export default function usePlayAlbum() {
// Clear the queue and add all tracks
await TrackPlayer.removeUpcomingTracks();
await TrackPlayer.add(newTracks);
await TrackPlayer.skip(trackIds[0]);
TrackPlayer.play();
}, [credentials, albums, tracks]);
// Then, we'll dispatch the added track event
dispatch(player.actions.addNewTrackToPlayer());
if (play) {
await TrackPlayer.skip(trackIds[0]);
await TrackPlayer.play();
}
return newTracks;
}, [credentials, albums, tracks, queue, dispatch]);
}

View File

@@ -3,17 +3,20 @@ 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) {
return useCallback(async function playTrack(trackId: string, play = true, addToEnd = true) {
// Get the relevant track
const track = tracks[trackId];
@@ -30,10 +33,35 @@ export default function usePlayTrack() {
...(trackInstances.length ? trackInstances[0] : generateTrack(track, credentials)),
id: `${trackId}_${trackInstances.length}`
};
await TrackPlayer.add([ newTrack ]);
// Then, we'll need to check where to add the track
if (addToEnd) {
await TrackPlayer.add([ newTrack ]);
} else {
// Try and locate the current track
const currentTrackId = await TrackPlayer.getCurrentTrack();
const currentTrackIndex = queue.findIndex(track => track.id === currentTrackId);
// Since the argument is the id to insert the track BEFORE, we need
// to get the current track + 1
const targetTrack = currentTrackIndex >= 0 && queue.length > 1
? queue[currentTrackIndex + 1].id
: undefined;
// 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
await TrackPlayer.skip(newTrack.id);
TrackPlayer.play();
}, [credentials, tracks, queue]);
if (play) {
await TrackPlayer.skip(newTrack.id);
await TrackPlayer.play();
}
return newTrack;
}, [credentials, tracks, queue, dispatch]);
}

View File

@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player';
import { useTypedSelector } from 'store';
/**
* This hook retrieves the current playing track from TrackPlayer
@@ -7,10 +8,11 @@ import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player'
export default function useQueue(): Track[] {
const state = usePlaybackState();
const [queue, setQueue] = useState<Track[]>([]);
const addedTrackCount = useTypedSelector(state => state.player);
useEffect(() => {
TrackPlayer.getQueue().then(setQueue);
}, [state]);
}, [state, addedTrackCount]);
return queue;
}