Rebuild of touch and click events on tracks

This commit is contained in:
James Barnsley
2017-07-22 06:36:47 +12:00
parent 52ce953c29
commit 48b32a4a5d
8 changed files with 332 additions and 255 deletions

View File

@ -201,7 +201,7 @@ class ContextMenu extends React.Component{
goToRecommendations(e){
this.props.uiActions.hideContextMenu()
var uris_string = helpers.asURIs(this.props.menu.items).join(',')
var uris_string = helpers.arrayOf('uri',this.props.menu.items).join(',')
hashHistory.push( global.baseURL +'discover/recommendations/'+ uris_string )
}

View File

@ -15,25 +15,58 @@ export default class Track extends React.Component{
this.state = {
hover: false
}
this.drag_threshold = 5
this.start_position = false
}
handleMouseDown(e){
var target = $(e.target);
if( !target.is('a') && target.closest('a').length <= 0 ){
this.props.handleMouseDown(e);
this.start_position = {
x: e.pageX,
y: e.pageY
}
}
handleTouchEnd(e){
var target = $(e.target);
if( !target.is('a') && target.closest('a').length <= 0 ){
this.props.handleTouchEnd(e);
handleMouseMove(e){
// No drag handling means no
if (this.props.handleDrag === undefined){
return false
}
if (this.start_position){
let start_x = this.start_position.x
let start_y = this.start_position.y
let threshold = 5
// Have we dragged outside of our threshold zone?
if (e.pageX > start_x + threshold || e.pageX < start_x - threshold || e.pageY > start_y + threshold || e.pageY < start_y - threshold){
// Handover to parent for dragging. We can unset all our behaviour now.
this.props.handleDrag(e)
this.start_position = false
}
}
}
handleContextMenu(e){
e.preventDefault();
this.props.handleContextMenu(e);
handleMouseUp(e){
if (this.props.dragger){
e.preventDefault()
if (this.props.handleDrop !== undefined){
this.props.handleDrop(e)
}
} else {
var target = $(e.target);
if( !target.is('a') && target.closest('a').length <= 0 ){
this.props.handleSelection(e)
this.start_position = false
}
}
}
handleTouchStart(e){
this.props.handleTouchDrag(e)
}
render(){
@ -42,6 +75,7 @@ export default class Track extends React.Component{
var track = this.props.track
var className = 'list-item track'
if (this.props.selected) className += ' selected'
if (this.props.can_sort) className += ' can-sort'
if (track.type !== undefined) className += ' '+track.type
if (track.playing) className += ' playing'
if (this.state.hover) className += ' hover'
@ -55,113 +89,154 @@ export default class Track extends React.Component{
}
}
let track_columns = []
if (track.type == 'history'){
var track_columns = (
<span>
<span className="select-zone">
{this.props.track.selected ? <FontAwesome name="check" className="selected" fixedWidth /> : null}
</span>
<span className="col name">
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
</span>
<span className="col source">
{helpers.uriSource(track.uri)}
</span>
<span className="col played_at">
{track.played_at ? <span><Dater type="ago" data={track.played_at} /> ago</span> : null}
</span>
track_columns.push(
<span className="col name">
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
</span>
)
track_columns.push(
<span className="col source">
{helpers.uriSource(track.uri)}
</span>
)
track_columns.push(
<span className="col played_at">
{track.played_at ? <span><Dater type="ago" data={track.played_at} /> ago</span> : null}
</span>
)
} else if (this.props.context == 'queue'){
if (track.added_from && track.added_by){
var type = (track.added_from ? helpers.uriType(track.added_from) : null)
if (type == 'discover'){
var link = <Link to={global.baseURL+'discover/recommendations/'+helpers.getFromUri('seeds',track.added_from)}>discover</Link>
} else {
var link = <Link to={global.baseURL+type+'/'+track.added_from}>{type}</Link>
}
var added = <span>{track.added_by} <span className="grey-text"> (from {link})</span></span>
} else if (track.added_by){
var added = track.added_by
if (track.added_from && track.added_by){
var type = (track.added_from ? helpers.uriType(track.added_from) : null)
if (type == 'discover'){
var link = <Link to={global.baseURL+'discover/recommendations/'+helpers.getFromUri('seeds',track.added_from)}>discover</Link>
} else {
var added = '-'
var link = <Link to={global.baseURL+type+'/'+track.added_from}>{type}</Link>
}
var added = <span>{track.added_by} <span className="grey-text"> (from {link})</span></span>
className+= ' has-drag-zone'
} else if (track.added_by){
var added = track.added_by
var track_columns = (
<span>
<span className="select-zone">
{this.props.track.selected ? <FontAwesome name="check" className="selected" fixedWidth /> : null}
</span>
<span className="drag-zone">
<FontAwesome name="bars" />
</span>
<span className="col name">
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
{track.explicit ? <span className="flag dark">EXPLICIT</span> : null}
</span>
{this.props.show_source_icon ? <FontAwesome name={helpers.sourceIcon(track.uri)} className="source" fixedWidth /> : null}
<span className="col artists">
{track.artists ? <ArtistSentence artists={track.artists} /> : '-'}
</span>
<span className="col album">
{album}
</span>
<span className="col added">
{added}
</span>
<span className="col duration">
{track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null}
{track.length ? <Dater type="length" data={track.length} /> : null}
</span>
</span>
} else {
var added = '-'
}
track_columns.push(
<span className="col name" key="name">
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
{track.explicit ? <span className="flag dark">EXPLICIT</span> : null}
</span>
)
if (this.props.show_source_icon){
track_columns.push(
<FontAwesome name={helpers.sourceIcon(track.uri)} className="source" key="source" fixedWidth />
)
}
track_columns.push(
<span className="col artists" key="artists">
{track.artists ? <ArtistSentence artists={track.artists} /> : '-'}
</span>
)
track_columns.push(
<span className="col album" key="album">
{album}
</span>
)
track_columns.push(
<span className="col added" key="added">
{added}
</span>
)
track_columns.push(
<span className="col duration" key="duration">
{track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null}
{track.length ? <Dater type="length" data={track.length} /> : null}
</span>
)
} else {
var track_columns = (
<span>
<span className="select-zone">
{this.props.track.selected ? <FontAwesome name="check" className="selected" fixedWidth /> : null}
</span>
<span className="col name">
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
{track.explicit ? <span className="flag dark">EXPLICIT</span> : null}
</span>
{this.props.show_source_icon ? <FontAwesome name={helpers.sourceIcon(track.uri)} className="source" fixedWidth /> : null}
<span className="col artists">
{track.artists ? <ArtistSentence artists={track.artists} /> : '-'}
</span>
<span className="col album">
{album}
</span>
<span className="col duration">
{track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null}
{track.length ? <Dater type="length" data={track.length} /> : null}
</span>
track_columns.push(
<span className="col name" key="name">
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
{track.explicit ? <span className="flag dark">EXPLICIT</span> : null}
</span>
)
if (this.props.show_source_icon){
track_columns.push(
<FontAwesome name={helpers.sourceIcon(track.uri)} className="source" key="source" fixedWidth />
)
}
track_columns.push(
<span className="col artists" key="artists">
{track.artists ? <ArtistSentence artists={track.artists} /> : '-'}
</span>
)
track_columns.push(
<span className="col album" key="album">
{album}
</span>
)
track_columns.push(
<span className="col duration" key="duration">
{track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null}
{track.length ? <Dater type="length" data={track.length} /> : null}
</span>
)
}
return (
<div
className={className}
onMouseEnter={e => this.setState({hover: true})}
onMouseLeave={e => this.setState({hover: false})}
onTouchStart={ e => this.props.handleTouchStart(e) }
onTouchEnd={ e => this.handleTouchEnd(e) }
onMouseDown={ e => this.handleMouseDown(e) }
onMouseUp={ e => this.props.handleMouseUp(e) }
onDoubleClick={ e => this.props.handleDoubleClick(e) }
onContextMenu={ e => this.handleContextMenu(e) }>
if (this.props.slim_mode){
// Select zone handles selection events only
// We use onClick to capture touch as well as mouse events in one tidy parcel
track_columns.push(
<span
className="select-zone"
key="select-zone"
onClick={e => this.props.handleSelection(e)}>
{this.props.selected ? <FontAwesome name="check" className="selected" fixedWidth /> : null}
</span>
)
if (this.props.can_sort){
track_columns.push(
<span
className="drag-zone"
key="drag-zone"
onTouchStart={e => this.handleTouchStart(e)}>
<FontAwesome name="bars" fixedWidth />
</span>
)
}
// No events attached directly to the track. Instead events are attached to
// the appropriate select/drag zone sub-elements
return (
<div className={className}>
{ track_columns }
</div>
);
</div>
)
} else {
return (
<div
className={className}
onMouseEnter={e => this.setState({hover: true})}
onMouseLeave={e => this.setState({hover: false})}
onTouchEnd={e => this.handleTouchEnd(e)} // When touch dragging is dropped on me
onMouseDown={e => this.handleMouseDown(e)} // Click (or potentially a mouse drag start)
onMouseMove={e => this.handleMouseMove(e)} // Any movement over me
onMouseUp={e => this.handleMouseUp(e)} // End of click, or potentially a dragging drop event
onDoubleClick={e => this.props.handleDoubleClick(e)}
onContextMenu={e => {e.preventDefault(); this.props.handleContextMenu(e)}}>
{ track_columns }
</div>
)
}
}
}

View File

@ -16,19 +16,23 @@ class TrackList extends React.Component{
constructor(props) {
super(props)
this.touch_dragging = false
this.touch_dragging_tracks_keys = false
this.handleKeyUp = this.handleKeyUp.bind(this)
this.handleTouchMove = this.handleTouchMove.bind(this)
this.handleTouchEnd = this.handleTouchEnd.bind(this)
}
componentWillMount(){
window.addEventListener("keyup", this.handleKeyUp, false)
window.addEventListener("touchmove", this.handleTouchMove, false)
window.addEventListener("touchend", this.handleTouchEnd, false)
}
componentWillUnmount(){
window.removeEventListener("keyup", this.handleKeyUp, false)
window.removeEventListener("touchmove", this.handleTouchMove, false)
window.removeEventListener("touchend", this.handleTouchEnd, false)
}
/**
@ -68,7 +72,7 @@ class TrackList extends React.Component{
}
handleKeyUp(e){
if (!this.selectedTracks()) return
if (!this.digestTracksKeys()) return
switch(e.keyCode){
@ -82,18 +86,62 @@ class TrackList extends React.Component{
}
}
handleTouchStart(e,index){
var target = $(e.target)
if (target.hasClass('drag-zone')){
$('body').addClass('touch-dragging')
this.touch_dragging = true
e.preventDefault()
handleDrag(e,track_key){
let selected_tracks = []
// Dragging a non-selected track. We need to deselect everything
// else and select only this track
if (!this.props.selected_tracks.includes(track_key)){
this.props.uiActions.setSelectedTracks([track_key])
selected_tracks = this.digestTracksKeys([track_key])
} else {
selected_tracks = this.digestTracksKeys()
}
let selected_tracks_indexes = helpers.arrayOf('index',selected_tracks)
this.props.uiActions.dragStart(
e,
this.props.context,
this.props.uri,
selected_tracks,
selected_tracks_indexes
)
}
handleDrop(e,track_key){
if (this.props.dragger && this.props.dragger.active){
// if this tracklist handles sorting, handle it
if (this.props.reorderTracks !== undefined){
let indexes = this.props.dragger.victims_indexes
let tracks = this.digestTracksKeys([track_key])
return this.props.reorderTracks(indexes, tracks[0].index)
}
}
this.touch_dragging_tracks_keys = false
}
handleTouchDrag(e,track_key){
let selected_tracks = []
// Drag initiated on a selected track
if (this.props.selected_tracks.includes(track_key)){
// They're all dragging
this.touch_dragging_tracks_keys = this.props.selected_tracks
// Not already selected
} else {
this.touch_dragging_tracks_keys = [track_key]
this.props.uiActions.setSelectedTracks([track_key])
}
}
handleTouchMove(e){
if (this.touch_dragging){
e.preventDefault()
if (this.touch_dragging_tracks_keys){
let touch = e.touches[0]
let over = $(document.elementFromPoint(touch.clientX, touch.clientY))
if (!over.is('.track')){
@ -103,11 +151,17 @@ class TrackList extends React.Component{
if (over.length > 0){
over.addClass('touch-drag-hover')
}
e.returnValue = false
e.cancelBubble = true
e.preventDefault()
e.stopPropagation()
return false
}
}
handleTouchEnd(e,index){
if (this.touch_dragging){
handleTouchEnd(e){
if (this.touch_dragging_tracks_keys){
let touch = e.changedTouches[0]
let over = $(document.elementFromPoint(touch.clientX, touch.clientY))
if (!over.is('.track')){
@ -117,22 +171,16 @@ class TrackList extends React.Component{
let siblings = over.parent().children('.track')
let dropped_at = siblings.index(over) - 1
if (typeof(this.props.reorderTracks) !== 'undefined'){
this.props.reorderTracks([index],dropped_at)
this.render()
if (this.props.reorderTracks !== undefined){
this.props.reorderTracks(helpers.arrayOf('index',this.digestTracksKeys()),dropped_at)
}
}
$(document).find('.touch-drag-hover').removeClass('touch-drag-hover')
$('body').removeClass('touch-dragging')
this.touch_dragging = false
} else {
this.handleMouseDown(e,index)
}
// Prevent any event bubbling. This prevents clicks and mouse events
// from also being fired
e.preventDefault()
this.touch_dragging_tracks_keys = false
}
handleDoubleClick(e,track_key){
@ -140,76 +188,29 @@ class TrackList extends React.Component{
this.playTracks()
}
handleMouseDown(e,track_key){
if (this.props.context_menu) this.props.uiActions.hideContextMenu()
this.toggleTrackSelections(e, track_key)
if (this.props.dragger && this.props.dragger.active){
// if this tracklist handles sorting, handle it
if (typeof(this.props.reorderTracks) !== 'undefined'){
var indexes = this.props.dragger.victims_indexes
return this.props.reorderTracks( indexes, index )
}
}
/*
// Regular clicking an un-selected element
// This selects the track before we potentially drag
switch (this.triggerType(e)){
case 'mobile':
// simple toggle
var tracks = this.state.tracks
tracks[index].selected = !tracks[index].selected
this.setState({tracks: tracks, last_selected_track: index})
break
case 'default':
if (!this.state.tracks[index].selected && !this.isRightClick(e) && !e.ctrlKey){
this.toggleTrackSelections(e, index)
}
break
}
*/
//this.props.uiActions.dragStart(e, this.props.context, this.props.uri, selected_tracks, this.tracksIndexes(selected_tracks))
}
handleMouseUp(e,track_key){
}
handleContextMenu(e, native_event = true){
let selected_tracks = this.digestTracksKeys()
let selected_tracks_uris = helpers.arrayOf('uri',selected_tracks)
let selected_tracks_indexes = helpers.arrayOf('index',selected_tracks)
// Only enable direct context menu events in default (desktop) view
// This prevents tap-and-hold functionality
if (native_event && this.triggerType(e) != 'default'){
return false
}
var selected_tracks = this.selectedTracks()
var data = {
let data = {
e: e,
context: (this.props.context ? this.props.context+'-track' : 'track'),
tracklist_uri: (this.props.uri ? this.props.uri : null),
items: selected_tracks,
uris: helpers.asURIs(selected_tracks),
indexes: this.tracksIndexes(selected_tracks)
uris: selected_tracks_uris,
indexes: selected_tracks_indexes
}
this.props.uiActions.showContextMenu(data)
if (!native_event){
// Deselect all tracks
this.props.uiActions.setSelectedTracks([])
}
// Deselect all tracks
this.props.uiActions.setSelectedTracks([])
}
toggleTrackSelections(e,track_key){
handleSelection(e,track_key){
let selected_tracks = this.props.selected_tracks
if (e.ctrlKey){
if (e.ctrlKey || this.props.slim_mode){
// Already selected, so unselect it
if (selected_tracks.includes(track_key)){
@ -261,67 +262,63 @@ class TrackList extends React.Component{
return false
}
keyifyTracks(tracks, deselect = false){
for( var i = 0; i < tracks.length; i++ ){
var new_properties = {
key: i+'_'+tracks[i].uri
}
if (deselect){
new_properties.selected = false
}
tracks[i] = Object.assign(
{},
tracks[i],
new_properties
)
}
return tracks
}
selectedTracks(indexes_only = false){
if (!this.props.selected_tracks){
/**
* Digest our selected tracks
*
* @param tracks = array (defaults to stored value)
* @param indexex_only = boolean (do we just want an array of indexes)
* @return mixed
**/
digestTracksKeys(keys = this.props.selected_tracks, indexes_only = false){
if (!keys || keys.length <= 0){
return false
}
// Construct a basic track object, based on our unique track key
// This is enough to perform interactions (dragging, selecting, etc)
let response = []
for (let i = 0; i < this.props.selected_tracks.length; i++){
let track = this.props.selected_tracks[i].split('_')
let array = []
for (let i = 0; i < keys.length; i++){
let key = keys[i].split('_')
if (indexes_only){
response.push(track[0])
array.push(key[0])
} else {
response.push({
index: track[0],
uri: track[1],
context: track[2],
context_uri: track[3]
array.push({
index: parseInt(key[0]),
uri: key[1],
context: key[2],
context_uri: key[3]
})
}
}
return response
return array
}
playTracks(){
let selected_tracks = this.selectedTracks()
let selected_tracks = this.digestTracksKeys()
let selected_tracks_indexes = helpers.arrayOf('index',selected_tracks)
// Our parent handles playing
if (this.props.playTracks !== undefined){
return this.props.playTracks( selected_tracks );
}
return this.props.playTracks( selected_tracks )
// Default to playing the URIs
return this.props.mopidyActions.playURIs(helpers.asURIs(selected_tracks), this.props.uri )
} else {
let selected_tracks_uris = helpers.arrayOf('uri',selected_tracks)
return this.props.mopidyActions.playURIs(selected_tracks_uris, this.props.uri)
}
}
removeTracks(){
// Our parent handles removal
if (this.props.removeTracks !== undefined){
return this.props.removeTracks(this.selectedTracks(true))
let selected_tracks = this.digestTracksKeys()
let selected_tracks_indexes = helpers.arrayOf('index',selected_tracks)
return this.props.removeTracks(selected_tracks_indexes)
}
// By default, do nothing
@ -387,19 +384,23 @@ class TrackList extends React.Component{
key={track_key}
track={track}
context={this.props.context}
can_sort={this.props.context == 'queue' || this.props.context == 'editable-playlist'}
slim_mode={this.props.slim_mode}
selected={this.props.selected_tracks.includes(track_key)}
dragger={this.props.dragger}
handleSelection={e => this.handleSelection(e, track_key)}
handleDoubleClick={e => this.handleDoubleClick(e, track_key)}
handleMouseUp={e => this.handleMouseUp(e, track_key)}
handleMouseDown={e => this.handleMouseDown(e, track_key)}
handleTouchStart={e => this.handleTouchStart(e, track_key)}
handleTouchEnd={e => this.handleTouchEnd(e, track_key)}
handleContextMenu={e => this.handleContextMenu(e)}
handleDrag={e => this.handleDrag(e, track_key)}
handleDrop={e => this.handleDrop(e, track_key)}
handleTouchDrag={e => this.handleTouchDrag(e, track_key)}
handleTouchDrop={e => this.handleTouchDrop(e, track_key)}
/>
)
}
)
}
{this.selectedTracks() ? <ContextMenuTrigger onTrigger={e => this.handleContextMenu(e, false)} /> : null}
{this.digestTracksKeys() ? <ContextMenuTrigger onTrigger={e => this.handleContextMenu(e, false)} /> : null}
</div>
);
}

View File

@ -271,17 +271,18 @@ export let indexFriendlyUri = function (uri){
/**
* Digest an array of objects, and pull into simple array of uris
* Digest an array of objects and pull into simple array of one property
*
* @param items Array
* @param property = string
* @param items = Array
* @return Array
**/
export let asURIs = function(items){
var uris = []
for( var i = 0; i < items.length; i++ ){
uris.push( items[i].uri )
export let arrayOf = function(property, items){
let array = []
for (let i = 0; i < items.length; i++){
array.push(items[i][property])
}
return uris
return array
}

View File

@ -286,7 +286,7 @@ const MopidyMiddleware = (function(){
if (typeof(response.tracks) === 'undefined'){
store.dispatch(uiActions.createNotification('Failed to load playlist tracks','bad'))
} else {
var tracks_uris = helpers.asURIs(response.tracks)
var tracks_uris = helpers.arrayOf('uri',response.tracks)
store.dispatch(mopidyActions.playURIs(tracks_uris, action.uri))
}
})
@ -582,7 +582,7 @@ const MopidyMiddleware = (function(){
.then( response => {
// drop in our URI list
var playlist_uris = helpers.asURIs(response)
var playlist_uris = helpers.arrayOf('uri',response)
store.dispatch({ type: 'LIBRARY_PLAYLISTS_LOADED', uris: playlist_uris });
@ -645,7 +645,7 @@ const MopidyMiddleware = (function(){
case 'MOPIDY_RESOLVE_PLAYLIST_TRACKS':
var tracks = Object.assign([], action.tracks)
var uris = helpers.asURIs(tracks)
var uris = helpers.arrayOf('uri',tracks)
instruct( socket, store, 'library.lookup', { uris: uris } )
.then( response => {
@ -839,7 +839,7 @@ const MopidyMiddleware = (function(){
.then( response => {
if (response.length <= 0) return
var uris = helpers.asURIs(response)
var uris = helpers.arrayOf('uri',response)
store.dispatch({
type: 'MOPIDY_GET_ALBUMS',
@ -964,7 +964,7 @@ const MopidyMiddleware = (function(){
});
store.dispatch({
type: 'LOCAL_ARTISTS_LOADED',
uris: helpers.asURIs(response)
uris: helpers.arrayOf('uri',response)
});
})
break;
@ -999,7 +999,7 @@ const MopidyMiddleware = (function(){
(response ? response[0].artists[0] : {}),
{
is_mopidy: true,
albums_uris: helpers.asURIs(albums),
albums_uris: helpers.arrayOf('uri',albums),
tracks: response.slice(0,10)
}
)

View File

@ -334,7 +334,7 @@ export function getFeaturedPlaylists(){
type: 'SPOTIFY_FEATURED_PLAYLISTS_LOADED',
data: {
message: response.message,
playlists: helpers.asURIs(response.playlists.items)
playlists: helpers.arrayOf('uri',response.playlists.items)
}
});
});
@ -471,11 +471,11 @@ export function getSearchResults(query, type = 'album,artist,playlist,track', li
dispatch({
type: 'SEARCH_RESULTS_LOADED',
playlists_uris: helpers.asURIs(playlists),
playlists_uris: helpers.arrayOf('uri',playlists),
playlists_more: response.playlists.next,
artists_uris: helpers.asURIs(response.artists.items),
artists_uris: helpers.arrayOf('uri',response.artists.items),
artists_more: response.artists.next,
albums_uris: helpers.asURIs(response.albums.items),
albums_uris: helpers.arrayOf('uri',response.albums.items),
albums_more: response.albums.next,
tracks: response.tracks.items,
tracks_more: response.tracks.next
@ -765,7 +765,7 @@ export function getRecommendations(uris = [], limit = 20){
seeds_uris: uris,
tracks: response.tracks,
artists_uris: artists_uris,
albums_uris: helpers.asURIs(albums)
albums_uris: helpers.arrayOf('uri',albums)
})
})
}
@ -833,7 +833,7 @@ export function getArtist(uri, full = false){
type: 'ARTISTS_LOADED',
artists: response.artists
});
Object.assign(artist, { related_artists_uris: helpers.asURIs(response.artists) });
Object.assign(artist, { related_artists_uris: helpers.arrayOf('uri',response.artists) });
})
)
}
@ -888,7 +888,7 @@ export function getArtists( uris ){
album: artist.albums[i]
});
}
artist.albums = helpers.asURIs(artist.albums)
artist.albums = helpers.arrayOf('uri',artist.albums)
artist.albums_more = artist.albums.next
dispatch({
type: 'ARTIST_LOADED',
@ -919,14 +919,14 @@ export function playArtistTopTracks(uri){
// Do we have this artist (and their tracks) in our index already?
if (typeof(artists[uri]) !== 'undefined' && typeof(artists[uri].tracks) !== 'undefined'){
const uris = helpers.asURIs(artists[uri].tracks)
const uris = helpers.arrayOf('uri',artists[uri].tracks)
dispatch(mopidyActions.playURIs(uris, uri))
// We need to load the artist's top tracks first
} else {
sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/top-tracks?country='+getState().spotify.country )
.then( response => {
const uris = helpers.asURIs(response.tracks)
const uris = helpers.arrayOf('uri',response.tracks)
dispatch(mopidyActions.playURIs(uris, uri))
})
}
@ -1013,7 +1013,7 @@ export function getAlbum( uri ){
{},
response,
{
artists_uris: helpers.asURIs(response.artists),
artists_uris: helpers.arrayOf('uri',response.artists),
tracks: response.tracks.items,
tracks_more: response.tracks.next,
tracks_total: response.tracks.total

View File

@ -94,7 +94,7 @@ const SpotifyMiddleware = (function(){
});
store.dispatch({
type: 'NEW_RELEASES_LOADED',
uris: helpers.asURIs(action.data.albums.items),
uris: helpers.arrayOf('uri',action.data.albums.items),
more: action.data.albums.next,
total: action.data.albums.total
});
@ -108,7 +108,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'ARTIST_ALBUMS_LOADED',
key: action.key,
uris: helpers.asURIs(action.data.items),
uris: helpers.arrayOf('uri',action.data.items),
more: action.data.next,
total: action.data.total
});
@ -139,7 +139,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'USER_PLAYLISTS_LOADED',
key: action.key,
uris: helpers.asURIs(playlists),
uris: helpers.arrayOf('uri',playlists),
more: action.data.next,
total: action.data.total
});
@ -170,7 +170,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'CATEGORY_PLAYLISTS_LOADED',
key: action.key,
uris: helpers.asURIs(playlists),
uris: helpers.arrayOf('uri',playlists),
more: action.data.playlists.next,
total: action.data.playlists.total
});
@ -202,7 +202,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'LIBRARY_PLAYLISTS_LOADED',
uris: helpers.asURIs(playlists)
uris: helpers.arrayOf('uri',playlists)
});
break
@ -225,7 +225,7 @@ const SpotifyMiddleware = (function(){
});
store.dispatch({
type: 'LIBRARY_ARTISTS_LOADED',
uris: helpers.asURIs(artists),
uris: helpers.arrayOf('uri',artists),
more: action.data.artists.next,
total: action.data.artists.total
});
@ -256,7 +256,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'LIBRARY_ALBUMS_LOADED',
uris: helpers.asURIs(albums),
uris: helpers.arrayOf('uri',albums),
more: action.data.next,
total: action.data.total
});
@ -268,14 +268,14 @@ const SpotifyMiddleware = (function(){
type: 'ARTISTS_LOADED',
artists: action.artists
})
action.artists_uris = helpers.asURIs(action.artists)
action.artists_uris = helpers.arrayOf('uri',action.artists)
}
if (action.tracks.length > 0){
store.dispatch({
type: 'TRACKS_LOADED',
tracks: action.tracks
})
action.tracks_uris = helpers.asURIs(action.tracks)
action.tracks_uris = helpers.arrayOf('uri',action.tracks)
}
next(action)
break
@ -305,7 +305,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'SEARCH_RESULTS_LOADED',
playlists_uris: helpers.asURIs(action.data.playlists.items),
playlists_uris: helpers.arrayOf('uri',action.data.playlists.items),
playlists_more: action.data.playlists.next
});
break
@ -319,7 +319,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'SEARCH_RESULTS_LOADED',
albums_uris: helpers.asURIs(action.data.albums.items),
albums_uris: helpers.arrayOf('uri',action.data.albums.items),
albums_more: action.data.albums.next
});
break
@ -344,7 +344,7 @@ const SpotifyMiddleware = (function(){
store.dispatch({
type: 'SEARCH_RESULTS_LOADED',
playlists_uris: helpers.asURIs(action.data.playlists.items),
playlists_uris: helpers.arrayOf('uri',action.data.playlists.items),
playlists_more: action.data.playlists.next
});
break

View File

@ -232,7 +232,7 @@
.fa {
position: absolute;
top: 20px;
left: 13px;
left: 19px;
pointer-events: none;
color: $white;
z-index: 1;
@ -247,7 +247,7 @@
height: 14px;
position: absolute;
top: 16px;
left: 10px;
left: 16px;
}
}
@ -333,10 +333,10 @@
}
&.track-list {
.list-item.has-drag-zone {
.list-item.can-sort {
padding: 8px 30px 8px 78px !important;
}
.list-item:not(.has-drag-zone){
.list-item:not(.can-sort){
padding: 8px 30px 8px 38px !important;
}
}