From 84ad006105a96c6e08900d38555ef86d42252af9 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Wed, 11 Jan 2017 13:52:55 +1300 Subject: [PATCH] Discover view (requires dynamic height and responsive) --- src/js/components/GridSlider.js | 33 ++++++++++++ src/js/services/spotify/actions.js | 75 +++++++++++++++++++++++---- src/js/services/spotify/reducer.js | 4 ++ src/js/views/discover/Discover.js | 32 +++++++++++- src/scss/app.scss | 2 + src/scss/components/_grid-slider.scss | 24 +++++++++ src/scss/views/_discover.scss | 6 +++ 7 files changed, 165 insertions(+), 11 deletions(-) create mode 100755 src/js/components/GridSlider.js create mode 100755 src/scss/components/_grid-slider.scss create mode 100755 src/scss/views/_discover.scss diff --git a/src/js/components/GridSlider.js b/src/js/components/GridSlider.js new file mode 100755 index 00000000..ee7632b6 --- /dev/null +++ b/src/js/components/GridSlider.js @@ -0,0 +1,33 @@ + +import React, { PropTypes } from 'react' +import GridItem from './GridItem' + +export default class GridSlider extends React.Component{ + + constructor(props) { + super(props); + } + + render(){ + if( this.props.tracks ){ + var className = "grid-slider" + if( this.props.className ) className += ' '+this.props.className + return ( +
+
+ { + this.props.tracks.map( + (track, index) => { + var item = Object.assign({}, track.album, { artists: track.artists }) + return + } + ) + } +
+
+ ); + } + return null; + } +} + diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 9dc1a77c..11ca1f07 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -16,18 +16,24 @@ const sendRequest = ( dispatch, getState, endpoint, method = 'GET', data = false getToken( dispatch, getState ) .then( response => { + // prepend the API baseurl, unless the endpoint already has it (ie pagination requests) var url = 'https://api.spotify.com/v1/'+endpoint if( endpoint.startsWith('https://api.spotify.com/') ) url = endpoint; - $.ajax({ - method: method, - cache: true, - url: url, - headers: { - Authorization: 'Bearer '+ response - }, - data: JSON.stringify(data) - }).then( + // create our ajax request config + var config = { + method: method, + url: url, + cached: true, + headers: { + Authorization: 'Bearer '+ response + } + } + + // only if we've got data do we add it to the request (this prevents appending of "&false" to the URL) + if (data) config.data = JSON.stringify(data) + + $.ajax(config).then( response => { resolve(response) }, @@ -477,6 +483,57 @@ export function resolveRadioSeeds( radio ){ } +/** + * =============================================================== DISCOVER ============= + * ====================================================================================== + **/ + + +/** + * Get our recommendations + * This is based off our 'favorites' and then we use those as seeds + * + * @param uri string + **/ +export function getDiscover(){ + return (dispatch, getState) => { + + dispatch({ type: 'SPOTIFY_DISCOVER_LOADED', data: false }) + + // get favorite tracks + sendRequest( dispatch, getState, 'me/top/tracks?limit=50&time_range=long_term' ) + .then( response => { + + var favorite_tracks_uris = helpers.asURIs(response.items) + favorite_tracks_uris = favorite_tracks_uris.sort(() => .5 - Math.random()) + favorite_tracks_uris = favorite_tracks_uris.slice(0,10) + + for (var i = 0; i < favorite_tracks_uris.length; i++){ + + var seed_id = helpers.getFromUri('trackid',favorite_tracks_uris[i] ) + + $.when( + + sendRequest( dispatch, getState, 'recommendations?seed_tracks='+seed_id ), + sendRequest( dispatch, getState, 'tracks/'+seed_id ) + + ).then( ( tracks_response, seed_response ) => { + dispatch({ + type: 'SPOTIFY_DISCOVER_LOADED', + data: { + tracks: tracks_response.tracks, + seed: seed_response + } + }); + }); + } + }) + } +} + + + + /** * =============================================================== ARTIST(S) ============ * ====================================================================================== diff --git a/src/js/services/spotify/reducer.js b/src/js/services/spotify/reducer.js index 5ec46a0b..cd91ae27 100755 --- a/src/js/services/spotify/reducer.js +++ b/src/js/services/spotify/reducer.js @@ -178,6 +178,10 @@ export default function reducer(spotify = {}, action){ items: [ ...spotify.new_releases.items, ...action.data.albums.items ] }}) + case 'SPOTIFY_DISCOVER_LOADED': + if( !action.data ) return Object.assign({}, spotify, { discover: [] }) + return Object.assign({}, spotify, { discover: [...spotify.discover, ...[action.data]] }) + default: return spotify } diff --git a/src/js/views/discover/Discover.js b/src/js/views/discover/Discover.js index f08fe007..d8841978 100755 --- a/src/js/views/discover/Discover.js +++ b/src/js/views/discover/Discover.js @@ -4,6 +4,8 @@ import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import Header from '../../components/Header' +import GridSlider from '../../components/GridSlider' +import ArtistSentence from '../../components/ArtistSentence' import * as helpers from '../../helpers' import * as spotifyActions from '../../services/spotify/actions' @@ -14,11 +16,36 @@ class Discover extends React.Component{ super(props); } + componentDidMount(){ + this.props.spotifyActions.getDiscover() + } + + renderRecommendations(){ + if (!this.props.discover || this.props.discover.length <= 0) return null + + return ( +
+ { + this.props.discover.map( + (discover, index) => { + return ( +
+

Because you listened to

+ +
+ ) + } + ) + } +
+ ) + } + render(){ return (
-

To come

+ { this.renderRecommendations() }
) } @@ -33,7 +60,8 @@ class Discover extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - + authorized: state.spotify.authorized, + discover: state.spotify.discover } } diff --git a/src/scss/app.scss b/src/scss/app.scss index bde4c461..9b71437f 100755 --- a/src/scss/app.scss +++ b/src/scss/app.scss @@ -18,6 +18,7 @@ @import 'components/sidebar-toggle-button'; @import 'components/lists'; @import 'components/grid'; +@import 'components/grid-slider'; @import 'components/header'; @import 'components/notifications'; @import 'components/dropdown-field'; @@ -29,5 +30,6 @@ @import 'views/queue'; @import 'views/search'; @import 'views/library'; +@import 'views/discover'; @import 'views/settings'; diff --git a/src/scss/components/_grid-slider.scss b/src/scss/components/_grid-slider.scss new file mode 100755 index 00000000..629b8bb8 --- /dev/null +++ b/src/scss/components/_grid-slider.scss @@ -0,0 +1,24 @@ + +.grid-slider-wrapper { + padding-bottom: 40px; + + .grid-slider { + position: relative; + overflow-x: scroll; + overflow-y: hidden; + height: 220px; + + .liner { + position: absolute; + top: 0; + left: 0; + width: 400%; + box-sizing: border-box; + + .grid-item { + width: 4%; + margin: 0 0.5% !important; + } + } + } +} \ No newline at end of file diff --git a/src/scss/views/_discover.scss b/src/scss/views/_discover.scss new file mode 100755 index 00000000..b27a9826 --- /dev/null +++ b/src/scss/views/_discover.scss @@ -0,0 +1,6 @@ + +.discover-view { + section { + padding: 40px; + } +} \ No newline at end of file