Jammming Project, SAVE TO SPOTIFY not working

Hi the button save to spotify doesnt seem to work for me. I have no idea what is wrong double checked my code with the walk through and no luck

What is happening is when I push over the results of artists from the search results and into the Playlist as I try to press the Save to Spotify button the page reloads clears everything on the screen at first you may think its nothing wrong but when I look in the console I can see that errors are thrown. So when the page quickly then reloads then the errors reloads with the page, so I am not able to inspect what is wrong.

So for debugging how can I prevent the page from reloading I have even tried passing an argument off event into the method
App.savePlaylist() with (e) then e.preventDefault() still it reloads. Even decided to make a element wrapped the input and button into the so onSubmit placed a method handler for e.preventDefault() still cant stop the page from reloading.
So how can I stop the reload?

this is what I managed to capture using my phone because the page reloads to fast the GET is 401 status from the Spotify.savePlaylist() and after the POST is the same 401 status

here are my codes

App.js

import React from 'react';
import './App.css';
import { SearchBar } from '../SearchBar/SearchBar.js';
import { SearchResults } from '../SearchResults/SearchResults.js';
import { Playlist } from '../Playlist/Playlist.js';
import Spotify from '../../util/Spotify.js'


export class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      searchResults: [],
      playlistName: 'My Playlist',
      playlistTracks: []
    };
    this.addTrack = this.addTrack.bind(this);
    this.removeTrack = this.removeTrack.bind(this);
    this.updatePlayListName = this.updatePlayListName.bind(this);
    this.savePlaylist = this.savePlaylist.bind(this);
    this.search = this.search.bind(this);
  }

  addTrack(track) {

    const tracks = this.state.playlistTracks;

    // tracks.find(saved => (saved.id !== track.id) && this.state.playlistTracks.push(track));

    // this.setState({ playlistTracks: tracks });
      
    /// my way has problems
    
    if (tracks.find(savedTrack => savedTrack.id === track.id)) {
      return; 
    }

    tracks.push(track);
    this.setState({ playlistTracks: tracks });

  }
  removeTrack(track) {
    let tracks = this.state.playlistTracks;
    
    tracks = tracks.filter(savedTrack => savedTrack.id !== track.id);
  
    this.setState({ playlistTracks: tracks });
  }

  updatePlayListName(name) {
    this.setState({ playlistName: name });
  }

  savePlaylist() {
    const trackUris = this.state.playlistTracks.map(track => track.uri);
    const name = this.state.playlistName;
    Spotify.savePlaylist(name, trackUris).then(() => {
      this.setState({
        playlistName: 'New Playlist',
        playlistTracks: []
      })
    })
  }

  search(term) {
    // console.log("appSearch " + term);
    // alert('searchbar clicked!')
    Spotify.search(term).then(searchResults => {
      this.setState({ searchResults: searchResults })
    });
    // console.log(this.state.searchResults.push("End of it"));
    // console.log(this.state.searchResults)
  }

  render() {
    return (
      <div>
        <h1>Ja<span className="highlight">mmm</span>ing</h1>
        <div className="App">
          <SearchBar onSearch={this.search} />
          <div className="App-playlist">
          <SearchResults 
            searchResults={this.state.searchResults}
            onAdd={this.addTrack} />
          <Playlist 
            playlistName={this.state.playlistName}
            playlistTracks={this.state.playlistTracks}
            onRemove={this.removeTrack}
            onNameChange={this.updatePlayListName}
            onSave={this.savePlaylist} />
        </div>
        </div>
      </div>
    )
  }
  /*componentDidMount() {
    window.addEventListener('load', (e) => {//
      e.preventDefault();
      Spotify.getAccessToken();
    })
  }*/
}

// console.log(Spotify.getAccessToken())
// console.log(Spotify.search());
// console.log(Spotify.savePlaylist());
// console.log(App);

Playlist.js

import React from 'react';
import './Playlist.css';
import { TrackList } from '../TrackList/TrackList.js';


export class Playlist extends React.Component {
    constructor(props) {
        super(props);
        this.handleNameChange = this.handleNameChange.bind(this);
    }
    handleNameChange(e) {
        this.props.onNameChange(e.target.value);
    }
    render() {
        return (
            <div className="Playlist">
                <input 
                    defaultValue={'New Playlist'}
                    onChange={this.handleNameChange} />
                <TrackList 
                    tracks={this.props.playlistTracks}
                    onRemove={this.props.onRemove}
                    isRemoval={true} />
                <button 
                    className="Playlist-save"
                    onClick={this.props.onSave}>
                        SAVE TO SPOTIFY
                </button>
            </div>
        )
    }
}

SearchBar.js

import React from 'react';
// import Spotify from '../../util/Spotify';
import './SearchBar.css';

export class SearchBar extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 
            term: ""
        }
        this.search = this.search.bind(this);
        this.handleTermChange = this.handleTermChange.bind(this);
    }
    search() {
        // console.log(this.state.term);
        this.props.onSearch(this.state.term);
        // window.addEventListener('load', e => {
        //     e.preventDefault();
        //     Spotify.getAccessToken();
        // });
    }
    handleTermChange(event) {
        // console.log(event.target.value);
        this.setState({ term: event.target.value })
        // this.search(e.target.value);
    }
    render() {
        return (
            <div className="SearchBar">
                <input 
                    placeholder="Enter a song, album or an Artist"
                    onChange={this.handleTermChange} />
                <button onClick={this.search} className="SearchButton">Search</button>
            </div>
        )
    }
}

SearchResult.js

