diff --git a/src/js/bootstrap.js b/src/js/bootstrap.js
index 2594daef..59a0a12a 100755
--- a/src/js/bootstrap.js
+++ b/src/js/bootstrap.js
@@ -56,6 +56,7 @@ var initialState = {
slim_mode: false,
current_tracklist: [],
current_tltrack: false,
+ selected_tracks: [],
notifications: [],
config: {
authorization_url: 'https://jamesbarnsley.co.nz/auth_v2.php'
diff --git a/src/js/components/DebugInfo.js b/src/js/components/DebugInfo.js
index 41ff9e96..607bce7b 100755
--- a/src/js/components/DebugInfo.js
+++ b/src/js/components/DebugInfo.js
@@ -66,7 +66,7 @@ class DebugInfo extends React.Component{
Playlists: {this.props.ui.playlists ? Object.keys(this.props.ui.playlists).length : '0'}
- Tracks: {this.props.ui.tracks ? Object.keys(this.props.ui.tracks).length : '0'}
+ Tracks: {this.props.ui.tracks ? Object.keys(this.props.ui.tracks).length : '0'} ({this.props.ui.selected_tracks.length} selected)
Users: {this.props.ui.users ? Object.keys(this.props.ui.users).length : '0'}
diff --git a/src/js/components/Track.js b/src/js/components/Track.js
index 4b44b725..00ff344b 100755
--- a/src/js/components/Track.js
+++ b/src/js/components/Track.js
@@ -41,8 +41,8 @@ export default class Track extends React.Component{
var track = this.props.track
var className = 'list-item track'
- if (typeof(track.selected) !== 'undefined' && track.selected) className += ' selected'
- if (typeof(track.type) !== 'undefined') className += ' '+track.type
+ if (this.props.selected) className += ' selected'
+ if (track.type !== undefined) className += ' '+track.type
if (track.playing) className += ' playing'
if (this.state.hover) className += ' hover'
diff --git a/src/js/components/TrackList.js b/src/js/components/TrackList.js
index cccd014e..d356dc7c 100755
--- a/src/js/components/TrackList.js
+++ b/src/js/components/TrackList.js
@@ -16,11 +16,6 @@ class TrackList extends React.Component{
constructor(props) {
super(props)
- this.state = {
- tracks: [],
- last_selected_track: false
- }
-
this.touch_dragging = false
this.handleKeyUp = this.handleKeyUp.bind(this)
this.handleTouchMove = this.handleTouchMove.bind(this)
@@ -36,14 +31,6 @@ class TrackList extends React.Component{
window.removeEventListener("touchmove", this.handleTouchMove, false)
}
- componentDidMount(){
- this.setState({ tracks: this.keyifyTracks(this.props.tracks, true) })
- }
-
- componentWillReceiveProps(nextProps){
- this.setState({ tracks: this.keyifyTracks(nextProps.tracks) });
- }
-
/**
* Figure out if our click/touch event is valid and we can act accordingly
*
@@ -63,8 +50,25 @@ class TrackList extends React.Component{
return false
}
+
+ /**
+ * Build the track key
+ * This is our unique reference to a track in a particular tracklist
+ *
+ * @param track = Track obj
+ * @param index = int (position of track in tracklist)
+ * @return string
+ **/
+ buildTrackKey(track, index){
+ let key = index
+ key += '_'+track.uri
+ key += '_'+(this.props.uri ? this.props.uri : 'none')
+ key += '_'+(this.props.context ? this.props.context : 'none')
+ return key
+ }
+
handleKeyUp(e){
- if( this.selectedTracks().length <= 0 ) return;
+ if (!this.selectedTracks()) return
switch(e.keyCode){
@@ -131,14 +135,26 @@ class TrackList extends React.Component{
e.preventDefault()
}
- handleDoubleClick(e,index){
+ handleDoubleClick(e,track_key){
if (this.props.context_menu) this.props.uiActions.hideContextMenu()
this.playTracks()
}
- handleMouseDown(e,index){
+ 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)){
@@ -156,36 +172,12 @@ class TrackList extends React.Component{
}
break
}
+ */
- var selected_tracks = this.selectedTracks()
- this.props.uiActions.dragStart(e, this.props.context, this.props.uri, selected_tracks, this.tracksIndexes(selected_tracks))
+ //this.props.uiActions.dragStart(e, this.props.context, this.props.uri, selected_tracks, this.tracksIndexes(selected_tracks))
}
- handleMouseUp(e,index){
- if (this.triggerType(e) == 'default'){
-
- // 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 )
- }
- }
- }
+ handleMouseUp(e,track_key){
}
handleContextMenu(e, native_event = true){
@@ -208,60 +200,57 @@ class TrackList extends React.Component{
this.props.uiActions.showContextMenu(data)
if (!native_event){
- this.deselectAllTracks()
- this.setState({edit_mode: false})
+
+ // Deselect all tracks
+ this.props.uiActions.setSelectedTracks([])
}
}
- toggleTrackSelections(e,index){
- var tracks = this.state.tracks
- var last_selected_track = this.state.last_selected_track
+ toggleTrackSelections(e,track_key){
+ let selected_tracks = this.props.selected_tracks
- if( e.ctrlKey ){
+ if (e.ctrlKey){
- if (tracks[index].selected){
- tracks[index].selected = false
+ // Already selected, so unselect it
+ if (selected_tracks.includes(track_key)){
+ var index = selected_tracks.indexOf(track_key)
+ selected_tracks.splice(index,1)
+
+ // Not selected, so add it
} else {
- tracks[index].selected = true
- last_selected_track = index
+ selected_tracks.push(track_key)
}
- }else if( e.shiftKey ){
+ } else if (e.shiftKey){
- if( this.state.last_selected_track < index ){
- var start = this.state.last_selected_track
- var end = index
- }else{
- var start = index
- var end = this.state.last_selected_track
+ let last_selected_track_index = selected_tracks[selected_tracks.length-1].split('_')[0]
+ let newly_selected_track_index = track_key.split('_')[0]
+
+ // We've selected a track further down the list,
+ // so proceed normally
+ if (last_selected_track_index < newly_selected_track_index){
+ var start = last_selected_track_index
+ var end = newly_selected_track_index
+
+ // Selected a track up the list, so
+ // our last selected is the END of our range
+ } else {
+ var start = newly_selected_track_index
+ var end = last_selected_track_index
}
- if (start !== false && end !== false){
- for( var i = start; i <= end; i++ ){
- tracks[i].selected = true
- last_selected_track = index
+ if (start !== false && start >= 0 && end !== false && end >= 0){
+ for (let i = start; i <= end; i++){
+ selected_tracks.push(this.buildTrackKey(this.props.tracks[i], i))
}
}
- }else{
-
- for( var i = 0; i < tracks.length; i++ ){
- tracks[i].selected = false
- }
-
- tracks[index].selected = true
- last_selected_track = index
+ // Regular, unmodified left click
+ } else {
+ selected_tracks = [track_key]
}
- this.setState({ tracks: tracks, last_selected_track: last_selected_track })
- }
-
- deselectAllTracks(){
- var tracks = this.props.tracks
- for (var i = 0; i < tracks.length; i++){
- tracks[i].selected = false
- }
- this.setState({tracks: tracks})
+ this.props.uiActions.setSelectedTracks(selected_tracks)
}
isRightClick(e){
@@ -289,52 +278,53 @@ class TrackList extends React.Component{
return tracks
}
- selectedTracks(){
- if (!this.state.tracks || this.state.tracks.length <= 0){
- return []
- } else {
- function isSelected( track ){
- return ( typeof(track.selected) !== 'undefined' && track.selected );
- }
- return this.state.tracks.filter(isSelected)
+ selectedTracks(indexes_only = false){
+ if (!this.props.selected_tracks){
+ return false
}
- }
- tracksIndexes(tracks){
- var indexes = []
- for( var i = 0; i < tracks.length; i++ ){
- indexes.push( this.props.tracks.indexOf(tracks[i]))
+ // 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('_')
+
+ if (indexes_only){
+ response.push(track[0])
+
+ } else {
+ response.push({
+ index: track[0],
+ uri: track[1],
+ context: track[2],
+ context_uri: track[3]
+ })
+ }
}
- return indexes
+
+ return response
}
playTracks(){
+ let selected_tracks = this.selectedTracks()
- var tracks = this.selectedTracks();
-
- // if we've got a specific action, run it
- if( typeof(this.props.playTracks) !== 'undefined' ){
- return this.props.playTracks( tracks );
+ // Our parent handles playing
+ if (this.props.playTracks !== undefined){
+ return this.props.playTracks( selected_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, this.props.uri )
+ // Default to playing the URIs
+ return this.props.mopidyActions.playURIs(helpers.asURIs(selected_tracks), this.props.uri )
}
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 );
+ // Our parent handles removal
+ if (this.props.removeTracks !== undefined){
+ return this.props.removeTracks(this.selectedTracks(true))
}
- // by default, do nothing
+ // By default, do nothing
}
renderHeader(){
@@ -377,9 +367,8 @@ class TrackList extends React.Component{
}
render(){
- if( !this.state.tracks || Object.prototype.toString.call(this.state.tracks) !== '[object Array]' ) return null
+ if (!this.props.tracks || Object.prototype.toString.call(this.props.tracks) !== '[object Array]' ) return null
- let self = this;
var className = 'list track-list '+this.props.context
if (this.props.className){
className += ' '+this.props.className
@@ -389,26 +378,28 @@ class TrackList extends React.Component{
{ this.renderHeader() }
{
- this.state.tracks.map(
+ this.props.tracks.map(
(track, index) => {
+ let track_key = this.buildTrackKey(track, index)
return (
);
}
@@ -423,6 +414,7 @@ class TrackList extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
+ selected_tracks: state.ui.selected_tracks,
slim_mode: state.ui.slim_mode,
dragger: state.ui.dragger,
current_track: state.ui.current_track,
diff --git a/src/js/services/ui/actions.js b/src/js/services/ui/actions.js
index 5a7c8c75..0b416d38 100755
--- a/src/js/services/ui/actions.js
+++ b/src/js/services/ui/actions.js
@@ -21,6 +21,16 @@ export function getBroadcasts(){
}
}
+export function setSelectedTracks(keys = []){
+ if (typeof(keys) === 'string'){
+ keys = [keys]
+ }
+ return {
+ type: 'SET_SELECTED_TRACKS',
+ keys: keys
+ }
+}
+
export function showContextMenu(data){
data.position_x = data.e.clientX
data.position_y = data.e.clientY
diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js
index 1aedd621..9b0c1ae5 100755
--- a/src/js/services/ui/reducer.js
+++ b/src/js/services/ui/reducer.js
@@ -24,6 +24,9 @@ export default function reducer(ui = {}, action){
if( typeof(action.new_state) !== 'undefined' ) new_state = action.new_state
return Object.assign({}, ui, { sidebar_open : new_state })
+ case 'SET_SELECTED_TRACKS':
+ return Object.assign({}, ui, { selected_tracks : Object.assign([],action.keys) })
+
/**
* Context menu