Previous & Next buttons showing different data

Viewed 270

I am building a Music Player project in React/Redux and am having some issues with the Prev/Next buttons... I can't really pinpoint exactly what is causing the issue, but the following is my goal:

Previous button: Should always play the last song played before the currently playing song.

Next button: Should play the next song on the playlist.

I am able to achieve each functionality separately, meaning if i make an n-number of song selections, and hit previous n-number of times (in any order), I am getting the correct list of songs previously played. The issue arises when I press both the next and previous buttons in random order. I get a whole jumbled mess of songs instead of the correct orders.

So this is my workflow, just so that you get a better understanding of what is going on here so far:

  1. On song click - sends up the current song object as a dispatched action to the store {current}.
  2. We send the same currently selected song up to the store into a previously_played array so that we have a log/history of previously played songs.
  3. The rest of the action happens in the dashboard.js file shown below with comments.

My goal: To get the previous button to log all the previously played songs, then on next-button-press, should go down the regular playlist and play the songs. Also, if the current song is the last song in the list, start again from the first in the playlist (vice versa). Any help would be awesome guys! Thanks in advance.

I have used redux, and here is my code:

REDUCER:

import { CURRENT, PREV, NEXT, PLAYLIST, PREVIOUSLY_PLAYED } from '../actions/action_types';

const initial_state = {
    playlist: [],
    previously_played: [],
    current_song: {},
    prev_song: {},
    next_song: {},
}

export default function songs_reducer (state = initial_state, action) {
    switch(action.type) {
        case PLAYLIST:
            return {
                ...state,
                playlist: action.payload
            }

        case PREVIOUSLY_PLAYED:
            return {
                ...state,
                previously_played: [...state.previously_played, action.payload]
            }

        case CURRENT:
            return {
                ...state,
                current_song: action.payload
            }

        case PREV:
            return {
                ...state,
                prev_song: action.payload
            }

        case NEXT:
            return {
                ...state,
                next_song: action.payload
            }

        default:
            return state;
    }
}

ACTIONS:

import { PLAYLIST, PREVIOUSLY_PLAYED, CURRENT, PREV, NEXT, CURRENT_ELEMENT } from './action_types';

export const playlist = data => ({
    type: PLAYLIST,
    payload: data
});

export const previously_played = data => ({
    type: PREVIOUSLY_PLAYED,
    payload: data
});

export const current = data => ({
    type: CURRENT,
    payload: data
});

export const prev = data => ({
    type: PREV,
    payload: data
});

export const next = data => ({
    type: NEXT,
    payload: data
});

export const current_element = data => ({
    type: CURRENT_ELEMENT,
    payload: data
});

DASHBOARD:

import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { playlist, previously_played, current, prev, next, current_element } from '../../redux/actions/actions';

// IMPORT COMPONENTS
import ControlsWidget from '../global/controls/controls.js';
import InfoWidget from '../global/info-widget/info_widget.js';

const Dashboard = (props) => {

    const [playing_track, set_playing_track] = useState({}); // CURRENTLY PLAYING SONG
    
    const [previous_song_name, set_previous_song_name] = useState('');
    const [previous_song, set_previous_song] = useState('');

    // COUNTER TO TRAVERSE BACKWARDS FOR PREVIOUS LOG
    let [counter, set_counter] = useState(1);

    // ON CURRENT TRACK UPDATE, SET THE CURRENTLY PLAYING TRACK DATA TO STATE, KEEPING IT GENERIC
    useEffect(() => {       
        set_playing_track({track: props.current_track.track, url: props.current_track.url});        
    }, [props.current_track]);

    // SHOW PREVIOUS SONG
    const show_previous = () => {   
        console.log('CURRENT TRACK (PREV BTN)', playing_track);
        // console.log('PREVIOUSLY PLAYED SONGS', props.previously_played_songs);

        counter++;
        set_counter(counter);
        
        let prev_url = props.previously_played_songs[props.previously_played_songs.length - (counter)].url;
        let prev_track = props.previously_played_songs[props.previously_played_songs.length - (counter)].track;

        // console.log('PREVIOUS SONG ', prev_url, prev_track);

        set_playing_track({track: prev_track, url: prev_url});

        // SET CURRENT
        props.current(props.previously_played_songs[props.previously_played_songs.length - (counter)]);
        
    }

    // SHOW NEXT SONG
    const show_next = () => {       

        console.log('CURRENT TRACK (NXT BTN)', props.current_track);

        // LOOP OVER PLAYLIST AND SET NEXT SONG
        for (let i = 0; i < props.all_songs.length; i++) {          
            if (((props.current_track.url.localeCompare(props.all_songs[i].url)) === 0)) {              
                
                // SET AS CURRENT IN REDUX STORE
                props.current(props.all_songs[i + 1]);              

                // PUSH IN PREVIOUSLY PLAYED LOG
                props.previously_played(props.all_songs[i + 1]);                

                // SET AS CURRENTLY PLAYING GENERIC STATE VARIABLE
                set_playing_track({track: props.all_songs[i + 1].track, url: props.all_songs[i + 1].url});                  
            }
        }

    }

    return (
        <aside className='sec-std' id='main-dashboard'>         
            <InfoWidget track_name={playing_track.track}/>
            <ControlsWidget url={playing_track.url} show_previous={show_previous} show_next={show_next} />
        </aside>
    )
}

const mapStateToProps = (state) => {    
    return {
        current_track: state ? state.songs.current_song : null,
        previously_played_songs: state ? state.songs.previously_played : null,
        all_songs: state ? state.songs.playlist : null
    }
}

export default connect(mapStateToProps, {current, previously_played})(Dashboard);
1 Answers

This is actually an algorithm question, I think.

I think we need to give some examples first.

Suppose the playlist is static, like [0,1,2,3,4,5,6,7].

Then suppose the user could randomly play from any song, and next song is automatically played after current song ends.

Now consider some sequences:

Step 1, play 4

=> history=[4], cur=4, prev=undefined, next=5

Step 2, just let 4 plays until the end, then 5 automatically plays, so that:

=> history=[4,5], cur=5, prev=4, next=6

Step 3, hit 7 before 5 ends

=> history=[4,5,7], cur=7, prev=5, next=8

Step 4, before 7 ends, hit 4 again

=> history=[5,7,4], cur=4, prev=7, next=5

Step 5, before 4 ends, hit prev, now 7 is the current song

=> history=[5,4,7], cur=7, prev=4, next=8

Step, before 7 ends, hit next, now 8 uis the current song

=> history=[5,4,7,8], cur=8, prev=7, next=undefined

Not sure if the above example matches your request?

If so, then the algorithm is:

1. If the current song is not in history array, push it
2. Else if the current song in in history array, erase it, then push it

(So that current song is actually always history[length - 1])

3. So the prev song is actually always history[length - 2]
4. Next song is always playlist[current song's index +1]

Related