React slider

This commit is contained in:
James Barnsley
2018-01-11 16:46:57 +13:00
parent 2393551229
commit 59e751eea9
12 changed files with 4280 additions and 1511 deletions

View File

@ -108,7 +108,7 @@ class ContextMenu extends React.Component{
case 'playlist':
case 'editable-playlist':
context.nice_name = 'playlist';
break
break;
case 'track':
case 'queue-track':
@ -116,7 +116,7 @@ class ContextMenu extends React.Component{
case 'editable-playlist-track':
context.nice_name = 'track';
context.is_track = true;
break
break;
}
// Consider the object(s) themselves
@ -138,21 +138,21 @@ class ContextMenu extends React.Component{
inLibrary(item = null){
if (!item){
return false
return false;
}
switch (helpers.uriType(item.uri)){
case 'artist':
return (this.props.spotify_library_artists && this.props.spotify_library_artists.indexOf(item.uri) > -1)
break
return (this.props.spotify_library_artists && this.props.spotify_library_artists.indexOf(item.uri) > -1);
break;
case 'album':
return (this.props.spotify_library_albums && this.props.spotify_library_albums.indexOf(item.uri) > -1)
break
return (this.props.spotify_library_albums && this.props.spotify_library_albums.indexOf(item.uri) > -1);
break;
case 'playlist':
return (this.props.spotify_library_playlists && this.props.spotify_library_playlists.indexOf(item.uri) > -1)
break
return (this.props.spotify_library_playlists && this.props.spotify_library_playlists.indexOf(item.uri) > -1);
break;
}
return false
return false;
}
/**
@ -191,7 +191,7 @@ class ContextMenu extends React.Component{
playQueueItem(e){
this.props.uiActions.hideContextMenu()
var tracks = this.props.menu.items;
this.props.mopidyActions.changeTrack(tracks[0].tlid )
this.props.mopidyActions.changeTrack(tracks[0].tlid);
}
removeFromQueue(e){
@ -201,102 +201,102 @@ class ContextMenu extends React.Component{
for(var i = 0; i < tracks.length; i++){
tracks_tlids.push(tracks[i].tlid );
}
this.props.mopidyActions.removeTracks(tracks_tlids )
this.props.mopidyActions.removeTracks(tracks_tlids);
}
playURIs(e){
this.props.uiActions.hideContextMenu()
this.props.mopidyActions.playURIs(this.props.menu.uris, this.props.menu.tracklist_uri)
this.props.uiActions.hideContextMenu();
this.props.mopidyActions.playURIs(this.props.menu.uris, this.props.menu.tracklist_uri);
}
playPlaylist(e){
this.props.uiActions.hideContextMenu()
this.props.mopidyActions.playPlaylist(this.props.menu.uris[0])
this.props.uiActions.hideContextMenu();
this.props.mopidyActions.playPlaylist(this.props.menu.uris[0]);
}
playArtistTopTracks(e){
this.props.uiActions.hideContextMenu()
this.props.spotifyActions.playArtistTopTracks(this.props.menu.uris[0])
this.props.uiActions.hideContextMenu();
this.props.spotifyActions.playArtistTopTracks(this.props.menu.uris[0]);
}
addToQueue(e, next = false){
this.props.uiActions.hideContextMenu()
this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri, next)
this.props.uiActions.hideContextMenu();
this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri, next);
}
addTracksToPlaylist(e, playlist_uri){
this.props.uiActions.hideContextMenu()
this.props.coreActions.addTracksToPlaylist(playlist_uri, this.props.menu.uris)
this.props.uiActions.hideContextMenu();
this.props.coreActions.addTracksToPlaylist(playlist_uri, this.props.menu.uris);
}
toggleLoved(e, is_loved){
this.props.uiActions.hideContextMenu()
this.props.uiActions.hideContextMenu();
if (is_loved){
this.props.lastfmActions.unloveTrack(this.props.menu.items[0].uri)
this.props.lastfmActions.unloveTrack(this.props.menu.items[0].uri);
} else {
this.props.lastfmActions.loveTrack(this.props.menu.items[0].uri)
this.props.lastfmActions.loveTrack(this.props.menu.items[0].uri);
}
}
unloveTrack(e){
this.props.uiActions.hideContextMenu()
this.props.lastfmActions.unloveTrack(this.props.menu.items[0])
this.props.uiActions.hideContextMenu();
this.props.lastfmActions.unloveTrack(this.props.menu.items[0]);
}
removeFromPlaylist(e){
this.props.uiActions.hideContextMenu()
this.props.coreActions.removeTracksFromPlaylist(this.props.menu.tracklist_uri, this.props.menu.indexes)
this.props.uiActions.hideContextMenu();
this.props.coreActions.removeTracksFromPlaylist(this.props.menu.tracklist_uri, this.props.menu.indexes);
}
deletePlaylist(e){
this.props.uiActions.hideContextMenu()
this.props.coreActions.deletePlaylist(this.props.menu.uris[0])
this.props.uiActions.hideContextMenu();
this.props.coreActions.deletePlaylist(this.props.menu.uris[0]);
}
startRadio(e){
this.props.uiActions.hideContextMenu()
this.props.pusherActions.startRadio(this.props.menu.uris)
this.props.uiActions.hideContextMenu();
this.props.pusherActions.startRadio(this.props.menu.uris);
}
goToRecommendations(e){
this.props.uiActions.hideContextMenu()
var uris_string = helpers.arrayOf('uri',this.props.menu.items).join(',')
hashHistory.push(global.baseURL +'discover/recommendations/'+ uris_string )
this.props.uiActions.hideContextMenu();
var uris_string = helpers.arrayOf('uri',this.props.menu.items).join(',');
hashHistory.push(global.baseURL +'discover/recommendations/'+ uris_string );
}
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
return null;
} else {
this.props.uiActions.hideContextMenu()
hashHistory.push(global.baseURL +'artist/'+ this.props.menu.items[0].artists[0].uri )
this.props.uiActions.hideContextMenu();
hashHistory.push(global.baseURL +'artist/'+ this.props.menu.items[0].artists[0].uri);
}
}
goToAlbum(e){
if (!this.props.menu.items || this.props.menu.items.length <= 0 || !this.props.menu.items[0].album){
return null
return null;
} else {
this.props.uiActions.hideContextMenu()
hashHistory.push(global.baseURL +'album/'+ this.props.menu.items[0].album.uri )
this.props.uiActions.hideContextMenu();
hashHistory.push(global.baseURL +'album/'+ this.props.menu.items[0].album.uri);
}
}
goToUser(e){
if (!this.props.menu.items || this.props.menu.items.length <= 0){
return null
return null;
} else {
this.props.uiActions.hideContextMenu()
hashHistory.push(global.baseURL +'user/'+ this.props.menu.items[0].owner.uri )
this.props.uiActions.hideContextMenu();
hashHistory.push(global.baseURL +'user/'+ this.props.menu.items[0].owner.uri);
}
}
goToTrack(e){
if (!this.props.menu.items || this.props.menu.items.length <= 0){
return null
return null;
} else {
this.props.uiActions.hideContextMenu()
hashHistory.push(global.baseURL +'track/'+ encodeURIComponent(this.props.menu.items[0].uri))
this.props.uiActions.hideContextMenu();
hashHistory.push(global.baseURL +'track/'+ encodeURIComponent(this.props.menu.items[0].uri));
}
}

View File

@ -923,7 +923,7 @@ export function getRecommendations(uris = [], limit = 20){
// anyway so we can proceed in the meantime
var artists_uris = []
if (tracks.length > artists_ids.length && tracks.length > 10){
while (artists_uris.length < 5){
while (artists_uris.length < 6){
var random_index = Math.round(Math.random() * (tracks.length - 1))
var artist = tracks[random_index].artists[0]
@ -940,7 +940,7 @@ export function getRecommendations(uris = [], limit = 20){
var albums = []
var albums_uris = []
if (tracks.length > 10){
while (albums.length < 5){
while (albums.length < 6){
var random_index = Math.round(Math.random() * (tracks.length - 1))
var album = tracks[random_index].album

View File

@ -3,6 +3,7 @@ import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
import InputRange from 'react-input-range';
import Header from '../../components/Header'
import ArtistSentence from '../../components/ArtistSentence'
@ -12,8 +13,12 @@ import TrackList from '../../components/TrackList'
import Parallax from '../../components/Parallax'
import AddSeedField from '../../components/AddSeedField'
import URILink from '../../components/URILink'
import ContextMenuTrigger from '../../components/ContextMenuTrigger'
import RelatedArtists from '../../components/RelatedArtists'
import * as helpers from '../../helpers'
import * as uiActions from '../../services/ui/actions'
import * as mopidyActions from '../../services/mopidy/actions'
import * as spotifyActions from '../../services/spotify/actions'
class Discover extends React.Component{
@ -24,11 +29,15 @@ class Discover extends React.Component{
this._autocomplete_timer = false
this.state = {
seeds: [
'spotify:genre:chill'
],
add_seed: '',
adding_seed: false
adding_seed: false,
seeds: [],
tunabilities: {
loudness: {
min: 0,
max: 100
}
}
}
}
@ -36,12 +45,8 @@ class Discover extends React.Component{
// We have seeds provided in the URL
if (this.props.params.seeds){
this.handleURLSeeds(this.props.params.seeds)
// BAU
} else {
this.props.spotifyActions.getFavorites()
}
this.handleURLSeeds(this.props.params.seeds);
}
}
componentWillReceiveProps(newProps, newState){
@ -50,17 +55,38 @@ class Discover extends React.Component{
if (newProps.params.seeds != this.props.params.seeds){
this.handleURLSeeds(newProps.params.seeds)
}
}
// When we've loaded favorite_artists.
// This indirectly listens for when the action has
// loaded new data.
if (this.props.favorite_artists.length <= 0 && newProps.favorite_artists.length > 0){
var initial_seeds = newProps.favorite_artists.sort(() => .5 - Math.random())
initial_seeds = initial_seeds.slice(0,2)
handleContextMenu(e){
this.setState({seeds: initial_seeds})
this.getRecommendations(initial_seeds)
var uri = 'iris:discover';
if (this.state.seeds){
uri += ':';
for (var i = 0; i < this.state.seeds.length; i++){
if (i > 0){
uri += ',';
}
uri += this.state.seeds[i].split(':').join('_');
}
}
var tracks = [];
if (this.props.recommendations.tracks_uris && this.props.tracks){
for (var i = 0; i < this.props.recommendations.tracks_uris.length; i++){
var uri = this.props.recommendations.tracks_uris[i];
if (this.props.tracks.hasOwnProperty(uri)){
tracks.push(this.props.tracks[uri]);
}
}
}
var data = {
e: e,
context: 'track',
items: tracks,
uris: [uri]
}
this.props.uiActions.showContextMenu(data);
}
handleURLSeeds(seeds_string = this.props.params.seeds){
@ -87,12 +113,16 @@ class Discover extends React.Component{
this.getRecommendations(seeds)
}
getRecommendations(seeds = this.state.seeds){
getRecommendations(seeds = this.state.seeds, tunabilities = this.state.tunabilities){
if (seeds.length > 0){
this.props.spotifyActions.getRecommendations(seeds, 50)
this.props.spotifyActions.getRecommendations(seeds, 50, tunabilities)
}
}
playTracks(){
this.props.mopidyActions.playURIs(this.props.recommendations.tracks_uris);
}
removeSeed(index){
var seeds = this.state.seeds
seeds.splice(index,1)
@ -168,6 +198,49 @@ class Discover extends React.Component{
)
}
setTunability(name, value){
var tunabilities = this.state.tunabilities;
tunabilities[name] = value;
console.log(tunabilities);
this.setState({ tunabilities: tunabilities });
}
renderTunabilities(){
var tunabilities = [];
for (var key in this.state.tunabilities){
if (this.state.tunabilities.hasOwnProperty(key)){
var tunability = {
name: key,
value: this.state.tunabilities[key]
};
tunabilities.push(tunability);
}
}
return (
<div className="tunabilities">
{
tunabilities.map(tunability => {
return (
<div className="field tunability range" key={tunability.name}>
<label>
{tunability.name}
</label>
<InputRange
maxValue={100}
minValue={0}
value={tunability.value}
onChange={value => this.setTunability(tunability.name, value)}
/>
</div>
);
})
}
</div>
);
}
renderResults(){
if (helpers.isLoading(this.props.load_queue,['spotify_recommendations'])){
return (
@ -177,16 +250,16 @@ class Discover extends React.Component{
)
}
if (!this.props.recommendations || typeof(this.props.recommendations.albums_uris) === 'undefined' || typeof(this.props.recommendations.artists_uris) === 'undefined'){
if (!this.props.recommendations || this.props.recommendations.albums_uris === undefined || this.props.recommendations.artists_uris === undefined){
return null;
}
var albums = [];
if (this.props.recommendations.albums_uris && this.props.albums){
for (var i = 0; i < this.props.recommendations.albums_uris.length; i++){
var uri = this.props.recommendations.albums_uris[i];
if (this.props.albums.hasOwnProperty(uri)){
albums.push(this.props.albums[uri]);
var tracks = [];
if (this.props.recommendations.tracks_uris && this.props.tracks){
for (var i = 0; i < this.props.recommendations.tracks_uris.length; i++){
var uri = this.props.recommendations.tracks_uris[i];
if (this.props.tracks.hasOwnProperty(uri)){
tracks.push(this.props.tracks[uri]);
}
}
}
@ -201,35 +274,54 @@ class Discover extends React.Component{
}
}
var tracks = [];
if (this.props.recommendations.tracks_uris && this.props.tracks){
for (var i = 0; i < this.props.recommendations.tracks_uris.length; i++){
var uri = this.props.recommendations.tracks_uris[i];
if (this.props.tracks.hasOwnProperty(uri)){
tracks.push(this.props.tracks[uri]);
var albums = [];
if (this.props.recommendations.albums_uris && this.props.albums){
for (var i = 0; i < this.props.recommendations.albums_uris.length; i++){
var uri = this.props.recommendations.albums_uris[i];
if (this.props.albums.hasOwnProperty(uri)){
albums.push(this.props.albums[uri]);
}
}
}
var uri = 'iris:discover';
if (this.props.params.seeds){
uri += ':'+this.props.params.seeds.split(':').join('_');
if (this.state.seeds){
uri += ':';
for (var i = 0; i < this.state.seeds.length; i++){
if (i > 0){
uri += ',';
}
uri += this.state.seeds[i].split(':').join('_');
}
}
return (
<div className="content-wrapper recommendations-results">
<section>
<h4>Artists</h4>
<ArtistGrid single_row artists={artists} />
</section>
<section>
<h4>Albums</h4>
<AlbumGrid single_row albums={albums} />
</section>
<section>
<h4>Tracks</h4>
<div className="content-wrapper recommendations-results cf">
<section className="col w70">
<h4>
Tracks
<ContextMenuTrigger onTrigger={e => this.handleContextMenu(e)} />
<button className="primary pull-right" onClick={e => this.playTracks(e)}>Play all</button>
</h4>
<TrackList className="discover-track-list" uri={uri} tracks={tracks} />
</section>
<div className="col w5"></div>
<div className="col w25">
<section>
<h4>Artists</h4>
<RelatedArtists artists={artists} />
</section>
<br />
<br />
<section>
<h4>Albums</h4>
<AlbumGrid className="mini" albums={albums} />
</section>
</div>
</div>
)
}
@ -244,10 +336,10 @@ class Discover extends React.Component{
<div className="liner">
<h1>Explore new music</h1>
<h2 className="grey-text">
Add seeds below to build your sound
{!this.props.params.seeds ? ". Let's start with two of your favorite artists." : null}
Add seeds and musical properties below to build your sound
</h2>
{this.renderSeeds()}
{this.renderTunabilities()}
</div>
</div>
@ -283,6 +375,8 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)
}
}

View File

@ -38,3 +38,4 @@
@import 'views/discover';
@import 'views/settings';
@import "../../node_modules/react-input-range/lib/css/index.css";

View File

@ -291,6 +291,11 @@
border-radius: 3px;
text-align: center;
h4 &,
h4 &:active {
margin: -8px 0 0 10px;
}
.dot {
width: 6px;
height: 6px;

View File

@ -75,7 +75,7 @@
@include responsive(null, 1900px){
&.mini {
.grid-item {
@include grid_item(3 )
@include grid_item(3)
}
&.single-row {
.grid-item:nth-child(1n+4){

View File

@ -99,6 +99,15 @@ input[type="submit"] {
margin-bottom: 9px;
}
&.small {
padding: 6px 18px 4px;
}
&.pull-right {
margin-right: 0;
margin-left: 30px;
}
&.primary {
background: $turquoise;
color: $white;
@ -284,6 +293,13 @@ input[type="submit"] {
}
}
&.range {
.input-range__track,
.input-range__slider-container {
transition: none;
}
}
.description {
display: inline-block;
padding: 10px 14px;