Merge branch 'master' of github.com:jaedb/Iris
This commit is contained in:
33
src/js/components/GridSlider.js
Executable file
33
src/js/components/GridSlider.js
Executable file
@ -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 (
|
||||
<div className={className}>
|
||||
<div className="grid artist-grid liner">
|
||||
{
|
||||
this.props.tracks.map(
|
||||
(track, index) => {
|
||||
var item = Object.assign({}, track.album, { artists: track.artists })
|
||||
return <GridItem item={item} key={index} link={global.baseURL+'album/'+track.album.uri} />
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) ============
|
||||
* ======================================================================================
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 (
|
||||
<section className="recommendations">
|
||||
{
|
||||
this.props.discover.map(
|
||||
(discover, index) => {
|
||||
return (
|
||||
<div className="grid-slider-wrapper" key={index}>
|
||||
<h4>Because you listened to <ArtistSentence artists={discover.seed.artists} /></h4>
|
||||
<GridSlider tracks={discover.tracks} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div className="view discover-view">
|
||||
<Header icon="compass" title="Discover" />
|
||||
<h1>To come</h1>
|
||||
{ this.renderRecommendations() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -33,7 +60,8 @@ class Discover extends React.Component{
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
|
||||
authorized: state.spotify.authorized,
|
||||
discover: state.spotify.discover
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
24
src/scss/components/_grid-slider.scss
Executable file
24
src/scss/components/_grid-slider.scss
Executable file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/scss/views/_discover.scss
Executable file
6
src/scss/views/_discover.scss
Executable file
@ -0,0 +1,6 @@
|
||||
|
||||
.discover-view {
|
||||
section {
|
||||
padding: 40px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user