diff --git a/src/js/components/ContextMenu.js b/src/js/components/ContextMenu.js
index 720f7ca9..8dbd58dd 100755
--- a/src/js/components/ContextMenu.js
+++ b/src/js/components/ContextMenu.js
@@ -10,6 +10,7 @@ import * as coreActions from '../services/core/actions'
import * as uiActions from '../services/ui/actions'
import * as pusherActions from '../services/pusher/actions'
import * as mopidyActions from '../services/mopidy/actions'
+import * as lastfmActions from '../services/lastfm/actions'
import * as spotifyActions from '../services/spotify/actions'
import TrackList from './TrackList'
@@ -41,10 +42,10 @@ class ContextMenu extends React.Component{
this.setState({ submenu_expanded: false })
$('body').addClass('context-menu-open')
- var context = this.getContext()
+ var context = this.getContext(nextProps);
// if we're able to be in the library, run a check
- if (this.props.spotify_authorized && context.source == 'spotify'){
+ if (nextProps.spotify_authorized && context.source == 'spotify'){
switch (nextProps.menu.context){
case 'artist':
case 'album':
@@ -54,6 +55,14 @@ class ContextMenu extends React.Component{
}
}
+ // if we're able to be in the LastFM library, run a check
+ if (nextProps.lastfm_authorized && context.is_track && context.items_count == 1){
+ if (nextProps.menu.items[0].uri && this.props.tracks[nextProps.menu.items[0].uri] !== undefined){
+ var track = this.props.tracks[nextProps.menu.items[0].uri];
+ this.props.lastfmActions.getTrack(track);
+ }
+ }
+
// we DID have one prior, and now we don't
} else if (this.props.menu && !nextProps.menu){
$('body').removeClass('context-menu-open')
@@ -73,41 +82,44 @@ class ContextMenu extends React.Component{
}
}
- getContext(){
+ getContext(props = this.props){
var context = {
name: null,
- nice_name: 'Unknown'
+ nice_name: 'Unknown',
+ is_track: false
}
- if (this.props.menu && this.props.menu.context){
- context.name = this.props.menu.context
- context.nice_name = this.props.menu.context
+ if (props.menu && props.menu.context){
+ context.name = props.menu.context;
+ context.nice_name = props.menu.context;
// handle ugly labels
- switch (this.props.menu.context){
+ switch (props.menu.context){
case 'playlist':
case 'editable-playlist':
- context.nice_name = 'playlist'
+ context.nice_name = 'playlist';
break
case 'track':
case 'queue-track':
case 'playlist-track':
case 'editable-playlist-track':
- context.nice_name = 'track'
+ context.nice_name = 'track';
+ context.is_track = true;
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.items_count = this.props.menu.items.length
- context.source = helpers.uriSource(item.uri)
- context.type = helpers.uriType(item.uri)
- context.in_library = this.inLibrary(item)
+ if (props.menu.items && props.menu.items.length > 0){
+ var item = props.menu.items[0]
+ context.item = item;
+ context.items_count = props.menu.items.length;
+ context.source = helpers.uriSource(item.uri);
+ context.type = helpers.uriType(item.uri);
+ context.in_library = this.inLibrary(item);
+ context.is_loved = this.isLoved(item);
}
}
@@ -133,6 +145,23 @@ class ContextMenu extends React.Component{
return false
}
+ /**
+ * TODO: Currently the select track keys are the only details available. We need
+ * the actual track object reference (including name and artists) to getTrack from LastFM
+ **/
+ isLoved(item = null){
+ if (!item){
+ return false
+ }
+
+ if (this.props.tracks[item.uri] === undefined){
+ return false;
+ }
+ var track = this.props.tracks[item.uri];
+
+ return (track.userloved !== undefined && track.userloved == "1");
+ }
+
canBeInLibrary(){
if (!this.props.spotify_authorized){
return false
@@ -190,6 +219,20 @@ class ContextMenu extends React.Component{
this.props.coreActions.addTracksToPlaylist(playlist_uri, this.props.menu.uris)
}
+ toggleLoved(e, is_loved){
+ this.props.uiActions.hideContextMenu()
+ if (is_loved){
+ this.props.lastfmActions.unloveTrack(this.props.menu.items[0])
+ } else {
+ this.props.lastfmActions.loveTrack(this.props.menu.items[0])
+ }
+ }
+
+ unloveTrack(e){
+ this.props.uiActions.hideContextMenu()
+ this.props.lastfmActions.unloveTrack(this.props.menu.items[0])
+ }
+
removeFromPlaylist(e){
this.props.uiActions.hideContextMenu()
this.props.coreActions.removeTracksFromPlaylist(this.props.menu.tracklist_uri, this.props.menu.indexes)
@@ -445,6 +488,29 @@ class ContextMenu extends React.Component{
)
+
+ if (helpers.isLoading(this.props.load_queue,['lastfm_track.getInfo'])){
+ var toggle_loved = (
+
+
+
+ Love track
+
+
+
+ )
+ } else {
+ var toggle_loved = (
+
+ this.toggleLoved(e, context.is_loved)}>
+
+ {context.is_loved ? 'Unlove' : 'Love'} track
+
+
+
+ )
+ }
+
var go_to_artist = (
this.goToArtist(e)}>
@@ -580,6 +646,7 @@ class ContextMenu extends React.Component{
{context.items_count == 1 ? play_queue_item : null}
{context.items_count == 1 ? : null}
{add_to_playlist}
+ {context.items_count == 1 ? toggle_loved : null}
{context.source == 'spotify' && context.items_count <= 5 ? go_to_recommendations : null}
{context.items_count == 1 ? go_to_track : null}
@@ -598,6 +665,7 @@ class ContextMenu extends React.Component{
{context.source == 'spotify' && context.items_count == 1 ? start_radio : null}
{add_to_playlist}
+ {context.items_count == 1 ? toggle_loved : null}
{context.source == 'spotify' && context.items_count <= 5 ? go_to_recommendations : null}
{context.items_count == 1 ? go_to_track : null}
@@ -616,6 +684,7 @@ class ContextMenu extends React.Component{
{context.source == 'spotify' && context.items_count == 1 ? start_radio : null}
{add_to_playlist}
+ {context.items_count == 1 ? toggle_loved : null}
{context.source == 'spotify' && context.items_count <= 5 ? go_to_recommendations : null}
{context.items_count == 1 ? go_to_track : null}
@@ -663,6 +732,7 @@ class ContextMenu extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
menu: state.ui.context_menu,
+ load_queue: state.ui.load_queue,
processes: state.ui.processes,
current_track: state.core.current_track,
current_tracklist: state.core.current_tracklist,
@@ -675,7 +745,9 @@ const mapStateToProps = (state, ownProps) => {
spotify_library_albums: state.spotify.library_albums,
mopidy_library_albums: state.mopidy.library_albums,
playlists: state.core.playlists,
- spotify_authorized: state.spotify.authorization
+ tracks: state.core.tracks,
+ spotify_authorized: state.spotify.authorization,
+ lastfm_authorized: state.lastfm.session
}
}
@@ -685,6 +757,7 @@ const mapDispatchToProps = (dispatch) => {
uiActions: bindActionCreators(uiActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch),
+ lastfmActions: bindActionCreators(lastfmActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch)
}
}
diff --git a/src/js/components/TrackList.js b/src/js/components/TrackList.js
index 61a47830..a712fc8f 100755
--- a/src/js/components/TrackList.js
+++ b/src/js/components/TrackList.js
@@ -202,7 +202,7 @@ class TrackList extends React.Component{
uris: selected_tracks_uris,
indexes: selected_tracks_indexes
}
- this.props.uiActions.showContextMenu(data)
+ this.props.uiActions.showContextMenu(data);
}
handleSelection(e,track_key){
diff --git a/src/js/services/core/middleware.js b/src/js/services/core/middleware.js
index 3559a18f..820e28c6 100755
--- a/src/js/services/core/middleware.js
+++ b/src/js/services/core/middleware.js
@@ -119,6 +119,14 @@ const CoreMiddleware = (function(){
action.album.images = helpers.digestMopidyImages(store.getState().mopidy, action.album.images);
}
+ // Load our tracks
+ if (action.album.tracks){
+ store.dispatch({
+ type: 'TRACKS_LOADED',
+ tracks: action.album.tracks
+ });
+ }
+
next(action)
break
@@ -129,13 +137,30 @@ const CoreMiddleware = (function(){
if (action.albums[i].images && action.albums[i].images.length > 0){
action.albums[i].images = helpers.digestMopidyImages(store.getState().mopidy, action.albums[i].images);
}
+
+ // Load our tracks
+ if (action.albums[i].tracks){
+ store.dispatch({
+ type: 'TRACKS_LOADED',
+ tracks: action.albums[i].tracks
+ });
+ }
}
next(action)
break
case 'ARTIST_LOADED':
- if (action.data) ReactGA.event({ category: 'Artist', action: 'Load', label: action.artist.uri })
+ if (action.data) ReactGA.event({ category: 'Artist', action: 'Load', label: action.artist.uri });
+
+ // Load our tracks
+ if (action.artist.tracks){
+ store.dispatch({
+ type: 'TRACKS_LOADED',
+ tracks: action.artist.tracks
+ });
+ }
+
next(action)
break
@@ -239,9 +264,17 @@ const CoreMiddleware = (function(){
}
}
+ // Load our tracks
+ if (action.playlist.tracks){
+ store.dispatch({
+ type: 'TRACKS_LOADED',
+ tracks: action.playlist.tracks
+ });
+ }
+
// proceed as usual
- action.playlist = playlist
- next(action)
+ action.playlist = playlist;
+ next(action);
break
case 'PLAYLISTS_LOADED':
@@ -290,6 +323,49 @@ const CoreMiddleware = (function(){
next(action)
break
+ case 'MOPIDY_TLTRACKS':
+
+ var core = store.getState().core;
+ var tracklist = []
+ for (var i = 0; i < action.data.length; i++){
+
+ var tltrack = helpers.formatTracks(action.data[i]);
+
+ // load our metadata (if we have any for that tlid)
+ if (core.queue_metadata !== undefined && core.queue_metadata['tlid_'+tltrack.tlid] !== undefined){
+ var metadata = core.queue_metadata['tlid_'+tltrack.tlid]
+ } else {
+ var metadata = {}
+ }
+
+ var current_tlid = null;
+ if (core.current_track && core.tracks && core.tracks[core.current_track] !== undefined && core.tracks[core.current_track].tlid !== undefined){
+ current_tlid = core.tracks[core.current_track].tlid;
+ }
+
+ var track = Object.assign(
+ {},
+ tltrack,
+ metadata,
+ {
+ playing: (tltrack.tlid == current_tlid)
+ })
+ tracklist.push(track)
+ }
+
+ // Append to our action
+ tracklist = helpers.formatTracks(tracklist);
+ action.tracklist = tracklist;
+
+ // Load our tracks into index
+ store.dispatch({
+ type: 'TRACKS_LOADED',
+ tracks: tracklist
+ });
+
+ next(action);
+ break
+
// Get assets from all of our providers
case 'GET_LIBRARY_PLAYLISTS':
if (store.getState().spotify.connected){
diff --git a/src/js/services/core/reducer.js b/src/js/services/core/reducer.js
index 87b5f2b2..c258c2f3 100755
--- a/src/js/services/core/reducer.js
+++ b/src/js/services/core/reducer.js
@@ -12,38 +12,10 @@ export default function reducer(core = {}, action){
**/
case 'MOPIDY_TLTRACKS':
- if (!action.data ) return core
-
- var tracklist = []
- for (var i = 0; i < action.data.length; i++){
-
- var tltrack = helpers.formatTracks(action.data[i]);
-
- // load our metadata (if we have any for that tlid)
- if (core.queue_metadata !== undefined && core.queue_metadata['tlid_'+tltrack.tlid] !== undefined){
- var metadata = core.queue_metadata['tlid_'+tltrack.tlid]
- } else {
- var metadata = {}
- }
-
- var current_tlid = null;
- if (core.current_track && core.tracks && core.tracks[core.current_track] !== undefined && core.tracks[core.current_track].tlid !== undefined){
- current_tlid = core.tracks[core.current_track].tlid;
- }
-
- var track = Object.assign(
- {},
- tltrack,
- metadata,
- {
- playing: (tltrack.tlid == current_tlid)
- })
- tracklist.push(track)
+ if (!action.tracklist){
+ return core;
}
-
- tracklist = helpers.formatTracks(tracklist);
-
- return Object.assign({}, core, { current_tracklist: tracklist });
+ return Object.assign({}, core, { current_tracklist: action.tracklist });
case 'MOPIDY_CURRENTTLTRACK':
if (!action.data) return core
diff --git a/src/js/services/lastfm/actions.js b/src/js/services/lastfm/actions.js
index 36c6bfbf..a65f1546 100755
--- a/src/js/services/lastfm/actions.js
+++ b/src/js/services/lastfm/actions.js
@@ -15,7 +15,10 @@ const sendRequest = (dispatch, getState, params, signed = false) => {
return new Promise((resolve, reject) => {
var loader_key = helpers.generateGuid();
- dispatch(uiActions.startLoading(loader_key, 'lastfm_'+params));
+ var method = params.substring(params.indexOf("method=")+7, params.length);
+ method = method.substring(0, method.indexOf("&"));
+
+ dispatch(uiActions.startLoading(loader_key, 'lastfm_'+method));
var config = {
method: 'GET',
@@ -83,8 +86,11 @@ const sendSignedRequest = (dispatch, getState, params) => {
});
}
- var loader_key = helpers.generateGuid()
- dispatch(uiActions.startLoading(loader_key, 'lastfm_'+params));
+ var loader_key = helpers.generateGuid();
+ var method = params.substring(params.indexOf("method=")+7, params.length);
+ method = method.substring(0, method.indexOf("&"));
+
+ dispatch(uiActions.startLoading(loader_key, 'lastfm_'+method));
params += "&sk="+getState().lastfm.session.key;
diff --git a/src/js/services/lastfm/middleware.js b/src/js/services/lastfm/middleware.js
index 6f0a7e6a..feaac867 100755
--- a/src/js/services/lastfm/middleware.js
+++ b/src/js/services/lastfm/middleware.js
@@ -32,15 +32,6 @@ const LastfmMiddleware = (function(){
next(action);
break;
- case 'MOPIDY_CURRENTTLTRACK':
- if (action.data && action.data.track){
- if (state.lastfm.session){
- store.dispatch(lastfmActions.scrobble(action.data.track));
- }
- }
- next(action);
- break;
-
// This action is irrelevant to us, pass it on to the next middleware
default: