Discover moved to recommendations; URL handles context loading

This commit is contained in:
James Barnsley
2017-06-02 09:31:05 +12:00
parent b5f74e119f
commit a2d326563d
10 changed files with 214 additions and 43 deletions

View File

@ -6,6 +6,7 @@ import { Link, hashHistory } from 'react-router'
import ArtistSentence from './ArtistSentence'
import * as helpers from '../helpers'
import * as uiActions from '../services/ui/actions'
import * as spotifyActions from '../services/spotify/actions'
class AddSeedField extends React.Component{
@ -59,8 +60,20 @@ class AddSeedField extends React.Component{
handleSelect(e,item){
this.setState({value: ''})
this.props.onSelect(e,item)
this.props.onSelect(e,item.uri)
this.props.spotifyActions.clearAutocompleteResults(this.id)
// Add our selected item to our global index
switch (helpers.uriType(item.uri)){
case 'artist':
this.props.uiActions.albumLoaded(item.uri,item)
break
case 'track':
this.props.uiActions.trackLoaded(item.uri,item)
break
}
}
results(){
@ -123,13 +136,14 @@ class AddSeedField extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
genres: (state.spotify.genres ? state.spotify.genres : null),
genres: (state.ui.genres ? state.ui.genres : null),
results: (state.spotify.autocomplete_results ? state.spotify.autocomplete_results : {})
}
}
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)
}
}

View File

@ -194,6 +194,11 @@ class ContextMenu extends React.Component{
this.props.pusherActions.startRadio(this.props.menu.uris)
}
goToRecommendations(e){
this.props.uiActions.hideContextMenu()
hashHistory.push( global.baseURL +'discover/recommendations/'+ this.props.menu.items[0].uri )
}
goToArtist(e){
if (!this.props.menu.items || this.props.menu.items.length <= 0 || !this.props.menu.items[0].artists || this.props.menu.items[0].artists.length <= 0){
return null
@ -399,6 +404,14 @@ class ContextMenu extends React.Component{
</span>
)
var go_to_recommendations = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.goToRecommendations(e)}>
<span className="label">More like this</span>
</a>
</span>
)
var start_radio = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.startRadio(e)}>
@ -451,6 +464,7 @@ class ContextMenu extends React.Component{
<div>
{start_radio}
{this.canBeInLibrary() ? toggle_in_library : null}
{go_to_recommendations}
{copy_uris}
</div>
)

View File

