Files
Iris/src/js/components/TrackList.js

271 lines
6.9 KiB
JavaScript
Raw Normal View History

2016-10-03 14:44:27 +13:00
import React, { PropTypes } from 'react'
2016-10-14 18:15:34 +13:00
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
2016-10-03 21:21:14 +13:00
import Track from './Track'
2016-10-03 14:44:27 +13:00
2016-11-10 16:17:10 +13:00
import * as mopidyActions from '../services/mopidy/actions'
import * as uiActions from '../services/ui/actions'
2016-10-14 18:15:34 +13:00
class TrackList extends React.Component{
2016-10-03 14:44:27 +13:00
constructor(props) {
super(props);
2016-10-06 16:38:06 +13:00
2016-12-08 08:50:31 +13:00
this._touch_threshold = 10
this._touch_x = null
this._touch_y = null
2016-10-06 16:38:06 +13:00
this.state = {
tracks: this.keyifyTracks(this.props.tracks),
lastSelectedTrack: false
2016-10-06 16:38:06 +13:00
}
2016-10-27 13:02:10 +13:00
this.handleKeyUp = this.handleKeyUp.bind(this)
}
componentWillMount(){
window.addEventListener("keyup", this.handleKeyUp, false);
}
componentWillUnmount(){
window.removeEventListener("keyup", this.handleKeyUp, false);
}
componentWillReceiveProps( nextProps ){
this.setState({ tracks: this.keyifyTracks(nextProps.tracks) });
}
2016-10-27 13:02:10 +13:00
handleKeyUp(e){
if( this.selectedTracks().length <= 0 ) return;
2016-10-27 13:02:10 +13:00
switch(e.keyCode){
case 13: // enter
this.playTracks();
2016-10-27 13:02:10 +13:00
break;
case 46: // delete
this.removeTracks();
2016-10-27 13:02:10 +13:00
break;
}
2016-10-03 14:44:27 +13:00
}
2016-12-08 08:50:31 +13:00
handleTouchStart(e, index){
this._touch_x = Math.round(e.changedTouches[0].pageX)
this._touch_y = Math.round(e.changedTouches[0].pageY)
}
handleTouchEnd(e, index){
2016-12-08 08:50:31 +13:00
var pageX = Math.round(e.changedTouches[0].pageX)
var pageY = Math.round(e.changedTouches[0].pageY)
if( this._touch_x < ( pageX + this._touch_threshold ) &&
this._touch_x > ( pageX - this._touch_threshold ) &&
this._touch_y < ( pageY + this._touch_threshold ) &&
this._touch_y > ( pageY - this._touch_threshold ) ){
var tracks = this.state.tracks
tracks[index].selected = !tracks[index].selected
this.setState({ tracks: tracks, lastSelectedTrack: index })
}
e.preventDefault()
}
handleDoubleClick(e, index){
if( this.props.context_menu.show ) this.props.uiActions.hideContextMenu()
this.playTracks()
}
handleMouseDown(e, index){
if( this.props.context_menu.show ) this.props.uiActions.hideContextMenu()
2016-10-27 10:02:12 +13:00
if( !this.state.tracks[index].selected && !this.isRightClick(e) && !e.ctrlKey ) this.toggleTrackSelections(e, index)
var selected_tracks = this.selectedTracks()
this.props.uiActions.dragStart( e, this.props.context, selected_tracks, this.tracksIndexes(selected_tracks) )
}
handleMouseUp(e, index){
// right-clicking on an un-highlighted track
if( !this.state.tracks[index].selected && this.isRightClick(e) ){
this.toggleTrackSelections(e, index)
// selected track, regular click
}else if( this.state.tracks[index].selected && !this.isRightClick(e) ){
this.toggleTrackSelections(e, index)
// ctrl key
}else if( e.ctrlKey ){
this.toggleTrackSelections(e, index)
}
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 );
}
}
}
handleContextMenu(e, index){
var selected_tracks = this.selectedTracks()
var data = {
selected_tracks: selected_tracks,
selected_tracks_indexes: this.tracksIndexes( selected_tracks )
}
this.props.uiActions.showContextMenu( e, this.props.context, data )
}
toggleTrackSelections(e, index){
var tracks = this.state.tracks
if( e.ctrlKey ){
tracks[index].selected = !tracks[index].selected
}else if( e.shiftKey ){
if( this.state.lastSelectedTrack < index ){
var start = this.state.lastSelectedTrack
var end = index
}else{
var start = index
var end = this.state.lastSelectedTrack
}
for( var i = start; i <= end; i++ ){
tracks[i].selected = true
}
}else{
for( var i = 0; i < tracks.length; i++ ){
tracks[i].selected = false
}
tracks[index].selected = !tracks[index].selected
}
this.setState({ tracks: tracks, lastSelectedTrack: index })
}
isRightClick(e){
if( 'which' in e )
return e.which == 3
if( 'button' in e )
return e.button == 2
return false
2016-10-27 10:02:12 +13:00
}
keyifyTracks( tracks ){
for( var i = 0; i < tracks.length; i++ ){
tracks[i] = Object.assign({}, tracks[i], { key: i+'_'+tracks[i].uri })
}
return tracks
}
2016-10-19 14:09:34 +13:00
selectedTracks(){
function isSelected( track ){
return ( typeof(track.selected) !== 'undefined' && track.selected );
}
2016-10-19 14:09:34 +13:00
return this.state.tracks.filter(isSelected)
}
tracksIndexes(tracks){
var indexes = []
for( var i = 0; i < tracks.length; i++ ){
indexes.push( this.props.tracks.indexOf(tracks[i]))
}
return indexes
}
playTracks(){
var tracks = this.selectedTracks();
// if we've got a specific action, run it
if( typeof(this.props.playTracks) !== 'undefined' ){
2016-11-21 21:46:53 +13:00
return this.props.playTracks( tracks );
}
// default to playing a bunch of uris
var uris = [];
for( var i = 0; i < tracks.length; i++ ){
uris.push( tracks[i].uri )
}
return this.props.mopidyActions.playURIs( uris )
2016-10-19 14:09:34 +13:00
}
removeTracks(){
// if this tracklist handles removal, handle it
if( typeof(this.props.removeTracks) !== 'undefined' ){
var tracks = this.selectedTracks();
var tracks_indexes = this.tracksIndexes(tracks);
return this.props.removeTracks( tracks_indexes );
}
// by default, do nothing
2016-10-06 16:38:06 +13:00
}
render(){
if( !this.state.tracks ) return null
2016-10-06 16:38:06 +13:00
let self = this;
return (
2016-11-25 22:49:34 +13:00
<div className="track-list">
<div className="list-item header track">
<span className="col name">Name</span>
<span className="col artists">Artists</span>
<span className="col album">Album</span>
<span className="col duration">Length</span>
2016-11-25 22:49:34 +13:00
</div>
{
this.state.tracks.map(
(track, index) => {
return <Track
show_source_icon={ this.props.show_source_icon }
key={track.key}
track={track}
2016-11-10 16:17:10 +13:00
handleDoubleClick={ e => self.handleDoubleClick(e, index)}
2016-11-15 17:08:21 +13:00
handleMouseUp={ e => self.handleMouseUp(e, index)}
handleMouseDown={ e => self.handleMouseDown(e, index)}
handleTouchStart={ e => self.handleTouchStart(e, index)}
handleTouchEnd={ e => self.handleTouchEnd(e, index)}
2016-11-10 16:17:10 +13:00
handleContextMenu={ e => self.handleContextMenu(e, index)} />
}
)
}
2016-11-25 22:49:34 +13:00
</div>
);
2016-10-03 14:44:27 +13:00
}
2016-10-14 18:15:34 +13:00
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return {
2016-11-15 17:08:21 +13:00
dragger: state.ui.dragger,
current_track: state.ui.current_track,
context_menu: state.ui.context_menu
}
2016-10-14 18:15:34 +13:00
}
const mapDispatchToProps = (dispatch) => {
return {
2016-10-27 10:02:12 +13:00
mopidyActions: bindActionCreators(mopidyActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch)
2016-10-14 18:15:34 +13:00
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TrackList)