Merging playlists by uri; Hide context on scroll
This commit is contained in:
@ -13,6 +13,90 @@ class ContextMenu extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleScroll = this.handleScroll.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
window.addEventListener("scroll", this.handleScroll, false);
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
window.removeEventListener("scroll", this.handleScroll, false);
|
||||
}
|
||||
|
||||
handleScroll(){
|
||||
if( this.props.context_menu.show ){
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
playQueueItem(){
|
||||
var selectedTracks = this.props.context_menu.data.selected_tracks;
|
||||
this.props.mopidyActions.changeTrack( selectedTracks[0].tlid );
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
removeFromQueue(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_tlids = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_tlids.push( selected_tracks[i].tlid );
|
||||
}
|
||||
this.props.mopidyActions.removeTracks( selected_tracks_tlids );
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
playItems(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_uris = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_uris.push( selected_tracks[i].uri );
|
||||
}
|
||||
this.props.mopidyActions.playTracks(selected_tracks_uris);
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
playItemsNext(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_uris = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_uris.push( selected_tracks[i].uri );
|
||||
}
|
||||
|
||||
var current_track = this.props.current_track
|
||||
var current_track_index = -1
|
||||
for( var i = 0; i < this.props.current_tracklist.length; i++ ){
|
||||
if( this.props.current_tracklist[i].tlid == this.props.current_track.tlid ){
|
||||
current_track_index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var at_position = null
|
||||
if( current_track_index > -1 ) at_position = current_track_index + 1
|
||||
|
||||
this.props.mopidyActions.enqueueTracks(selected_tracks_uris, at_position);
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
addToQueue(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_uris = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_uris.push( selected_tracks[i].uri );
|
||||
}
|
||||
this.props.mopidyActions.enqueueTracks(selected_tracks_uris);
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
addToPlaylist(){
|
||||
this.props.uiActions.openModal( 'AddToPlaylistModal', { track_indexes: this.props.context_menu.data.selected_tracks_indexes })
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
removeFromPlaylist(){
|
||||
this.props.uiActions.removeTracksFromPlaylist( this.props.playlist.uri, this.props.context_menu.data.selected_tracks_indexes )
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
renderPlaylistSubmenu(){
|
||||
@ -96,75 +180,6 @@ class ContextMenu extends React.Component{
|
||||
);
|
||||
}
|
||||
|
||||
playQueueItem(){
|
||||
var selectedTracks = this.props.context_menu.data.selected_tracks;
|
||||
this.props.mopidyActions.changeTrack( selectedTracks[0].tlid );
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
removeFromQueue(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_tlids = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_tlids.push( selected_tracks[i].tlid );
|
||||
}
|
||||
this.props.mopidyActions.removeTracks( selected_tracks_tlids );
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
playItems(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_uris = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_uris.push( selected_tracks[i].uri );
|
||||
}
|
||||
this.props.mopidyActions.playTracks(selected_tracks_uris);
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
playItemsNext(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_uris = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_uris.push( selected_tracks[i].uri );
|
||||
}
|
||||
|
||||
var current_track = this.props.current_track
|
||||
var current_track_index = -1
|
||||
for( var i = 0; i < this.props.current_tracklist.length; i++ ){
|
||||
if( this.props.current_tracklist[i].tlid == this.props.current_track.tlid ){
|
||||
current_track_index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var at_position = null
|
||||
if( current_track_index > -1 ) at_position = current_track_index + 1
|
||||
|
||||
this.props.mopidyActions.enqueueTracks(selected_tracks_uris, at_position);
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
addToQueue(){
|
||||
var selected_tracks = this.props.context_menu.data.selected_tracks;
|
||||
var selected_tracks_uris = [];
|
||||
for( var i = 0; i < selected_tracks.length; i++ ){
|
||||
selected_tracks_uris.push( selected_tracks[i].uri );
|
||||
}
|
||||
this.props.mopidyActions.enqueueTracks(selected_tracks_uris);
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
addToPlaylist(){
|
||||
this.props.uiActions.openModal( 'AddToPlaylistModal', { track_indexes: this.props.context_menu.data.selected_tracks_indexes })
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
removeFromPlaylist(){
|
||||
this.props.uiActions.removeTracksFromPlaylist( this.props.playlist.uri, this.props.context_menu.data.selected_tracks_indexes )
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
render(){
|
||||
if( !this.props.context_menu.show ) return null;
|
||||
|
||||
|
||||
@ -154,4 +154,29 @@ export let uriType = function( uri ){
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge duplicated items in an array
|
||||
*
|
||||
* @param list Array the unclean array
|
||||
* @param key string = the unique key (uri, tlid, etc)
|
||||
**/
|
||||
export let mergeDuplicates = function(list, key){
|
||||
var clean_list = [];
|
||||
var keyed_list = {};
|
||||
|
||||
for( var i in list ){
|
||||
var item = list[i]
|
||||
if( item[key] in keyed_list ){
|
||||
item = Object.assign({}, keyed_list[item[key]], item)
|
||||
}
|
||||
keyed_list[item[key]] = item;
|
||||
}
|
||||
|
||||
for( i in keyed_list ){
|
||||
clean_list.push(keyed_list[i]);
|
||||
}
|
||||
|
||||
return clean_list;
|
||||
}
|
||||
@ -248,6 +248,28 @@ const MopidyMiddleware = (function(){
|
||||
})
|
||||
break;
|
||||
|
||||
case 'MOPIDY_ADD_PLAYLIST_TRACKS':
|
||||
|
||||
instruct( socket, store, 'playlists.lookup', { uri: action.playlist_uri })
|
||||
.then( response => {
|
||||
var tracks = [];
|
||||
for( var i = 0; i < action.tracks_uris.length; i++ ){
|
||||
tracks.push({
|
||||
__model__: "Track",
|
||||
uri: action.tracks_uris[i]
|
||||
});
|
||||
}
|
||||
|
||||
var playlist = Object.assign({}, response)
|
||||
playlist.tracks = [...playlist.tracks, ...tracks]
|
||||
|
||||
instruct( socket, store, 'playlists.save', { playlist: playlist } )
|
||||
.then( response => {
|
||||
store.dispatch({ type: 'PLAYLIST_TRACKS_ADDED', tracks_uris: action.tracks_uris });
|
||||
})
|
||||
});
|
||||
break
|
||||
|
||||
case 'MOPIDY_REMOVE_PLAYLIST_TRACKS':
|
||||
|
||||
// reverse order our indexes (otherwise removing from top will affect the keys following)
|
||||
|
||||
@ -588,6 +588,19 @@ export function toggleFollowingPlaylist( uri, method ){
|
||||
}
|
||||
}
|
||||
|
||||
export function addTracksToPlaylist( playlist_uri, tracks_uris ){
|
||||
return (dispatch, getState) => {
|
||||
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',playlist_uri) + '/playlists/'+ helpers.getFromUri('playlistid',playlist_uri) + '/tracks', 'POST', { uris: tracks_uris } )
|
||||
.then( response => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_ADDED',
|
||||
tracks_uris: tracks_uris,
|
||||
snapshot_id: response.snapshot_id
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteTracksFromPlaylist( uri, snapshot_id, tracks_indexes ){
|
||||
return (dispatch, getState) => {
|
||||
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/tracks', 'DELETE', { snapshot_id: snapshot_id, positions: tracks_indexes } )
|
||||
|
||||
@ -24,12 +24,22 @@ const SpotifyMiddleware = (function(){
|
||||
return
|
||||
}
|
||||
if( !store.getState().spotify.me || store.getState().spotify.me.id != playlist.owner.id ){
|
||||
alert('You can only modify tracks you own')
|
||||
alert('You can only modify playlists you own')
|
||||
return
|
||||
}
|
||||
store.dispatch( actions.deleteTracksFromPlaylist( playlist.uri, playlist.snapshot_id, action.tracks_indexes ))
|
||||
break
|
||||
|
||||
|
||||
case 'SPOTIFY_ADD_PLAYLIST_TRACKS':
|
||||
|
||||
if( !store.getState().spotify.authorized ){
|
||||
alert('Must be logged in to Spotify to do this')
|
||||
return
|
||||
}
|
||||
store.dispatch( actions.addTracksToPlaylist( action.playlist_uri, action.tracks_uris ))
|
||||
break
|
||||
|
||||
// when our mopidy server current track changes
|
||||
case 'MOPIDY_CURRENTTLTRACK':
|
||||
|
||||
|
||||
@ -107,7 +107,7 @@ export function addTracksToPlaylist( playlist_uri, tracks ){
|
||||
|
||||
case 'm3u':
|
||||
return {
|
||||
type: 'MOPIDY_ADD_TRACKS_TO_PLAYLIST',
|
||||
type: 'MOPIDY_ADD_PLAYLIST_TRACKS',
|
||||
playlist_uri: playlist_uri,
|
||||
tracks_uris: tracks_uris
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
|
||||
import * as helpers from '../../helpers'
|
||||
|
||||
export default function reducer(ui = {}, action){
|
||||
switch (action.type) {
|
||||
|
||||
@ -246,15 +248,12 @@ export default function reducer(ui = {}, action){
|
||||
**/
|
||||
|
||||
case 'MOPIDY_PLAYLISTS_LOADED':
|
||||
if( !action.data ) return ui
|
||||
return Object.assign({}, ui, {
|
||||
playlists: [...ui.playlists, ...action.data],
|
||||
});
|
||||
|
||||
case 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED':
|
||||
if( !action.data ) return ui
|
||||
var playlists = [...ui.playlists, ...action.data]
|
||||
playlists = helpers.mergeDuplicates(playlists, 'uri')
|
||||
return Object.assign({}, ui, {
|
||||
playlists: [...ui.playlists, ...action.data]
|
||||
playlists: playlists
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -80,12 +80,12 @@ class Playlist extends React.Component{
|
||||
switch( helpers.uriSource( this.props.params.uri ) ){
|
||||
|
||||
case 'm3u':
|
||||
return <ConfirmationButton className="large tertiary" content="Delete" confirmingContent="Confirm" onConfirm={ e => this.delete() } />
|
||||
return <ConfirmationButton className="large tertiary" content="Delete" confirmingContent="Are you sure?" onConfirm={ e => this.delete() } />
|
||||
break
|
||||
|
||||
case 'spotify':
|
||||
if( this.props.playlist.owner && this.props.playlist.owner.id == this.props.spotify_userid ){
|
||||
return <ConfirmationButton className="large tertiary" content="Delete" confirmingContent="Are you sure?" onConfirm={ e => this.delete() } />
|
||||
return <ConfirmationButton className="large tertiary" content="Delete" confirmingContent="Are you sure?" onConfirm={ e => this.unfollow() } />
|
||||
}else if( this.props.playlist.following ){
|
||||
return <button className="large tertiary" onClick={ e => this.unfollow() }>Unfollow</button>
|
||||
}else{
|
||||
|
||||
Reference in New Issue
Block a user