Track template with extra info; Context menu to get to tracks

This commit is contained in:
James Barnsley
2017-10-10 11:50:29 +13:00
parent a793830d8d
commit 0ca37b0f62
9 changed files with 187 additions and 13 deletions

View File

@ -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 = $("<input>");
$("body").append(temp);
@ -452,6 +461,14 @@ class ContextMenu extends React.Component{
</span>
)
var go_to_track = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.goToTrack(e)}>
<span className="label">Track info</span>
</a>
</span>
)
var go_to_recommendations = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.goToRecommendations(e)}>
@ -555,8 +572,9 @@ class ContextMenu extends React.Component{
<div>
{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}
</div>
)
@ -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}
</div>
)
@ -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}
</div>
)

View File

@ -111,6 +111,10 @@ export default class Dater extends React.Component{
}
render(){
return <span className="dater">{ this.calculate() }</span>
if (!this.props.data){
return null;
} else {
return <span className="dater">{ this.calculate() }</span>
}
}
}

View File

@ -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+'<p class="description">'+xhr_response.error.message+'</p>';

View File

@ -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'

View File

@ -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
/**

View File

@ -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 (
<div className="view track-view content-wrapper">
{this.props.slim_mode ? <Header icon="cd" title="Album" handleContextMenuTrigger={e => this.handleContextMenu(e)} uiActions={this.props.uiActions} /> : null}
{this.props.slim_mode ? <Header
icon="music"
title="Track"
handleContextMenuTrigger={e => this.handleContextMenu(e)}
uiActions={this.props.uiActions} /> : null}
<div className="thumbnail-wrapper">
<Thumbnail size="large" canZoom images={ this.props.track.album.images } />
<Thumbnail size="large" canZoom images={track.images} />
</div>
<div className="title">
<h1>{ this.props.track.name }</h1>
<h1>{track.name}</h1>
<h2 className="grey-text">{track.album ? <Link to={global.baseURL+'album/'+track.album.uri}>{track.album.name}</Link> : "Unknown album"} by <ArtistSentence artists={track.artists} /></h2>
<ul className="details">
{ !this.props.slim_mode ? <li className="has-tooltip"><FontAwesome name={helpers.sourceIcon( this.props.params.uri )} /><span className="tooltip">{helpers.uriSource( this.props.params.uri )} track</span></li> : null }
<li><ArtistSentence artists={this.props.track.artists} /></li>
{!this.props.slim_mode ? <li className="has-tooltip"><FontAwesome name={helpers.sourceIcon(this.props.params.uri)} /><span className="tooltip">{helpers.uriSource(this.props.params.uri)} track</span></li> : null}
{track.date ? <li><Dater type="date" data={track.date} /></li> : null}
{track.explicit ? <li><span className="flag dark">EXPLICIT</span></li> : null}
<li>
{track.disc_no ? <span>Disc {track.disc_no}</span> : null}
{track.disc_number ? <span>Disc {track.disc_number}</span> : null}
{track.track_no ? <span>, track {track.track_no}</span> : null}
{track.track_number ? <span>, track {track.track_number}</span> : null}
</li>
<li>
{track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null}
{track.length ? <Dater type="length" data={track.length} /> : null}
</li>
</ul>
</div>

View File

@ -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';

View File

@ -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 {

83
src/scss/views/_track.scss Executable file
View File

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