diff --git a/src/js/components/ContextMenu.js b/src/js/components/ContextMenu.js index 660d4b48..9739386b 100755 --- a/src/js/components/ContextMenu.js +++ b/src/js/components/ContextMenu.js @@ -34,11 +34,25 @@ class ContextMenu extends React.Component{ } componentWillReceiveProps( nextProps ){ + // if we've been given a menu object (ie activated) when we didn't have one prior if (nextProps.menu && !this.props.menu){ this.setState({ submenu_expanded: false }) $('body').addClass('context-menu-open') + var context = this.getContext() + + // if we're able to be in the library, run a check + if (this.props.spotify_authorized && context.source == 'spotify'){ + switch (nextProps.menu.context){ + case 'artist': + case 'album': + case 'playlist': + this.props.spotifyActions.following(nextProps.menu.items[0].uri) + break + } + } + // we DID have one prior, and now we don't } else if (this.props.menu && !nextProps.menu){ $('body').removeClass('context-menu-open') @@ -58,6 +72,81 @@ class ContextMenu extends React.Component{ } } + getContext(){ + var context = { + name: null, + nice_name: 'Unknown' + } + + if (this.props.menu && this.props.menu.context){ + context.name = this.props.menu.context + context.nice_name = this.props.menu.context + + // handle ugly labels + switch (this.props.menu.context){ + case 'playlist': + case 'editable-playlist': + context.nice_name = 'playlist' + break + + case 'track': + case 'queue-track': + case 'playlist-track': + case 'editable-playlist-track': + context.nice_name = 'track' + break + } + + // Consider the object(s) themselves + // We can only really accommodate the first item. The only instances where + // there is multiple is tracklists, when they're all of the same source (except search?) + if (this.props.menu.items && this.props.menu.items.length > 0){ + var item = this.props.menu.items[0] + context.item = item + context.source = helpers.uriSource(item.uri) + context.type = helpers.uriType(item.uri) + context.in_library = this.inLibrary(item) + } + } + + return context + } + + inLibrary(item = null){ + if (!item){ + return false + } + + switch (helpers.uriType(item.uri)){ + case 'artist': + return (this.props.library_artists && this.props.library_artists.indexOf(item.uri) > -1) + break + case 'album': + return (this.props.library_albums && this.props.library_albums.indexOf(item.uri) > -1) + break + case 'playlist': + return (this.props.library_playlists && this.props.library_playlists.indexOf(item.uri) > -1) + break + } + return false + } + + canBeInLibrary(){ + if (!this.props.spotify_authorized){ + return false + } + return (helpers.uriSource(this.props.menu.items[0].uri) == 'spotify') + } + + toggleInLibrary(in_library){ + this.props.uiActions.hideContextMenu() + if (in_library){ + this.props.spotifyActions.following(this.props.menu.items[0].uri, 'DELETE') + } else { + this.props.spotifyActions.following(this.props.menu.items[0].uri, 'PUT') + } + } + playQueueItem(){ this.props.uiActions.hideContextMenu() var tracks = this.props.menu.items; @@ -84,10 +173,6 @@ class ContextMenu extends React.Component{ this.props.mopidyActions.enqueueURIsNext(this.props.menu.uris, this.props.menu.tracklist_uri) } - addToPlaylist(){ - this.setState({ submenu_expanded: !this.state.submenu_expanded }) - } - addToQueue(){ this.props.uiActions.hideContextMenu() this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri) @@ -139,6 +224,7 @@ class ContextMenu extends React.Component{ closeAndDeselectTracks(){ this.props.uiActions.hideContextMenu(); + // TODO } renderPlaylistSubmenu(){ @@ -175,100 +261,30 @@ class ContextMenu extends React.Component{ ) } - getItems(){ - switch (this.props.menu.context) { - - case 'album': - var items = [ - { handleClick: 'playURIs', label: 'Play' }, - { handleClick: 'playURIsNext', label: 'Play next' }, - { handleClick: 'addToQueue', label: 'Add to queue' }, - { handleClick: 'goToArtist', label: 'Go to artist' }, - // { handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO - { handleClick: 'copyURIs', label: 'Copy URI' } - ] - break - - case 'artist': - var items = [ - //{ handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO - { handleClick: 'startRadio', label: 'Start radio' }, - { handleClick: 'copyURIs', label: 'Copy URI' } - ] - break - - case 'playlist': - var items = [ - { handleClick: 'playURIs', label: 'Play' }, - { handleClick: 'goToUser', label: 'Go to user' }, - // { handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO - { handleClick: 'copyURIs', label: 'Copy URI' } - ] - break - - case 'queue': - var items = [ - { handleClick: 'playQueueItem', label: 'Play' }, - { handleClick: 'addToPlaylist', label: 'Add to playlist' }, - { handleClick: 'copyURIs', label: 'Copy URIs' }, - { handleClick: 'removeFromQueue', label: 'Remove' } - ] - break - - case 'editable-playlist-track': - var items = [ - { handleClick: 'playURIs', label: 'Play' }, - { handleClick: 'playURIsNext', label: 'Play next' }, - { handleClick: 'addToQueue', label: 'Add to queue' }, - { handleClick: 'addToPlaylist', label: 'Add to playlist' }, - { handleClick: 'startRadio', label: 'Start radio' }, - { handleClick: 'copyURIs', label: 'Copy URIs' }, - { handleClick: 'removeFromPlaylist', label: 'Remove' } - ] - break - - default: - var items = [ - { handleClick: 'playURIs', label: 'Play' }, - { handleClick: 'playURIsNext', label: 'Play next' }, - { handleClick: 'addToQueue', label: 'Add to queue' }, - { handleClick: 'addToPlaylist', label: 'Add to playlist' }, - { handleClick: 'startRadio', label: 'Start radio' }, - { handleClick: 'copyURIs', label: 'Copy URIs' } - ] - break - } - - return items - } - renderTitle(){ - if (!this.props.menu.items || this.props.menu.items.length <= 0){ - return null - } + var context = this.getContext() - switch (this.props.menu.context){ + switch (context.type){ case 'artist': case 'album': case 'playlist': - var item = this.props.menu.items[0] var style = null - if (item && item.images){ + if (context.item && context.item.images){ style = { - backgroundImage: 'url('+helpers.sizedImages(item.images).medium+')' + backgroundImage: 'url('+helpers.sizedImages(context.item.images).medium+')' } } return ( - + {style ?
: null}
- {helpers.uriSource(item.uri)} + {context.source}   - {this.props.menu.context} + {context.nice_name}
-
{item.name}
+
{context.item.name}
) break @@ -277,9 +293,9 @@ class ContextMenu extends React.Component{ return (
- {helpers.uriSource(this.props.menu.items[0].uri)} + {context.source}   - {this.props.menu.context}s + {context.nice_name}s
{this.props.menu.items.length} items @@ -292,35 +308,182 @@ class ContextMenu extends React.Component{ } renderItems(){ - var items = this.getItems() + var context = this.getContext() - return ( -
- { - items.map((item, index) => { - if (item.handleClick == 'addToPlaylist'){ - return ( - - this[item.handleClick](e)}> - { item.label } - - - {this.renderPlaylistSubmenu()} - - ) - }else{ - return ( - - this[item.handleClick](e)}> - { item.label } - - - ) - } - }) - } -
+ var play_uris = ( + + this.playURIs(e)}> + Play + + ) + + var play_queue_item = ( + + this.playQueueItem(e)}> + Play + + + ) + + var play_uris_next = ( + + this.playURIsNext(e)}> + Play next + + + ) + + var add_to_queue = ( + + this.addToQueue(e)}> + Add to queue + + + ) + + var add_to_playlist = ( + + this.setState({ submenu_expanded: !this.state.submenu_expanded })}> + Add to playlist + + + {this.renderPlaylistSubmenu()} + + ) + + var toggle_in_library = ( + + this.toggleInLibrary(context.in_library)}> + + {context.in_library ? 'Remove from library' : 'Add to library'} + + + + ) + + var go_to_artist = ( + + this.goToArtist(e)}> + Go to artist + + + ) + + var go_to_user = ( + + this.goToUser(e)}> + Go to user + + + ) + + var start_radio = ( + + this.startRadio(e)}> + Start {this.context.nice_name} radio + + + ) + + var remove_from_queue = ( + + this.removeFromQueue(e)}> + Remove + + + ) + + var remove_from_playlist = ( + + this.removeFromPlaylist(e)}> + Remove + + + ) + + var copy_uris = ( + + this.copyURIs(e)}> + Copy URIs + + + ) + + switch (context.name){ + + case 'album': + return ( +
+ {play_uris} + {play_uris_next} + {add_to_queue} + {this.canBeInLibrary() ? toggle_in_library : null} + {go_to_artist} + {copy_uris} +
+ ) + break + + case 'artist': + return ( +
+ {start_radio} + {this.canBeInLibrary() ? toggle_in_library : null} + {copy_uris} +
+ ) + break + + case 'playlist': + return ( +
+ {play_uris} + {this.canBeInLibrary() ? toggle_in_library : null} + {context.source == 'spotify' ? go_to_user : null} + {copy_uris} +
+ ) + break + + case 'queue': + return ( +
+ {play_queue_item} + {add_to_playlist} + {copy_uris} + {remove_from_queue} +
+ ) + break + + case 'editable-playlist-track': + return ( +
+ {play_uris} + {play_uris_next} + {add_to_queue} + {add_to_playlist} + {start_radio} + {copy_uris} + {remove_from_playlist} +
+ ) + break + + default: + return ( +
+ {play_uris} + {play_uris_next} + {add_to_queue} + {add_to_playlist} + {start_radio} + {copy_uris} +
+ ) + break + } } render(){ @@ -330,11 +493,7 @@ class ContextMenu extends React.Component{ left: this.props.menu.position_x, top: this.props.menu.position_y, } - - var items = this.getItems() - var height = 0 - if (items) height = items.length * 34 // this is an approximation of how tall each menu item is - + var height = 0 // TODO: use jquery to detect height var className = "context-menu "+this.props.menu.context if (this.state.submenu_expanded) className += ' submenu-expanded' if (this.props.menu.position_x > (window.innerWidth - 154)) className += ' right-align' @@ -361,7 +520,11 @@ const mapStateToProps = (state, ownProps) => { menu: state.ui.context_menu, current_track: state.ui.current_track, current_tracklist: state.ui.current_tracklist, - playlists: state.ui.playlists + library_artists: state.ui.library_artists, + library_albums: state.ui.library_albums, + library_playlists: state.ui.library_playlists, + playlists: state.ui.playlists, + spotify_authorized: state.spotify.authorized } } diff --git a/src/js/components/List.js b/src/js/components/List.js index 69f4d3fa..12cad121 100755 --- a/src/js/components/List.js +++ b/src/js/components/List.js @@ -39,7 +39,8 @@ class List extends React.Component{
{ this.props.columns.map( (col, col_index) => { - return
{ col.label ? col.label : col.name }
+ var className = 'col '+col.name.replace('.','_') + return
{ col.label ? col.label : col.name }
}) }
@@ -88,8 +89,9 @@ class List extends React.Component{ key={row_index}> { this.props.columns.map( (col, col_index) => { + var className = 'col '+col.name.replace('.','_') return ( -
+
{ this.renderValue(row, col.name) }
) diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index bc43e9aa..98535377 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -538,6 +538,7 @@ export default function reducer(ui = {}, action){ return Object.assign({}, ui, { library_playlists: library_playlists, + library_playlists_started: true library_playlists_loaded: true }); diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 075dd7b3..e5a05cae 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -218,7 +218,7 @@ class Artist extends React.Component{

{this.props.artist ? this.props.artist.name : null}

{ can_play_radio ? : null} - { can_follow ? : null} + { can_follow ? : null} this.handleContextMenu(e)} />
{ this.renderSubViewMenu() } diff --git a/src/js/views/library/LibraryAlbums.js b/src/js/views/library/LibraryAlbums.js index 17eb7936..eba0d964 100755 --- a/src/js/views/library/LibraryAlbums.js +++ b/src/js/views/library/LibraryAlbums.js @@ -35,7 +35,7 @@ class LibraryAlbums extends React.Component{ e: e, context: 'album', uris: [item.uri], - item: item + items: [item] } this.props.uiActions.showContextMenu(data) } @@ -61,33 +61,28 @@ class LibraryAlbums extends React.Component{ if( this.props.view == 'list' ){ var columns = [ { - width: 30, label: 'Name', name: 'name' }, { - width: 30, label: 'Artists', name: 'artists' }, { - width: 15, label: 'Added', name: 'added_at' }, { - width: 15, label: 'Released', name: 'release_date' }, { - width: 10, label: 'Tracks', name: 'tracks_total' } ] return ( -
+
this.handleContextMenu(e,item)} rows={albums} @@ -216,8 +211,8 @@ const mapStateToProps = (state, ownProps) => { sort: state.ui.library_albums_sort, sort_reverse: state.ui.library_albums_sort_reverse, library_albums: state.ui.library_albums, - library_albums_more: state.ui.library_albums_more, library_albums_started: state.ui.library_albums_started, + library_albums_more: state.ui.library_albums_more } } diff --git a/src/js/views/library/LibraryArtists.js b/src/js/views/library/LibraryArtists.js index 207544b4..fafec3f1 100755 --- a/src/js/views/library/LibraryArtists.js +++ b/src/js/views/library/LibraryArtists.js @@ -26,7 +26,7 @@ class LibraryArtists extends React.Component{ e: e, context: 'artist', uris: [item.uri], - item: item + items: [item] } this.props.uiActions.showContextMenu(data) } @@ -57,22 +57,19 @@ class LibraryArtists extends React.Component{ var columns = [ { label: 'Name', - width: 70, name: 'name' }, { label: 'Followers', - width: 15, name: 'followers.total' }, { label: 'Popularity', - width: 15, name: 'popularity' } ] return ( -
+
this.handleContextMenu(e,item)} rows={artists} @@ -163,6 +160,7 @@ const mapStateToProps = (state, ownProps) => { sort: state.ui.library_artists_sort, sort_reverse: state.ui.library_artists_sort_reverse, library_artists: state.ui.library_artists, + library_artists_started: state.ui.library_artists_started, library_artists_more: state.ui.library_artists_more, library_artists_started: state.ui.library_artists_started, view: state.ui.library_artists_view diff --git a/src/js/views/library/LibraryLocalArtists.js b/src/js/views/library/LibraryLocalArtists.js index 1929e079..1ec6297f 100755 --- a/src/js/views/library/LibraryLocalArtists.js +++ b/src/js/views/library/LibraryLocalArtists.js @@ -32,7 +32,7 @@ class LibraryLocalArtists extends React.Component{ e: e, context: 'artist', uris: [item.uri], - item: item + items: [item] } this.props.uiActions.showContextMenu(data) } diff --git a/src/js/views/library/LibraryPlaylists.js b/src/js/views/library/LibraryPlaylists.js index bf7e37f5..07299631 100755 --- a/src/js/views/library/LibraryPlaylists.js +++ b/src/js/views/library/LibraryPlaylists.js @@ -26,7 +26,7 @@ class LibraryPlaylists extends React.Component{ e: e, context: 'playlist', uris: [item.uri], - item: item + items: [item] } this.props.uiActions.showContextMenu(data) } @@ -60,33 +60,28 @@ class LibraryPlaylists extends React.Component{ if( this.props.view == 'list' ){ var columns = [ { - width: 50, label: 'Name', name: 'name' }, { - width: 20, label: 'Owner', name: 'owner' }, { - width: 10, label: 'Source', name: 'source' }, { - width: 10, label: 'Tracks', name: 'tracks_total' }, { - width: 10, label: 'Editable', name: 'can_edit' } ] return ( -
+
this.handleContextMenu(e,item)} rows={playlists} diff --git a/src/scss/components/_lists.scss b/src/scss/components/_lists.scss index 3c47bb5e..c18ce7f0 100755 --- a/src/scss/components/_lists.scss +++ b/src/scss/components/_lists.scss @@ -79,57 +79,105 @@ margin-top: -3px; background: transparent; } - - @include responsive( $bp_medium ){ - padding: 7px 10px 7px 20px; - - .col { - &.name { - width: 90%; - padding-right: 10%; - } - - &.duration { - width: auto; - position: absolute; - right: 10px; - text-align: right; - } - - &.artists, - &.album { - width: auto; - color: $mid_grey; - display: inline-block; - } - - &.album { - @include bullet(); - } - } - - &.header { - display: none; - } - - .select-state, - .play-state { - top: 11px; - left: 5px; - } - } } - - &.playlist { + @include responsive( $bp_medium ){ + padding: 7px 10px 7px 20px; .col { &.name { - width: 30%; + width: 90% !important; + padding-right: 10%; } - &.owner { - width: 20%; + + &.duration { + width: auto; + position: absolute; + right: 10px; + text-align: right; } + + &:not(.name){ + color: $mid_grey; + display: inline-block; + width: auto !important; + } + + &:not(.name):not(:nth-child(2)){ + @include bullet(); + } + + &.followers_total:after { + content: ' followers'; + } + + &.tracks_total:after { + content: ' tracks'; + } + + &.popularity:after { + content: '% popularity'; + } + + &.can_edit, + &.release_date { + display: none; + } + } + + &.header { + display: none; + } + + .select-state, + .play-state { + top: 11px; + left: 5px; + } + } + } + + &.playlist-list .list-item { + .col { + &.name { + width: 50%; + } + &.owner { + width: 20% + } + &.source, + &.can_edit, + &.tracks_total, + &.popularity { + width: 10%; + } + } + } + + &.album-list .list-item { + .col { + &.name, + &.artists { + width: 30%; + } + &.added_at, + &.release_date { + width: 15%; + } + &.tracks_total { + width: 10%; + } + } + } + + &.artist-list .list-item { + .col { + &.name { + width: 70%; + } + &.followers_total, + &.popularity { + width: 15%; } } }