Hi, I have been working on the Jammming project in the front-end engineering path
https://www.codecademy.com/journeys/front-end-engineer/paths/fecj-22-front-end-development/tracks/fecj-22-react-part-ii/modules/wdcp-22-jammming-b26fb9a6-a249-4dda-91a0-ebaa5c8de0ce/kanban_projects/jammming-react18
I’ve been using the forums for help as I go along, but Codecademy recently updated a lot of the course and this project changed slightly as a result (it’s now a kanban project and the instructions don’t seem to be as explicit as the previous version), so I’m sure my final version will look very different to people who did the course a few months ago.
My problem is with the authorisation. When I first set it up and got it working, every time I made other changes to the app and refreshed, the app in my browser would refresh and ask for authorisation again (which is what I would have expected). Today when I was doing the finishing touches I realised it no longer does that. It seems that, even if the access token expires, it auto refreshes without me having to click on anything. In the Spotify API documentation I can see that there is a way to do this by getting a refresh token, however I have not done that (I’ve used implicit grant flow as suggested in the lesson).
The strange this is that this was working fine initially, and as far as I remember I didn’t make any changes to the parts of code that would affect it.
Does anyone have any idea why this might be happening? I’ll post my Spotify component and App component.
Spotify:
let accessToken = "";
const client_id = {myClientId};
const redirect_uri = "http://localhost:3000";
const scope = "playlist-modify-public";
const Spotify = {
getAccessToken() {
if (accessToken) {
return accessToken;
}
const urlAccessToken = window.location.href.match(/access_token=([^&]*)/);
const urlExpiresIn = window.location.href.match(/expires_in=([^&]*)/);
if (urlAccessToken && urlExpiresIn) {
accessToken = urlAccessToken[1];
const expiresIn = Number(urlExpiresIn[1]);
window.setTimeout(() => (accessToken = ""), expiresIn * 1000);
window.history.pushState("Access Token", null, "/");
} else {
const redirect =
"https://accounts.spotify.com/authorize?response_type=token&client_id=" +
encodeURIComponent(client_id) +
"&scope=" +
encodeURIComponent(scope) +
"&redirect_uri=" +
encodeURIComponent(redirect_uri);
window.location = redirect;
}
},
async search(term, accessToken) {
const baseUrl = "https://api.spotify.com/v1";
const searchParam = "/search?q=";
const tracksParam = "&type=track";
let endpoint = baseUrl + searchParam + term + tracksParam;
const headers = { Authorization: "Bearer " + accessToken };
try {
const response = await fetch(endpoint, { headers });
if (response.ok) {
const jsonResponse = await response.json();
const realResultsArray = jsonResponse;
return realResultsArray;
}
} catch (error) {
console.log("There was a problem: " + error);
}
},
async save(playlistName, accessToken, uriArray) {
const baseUrl = "https://api.spotify.com/v1";
const userInfo = "/me";
const firstEndpoint = baseUrl + userInfo;
const headers = { Authorization: "Bearer " + accessToken };
try {
const response = await fetch(firstEndpoint, { headers });
if (response.ok) {
const jsonResponse = await response.json();
const userId = jsonResponse.id;
const newPlaylistParam = "/users/" + userId + "/playlists";
const secondEndpoint = baseUrl + newPlaylistParam;
const firstRequestOptions = {
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: playlistName }),
};
const playlistRef = await fetch(secondEndpoint, firstRequestOptions);
if (playlistRef.ok) {
const playlistResponse = await playlistRef.json();
const playlistId = playlistResponse.id;
const addTracksParam = "/playlists/" + playlistId + "/tracks";
const thirdEndpoint = baseUrl + addTracksParam;
const secondRequestOptions = {
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
"Content-Type": "application/json",
},
body: JSON.stringify(uriArray),
};
const tracksToAdd = await fetch(thirdEndpoint, secondRequestOptions);
if (tracksToAdd.ok) {
alert("Head over to Spotify to see your new playlist!");
} else {
throw new Error("You didn't add any tracks to your new playlist.");
}
} else {
throw new Error("Please give your playlist a name.");
}
} else {
throw new Error("Request for user ID has failed.");
}
} catch (error) {
alert(error);
}
},
};
export default Spotify;
And App:
import styles from "./App.module.css";
import Playlist from "../Playlist/Playlist";
import SearchBar from "../SearchBar/SearchBar";
import SearchResults from "../SearchResults/SearchResults";
import Header from "../Header/Header";
import React, { useState, useEffect } from "react";
import Spotify from "../../utilities/Spotify";
function App() {
const [playlistName, setPlaylistName] = useState("My new playlist");
const [playlist, setPlaylist] = useState([]);
const uriArray = [];
const [searchResults, setSearchResults] = useState({});
function handleAddClick(e) {
let resultsArray = searchResults.tracks.items;
let trackIndex = resultsArray.findIndex((item) => {
return item.id == e.target.id;
});
let track = resultsArray[trackIndex];
if (
playlist.some((song) => {
return song === track;
})
) {
alert(track.name + " is already in your playlist."); /*{
const addAgain = window.confirm(
track.name +
" is already in your playlist. Press OK to add it again, or CANCEL to skip."
);
if (addAgain) {
alert(track.id);
setPlaylist((prev) => [track, ...prev]);
} else {
setPlaylist((prev) => [...prev]);
}*/
} else {
setPlaylist((prev) => [track, ...prev]);
}
}
function handleRemoveClick(e) {
let track = e.target.id;
let updatedPlaylist = playlist.filter((song) => {
return song.id != track;
});
if (playlist.length - updatedPlaylist.length > 1) {
alert(
"This song is in the playlist more than once. This will remove all copies."
);
}
setPlaylist(updatedPlaylist);
}
function resetInput() {
setPlaylistName("");
}
function handleRename(e) {
let newName = e.target.value;
setPlaylistName(newName);
}
let accessToken = Spotify.getAccessToken();
useEffect(() => {
console.log(accessToken);
accessToken = Spotify.getAccessToken();
//console.log(accessToken);
}, []);
async function handleSearch() {
console.log(accessToken);
let searchTerm = document.getElementById("searchTerm").value;
if (searchTerm.length > 0) {
const newArray = await Spotify.search(searchTerm, accessToken);
setSearchResults(newArray);
} else {
alert("Please enter a search term.");
}
}
async function handleSavePlaylist(e) {
console.log(accessToken);
for (let i = 0; i < playlist.length; i++) {
uriArray.push(playlist[i].uri);
}
if (playlistName.length > 0) {
Spotify.save(playlistName, accessToken, uriArray);
setPlaylist([]);
setPlaylistName("My new playlist");
} else {
alert("Please give your playlist a name before saving.");
}
}
return (
<div className={styles.app}>
<header>
<Header />
</header>
<main>
<SearchBar id={"searchTerm"} onSearch={handleSearch} />
<div className={styles.col}>
<SearchResults
resultsArray={searchResults}
onClick={handleAddClick}
/>
<Playlist
customName={playlistName}
contents={playlist}
onClick={handleRemoveClick}
onRename={handleRename}
onSave={handleSavePlaylist}
resultsArray={searchResults}
playlistName={playlistName}
onReset={resetInput}
/>
</div>
</main>
</div>
);
}
export default App;
As you can see, I added useEffect() to stop the bug that a lot of people had where you had to search a term twice before it would actually work. It solved that problem which was good.
I have tried removing let accessToken = Spotify.getAccessToken(); before the useEffect, but then accessToken isn’t defined and it’s needed for other functions, so that doesn’t work.
I’ve tried getting rid of the useEffect and calling Spotify.getAccessToken within the handleSearch and handleSave functions, but that doesn’t make a difference.
I did some console.log statements to see what was happening, and even if there was no access token to start with (console.log(accessToken) printed “undefined”), it authorised automatically without me clicking. (a second console.log(accessToken) printed the token).
The app all works as it should - I can search terms and create and save a new playlist which then does appear in my Spotify account. So for all intents and purposes I’m finished with the project. I just want to know how I can fix this authentication problem! Thanks!