Rebuild of touch and click events on tracks
This commit is contained in:
@ -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 )
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user