diff --git a/src/components/player/NowPlayingView.tsx b/src/components/player/NowPlayingView.tsx index 6e4a885..db62ecc 100644 --- a/src/components/player/NowPlayingView.tsx +++ b/src/components/player/NowPlayingView.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from 'react'; import settings from 'electron-settings'; -import { Button, Checkbox } from 'rsuite'; +import { Checkbox } from 'rsuite'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import useSearchQuery from '../../hooks/useSearchQuery'; import { @@ -10,7 +10,9 @@ import { setPlayerVolume, fixPlayer2Index, clearPlayQueue, + shuffleInPlace, shufflePlayQueue, + toggleShuffle, } from '../../redux/playQueueSlice'; import { toggleSelected, @@ -130,7 +132,12 @@ const NowPlayingView = () => { { - dispatch(shufflePlayQueue()); + if (!playQueue.shuffle) { + dispatch(shufflePlayQueue()); + dispatch(toggleShuffle()); + } else { + dispatch(shuffleInPlace()); + } }} > Shuffle @@ -160,7 +167,13 @@ const NowPlayingView = () => { > { const cacheSongs = settings.getSync('cacheSongs'); const [title, setTitle] = useState(''); const [cachePath] = useState(path.join(getSongCachePath(), '/')); + const [currentEntryList, setCurrentEntryList] = useState('entry'); + + useEffect(() => { + if (playQueue.shuffle) { + setCurrentEntryList('shuffledEntry'); + } else { + setCurrentEntryList('entry'); + } + }, [playQueue.shuffle]); useImperativeHandle(ref, () => ({ get player1() { @@ -80,7 +89,7 @@ const Player = ({ children }: any, ref: any) => { // Don't fade if player2Index <= player1Index unless repeat==='all' if ( - playQueue.player1.index + 1 < playQueue.entry.length || + playQueue.player1.index + 1 < playQueue[currentEntryList].length || playQueue.repeat === 'all' ) { if (currentSeek >= fadeAtTime) { @@ -139,7 +148,7 @@ const Player = ({ children }: any, ref: any) => { const fadeAtTime = duration - fadeDuration; if ( - playQueue.player2.index + 1 < playQueue.entry.length || + playQueue.player2.index + 1 < playQueue[currentEntryList].length || playQueue.repeat === 'all' ) { if (currentSeek >= fadeAtTime) { @@ -182,8 +191,8 @@ const Player = ({ children }: any, ref: any) => { const handleOnEnded1 = () => { if (cacheSongs) { cacheSong( - `${playQueue.entry[playQueue.player1.index].id}.mp3`, - playQueue.entry[playQueue.player1.index].streamUrl.replace( + `${playQueue[currentEntryList][playQueue.player1.index].id}.mp3`, + playQueue[currentEntryList][playQueue.player1.index].streamUrl.replace( /stream/, 'download' ) @@ -201,10 +210,15 @@ const Player = ({ children }: any, ref: any) => { } else { if (!playQueue.autoIncremented) { dispatch(incrementCurrentIndex('none')); - dispatch(setCurrentIndex(playQueue.entry[playQueue.player2.index])); + dispatch( + setCurrentIndex(playQueue[currentEntryList][playQueue.player2.index]) + ); dispatch(setAutoIncremented(true)); } - if (playQueue.entry.length > 1 || playQueue.repeat === 'all') { + if ( + playQueue[currentEntryList].length > 1 || + playQueue.repeat === 'all' + ) { dispatch(setCurrentPlayer(2)); dispatch(incrementPlayerIndex(1)); dispatch(setPlayerVolume({ player: 1, volume: 0 })); @@ -218,8 +232,8 @@ const Player = ({ children }: any, ref: any) => { const handleOnEnded2 = () => { if (cacheSongs) { cacheSong( - `${playQueue.entry[playQueue.player2.index].id}.mp3`, - playQueue.entry[playQueue.player2.index].streamUrl.replace( + `${playQueue[currentEntryList][playQueue.player2.index].id}.mp3`, + playQueue[currentEntryList][playQueue.player2.index].streamUrl.replace( /stream/, 'download' ) @@ -237,10 +251,15 @@ const Player = ({ children }: any, ref: any) => { } else { if (!playQueue.autoIncremented) { dispatch(incrementCurrentIndex('none')); - dispatch(setCurrentIndex(playQueue.entry[playQueue.player1.index])); + dispatch( + setCurrentIndex(playQueue[currentEntryList][playQueue.player1.index]) + ); dispatch(setAutoIncremented(true)); } - if (playQueue.entry.length > 1 || playQueue.repeat === 'all') { + if ( + playQueue[currentEntryList].length > 1 || + playQueue.repeat === 'all' + ) { dispatch(setCurrentPlayer(1)); dispatch(incrementPlayerIndex(2)); dispatch(setPlayerVolume({ player: 1, volume: playQueue.volume })); @@ -260,17 +279,19 @@ const Player = ({ children }: any, ref: any) => { useEffect(() => { const playStatus = - player.status !== 'PLAYING' && playQueue.entry.length > 0 + player.status !== 'PLAYING' && playQueue[currentEntryList].length > 0 ? '(Paused)' : ''; - const songTitle = playQueue.entry[playQueue.currentIndex]?.title - ? `(${playQueue.currentIndex + 1} / ${playQueue.entry.length}) ~ ${ - playQueue.entry[playQueue.currentIndex]?.title - } ~ ${playQueue.entry[playQueue.currentIndex]?.artist} ` + const songTitle = playQueue[currentEntryList][playQueue.currentIndex]?.title + ? `(${playQueue.currentIndex + 1} / ${ + playQueue[currentEntryList].length + }) ~ ${playQueue[currentEntryList][playQueue.currentIndex]?.title} ~ ${ + playQueue[currentEntryList][playQueue.currentIndex]?.artist + } ` : 'sonixd'; setTitle(`${playStatus} ${songTitle}`); - }, [playQueue.currentIndex, playQueue.entry, player.status]); + }, [currentEntryList, playQueue, playQueue.currentIndex, player.status]); return ( <> @@ -282,10 +303,14 @@ const Player = ({ children }: any, ref: any) => { ref={player1Ref} src={ isCached( - `${cachePath}/${playQueue.entry[playQueue.player1.index]?.id}.mp3` + `${cachePath}/${ + playQueue[currentEntryList][playQueue.player1.index]?.id + }.mp3` ) - ? `${cachePath}/${playQueue.entry[playQueue.player1.index]?.id}.mp3` - : playQueue.entry[playQueue.player1.index]?.streamUrl + ? `${cachePath}/${ + playQueue[currentEntryList][playQueue.player1.index]?.id + }.mp3` + : playQueue[currentEntryList][playQueue.player1.index]?.streamUrl } listenInterval={150} preload="auto" @@ -302,10 +327,14 @@ const Player = ({ children }: any, ref: any) => { ref={player2Ref} src={ isCached( - `${cachePath}/${playQueue.entry[playQueue.player2.index]?.id}.mp3` + `${cachePath}/${ + playQueue[currentEntryList][playQueue.player2.index]?.id + }.mp3` ) - ? `${cachePath}/${playQueue.entry[playQueue.player2.index]?.id}.mp3` - : playQueue.entry[playQueue.player2.index]?.streamUrl + ? `${cachePath}/${ + playQueue[currentEntryList][playQueue.player2.index]?.id + }.mp3` + : playQueue[currentEntryList][playQueue.player2.index]?.streamUrl } listenInterval={150} preload="auto" diff --git a/src/components/player/PlayerBar.tsx b/src/components/player/PlayerBar.tsx index aba7b7c..cd683eb 100644 --- a/src/components/player/PlayerBar.tsx +++ b/src/components/player/PlayerBar.tsx @@ -22,6 +22,7 @@ import { toggleDisplayQueue, setStar, shufflePlayQueue, + toggleShuffle, } from '../../redux/playQueueSlice'; import { setStatus, resetPlayer } from '../../redux/playerSlice'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; @@ -43,6 +44,7 @@ const PlayerBar = () => { const [seek, setSeek] = useState(0); const [isDragging, setIsDragging] = useState(false); const [manualSeek, setManualSeek] = useState(0); + const [currentEntryList, setCurrentEntryList] = useState('entry'); const [seekForwardInterval] = useState( Number(settings.getSync('seekForwardInterval')) || 5 ); @@ -52,6 +54,14 @@ const PlayerBar = () => { const playersRef = useRef(); const history = useHistory(); + useEffect(() => { + if (playQueue.shuffle) { + setCurrentEntryList('shuffledEntry'); + } else { + setCurrentEntryList('entry'); + } + }, [playQueue.shuffle]); + useEffect(() => { setSeek(player.currentSeek); }, [player.currentSeek]); @@ -214,6 +224,7 @@ const PlayerBar = () => { const handleShuffle = () => { dispatch(shufflePlayQueue()); + dispatch(toggleShuffle()); }; const handleDisplayQueue = () => { @@ -221,11 +232,11 @@ const PlayerBar = () => { }; const handleFavorite = async () => { - if (!playQueue.entry[playQueue.currentIndex].starred) { - star(playQueue.entry[playQueue.currentIndex].id, 'song'); + if (!playQueue[currentEntryList][playQueue.currentIndex].starred) { + star(playQueue[currentEntryList][playQueue.currentIndex].id, 'song'); dispatch( setStar({ - id: playQueue.entry[playQueue.currentIndex].id, + id: playQueue[currentEntryList][playQueue.currentIndex].id, type: 'star', }) ); @@ -234,10 +245,10 @@ const PlayerBar = () => { exact: true, }); } else { - unstar(playQueue.entry[playQueue.currentIndex].id, 'song'); + unstar(playQueue[currentEntryList][playQueue.currentIndex].id, 'song'); dispatch( setStar({ - id: playQueue.entry[playQueue.currentIndex].id, + id: playQueue[currentEntryList][playQueue.currentIndex].id, type: 'unstar', }) ); @@ -266,10 +277,13 @@ const PlayerBar = () => { }} > - {playQueue.entry.length >= 1 && ( + {playQueue[currentEntryList].length >= 1 && ( { alignItems: 'flex-end', }} > - {playQueue.entry.length >= 1 && ( + {playQueue[currentEntryList].length >= 1 && ( @@ -306,14 +320,15 @@ const PlayerBar = () => { onClick={() => history.push( `/library/album/${ - playQueue.entry[playQueue.currentIndex] - ?.albumId + playQueue[currentEntryList][ + playQueue.currentIndex + ]?.albumId }` ) } > - {playQueue.entry[playQueue.currentIndex]?.title || - 'Unknown title'} + {playQueue[currentEntryList][playQueue.currentIndex] + ?.title || 'Unknown title'} )} @@ -326,11 +341,11 @@ const PlayerBar = () => { width: '50%', }} > - {playQueue.entry.length >= 1 && ( + {playQueue[currentEntryList].length >= 1 && ( @@ -347,14 +362,16 @@ const PlayerBar = () => { onClick={() => { history.push( `/library/artist/${ - playQueue.entry[playQueue.currentIndex] - ?.artistId + playQueue[currentEntryList][ + playQueue.currentIndex + ]?.artistId }` ); }} > - {playQueue.entry[playQueue.currentIndex] - ?.artist || 'Unknown artist'} + {playQueue[currentEntryList][ + playQueue.currentIndex + ]?.artist || 'Unknown artist'} @@ -479,7 +496,10 @@ const PlayerBar = () => { defaultValue={0} value={isDragging ? manualSeek : seek} tooltip={false} - max={playQueue.entry[playQueue.currentIndex]?.duration || 0} + max={ + playQueue[currentEntryList][playQueue.currentIndex] + ?.duration || 0 + } onChange={handleSeekSlider} style={{ width: '100%' }} /> @@ -493,8 +513,8 @@ const PlayerBar = () => { }} > {format( - playQueue.entry[playQueue.currentIndex]?.duration * 1000 || - 0 + playQueue[currentEntryList][playQueue.currentIndex] + ?.duration * 1000 || 0 )} @@ -514,21 +534,23 @@ const PlayerBar = () => { justifyContent: 'flex-end', }} > - {playQueue.entry.length >= 1 && ( + {playQueue[currentEntryList].length >= 1 && ( <> {/* Favorite Button */} { handleShuffle(); } }} + active={playQueue.shuffle ? 'true' : 'false'} /> {/* Display Queue Button */} diff --git a/src/components/settings/Config.tsx b/src/components/settings/Config.tsx index 114ce4f..aeaae0d 100644 --- a/src/components/settings/Config.tsx +++ b/src/components/settings/Config.tsx @@ -15,6 +15,8 @@ import { Message, Whisper, Popover, + RadioGroup, + Radio, } from 'rsuite'; import { ConfigPanel } from './styled'; import { startScan, getScanStatus } from '../../api/api'; @@ -186,6 +188,49 @@ const Config = () => { }} style={{ width: '150px' }} /> +
+ settings.setSync('defaultRepeat', e)} + > + + Repeat (default):{' '} + + All + One + None + + settings.setSync('defaultShuffle', e === 'true')} + > + + Shuffle (default):{' '} + + Enabled + Disabled + + +
diff --git a/src/components/shared/setDefaultSettings.ts b/src/components/shared/setDefaultSettings.ts index 33e6b52..30f5f1a 100644 --- a/src/components/shared/setDefaultSettings.ts +++ b/src/components/shared/setDefaultSettings.ts @@ -11,6 +11,14 @@ const setDefaultSettings = (force: boolean) => { fs.mkdirSync(getSongCachePath(), { recursive: true }); fs.mkdirSync(getImageCachePath(), { recursive: true }); + if (force || !settings.hasSync('defaultRepeat')) { + settings.setSync('defaultRepeat', 'all'); + } + + if (force || !settings.hasSync('defaultShuffle')) { + settings.setSync('defaultShuffle', false); + } + if (force || !settings.hasSync('scrollWithCurrentSong')) { settings.setSync('scrollWithCurrentSong', true); } @@ -58,7 +66,7 @@ const setDefaultSettings = (force: boolean) => { dataKey: 'combinedtitle', alignment: 'left', resizable: true, - width: 350, + width: 450, label: 'Title (Combined)', }, { @@ -66,7 +74,7 @@ const setDefaultSettings = (force: boolean) => { dataKey: 'album', alignment: 'left', resizable: true, - width: 350, + width: 450, label: 'Album', }, { @@ -111,7 +119,7 @@ const setDefaultSettings = (force: boolean) => { dataKey: 'combinedtitle', alignment: 'left', resizable: true, - width: 350, + width: 450, label: 'Title (Combined)', }, { diff --git a/src/redux/playQueueSlice.ts b/src/redux/playQueueSlice.ts index b3ec711..9914ebc 100644 --- a/src/redux/playQueueSlice.ts +++ b/src/redux/playQueueSlice.ts @@ -1,4 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import settings from 'electron-settings'; import _ from 'lodash'; import arrayMove from 'array-move'; import { areConsecutive, consecutiveRanges } from '../shared/utils'; @@ -50,6 +51,7 @@ export interface PlayQueue { shuffle: boolean; displayQueue: boolean; entry: Entry[]; + shuffledEntry: Entry[]; } const initialState: PlayQueue = { @@ -68,10 +70,11 @@ const initialState: PlayQueue = { autoIncremented: false, volume: 0.5, isLoading: false, - repeat: 'none', - shuffle: false, + repeat: String(settings.getSync('defaultRepeat') || 'all'), + shuffle: Boolean(settings.getSync('defaultShuffle') || false), displayQueue: false, entry: [], + shuffledEntry: [], }; const resetPlayerDefaults = (state: PlayQueue) => { @@ -93,31 +96,105 @@ const removeItem = (array: any, index: any) => { return [...array.slice(0, index), ...array.slice(index + 1)]; }; +const entrySelect = (state: PlayQueue) => + state.shuffle ? 'shuffledEntry' : 'entry'; + const playQueueSlice = createSlice({ name: 'nowPlaying', initialState, reducers: { + // ! Reducers need major refactoring and cleanup due to rapid + // ! development/experimentation to get things working + resetPlayQueue: (state) => { + const currentEntry = entrySelect(state); + resetPlayerDefaults(state); - state.currentSongId = state.entry[0].id; + state.currentSongId = state[currentEntry][0].id; }, - shufflePlayQueue: (state) => { - // Remove the current song before shuffling the array - // We do this so that the currently playing song doesn't - // suddenly switch because the index has changed - const shuffledEntriesWithoutCurrent = _.shuffle( - removeItem(state.entry, state.currentIndex) - ); + shuffleInPlace: (state) => { + if (state.shuffledEntry.length > 0) { + state.shuffle = true; - // Readd the current song back into its original index - const shuffledEntries = insertItem( - shuffledEntriesWithoutCurrent, - state.currentIndex, - state.entry[state.currentIndex] - ); + const shuffledEntriesWithoutCurrent = _.shuffle( + removeItem(state.shuffledEntry, state.currentIndex) + ); + + // Readd the current song back into its original index + const shuffledEntries = insertItem( + shuffledEntriesWithoutCurrent, + state.currentIndex, + state.shuffledEntry[state.currentIndex] + ); - state.entry = shuffledEntries; + state.shuffledEntry = shuffledEntries; + } + }, + + shufflePlayQueue: (state) => { + const currentEntry = entrySelect(state); + + if (state.shuffledEntry.length < 1) { + // Remove the current song before shuffling the array + // We do this so that the currently playing song doesn't + // suddenly switch because the index has changed + const shuffledEntriesWithoutCurrent = _.shuffle( + removeItem(state.entry, state.currentIndex) + ); + + // Readd the current song back into its original index + const shuffledEntries = insertItem( + shuffledEntriesWithoutCurrent, + state.currentIndex, + state.entry[state.currentIndex] + ); + + state.shuffledEntry = shuffledEntries; + state.currentSongId = shuffledEntries[0].id; + } else { + const findIndex = state.entry.findIndex( + (track) => track.id === state.currentSongId + ); + + if (state[currentEntry].length >= 1 && state.repeat !== 'one') { + if (state.currentIndex < state[currentEntry].length - 1) { + state.currentIndex = findIndex; + state.player1.index = findIndex; + state.currentPlayer = 1; + state.isFading = false; + state.player1.volume = state.volume; + state.player1.index = state.currentIndex; + if (state.currentIndex + 1 >= state[currentEntry].length) { + state.player2.index = 0; + } else { + state.player2.index = state.currentIndex + 1; + } + } else if (state.repeat === 'all') { + state.currentIndex = findIndex; + state.currentPlayer = 1; + state.isFading = false; + state.player1.volume = state.volume; + state.player1.index = state.currentIndex; + if (state.currentIndex + 1 >= state[currentEntry].length) { + state.player2.index = 0; + } else { + state.player2.index = state.currentIndex + 1; + } + } else if ( + state[currentEntry].length >= 1 && + state.repeat === 'one' + ) { + state.currentIndex = findIndex; + state.currentPlayer = 1; + state.isFading = false; + state.player1.volume = state.volume; + state.player1.index = state.currentIndex; + state.player2.index = state.currentIndex; + } + } + state.currentSongId = state.entry[findIndex].id; + } }, setAutoIncremented: (state, action: PayloadAction) => { @@ -125,17 +202,21 @@ const playQueueSlice = createSlice({ }, setStar: (state, action: PayloadAction<{ id: string; type: string }>) => { - const findIndex = state.entry.findIndex( + const currentEntry = entrySelect(state); + + const findIndex = state[currentEntry].findIndex( (track) => track.id === action.payload.id ); if (action.payload.type === 'unstar') { - state.entry[findIndex].starred = undefined; + state[currentEntry][findIndex].starred = undefined; } else { - state.entry[findIndex].starred = String(Date.now()); + state[currentEntry][findIndex].starred = String(Date.now()); } }, toggleRepeat: (state) => { + const currentEntry = entrySelect(state); + if (state.repeat === 'none') { state.repeat = 'all'; } else if (state.repeat === 'all') { @@ -154,11 +235,11 @@ const playQueueSlice = createSlice({ } } - if (state.player1.index > state.entry.length - 1) { + if (state.player1.index > state[currentEntry].length - 1) { state.player1.index = 0; } - if (state.player2.index > state.entry.length - 1) { + if (state.player2.index > state[currentEntry].length - 1) { state.player2.index = 0; } }, @@ -184,8 +265,9 @@ const playQueueSlice = createSlice({ }, incrementCurrentIndex: (state, action: PayloadAction) => { - if (state.entry.length >= 1 && state.repeat !== 'one') { - if (state.currentIndex < state.entry.length - 1) { + const currentEntry = entrySelect(state); + if (state[currentEntry].length >= 1 && state.repeat !== 'one') { + if (state.currentIndex < state[currentEntry].length - 1) { // Check that current index isn't on the last track of the queue state.currentIndex += 1; if (action.payload === 'usingHotkey') { @@ -193,7 +275,7 @@ const playQueueSlice = createSlice({ state.isFading = false; state.player1.volume = state.volume; state.player1.index = state.currentIndex; - if (state.currentIndex + 1 >= state.entry.length) { + if (state.currentIndex + 1 >= state[currentEntry].length) { state.player2.index = 0; } else { state.player2.index = state.currentIndex + 1; @@ -207,15 +289,15 @@ const playQueueSlice = createSlice({ state.isFading = false; state.player1.volume = state.volume; state.player1.index = state.currentIndex; - if (state.currentIndex + 1 >= state.entry.length) { + if (state.currentIndex + 1 >= state[currentEntry].length) { state.player2.index = 0; } else { state.player2.index = state.currentIndex + 1; } } } - state.currentSongId = state.entry[state.currentIndex].id; - } else if (state.entry.length >= 1 && state.repeat === 'one') { + state.currentSongId = state[currentEntry][state.currentIndex].id; + } else if (state[currentEntry].length >= 1 && state.repeat === 'one') { // If repeating one, then we can just increment to the next track state.currentIndex += 1; if (action.payload === 'usingHotkey') { @@ -225,23 +307,25 @@ const playQueueSlice = createSlice({ state.player1.index = state.currentIndex; state.player2.index = state.currentIndex; } - state.currentSongId = state.entry[state.currentIndex].id; + state.currentSongId = state[currentEntry][state.currentIndex].id; } }, incrementPlayerIndex: (state, action: PayloadAction) => { + const currentEntry = entrySelect(state); + // If the entry list is greater than two, we don't need to increment, // just keep swapping playback between the tracks [0 <=> 0] or [0 <=> 1] // without changing the index of either player - if (state.entry.length > 2 && state.repeat !== 'one') { + if (state[currentEntry].length > 2 && state.repeat !== 'one') { if (action.payload === 1) { if ( - state.player1.index + 1 === state.entry.length && + state.player1.index + 1 === state[currentEntry].length && state.repeat === 'none' ) { // Reset the player on the end of the playlist if no repeat resetPlayerDefaults(state); - } else if (state.player1.index + 2 >= state.entry.length) { + } else if (state.player1.index + 2 >= state[currentEntry].length) { // If incrementing would be greater than the total number of entries, // reset it back to 0. Also check if player1 is already set to 0. if (state.player2.index === 0) { @@ -255,12 +339,12 @@ const playQueueSlice = createSlice({ state.currentPlayer = 2; } else { if ( - state.player2.index + 1 === state.entry.length && + state.player2.index + 1 === state[currentEntry].length && state.repeat === 'none' ) { // Reset the player on the end of the playlist if no repeat resetPlayerDefaults(state); - } else if (state.player2.index + 2 >= state.entry.length) { + } else if (state.player2.index + 2 >= state[currentEntry].length) { // If incrementing would be greater than the total number of entries, // reset it back to 0. Also check if player1 is already set to 0. if (state.player1.index === 0) { @@ -277,7 +361,9 @@ const playQueueSlice = createSlice({ }, setPlayerIndex: (state, action: PayloadAction) => { - const findIndex = state.entry.findIndex( + const currentEntry = entrySelect(state); + + const findIndex = state[currentEntry].findIndex( (track) => track.id === action.payload.id ); @@ -307,7 +393,9 @@ const playQueueSlice = createSlice({ }, decrementCurrentIndex: (state, action: PayloadAction) => { - if (state.entry.length >= 1) { + const currentEntry = entrySelect(state); + + if (state[currentEntry].length >= 1) { if (state.currentIndex > 0) { state.currentIndex -= 1; if (action.payload === 'usingHotkey') { @@ -325,13 +413,19 @@ const playQueueSlice = createSlice({ } } - state.currentSongId = state.entry[state.currentIndex].id; + state.currentSongId = state[currentEntry][state.currentIndex].id; } }, fixPlayer2Index: (state) => { - if (state.entry.length >= 2 && state.repeat !== 'one') { - state.player2.index = state.currentIndex + 1; + const currentEntry = entrySelect(state); + + if (state[currentEntry].length >= 2 && state.repeat !== 'one') { + if (state.currentIndex + 1 === state[currentEntry].length) { + state.player2.index = 0; + } else { + state.player2.index = state.currentIndex + 1; + } } else if (state.repeat === 'one') { state.player2.index = state.currentIndex; } else { @@ -340,7 +434,9 @@ const playQueueSlice = createSlice({ }, setCurrentIndex: (state, action: PayloadAction) => { - const findIndex = state.entry.findIndex( + const currentEntry = entrySelect(state); + + const findIndex = state[currentEntry].findIndex( (track) => track.id === action.payload.id ); @@ -351,18 +447,30 @@ const playQueueSlice = createSlice({ setPlayQueue: (state, action: PayloadAction) => { // Reset player defaults state.entry = []; + state.shuffledEntry = []; resetPlayerDefaults(state); - state.currentSongId = action.payload[0].id; action.payload.map((entry: any) => state.entry.push(entry)); + if (state.shuffle) { + const shuffledEntries = _.shuffle(action.payload); + shuffledEntries.map((entry: any) => state.shuffledEntry.push(entry)); + state.currentSongId = shuffledEntries[0].id; + } else { + state.currentSongId = action.payload[0].id; + } }, appendPlayQueue: (state, action: PayloadAction) => { action.payload.map((entry: any) => state.entry.push(entry)); + if (state.shuffle) { + const shuffledEntries = _.shuffle(action.payload); + shuffledEntries.map((entry: any) => state.shuffledEntry.push(entry)); + } }, clearPlayQueue: (state) => { state.entry = []; + state.shuffledEntry = []; resetPlayerDefaults(state); }, @@ -379,8 +487,10 @@ const playQueueSlice = createSlice({ }, moveUp: (state, action: PayloadAction) => { + const currentEntry = entrySelect(state); + // Create a copy of the queue so we can mutate it in place with arrayMove.mutate - const tempQueue = state.entry.slice(); + const tempQueue = state[currentEntry].slice(); // Ascending index is needed to move the indexes in order const selectedIndexesAsc = action.payload.sort((a, b) => a - b); @@ -406,12 +516,14 @@ const playQueueSlice = createSlice({ }); } - state.entry = tempQueue; + state[currentEntry] = tempQueue; }, moveDown: (state, action: PayloadAction) => { + const currentEntry = entrySelect(state); + // Create a copy of the queue so we can mutate it in place with arrayMove.mutate - const tempQueue = state.entry.slice(); + const tempQueue = state[currentEntry].slice(); // Descending index is needed to move the indexes in order const cr = consecutiveRanges(action.payload.sort((a, b) => a - b)); @@ -437,7 +549,7 @@ const playQueueSlice = createSlice({ }); } - state.entry = tempQueue; + state[currentEntry] = tempQueue; }, }, }); @@ -466,5 +578,6 @@ export const { resetPlayQueue, setStar, shufflePlayQueue, + shuffleInPlace, } = playQueueSlice.actions; export default playQueueSlice.reducer;