Feature Request: Jammming

I’ve opened this thread so that we can work together in this project and share our solutions to the additionals features to be add to jamming which I think are quite difficult to implement and I found no other thread for help on this topic.
https://www.codecademy.com/paths/web-development/tracks/front-end-applications-with-react/modules/jammming/projects/feature-request-prj
I’ll start by implementing the Multiple Playlists feature as per the feature request document example
https://docs.google.com/document/d/1bHEQvYYAwSpAkdpv7hp8eu-iMnvqYVS4UffCLKvaKGg/edit
I encourage you to join me so we can solve together all the suggested points.
Happy coding! :smirk:

8 Likes

First of all I’ve uploaded my project to github in order to keep a copy working so I don’t have to worry of messing with my project.
Here’s a refresher on how to do it.
Getting Started with Git and GitHub
Here’s my repository with first part completed and working:
https://github.com/cblastor/busca-tu-musica

1 Like

Hi! Very nice my friend. I’m trying to add samples for each track. Not making it :’(

1 Like

Nice job, and nice idea for a thread. I’m trying to implement code to remove songs from the search results if they are already in the playlist, but not having much luck so far. I thought it would be just a matter of filtering the searchResults array when setting the searchResults state in App, but that doesn’t seem to be working. Below is the code I tried implementing with no luck:

search(term) {
    Spotify.search(term).then(searchResults => {
      this.setState({searchResults: searchResults.filter(result => {
        if (!this.state.playlistTracks.includes(result.id)) {
          return result
        }
      })
    });
    });
  }

Hi, it’s a great idea as I’m also trying to complete this project I couldn’t find much resources. I am trying to add a list of playlists to my app which is the same as your 1st step, but I couldn’t make it work yet. In the Github I couldn’t see any components, util etc. folders, am I looking at something wrong?

@cblastor how did you get on with implementing the multiple playlists features? I think I am also going to give that a try first!

hey @microplayer64661 @method3835719473 and @davidloynaz, I was wondering if any of you tried implementing the feature in the example for the user to update all of their playlists?

I’ve written the getCurrentUserId function like so, but I’m not sure if I’m doing it correctly in the way I’m creating a new promise if the id is already set:

from Spotify.js

  getCurrentUserId() {
        // Get access token to allow us to access the userId
        const accessToken = Spotify.getAccessToken();
        const headers = {
            Authorization:
            `Bearer ${accessToken}`
           };
        if (userId) {
        //if userId is already set, create and retun a promise that will resolve to that value 
        const promise = new Promise.resolve(userId); 
        promise.then(function(userId) {
            return userId; 
        });
        } else {
        // Otherwise, we will make the call to the /me endpoint and return that request's promise 
        return fetch('https://api.spotify.com/v1/me', { headers: headers }
        ).then((response) => response.json()
        ).then((jsonResponse) => {
                userId = jsonResponse.id;

    })
   }
  }

Update: I’ve now implemented all of the functions to display the user’s playlists, but it’s not working and I’m going mad trying to figure out why…

The errors I’m getting in the console are: Unhandled Rejection (SyntaxError): Unexpected end of JSON input

and

TypeError: Cannot read property ‘playlist’ of undefined

So there seems to be a problem with the jsonResponse returned from the endpoint: https://api.spotify.com/v1/users/${currentUserId}/playlists

However I cannot for the life of me figure out what it should be. The getUserId() function is definitely working, as when I console.log the response from the endpoint, my Spotify username is returned.

For reference: https://developer.spotify.com/documentation/web-api/reference/playlists/get-a-list-of-current-users-playlists/

Here are my getUserId and getUserPlaylist functions from Spotify.js:

getCurrentUserId() {
        // Get access token to allow us to access the userId
        const accessToken = Spotify.getAccessToken();
        const headers = {
            Authorization:
            `Bearer ${accessToken}`
           };
        if (userId) {
        // If userId is already set, create and return a promise that will resolve to that value 
        
        const executorFn = (resolve, reject) => {
            resolve(userId);
            reject(console.log('Error!'))
        }; 
        const promise = new Promise(executorFn); 
        return userId; 
    
        } else {
        // If userId is not already set, we will make a call to the /me endpoint and return that request's promise 
        return fetch('https://api.spotify.com/v1/me', { headers: headers }
        ).then((response) => response.json()
        ).then((jsonResponse) => {
                userId = jsonResponse.id;

    })
   }
  },

 getUserPlaylists() {
      // Retrieve the user's ID
      const currentUserId = Spotify.getCurrentUserId()
      const accessToken = Spotify.getAccessToken();
      const headers = {
            Authorization:
            `Bearer ${accessToken}`
           };
           let playlistId; 
           let playlistName; 
   
         return fetch("https://api.spotify.com/v1/me", {
               headers
           }).then((response) => response.json()).then((jsonResponse) => {
               userId = jsonResponse.id; 
               console.log(jsonResponse.id); 
           
   
      // Make a call to the /users/{user_id}/playlists endpoint 
      const proxyurl = "https://cors-anywhere.herokuapp.com/"; 
const url = `https://api.spotify.com/v1/users/${currentUserId}/playlists`; 
      return fetch((proxyurl + url), {
        headers: headers
// Upon completion of this request, we will update the current playlist's state to an array of the returned playlists 
// Rather than storing the entire playlists, we should create and store objects for each playlist that contain the playlistId and name of each playlist (below using the map method) 
        })

        .then((response) => { 
       response.json();
}).then((jsonResponse) => { 
 jsonResponse.playlist.items.map((playlist) => ({
             playlistId: playlist.items.id,
             playlistName: playlist.items.name
     
         }));
         console.log(jsonResponse.playlists.items); 

     }).catch(error => console.log('This error occurred', error.toString())); 
 })
}
}

