Skip to content

Commit 1af56ef

Browse files
committed
Adding project files
1 parent c4c91c8 commit 1af56ef

File tree

4 files changed

+321
-0
lines changed

4 files changed

+321
-0
lines changed

Diff for: app.js

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
const APIController = (function() {
2+
3+
const clientId = '';
4+
const clientSecret = '';
5+
6+
// private methods
7+
const _getToken = async () => {
8+
9+
const result = await fetch('https://accounts.spotify.com/api/token', {
10+
method: 'POST',
11+
headers: {
12+
'Content-Type' : 'application/x-www-form-urlencoded',
13+
'Authorization' : 'Basic ' + btoa(clientId + ':' + clientSecret)
14+
},
15+
body: 'grant_type=client_credentials'
16+
});
17+
18+
const data = await result.json();
19+
return data.access_token;
20+
}
21+
22+
const _getGenres = async (token) => {
23+
24+
const result = await fetch(`https://api.spotify.com/v1/browse/categories?locale=sv_US`, {
25+
method: 'GET',
26+
headers: { 'Authorization' : 'Bearer ' + token}
27+
});
28+
29+
const data = await result.json();
30+
return data.categories.items;
31+
}
32+
33+
const _getPlaylistByGenre = async (token, genreId) => {
34+
35+
const limit = 10;
36+
37+
const result = await fetch(`https://api.spotify.com/v1/browse/categories/${genreId}/playlists?limit=${limit}`, {
38+
method: 'GET',
39+
headers: { 'Authorization' : 'Bearer ' + token}
40+
});
41+
42+
const data = await result.json();
43+
return data.playlists.items;
44+
}
45+
46+
const _getTracks = async (token, tracksEndPoint) => {
47+
48+
const limit = 10;
49+
50+
const result = await fetch(`${tracksEndPoint}?limit=${limit}`, {
51+
method: 'GET',
52+
headers: { 'Authorization' : 'Bearer ' + token}
53+
});
54+
55+
const data = await result.json();
56+
return data.items;
57+
}
58+
59+
const _getTrack = async (token, trackEndPoint) => {
60+
61+
const result = await fetch(`${trackEndPoint}`, {
62+
method: 'GET',
63+
headers: { 'Authorization' : 'Bearer ' + token}
64+
});
65+
66+
const data = await result.json();
67+
return data;
68+
}
69+
70+
return {
71+
getToken() {
72+
return _getToken();
73+
},
74+
getGenres(token) {
75+
return _getGenres(token);
76+
},
77+
getPlaylistByGenre(token, genreId) {
78+
return _getPlaylistByGenre(token, genreId);
79+
},
80+
getTracks(token, tracksEndPoint) {
81+
return _getTracks(token, tracksEndPoint);
82+
},
83+
getTrack(token, trackEndPoint) {
84+
return _getTrack(token, trackEndPoint);
85+
}
86+
}
87+
})();
88+
89+
90+
// UI Module
91+
const UIController = (function() {
92+
93+
//object to hold references to html selectors
94+
const DOMElements = {
95+
selectGenre: '#select_genre',
96+
selectPlaylist: '#select_playlist',
97+
buttonSubmit: '#btn_submit',
98+
divSongDetail: '#song-detail',
99+
hfToken: '#hidden_token',
100+
divSonglist: '.song-list'
101+
}
102+
103+
//public methods
104+
return {
105+
106+
//method to get input fields
107+
inputField() {
108+
return {
109+
genre: document.querySelector(DOMElements.selectGenre),
110+
playlist: document.querySelector(DOMElements.selectPlaylist),
111+
tracks: document.querySelector(DOMElements.divSonglist),
112+
submit: document.querySelector(DOMElements.buttonSubmit),
113+
songDetail: document.querySelector(DOMElements.divSongDetail)
114+
}
115+
},
116+
117+
// need methods to create select list option
118+
createGenre(text, value) {
119+
const html = `<option value="${value}">${text}</option>`;
120+
document.querySelector(DOMElements.selectGenre).insertAdjacentHTML('beforeend', html);
121+
},
122+
123+
createPlaylist(text, value) {
124+
const html = `<option value="${value}">${text}</option>`;
125+
document.querySelector(DOMElements.selectPlaylist).insertAdjacentHTML('beforeend', html);
126+
},
127+
128+
// need method to create a track list group item
129+
createTrack(id, name) {
130+
const html = `<a href="#" class="list-group-item list-group-item-action list-group-item-light" id="${id}">${name}</a>`;
131+
document.querySelector(DOMElements.divSonglist).insertAdjacentHTML('beforeend', html);
132+
},
133+
134+
// need method to create the song detail
135+
createTrackDetail(img, title, artist) {
136+
137+
const detailDiv = document.querySelector(DOMElements.divSongDetail);
138+
// any time user clicks a new song, we need to clear out the song detail div
139+
detailDiv.innerHTML = '';
140+
141+
const html =
142+
`
143+
<div class="row col-sm-12 px-0">
144+
<img src="${img}" alt="">
145+
</div>
146+
<div class="row col-sm-12 px-0">
147+
<label for="Genre" class="form-label col-sm-12">${title}:</label>
148+
</div>
149+
<div class="row col-sm-12 px-0">
150+
<label for="artist" class="form-label col-sm-12">By ${artist}:</label>
151+
</div>
152+
`;
153+
154+
detailDiv.insertAdjacentHTML('beforeend', html)
155+
},
156+
157+
resetTrackDetail() {
158+
this.inputField().songDetail.innerHTML = '';
159+
},
160+
161+
resetTracks() {
162+
this.inputField().tracks.innerHTML = '';
163+
this.resetTrackDetail();
164+
},
165+
166+
resetPlaylist() {
167+
this.inputField().playlist.innerHTML = '';
168+
this.resetTracks();
169+
},
170+
171+
storeToken(value) {
172+
document.querySelector(DOMElements.hfToken).value = value;
173+
},
174+
175+
getStoredToken() {
176+
return {
177+
token: document.querySelector(DOMElements.hfToken).value
178+
}
179+
}
180+
}
181+
182+
})();
183+
184+
const APPController = (function(UICtrl, APICtrl) {
185+
186+
// get input field object ref
187+
const DOMInputs = UICtrl.inputField();
188+
189+
// get genres on page load
190+
const loadGenres = async () => {
191+
//get the token
192+
const token = await APICtrl.getToken();
193+
//store the token onto the page
194+
UICtrl.storeToken(token);
195+
//get the genres
196+
const genres = await APICtrl.getGenres(token);
197+
//populate our genres select element
198+
genres.forEach(element => UICtrl.createGenre(element.name, element.id));
199+
}
200+
201+
// create genre change event listener
202+
203+
204+
// create submit button click event listener
205+
DOMInputs.submit.addEventListener('click', async (e) => {
206+
// prevent page reset
207+
e.preventDefault();
208+
209+
});
210+
211+
// create song selection click event listener
212+
DOMInputs.tracks.addEventListener('click', async (e) => {
213+
// prevent page reset
214+
e.preventDefault();
215+
UICtrl.resetTrackDetail();
216+
// get the token
217+
const token = UICtrl.getStoredToken().token;
218+
// get the track endpoint
219+
const trackEndpoint = e.target.id;
220+
//get the track object
221+
const track = await APICtrl.getTrack(token, trackEndpoint);
222+
// load the track details
223+
UICtrl.createTrackDetail(track.album.images[2].url, track.name, track.artists[0].name);
224+
});
225+
226+
return {
227+
init() {
228+
console.log('App is starting');
229+
loadGenres();
230+
}
231+
}
232+
233+
})(UIController, APIController);
234+
235+
// will need to call a method to load the genres on page load
236+
APPController.init();
237+
238+
239+
240+

