local artists

This commit is contained in:
James Barnsley
2016-11-01 19:27:59 +13:00
parent 1738c2a29b
commit 4fdbef2b4e
17 changed files with 6258 additions and 5931 deletions

45
src/js/components/List.js Executable file
View File

@ -0,0 +1,45 @@
import React, { PropTypes } from 'react'
import { Link } from 'react-router'
export default class List extends React.Component{
constructor(props) {
super(props);
}
renderHeader(){
if( !this.props.columns ) return null
return (
<div className="list-item header cf">
{
this.props.columns.map( (col, col_index) => {
return <div className={'col w'+col.width} key={col_index}>{ col.name }</div>
})
}
</div>
)
}
render(){
return (
<div className="list">
{ this.renderHeader() }
{
this.props.rows.map( (row, row_index) => {
return (
<Link to={ this.props.link_prefix + encodeURIComponent(row.uri)} className="list-item cf" key={row_index}>
{
this.props.columns.map( (col, col_index) => {
return <div className={'col w'+col.width} key={col_index}>{ row[col.name] }</div>
})
}
</Link>
)
})
}
</div>
);
}
}

View File

@ -24,7 +24,7 @@ export default class Parallax extends React.Component{
}
componentDidMount(){
var url = helpers.SizedImages( this.props.images ).huge;
var url = helpers.sizedImages( this.props.images ).huge;
this.loadImage( url )
.then(
response => {

View File

@ -34,7 +34,7 @@ export default class Thumbnail extends React.Component{
huge: require('../../images/no-image.svg')
}
}else{
state = helpers.SizedImages( images );
state = helpers.sizedImages( images );
}
this.setState( state );

View File

@ -1,6 +1,6 @@
export let SizedImages = function( images ){
export let sizedImages = function( images ){
var sizes = {
small: false,
@ -14,7 +14,7 @@ export let SizedImages = function( images ){
for( var i = 0; i < images.length; i++ ){
// spotify-styled images
if( images[i].height ){
if( typeof(images[i].height) !== 'undefined' ){
if( images[i].height > 800 ){
sizes.huge = images[i].url;
}else if( images[i].height > 600 ){

View File

@ -31,6 +31,7 @@ import LibraryTracks from './views/library/LibraryTracks'
import LibraryPlaylists from './views/library/LibraryPlaylists'
import LibraryLocal from './views/library/LibraryLocal'
import LibraryLocalDirectory from './views/library/LibraryLocalDirectory'
import LibraryLocalArtists from './views/library/LibraryLocalArtists'
ReactDOM.render(
<Provider store={store}>
@ -52,6 +53,7 @@ ReactDOM.render(
<Route path="library/playlists" component={LibraryPlaylists} />
<Route path="library/local" component={LibraryLocal} />
<Route path="library/local/directory/:uri" component={LibraryLocalDirectory} />
<Route path="library/local/artists" component={LibraryLocalArtists} />
<Route path="album/:uri" component={Album} />
<Route path="artist/:uri" component={Artist} />

View File

@ -124,3 +124,9 @@ export function getArtist( uri ){
}
}
export function getArtists(){
return {
type: 'MOPIDY_ARTISTS'
}
}

View File

@ -155,6 +155,7 @@ const MopidyMiddleware = (function(){
break;
case 'MOPIDY_PLAYLIST':
store.dispatch({ type: 'MOPIDY_PLAYLIST_LOADED', data: false });
instruct( socket, store, 'playlists.lookup', action.data )
.then( response => {
var playlist = response;
@ -196,6 +197,7 @@ const MopidyMiddleware = (function(){
break;
case 'MOPIDY_ALBUM':
store.dispatch({ type: 'MOPIDY_ALBUM_LOADED', data: false });
instruct( socket, store, 'library.lookup', action.data )
.then( response => {
var album = response[0].album;
@ -238,14 +240,13 @@ const MopidyMiddleware = (function(){
break;
case 'MOPIDY_ARTIST':
store.dispatch({ type: 'MOPIDY_ARTIST_LOADED', data: false });
instruct( socket, store, 'library.lookup', action.data )
.then( response => {
var artist = {
name: 'yeah',
images: [],
albums: [],
tracks: response.slice(0,10)
}
var artist = response[0].artists[0];
artist.images = [];
artist.albums = [];
artist.tracks = response.slice(0,10);
for( var i = 0; i < response.length; i++ ){
var album = response[i].album;
@ -263,6 +264,16 @@ const MopidyMiddleware = (function(){
})
break;
case 'MOPIDY_ARTISTS':
store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: false });
instruct( socket, store, 'library.browse', { uri: 'local:directory?type=artist' } )
.then( response => {
var artists = response;
store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: artists });
})
break;
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);

View File

@ -92,6 +92,11 @@ export default function reducer(mopidy = {}, action){
artist: action.data
});
case 'MOPIDY_ARTISTS_LOADED':
return Object.assign({}, mopidy, {
artists: action.data
});
default:
return mopidy
}

View File

@ -98,19 +98,8 @@ class Artist extends React.Component{
<h1>{ artist.name }</h1>
</div>
<div className="col w70">
<h4 className="left-padding">Top tracks</h4>
{ artist.tracks ? <TrackList tracks={ artist.tracks } /> : null }
</div>
<div className="col w5"></div>
<div className="col w25">
<h4>Related artists</h4>
{ artist.related_artists ? <ArtistList artists={ artist.artist.related_artists.slice(0,6) } /> : null }
</div>
<div className="cf"></div>
<h4 className="left-padding">Top tracks</h4>
{ artist.tracks ? <TrackList tracks={ artist.tracks } /> : null }
<h4 className="left-padding">Albums</h4>
{ artist.albums ? <AlbumGrid className="no-top-padding" albums={ artist.albums } /> : null }

View File

@ -17,6 +17,7 @@ export default class LibraryLocal extends React.Component{
<div className="view library-local-view">
<Header icon="folder" title="Local" />
<Link to="/library/local/directory/local:directory">Folders</Link>
<Link to="/library/local/artists">Artists</Link>
</div>
);
}

View File

@ -0,0 +1,67 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Header from '../../components/Header'
import List from '../../components/List'
import * as mopidyActions from '../../services/mopidy/actions'
import * as spotifyActions from '../../services/spotify/actions'
class LibraryLocalArtists extends React.Component{
constructor(props) {
super(props);
}
// on render
componentDidMount(){
this.loadArtists()
}
componentWillReceiveProps( nextProps ){
if( !this.props.mopidy.connected && nextProps.mopidy.connected ){
this.loadArtists(nextProps);
}
}
loadArtists(props = this.props){
if( props.mopidy.connected ){
this.props.mopidyActions.getArtists();
}
}
render(){
if( !this.props.mopidy.artists ) return null
return (
<div className="view library-local-view">
<Header icon="music" title="Local" />
<div>
<List columns={[{ name: 'name', width: '100'}]} rows={this.props.mopidy.artists} link_prefix="/artist/" />
</div>
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LibraryLocalArtists)

View File

@ -11,24 +11,19 @@
display: block;
position: relative;
padding: 9px 10px 9px 40px;
border-top: 1px solid #EEEEEE;
border-bottom: 1px solid #EEEEEE;
border-top: 1px solid $faint_grey;
border-bottom: 1px solid $faint_grey;
margin-bottom: -1px;
@include clearfix;
.col {
width: 10%;
min-height: 1px;
float: left;
}
&.selected {
background: #FFF39C !important;
}
&:not(.header):hover {
background: #EEEEEE;
background: $faint_grey;
cursor: pointer;
border-color: $faint_grey;
}
&.header {

View File

@ -103,6 +103,7 @@ main {
&.w85 { width: 85%; }
&.w90 { width: 90%; }
&.w95 { width: 95%; }
&.w100 { width: 100%; }
}
.cf{