Removing tracks from playlists on a global stage

This commit is contained in:
James Barnsley
2016-11-11 12:12:50 +13:00
parent c0e910669c
commit 3e5a2c84d6
6 changed files with 125 additions and 85 deletions

View File

@ -29,6 +29,8 @@ class ContextMenu extends React.Component{
case 'editable-playlist':
items = [
{ handleClick: 'playItems', label: 'Play' },
{ handleClick: 'playItemsNext', label: 'Play next' },
{ handleClick: 'addToQueue', label: 'Add to queue' },
{ handleClick: 'removeFromPlaylist', label: 'Remove' }
];
break;
@ -113,7 +115,7 @@ class ContextMenu extends React.Component{
}
removeFromPlaylist(){
console.log('removeFromPlaylist')
this.props.uiActions.removeTracksFromPlaylist( this.props.context_menu.data.selected_tracks_indexes )
this.props.uiActions.hideContextMenu();
}

View File

@ -92,8 +92,10 @@ class TrackList extends React.Component{
}
handleContextMenu(e, index){
var selected_tracks = this.selectedTracks()
var data = {
selected_tracks: this.selectedTracks()
selected_tracks: selected_tracks,
selected_tracks_indexes: this.tracksIndexes( selected_tracks )
}
this.props.uiActions.showContextMenu( e, this.props.context, data )
}
@ -105,6 +107,14 @@ class TrackList extends React.Component{
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();
@ -123,12 +133,12 @@ class TrackList extends React.Component{
}
removeTracks(){
var tracks = this.selectedTracks();
// if we've got a specific action, run it
// if this tracklist handles removal, handle it
if( typeof(this.props.removeTracks) !== 'undefined' ){
return this.props.removeTracks( tracks );
var tracks = this.selectedTracks();
var tracks_indexes = this.tracksIndexes(tracks);
return this.props.removeTracks( tracks_indexes );
}
// by default, do nothing

View File

@ -26,7 +26,7 @@ const sendRequest = ( dispatch, getState, endpoint, method = 'GET', data = false
headers: {
Authorization: 'Bearer '+ response
},
data: data
data: JSON.stringify(data)
}).then(
response => {
resolve(response)
@ -326,75 +326,6 @@ export function getAlbum( uri ){
}
}
/**
* Single playlist
*
* @oaram uri string
**/
export function getPlaylist( uri ){
return (dispatch, getState) => {
// flush out the previous store value
dispatch({ type: 'SPOTIFY_PLAYLIST_LOADED', data: false });
var playlist = {};
$.when(
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'?market='+getState().spotify.country )
.then( response => {
Object.assign( playlist, response );
}),
// TODO: Check if we're authenticated before sending this request, otherwise we get a 403
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/followers/contains?ids='+getState().spotify.me.id )
.then( response => {
var is_following = 0
if( response.length > 0 ) is_following = response[0]
Object.assign(playlist, { following: response[0] } );
})
).then( () => {
dispatch({
type: 'SPOTIFY_PLAYLIST_LOADED',
data: playlist
});
});
}
}
/**
* All of my playlists
**/
function loadNextPlaylistsBatch( dispatch, getState, playlists, lastResponse ){
if( lastResponse.next ){
sendRequest( dispatch, getState, lastResponse.next )
.then( response => {
playlists = [...playlists, ...response.items]
loadNextPlaylistsBatch( dispatch, getState, playlists, response )
});
}else{
dispatch({
type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED',
data: playlists
});
}
}
export function getAllLibraryPlaylists(){
return (dispatch, getState) => {
dispatch({ type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', data: false });
sendRequest( dispatch, getState, 'me/playlists?limit=50' )
.then( response => {
loadNextPlaylistsBatch( dispatch, getState, response.items, response )
});
}
}
export function getLibraryArtists(){
return (dispatch, getState) => {
@ -567,10 +498,70 @@ export function getSearchResults( query, type = 'album,artist,playlist,track', l
/**
* Following toggles
* Playlists
**/
export function getPlaylist( uri ){
return (dispatch, getState) => {
// flush out the previous store value
dispatch({ type: 'SPOTIFY_PLAYLIST_LOADED', data: false });
var playlist = {};
$.when(
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'?market='+getState().spotify.country )
.then( response => {
Object.assign( playlist, response );
}),
// TODO: Check if we're authenticated before sending this request, otherwise we get a 403
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/followers/contains?ids='+getState().spotify.me.id )
.then( response => {
var is_following = 0
if( response.length > 0 ) is_following = response[0]
Object.assign(playlist, { following: response[0] } );
})
).then( () => {
dispatch({
type: 'SPOTIFY_PLAYLIST_LOADED',
data: playlist
});
});
}
}
function loadNextPlaylistsBatch( dispatch, getState, playlists, lastResponse ){
if( lastResponse.next ){
sendRequest( dispatch, getState, lastResponse.next )
.then( response => {
playlists = [...playlists, ...response.items]
loadNextPlaylistsBatch( dispatch, getState, playlists, response )
});
}else{
dispatch({
type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED',
data: playlists
});
}
}
export function getAllLibraryPlaylists(){
return (dispatch, getState) => {
dispatch({ type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', data: false });
sendRequest( dispatch, getState, 'me/playlists?limit=50' )
.then( response => {
loadNextPlaylistsBatch( dispatch, getState, response.items, response )
});
}
}
export function toggleFollowingPlaylist( uri, method ){
if( method == 'PUT' ) var new_state = 1
if( method == 'DELETE' ) var new_state = 0
@ -586,5 +577,16 @@ export function toggleFollowingPlaylist( uri, method ){
}
}
export function deleteTracksFromPlaylist( uri, snapshot_id, positions ){
return (dispatch, getState) => {
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/tracks', 'DELETE', { snapshot_id: snapshot_id, positions: positions } )
.then( response => {
dispatch({
type: 'SPOTIFY_PLAYLIST_TRACKS_REMOVED',
positions: positions,
snapshot_id: response.snapshot_id
});
});
}
}

View File

@ -65,4 +65,11 @@ export function dragEnd(){
return { type: 'DRAG_END' }
}
export function removeTracksFromPlaylist( track_indexes ){
return {
type: 'REMOVE_TRACKS_FROM_PLAYLIST',
track_indexes: track_indexes
}
}

View File

@ -178,6 +178,14 @@ export default function reducer(ui = {}, action){
var playlist = Object.assign({}, ui.playlist, { following: action.data })
return Object.assign({}, ui, { playlist: playlist });
case 'SPOTIFY_PLAYLIST_TRACKS_REMOVED':
var tracks = Object.assign([], ui.playlist.tracks)
for( var i = 0; i < action.positions.length; i++ ){
tracks.splice( action.positions[i], 1 )
}
var playlist = Object.assign({}, ui.playlist, action.snapshot_id, { tracks: tracks })
return Object.assign({}, ui, { playlist: playlist });
/**
* Library Playlists

View File

@ -11,8 +11,9 @@ import Dater from '../components/Dater'
import ConfirmationButton from '../components/ConfirmationButton'
import LazyLoadListener from '../components/LazyLoadListener'
import * as spotifyActions from '../services/spotify/actions'
import * as uiActions from '../services/ui/actions'
import * as mopidyActions from '../services/mopidy/actions'
import * as spotifyActions from '../services/spotify/actions'
class Playlist extends React.Component{
@ -35,11 +36,16 @@ class Playlist extends React.Component{
}
loadPlaylist( props = this.props ){
var source = helpers.uriSource( props.params.uri );
if( source == 'spotify' ){
this.props.spotifyActions.getPlaylist( props.params.uri );
}else if( source == 'm3u' && props.mopidy_connected ){
this.props.mopidyActions.getPlaylist( props.params.uri );
switch( helpers.uriSource( props.params.uri ) ){
case 'spotify':
this.props.spotifyActions.getPlaylist( props.params.uri );
break
case 'm3u':
if( props.mopidy_connected ) this.props.mopidyActions.getPlaylist( props.params.uri );
break
}
}
@ -64,6 +70,10 @@ class Playlist extends React.Component{
alert('Delete me')
}
removeTracks( track_indexes ){
this.props.uiActions.removeTracksFromPlaylist( track_indexes )
}
renderFollowOrDeleteButton(){
if( !this.props.spotify_authorized ) return null
@ -117,7 +127,7 @@ class Playlist extends React.Component{
</div>
<section className="list-wrapper">
{ this.props.playlist.tracks ? <TrackList tracks={ this.props.playlist.tracks } /> : null }
{ this.props.playlist.tracks ? <TrackList context="editable-playlist" tracks={ this.props.playlist.tracks } removeTracks={ track_indexes => this.removeTracks(track_indexes) } /> : null }
<LazyLoadListener loadMore={ () => this.loadMore() }/>
</section>
@ -145,6 +155,7 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)
}