@ -39,7 +39,7 @@ class Sidebar extends React.Component{
<section>
<title>Discover</title>
<Link activeClassName="active" to={global.baseURL+"discover"}>
<Link activeClassName="active" to={global.baseURL+"discover/recommendations"}>
<Icon name="compass" className="white" />
Discover
</Link>

View File

@ -23,7 +23,7 @@ import Settings from './views/Settings'
import Debug from './views/Debug'
import Search from './views/Search'
import Discover from './views/discover/Discover'
import DiscoverRecommendations from './views/discover/DiscoverRecommendations'
import DiscoverFeatured from './views/discover/DiscoverFeatured'
import DiscoverCategories from './views/discover/DiscoverCategories'
import DiscoverCategory from './views/discover/DiscoverCategory'
@ -66,7 +66,7 @@ ReactDOM.render(
<Route path="playlist/:uri" component={Playlist} />
<Route path="user/:uri" component={User} />
<Route path="discover" component={Discover} />
<Route path="discover/recommendations(/:seeds)" component={DiscoverRecommendations} />
<Route path="discover/featured" component={DiscoverFeatured} />
<Route path="discover/categories" component={DiscoverCategories} />
<Route path="discover/categories/:id" component={DiscoverCategory} />

View File

@ -498,14 +498,16 @@ export function getAutocompleteResults(field_id, query, types = ['album','artist
.then(response => {
var genres = []
if (genre_included){
var available_genres = getState().spotify.genres
for (var i = 0; i < available_genres.length; i++){
if (available_genres[i].includes(query)){
var genre = available_genres[i]
genres.push({
name: (genre.charAt(0).toUpperCase()+genre.slice(1)).replace('-',' '),
uri: 'spotify:genre:'+genre
})
var available_genres = getState().ui.genres
if (available_genres){
for (var i = 0; i < available_genres.length; i++){
if (available_genres[i].includes(query)){
var genre = available_genres[i]
genres.push({
name: (genre.charAt(0).toUpperCase()+genre.slice(1)).replace('-',' '),
uri: 'spotify:genre:'+genre
})
}
}
}
}

View File

@ -273,6 +273,32 @@ const SpotifyMiddleware = (function(){
});
break
case 'SPOTIFY_FAVORITES_LOADED':
if (action.artists.length > 0){
store.dispatch({
type: 'ARTISTS_LOADED',
artists: action.artists
})
action.artists_uris = helpers.asURIs(action.artists)
}
if (action.tracks.length > 0){
store.dispatch({
type: 'TRACKS_LOADED',
tracks: action.tracks
})
action.tracks_uris = helpers.asURIs(action.tracks)
}
next(action)
break
case 'SPOTIFY_TRACK_LOADED':
store.dispatch({
type: 'TRACK_LOADED',
key: action.data.uri,
track: action.data
});
break
case 'SPOTIFY_SEARCH_RESULTS_LOADED_MORE_TRACKS':
store.dispatch({
type: 'SEARCH_RESULTS_LOADED',

View File

@ -1,4 +1,6 @@
import * as helpers from '../../helpers'
export default function reducer(spotify = {}, action){
switch (action.type) {
@ -179,9 +181,6 @@ export default function reducer(spotify = {}, action){
if( !action.data ) return Object.assign({}, spotify, { discover: [] })
return Object.assign({}, spotify, { discover: [...spotify.discover, ...[action.data]] })
case 'SPOTIFY_GENRES_LOADED':
return Object.assign({}, spotify, {genres: action.genres})
case 'SPOTIFY_RECOMMENDATIONS_LOADED':
return Object.assign(
{},
@ -199,8 +198,8 @@ export default function reducer(spotify = {}, action){
{},
spotify,
{
favorite_artists: action.artists,
favorite_tracks: action.tracks
favorite_artists: action.artists_uris,
favorite_tracks: action.tracks_uris
})
case 'SPOTIFY_AUTOCOMPLETE_LOADING':

View File

@ -210,6 +210,57 @@ export function addTracksToPlaylist( uri, tracks_uris ){
}
/**
* Assets loaded
**/
export function albumLoaded(key,album){
return {
type: 'ALBUM_LOADED',
key: key,
album: album
}
}
export function albumsLoaded(albums){
return {
type: 'ALBUMS_LOADED',
albums: albums
}
}
export function artistLoaded(key,artist){
return {
type: 'ARTIST_LOADED',
key: key,
artist: artist
}
}
export function artistsLoaded(artists){
return {
type: 'ALBUMS_LOADED',
artists: artists
}
}
export function trackLoaded(key,track){
return {
type: 'TRACK_LOADED',
key: key,
track: track
}
}
export function tracksLoaded(tracks){
return {
type: 'TRACKS_LOADED',
tracks: tracks
}
}
/**
* Modal
*
@ -268,6 +319,7 @@ export function removeNotification(key){
}
/**
* Loaders
**/

View File

@ -105,12 +105,6 @@ export default function reducer(ui = {}, action){
return Object.assign({}, ui, { current_tracklist: tracklist });
case 'SPOTIFY_TRACK_LOADED':
if( !action.data ) return ui
var current_track = Object.assign({}, ui.current_track, action.data)
return Object.assign({}, ui, { current_track: current_track });
case 'MOPIDY_CURRENTTLTRACK':
if( !action.data ) return ui
@ -605,6 +599,14 @@ export default function reducer(ui = {}, action){
return Object.assign({}, ui, { library_playlists: items });
/**
* Genres
**/
case 'SPOTIFY_GENRES_LOADED':
return Object.assign({}, ui, {genres: action.genres})
/**
* Search results
**/

View File

@ -23,10 +23,7 @@ class Discover extends React.Component{
this.state = {
seeds: [
{
uri: 'spotify:genre:chill',
name: 'Chill'
}
'spotify:genre:chill'
],
add_seed: '',
adding_seed: false
@ -34,10 +31,36 @@ class Discover extends React.Component{
}
componentDidMount(){
if (this.props.authorized){
this.props.spotifyActions.getFavorites()
// We have seeds provided in the URL
if (this.props.params.seeds){
var seeds = this.props.params.seeds.split(',')
// TODO: lookup these seeds
for (var i = 0; i < seeds.length; i++){
switch (helpers.uriType(seeds[i])){
case 'artist':
this.props.spotifyActions.getArtist(seeds[i])
break
case 'track':
this.props.spotifyActions.getTrack(seeds[i])
break
}
}
this.setState({seeds: seeds})
this.getRecommendations(seeds)
// BAU
} else {
this.getRecommendations()
if (this.props.authorized){
this.props.spotifyActions.getFavorites()
} else {
this.getRecommendations()
}
}
}
@ -57,8 +80,7 @@ class Discover extends React.Component{
getRecommendations(seeds = this.state.seeds){
if (seeds.length > 0){
var uris = helpers.asURIs(seeds)
this.props.spotifyActions.getRecommendations(uris, 50)
this.props.spotifyActions.getRecommendations(seeds, 50)
}
}
@ -69,32 +91,70 @@ class Discover extends React.Component{
this.getRecommendations(seeds)
}
handleSelect(e,item){
handleSelect(e,uri){
var seeds = this.state.seeds
seeds.push(item)
seeds.push(uri)
this.setState({seeds: seeds})
this.getRecommendations(seeds)
}
renderSeeds(){
var seeds_objects = []
if (this.state.seeds.length > 0){
for (var i = 0; i < this.state.seeds.length; i++){
var uri = this.state.seeds[i]
switch (helpers.uriType(uri)){
case 'track':
if (typeof(this.props.tracks[uri]) !== 'undefined'){
seeds_objects.push(this.props.tracks[uri])
} else {
seeds_objects.push({
name: 'Loading...',
uri: uri
})
}
break
case 'artist':
if (typeof(this.props.artists[uri]) !== 'undefined'){
seeds_objects.push(this.props.artists[uri])
} else {
seeds_objects.push({
name: 'Loading...',
uri: uri
})
}
break
case 'genre':
var name = helpers.getFromUri('genreid',uri)
seeds_objects.push({
name: (name.charAt(0).toUpperCase() + name.slice(1)).replace('-',' '),
uri: uri
})
break
}
}
}
return (
<div className="seeds">
{
this.state.seeds.map((seed,index) => {
seeds_objects.map((seed,index) => {
var type = helpers.uriType(seed.uri)
if (!type){
type = 'genre'
}
return (
<span className="seed" key={seed.uri}>
{seed.name}
<span className="type">({type})</span>
<FontAwesome name="close" className="remove" onClick={() => this.removeSeed(index)} />
<FontAwesome name="close" className="remove" onClick={() => this.removeSeed(seed.uri)} />
</span>
)
})
}
<AddSeedField onSelect={(e,item) => this.handleSelect(e,item)} />
<AddSeedField onSelect={(e,uri) => this.handleSelect(e,uri)} />
</div>
)
}
@ -180,8 +240,10 @@ class Discover extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
albums: state.ui.albums,
artists: state.ui.artists,
albums: (state.ui.albums ? state.ui.albums : []),
artists: (state.ui.artists ? state.ui.artists : []),
tracks: (state.ui.tracks ? state.ui.tracks : []),
genres: (state.ui.genres ? state.ui.genres : []),
authorized: state.spotify.authorized,
load_queue: state.ui.load_queue,
quick_search_results: (state.spotify.quick_search_results ? state.spotify.quick_search_results : {artists: [], tracks: []}),