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;
+ }
+ }
+}