From 0ca37b0f62d515a2432e5af1ec77f68fe2455300 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Tue, 10 Oct 2017 11:50:29 +1300 Subject: [PATCH] Track template with extra info; Context menu to get to tracks --- src/js/components/ContextMenu.js | 28 ++++++++-- src/js/components/Dater.js | 6 +- src/js/services/core/middleware.js | 2 +- src/js/services/mopidy/actions.js | 7 +++ src/js/services/mopidy/middleware.js | 21 +++++++ src/js/views/Track.js | 45 ++++++++++++--- src/scss/app.scss | 1 + src/scss/global/_core.scss | 7 +++ src/scss/views/_track.scss | 83 ++++++++++++++++++++++++++++ 9 files changed, 187 insertions(+), 13 deletions(-) create mode 100755 src/scss/views/_track.scss diff --git a/src/js/components/ContextMenu.js b/src/js/components/ContextMenu.js index 0f5144cb..147b2462 100755 --- a/src/js/components/ContextMenu.js +++ b/src/js/components/ContextMenu.js @@ -229,6 +229,15 @@ class ContextMenu extends React.Component{ } } + goToTrack(e){ + if (!this.props.menu.items || this.props.menu.items.length <= 0){ + return null + } else { + this.props.uiActions.hideContextMenu() + hashHistory.push( global.baseURL +'track/'+ encodeURIComponent(this.props.menu.items[0].uri)) + } + } + copyURIs(e){ var temp = $(""); $("body").append(temp); @@ -452,6 +461,14 @@ class ContextMenu extends React.Component{ ) + var go_to_track = ( + + this.goToTrack(e)}> + Track info + + + ) + var go_to_recommendations = ( this.goToRecommendations(e)}> @@ -555,8 +572,9 @@ class ContextMenu extends React.Component{
{play_queue_item} {add_to_playlist} - {context.source == 'spotify' ? go_to_recommendations : null} + {context.source == 'spotify' && context.items_count == 1 ? go_to_recommendations : null} {copy_uris} + {context.items_count == 1 ? go_to_track : null} {remove_from_queue}
) @@ -569,8 +587,9 @@ class ContextMenu extends React.Component{ {play_uris_next} {add_to_queue} {add_to_playlist} - {start_radio} + {context.source == 'spotify' && context.items_count == 1 ? start_radio : null} {copy_uris} + {context.items_count == 1 ? go_to_track : null} {remove_from_playlist} ) @@ -583,8 +602,9 @@ class ContextMenu extends React.Component{ {play_uris_next} {add_to_queue} {add_to_playlist} - {context.source == 'spotify' ? start_radio : null} - {context.source == 'spotify' ? go_to_recommendations : null} + {context.source == 'spotify' && context.items_count == 1 ? start_radio : null} + {context.source == 'spotify' && context.items_count == 1 ? go_to_recommendations : null} + {context.items_count == 1 ? go_to_track : null} {copy_uris} ) diff --git a/src/js/components/Dater.js b/src/js/components/Dater.js index d580dcc8..dedeb56f 100755 --- a/src/js/components/Dater.js +++ b/src/js/components/Dater.js @@ -111,6 +111,10 @@ export default class Dater extends React.Component{ } render(){ - return { this.calculate() } + if (!this.props.data){ + return null; + } else { + return { this.calculate() } + } } } \ No newline at end of file diff --git a/src/js/services/core/middleware.js b/src/js/services/core/middleware.js index cd64d202..c973cf3a 100755 --- a/src/js/services/core/middleware.js +++ b/src/js/services/core/middleware.js @@ -59,7 +59,7 @@ const CoreMiddleware = (function(){ } ); - if (action.data.xhr){ + if (action.data.xhr && action.data.xhr.responseText){ var xhr_response = JSON.parse(action.data.xhr.responseText); if (xhr_response.error && xhr_response.error.message){ message = message+'

'+xhr_response.error.message+'

'; diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js index f3f33d3d..3b5871b8 100755 --- a/src/js/services/mopidy/actions.js +++ b/src/js/services/mopidy/actions.js @@ -201,6 +201,13 @@ export function getDirectory( uri ){ } } +export function getTrack( uri ){ + return { + type: 'MOPIDY_GET_TRACK', + data: { uri: uri } + } +} + export function getLibraryArtists(){ return { type: 'MOPIDY_GET_LIBRARY_ARTISTS' diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index a5f6be94..7c92671b 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -1703,6 +1703,27 @@ const MopidyMiddleware = (function(){ next(action) break + + case 'MOPIDY_GET_TRACK': + instruct( socket, store, 'library.lookup', action.data ) + .then( + response => { + if (response.length > 0){ + store.dispatch({ + type: 'TRACK_LOADED', + key: action.data.uri, + track: response[0] + }); + } + }, + error => { + store.dispatch(coreActions.handleException( + "Mopidy: "+(error.message ? error.message : "Could not get track"), + error + )); + } + ) + break /** diff --git a/src/js/views/Track.js b/src/js/views/Track.js index dd8d2305..4dd5b2b9 100755 --- a/src/js/views/Track.js +++ b/src/js/views/Track.js @@ -1,13 +1,13 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' +import { Link } from 'react-router' import { bindActionCreators } from 'redux' import FontAwesome from 'react-fontawesome' import Header from '../components/Header' import TrackList from '../components/TrackList' import Thumbnail from '../components/Thumbnail' -import Parallax from '../components/Parallax' import ArtistSentence from '../components/ArtistSentence' import ArtistGrid from '../components/ArtistGrid' import FollowButton from '../components/FollowButton' @@ -48,7 +48,10 @@ class Track extends React.Component{ if( helpers.uriSource( this.props.params.uri ) != 'spotify' ){ this.loadTrack( nextProps ) } - } else if (!nextProps.track.lyrics && nextProps.track.artists && !this.props.track.artists){ + } + + // We don't have lyrics, and we have just received our artists + if (!nextProps.track.lyrics && !this.props.track.artists && nextProps.track.artists){ this.props.geniusActions.getTrackLyrics(nextProps.track); } } @@ -73,6 +76,16 @@ class Track extends React.Component{ this.props.spotifyActions.getTrack( props.params.uri ); } break; + + default: + if (props.mopidy_connected){ + if (props.track){ + console.info('Loading track from index') + } else { + this.props.mopidyActions.getTrack( props.params.uri ); + } + } + break; } } @@ -105,24 +118,42 @@ class Track extends React.Component{ if (!this.props.track){ return null + } else { + var track = this.props.track } return (
- {this.props.slim_mode ?
this.handleContextMenu(e)} uiActions={this.props.uiActions} /> : null} + {this.props.slim_mode ?
this.handleContextMenu(e)} + uiActions={this.props.uiActions} /> : null}
- +
-

{ this.props.track.name }

+

{track.name}

+

{track.album ? {track.album.name} : "Unknown album"} by

    - { !this.props.slim_mode ?
  • {helpers.uriSource( this.props.params.uri )} track
  • : null } -
  • + {!this.props.slim_mode ?
  • {helpers.uriSource(this.props.params.uri)} track
  • : null} + {track.date ?
  • : null} + {track.explicit ?
  • EXPLICIT
  • : null} +
  • + {track.disc_no ? Disc {track.disc_no} : null} + {track.disc_number ? Disc {track.disc_number} : null} + {track.track_no ? , track {track.track_no} : null} + {track.track_number ? , track {track.track_number} : null} +
  • +
  • + {track.duration_ms ? : null} + {track.length ? : null} +
diff --git a/src/scss/app.scss b/src/scss/app.scss index 59b42636..b9706a71 100755 --- a/src/scss/app.scss +++ b/src/scss/app.scss @@ -32,6 +32,7 @@ @import 'views/user'; @import 'views/album'; @import 'views/playlist'; +@import 'views/track'; @import 'views/queue'; @import 'views/search'; @import 'views/library'; diff --git a/src/scss/global/_core.scss b/src/scss/global/_core.scss index ecb37fe1..d0dcf363 100755 --- a/src/scss/global/_core.scss +++ b/src/scss/global/_core.scss @@ -158,6 +158,13 @@ h2 { border-bottom: 1px solid $white; } } + + &.grey-text { + a:hover { + color: $mid_grey !important; + border-bottom: 1px solid $mid_grey; + } + } } h3 { diff --git a/src/scss/views/_track.scss b/src/scss/views/_track.scss new file mode 100755 index 00000000..e18c50ed --- /dev/null +++ b/src/scss/views/_track.scss @@ -0,0 +1,83 @@ + +main .track-view { + padding-left: 30%; + position: relative; + + .title { + h2 { + a:hover { + color: darken($mid_grey, 20%); + } + } + + .details { + padding: 25px 0 30px; + } + } + + .actions { + padding-bottom: 30px; + } + + .thumbnail-wrapper { + position: absolute; + padding: 40px; + width: 30%; + top: 0; + left: 0; + box-sizing: border-box; + + .thumbnail { + max-width: 100%; + } + } + + .lyrics { + p { + line-height: 0.8em; + } + } + + @include responsive( $bp_medium ){ + padding-top: 0; + padding-left: 10px; + + .thumbnail-wrapper { + width: 40%; + padding: 10px 20px 10px 0; + float: left; + position: static; + + .thumbnail { + float: none; + max-width: none; + margin: 0 0 0 0; + } + } + + .title { + padding: 30px 0 20px 40%; + + .details { + padding: 5px 0; + } + } + + .actions { + @include clearfix(); + padding: 10px 0 20px 40%; + } + } + + @include responsive( $bp_small ){ + .title { + @include clearfix(); + padding: 20px 20px 10px 40%; + min-height: 20vw; + } + + .actions { + padding: 0; + } + } +}