import React from 'react';
import './SearchResults.css';
import { TrackList } from '../TrackList/TrackList.js';

export class SearchResults extends React.Component {
    render() {
        return (
            <div className="SearchResults">
                <h2>Results</h2>
                <TrackList 
                    tracks={this.props.searchResults}
                    onAdd={this.props.onAdd}
                    isRemoval={false} />
            </div>
        )
    }
}

Tracklist.js

import React from 'react';
import './TrackList.css';
import { Track } from '../Track/Track.js';

export class TrackList extends React.Component {
    render() {
        return (
            <div className="TrackList">
                {
                    this.props.tracks &&
                    this.props.tracks.map(track => {
                        return <Track 
                            track={track}
                            key={track.id}
                            onAdd={this.props.onAdd}
                            onRemove={this.props.onRemove}
                            isRemoval={this.props.isRemoval} />
                    })
                }
            </div>
        )
    }
}

Track.js

import React from 'react';
import './Track.css';

export class Track extends React.Component {
    constructor(props) {
        super(props);
        this.addTrack = this.addTrack.bind(this);
        this.removeTrack = this.removeTrack.bind(this);
    }
    renderAction() {
        if (this.props.isRemoval) {
            return <button onClick={this.removeTrack} className="Track-action">-</button>
        } else {
            return <button onClick={this.addTrack} className="Track-action">+</button>
        }
    }
    addTrack() {
        this.props.onAdd(this.props.track);
    }
    removeTrack() {
        this.props.onRemove(this.props.track);
    }
    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>
                </div>
                {this.renderAction()}
            </div>
        )
    }
}

Spotify.js

const clientID = "covered up";
const redirectURI = "http://localhost:3000/";

let accessToken;


const Spotify = {
    getAccessToken() {
        if (accessToken) {
            return accessToken;
        }

        // check for access Token match 

        const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
        const expiresInMatch = window.location.href.match(/expires_in=([^&])*/);

        if (accessTokenMatch && expiresInMatch) {
            accessToken = accessTokenMatch[1];
            const expiresIn = Number(expiresInMatch[1]);
            // This clears the parameters, allowing us to grab a new access token when it expires.
            window.setTimeout(() => accessToken = "", expiresIn * 1000);
            window.history.pushState('Access Token', null, '/');
            return accessToken;
        } else {
            let accessURL = "https://accounts.spotify.com/authorize?client_id=" + clientID + "&response_type=token&scope=playlist-modify-public&redirect_uri=" + redirectURI;
         //                   "https://accounts.spotify.com/authorize?client_id=" + CLIENT_ID+ "&response_type=token&scope=playlist-modify-public&redirect_uri=" + REDIRECT_URI"
            window.location = accessURL;
        }
    },
    search(term) {
        const accessToken = this.getAccessToken();
        const headers = {'Authorization': `Bearer ${accessToken}`}
        console.log(headers)
                
        return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
            headers: headers
        }).then(response => {
            // console.log("response search " + response);
            return response.json()
        }).then(jsonResponse => {
            console.log(jsonResponse);
            if (!jsonResponse.tracks) {
                return [];
            } else {
                return jsonResponse.tracks.items.map(track => ({
                    id: track.id,
                    name: track.name,
                    artist: track.artists[0].name,
                    album: track.album.name,
                    uri: track.uri
                }));
            };
        });
    },
    savePlaylist(name, trackUris) {
        if (!name || !trackUris.length) {
            return;
        }

        const accessToken = this.getAccessToken();
        const headers = {Authorization: `Bearer ${accessToken}`}
        let userId;
        //return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
        
        return fetch('https://api.spotify.com/v1/me', { headers: headers }
        ).then(response => {
            console.log("first response " + response)
            return response.json()
        }).then(jsonResponse => {
            console.log("jsonResponse on GET " + jsonResponse)
            userId = jsonResponse.id;
            return fetch(`https://api.spotify.com/v1/users/${userId}/playlists`, { 
                headers: headers,
                method: 'POST',
                body: JSON.stringify({ name: name })
            })
        }).then(response => response.json()).then(jsonResponse => {
            const playlistId = jsonResponse.id;
            return fetch(`https://api.spotify.com/v1/users/${userId}/playlists/${playlistId}/tracks`, {
                headers: headers,
                method: 'POST',
                body: JSON.stringify({ uris: trackUris })
            })
        });
    } 
}

export default Spotify;

I suspect that what is happening is not a reload to much as a redirect. When we press the search button and the save playlist button the app interacts with the spotify API, which has a callback address in the settings.

[This was a challenge for me after I deployed my site live (putting a new url in the settings), and then doing further development in a localhost environment… the app redirects to the callback address after interacting with the spotify API… this is how I came to realise that what looks like a ‘reload’ is actually a callback/redirect].

From the message in your console, it at least shows that there is a problem in two places:

  1. Your GET call doesn’t seem to have worked.
  2. Which then possibly may also be responsible for the userId part of the POST url address returning as undefined.

From the log of the headers (which includes the accessToken), looks like the accessToken might be funky.

If it were me I’d double check whether the accessToken is storing the right bit of information, and not being overwritten at some point. console.log(accessToken) at various points in the Spotify.js methods to see if it is consistent in its return.

Might be a syntax issue. Try writing the accessURL as a template literal, as the other url addresses are, and be consistent in your code whether you are using " or ' around your strings, because if two bits of data are put together - one with " and the other with ' they might conflict in an unexpected way.

1 Like

Yes you are right the accessToken was funky had wrong regex code on the expire fixed it now thanks its working