For your reference, here are my PlaylistList and PlaylistListItem components:

PlaylistList.js (rendered by App.js)

class PlaylistList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            playlists: [ {
                id: '',
                name: ''
            }
             
            ]
        }
    }

componentDidMount() {
        Spotify.getUserPlaylists()
        .then((playlistId, playlistName) => {
            this.setState({
                playlists: [
                    {
                    id: playlistId,
                    name: playlistName
                 } 
                ]
            })
    
        })
    
    }

 render() {
      
    return  (
     <div className="PlaylistList">
     <h2>Local Playlists</h2>
    
     <PlaylistListItem playlistId={this.state.playlists.id}
     playlistName={this.state.playlists.name} />
    
 
     </div>
  
    ) 

 
 
    }
}

export default PlaylistList; 

PlaylistListItem:

class PlaylistListItem extends React.Component {
  render() {
    return (
    <div className="PlaylistListItems">
    <div className="playlist-info">
      {this.props.playlistId}
     <h3>{this.props.playlistName}</h3>
    </div>
    </div>
    )
    
  }
}

Thank you so much in advance!

1 Like

Hi there,

I’am not really fluent in English. Sorry for that.
So, I’ve spent my whole day to fingure this out.
I was desasperate not to find solution anywhere.
Here is one solution finaly found by myself. Not sure if it is the best one but it works.
hope it will help you.

First , pass down the state of playlistTracks from app to SearchResults component then to Trackslist component in SearchResults.js.

Here is the code in Tracklist.js :

class TrackList extends React.Component {
    isInPlaylist(track) {
        let tracksIn = this.props.tracksInPlaylist;
        // check if tracksIn is not "undefined"
        if (!tracksIn) {
            return; // if it is, break the code below
        }
         // check if the track passed in argument  is in the playlist or not and return a boolean 
        return (tracksIn.find(savedTrack => (savedTrack.id === track.id))) ? true : false;
    }

    render() {
        return (
            <div className="TrackList">
                {
                    this.props.tracks.map(track => {
                        if (!this.isInPlaylist(track)) { // display each track only if it is not in the playlist
                            return <Track key={track.id}
                                track={track}
                                onAdd={this.props.onAdd}
                                onRemove={this.props.onRemove}
                                isRemoval={this.props.isRemoval}
                            />
                        } else { // if it is in the playlist, display nothing
                            return null;
                        }

                    })
                }
            </div>
        );
    }
}

export default TrackList;
2 Likes

Hi, I also couldn’t see any components.
So, I’ve just created a new github project.
Feel you free to work on it.