Diff for: index.html

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Spotify Web API Tutorial</title>
7+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
8+
<link rel="stylesheet" type="text/css" href="main.css">
9+
</head>
10+
<body>
11+
12+
<div class="container">
13+
<form action="">
14+
<input type="hidden" id='hidden_token'>
15+
<div class="col-sm-6 form-group row mt-4 px-0">
16+
<label for="Genre" class="form-label col-sm-2">Genre:</label>
17+
<select name="" id="select_genre" class="form-control form-control-sm col-sm-10">
18+
<option>Select...</option>
19+
</select>
20+
</div>
21+
<div class="col-sm-6 form-group row px-0">
22+
<label for="Genre" class="form-label col-sm-2">Playlists:</label>
23+
<select name="" id="select_playlist" class="form-control form-control-sm col-sm-10">
24+
<option>Select...</option>
25+
</select>
26+
</div>
27+
<div class="col-sm-6 row form-group px-0">
28+
<button type="submit" id="btn_submit" class="btn btn-success col-sm-12">Search</button>
29+
</div>
30+
</form>
31+
<div class="row">
32+
<div class="col-sm-6 px-0">
33+
<div class="list-group song-list">
34+
35+
</div>
36+
</div>
37+
<div class="offset-md-1 col-sm-4" id="song-detail">
38+
</div>
39+
</div>
40+
</div>
41+
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
42+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
43+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
44+
<script src="app.js" type="text/javascript"></script>
45+
</body>
46+
</html>

Diff for: main.css

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.btn.dropdown-toggle {
2+
width:150px;
3+
text-align:left;
4+
}
5+
6+
span.caret {
7+
position: absolute;
8+
left: 90%;
9+
top: 45%;
10+
}
11+
12+
label {
13+
padding-top:5px;
14+
}
15+
16+
.form-label {
17+
padding-left:0px !important;
18+
}
19+
20+
img {
21+
width:140px;
22+
height:140px;
23+
}
24+
25+
.btn-success {
26+
background-color: #3b7d4a!important;
27+
}
28+
29+
.btn-success:hover {
30+
background-color: #4daf64!important;
31+
}
32+
33+
img.track {
34+
border: 1px solid #8a8a8a;
35+
}

Diff for: testImage.jpg

37.8 KB
Loading

0 commit comments

Comments
 (0)