1 Like

Hi @davidloynaz,

Here is one solution that I found.
Moreover, I’ve juste created a new link on Github :slight_smile:

I’m not really good in CSS so feel you free to modifie the Track.css in a new branch.

Code in Track.js :

 render() {
    
        return (
            <div className="Track">
                <div className="Track-information">
                    <h3>{this.props.track.name}</h3>
                    <p>{this.props.track.artist} | {this.props.track.album}</p>
                    <audio
                        controls
                        src = {this.props.track.preview}
                    >
                    </audio>
                </div>
                {this.renderAction()}
            </div>
        );
    }

and the code snippet in Spotify.js :

}
            return jsonResponse.tracks.items.map(track => ({
                id: track.id,
                name: track.name,
                artist: track.artists[0].name,
                album: track.album.name,
                uri: track.uri,
                preview: track.preview_url,
            }));
1 Like

Thanks! That worked for me as well. I was trying to do it via the Spotify component. In hindsight the TrackList component makes more sense, but I think the thing that is most challenging about this project is just following the flow of it and understanding how the different components interact. That’s what was throwing me here.

3 Likes

Great idea for a thread! My feature request was for drag/drop ability to reorder the playlist section, since I’ve always thought that it was harder than it needed to be on the Spotify mobile app and impossible on the Spotify web player. I used the react library react-beautiful-dnd and after kicking myself for how long I was checking a misnamed prop, I found it to be pretty simple and easy to use. Once I got that working I starting thinking about other modifications I could make; I played around with the user-playlist feature in the example, but spent most of my time building out a related-artist explorer feature, which I deployed here: http://mikes-rax.surge.sh/
Code for all of the above can be found on my github, master branch is vanilla Jammming, and the beautiful-dnd branch and related-artists-explorer branches should be fully functional.

2 Likes

Hey Chaplin! Thanks for the source code! Have you managed to get the audio to work? I’ve got no idea why it’s not showing up on mine.
Through React Dev Components Console I can actually get the preview URL. It’s size is 300x0. since it has no height, I’m trying to position it to the left of all the track info :smiley:

Still unable to feature the audio playback. Anyone knows how to deal with it? I’m guessing it’s a CSS issue but it’s not my forte.

Hey @lucasvinzon ,
here is my css code for Track.css and the result :
Hope that can help!

.Track {
    display: flex;
    align-items: center;
    border-bottom: 1px solid rgba(256, 256, 256, 0.8);
  }
  
  .Track-action {
    cursor: pointer;
    padding: .5rem;
    font-size: 1.05rem;
    transition: color .25s;
    border: 0px;
    background-color: rgba(0, 0, 0, 0);
    color: #fff;
  }
  
  .Track-action:hover {
    color: rgba(265, 265, 265, .5);
  }
  
  .Track-information {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    height: 90px;
  }
  
  .Track-information h3 {
    margin-bottom: .22rem;
  }
  
  .Track-information p {
    font-size: .83rem;
    font-weight: 300;
    color: rgba(256, 256, 256, 0.8);
  }

  .Track-information audio {
    padding : .22rem;
    max-height: 30%;
    max-width: 70%;
  }

4 Likes

Hi All,

This is my track preview solution.

You can play with a working version here: http://eric-jammming.surge.sh/

All feedback is welcome, as I am not entirely sure I am following React pattern. Anyhow, it took me a while to figure out. Hope you guys enjoy.

Regards,

Eric Alain

5 Likes

Hi memarino92,

do you have a tips on getting react-beautiful-dnd to work within your app? I’m trying to create a drag and drop functionality like you added, (to eventually add functionality dragging and dropping between Search Results and Playlist) but I can’t get it work at all!
I think I’m not using the innerRefs right? but I’m not sure.

Stop spamming everywhere already. The link won’t work.

The link doesn’t work because I have since migrated my projects elsewhere. Unfortunately, I am no longer able to modify the comment/URL.

Sharing your projects is encouraged here and is sometimes directly responsible for assisting and inspiring others. It has certainly helped me come unstuck in the past. Feedback about bugs, UI and UX is equally important to me.

I am sorry you consider it to be spam, although I’d be suprised if many other students would agree with you.