Merge branch 'develop'

This commit is contained in:
James Barnsley
2018-02-22 07:43:35 +13:00
10 changed files with 81 additions and 68 deletions

View File

@ -1 +1 @@
3.13.0
3.13.1

View File

@ -2,13 +2,15 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { createStore, bindActionCreators } from 'redux'
import { hashHistory } from 'react-router'
import FontAwesome from 'react-fontawesome'
import Thumbnail from './Thumbnail'
import URILink from './URILink'
import GridItem from './GridItem'
import * as helpers from '../helpers'
import * as uiActions from '../services/ui/actions'
import * as lastfmActions from '../services/lastfm/actions'
class ArtistGrid extends React.Component{
@ -40,22 +42,16 @@ class ArtistGrid extends React.Component{
this.props.artists.map(
(artist, index) => {
return (
<URILink
className="grid-item"
<GridItem
key={artist.uri}
type="artist"
uri={artist.uri}
key={index}
onContextMenu={e => this.handleContextMenu(e,artist)}>
<Thumbnail size="medium" images={artist.images} />
<div className="name">
{artist.name}
</div>
<div className="secondary">
{this.props.show_source_icon ? <FontAwesome name={helpers.sourceIcon(artist.uri)} className="source" /> : null}
{artist.followers ? artist.followers.total.toLocaleString()+' followers' : null}
{artist.albums_uris && !artist.followers ? artist.albums_uris.length+' albums' : null}
</div>
</URILink>
item={artist}
show_source_icon={this.props.show_source_icon}
onClick={e => {hashHistory.push(global.baseURL+'artist/'+encodeURIComponent(artist.uri))}}
onLoad={() => this.handleLoad(artist.uri)}
lastfmActions={this.props.lastfmActions}
onContextMenu={e => this.handleContextMenu(e,artist)}
/>
)
}
)
@ -73,7 +69,8 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch)
uiActions: bindActionCreators(uiActions, dispatch),
lastfmActions: bindActionCreators(lastfmActions, dispatch)
}
}

View File

@ -14,6 +14,16 @@ export default class GridItem extends React.Component{
super(props)
}
componentDidMount(){
if (this.props.item && !this.props.item.images){
switch (this.props.item.type){
case 'artist':
this.props.lastfmActions.getArtist(this.props.item.uri, this.props.item.name);
break;
}
}
}
handleClick(e){
if (this.props.onClick && e.target.tagName.toLowerCase() !== 'a'){
this.props.onClick(e)
@ -27,7 +37,7 @@ export default class GridItem extends React.Component{
}
shouldComponentUpdate(nextProps, nextState){
return nextProps.item != this.props.item
return nextProps.item != this.props.item;
}
renderSecondary(item){
@ -46,7 +56,8 @@ export default class GridItem extends React.Component{
case 'artist':
return (
<span>
{item.followers ? item.followers.total.toLocaleString()+' followers' : item.albums_uris.length+' albums'}
{item.followers ? item.followers.total.toLocaleString()+' followers' : null}
{item.albums_uris ? item.albums_uris.length+' albums' : null}
</span>
)
break

View File

@ -91,8 +91,8 @@ class Sidebar extends React.Component{
<Link className={this.linkClassName('settings')} to={global.baseURL+"settings"}>
<Icon name="cog" />
Settings
{this.props.test_mode ? <span className="has-tooltip right-tooltip pull-right"><FontAwesome name="info-circle" className="orange-text" /><span className="tooltip">Test mode active</span></span>: null}
{!this.props.mopidy_connected || (!this.props.spotify_connected && this.props.spotify_enabled) || !this.props.pusher_connected ? <FontAwesome name="exclamation-triangle" className="red-text pull-right" /> : null}
{this.props.test_mode ? <span className="status has-tooltip right-tooltip"><FontAwesome name="info-circle" className="orange-text" /><span className="tooltip">Test mode active</span></span>: null}
{!this.props.mopidy_connected || (!this.props.spotify_connected && this.props.spotify_enabled) || !this.props.pusher_connected ? <span className="status has-tooltip right-tooltip"><FontAwesome name="exclamation-triangle" className="red-text" /><span className="tooltip">{!this.props.mopidy_connected ? <span>Mopidy not connected<br /></span> : null}{!this.props.pusher_connected ? <span>Pusher not connected<br /></span> : null}{!this.props.spotify_connected && this.props.spotify_enabled ? <span>Spotify not connected</span> : null}</span></span> : null}
</Link>
</section>

View File

@ -131,17 +131,13 @@ export let sizedImages = function(images){
switch(image.size){
case 'mega':
case 'extralarge':
sizes.huge = image['#text']
break
case 'large':
sizes.large = image['#text']
break
case 'medium':
sizes.medium = image['#text']
break
break;
case 'medium':
case 'small':
sizes.small = image['#text']
break
break;
}
}
}

View File

@ -224,11 +224,13 @@ export function getTrack(uri){
}
export function getArtist(uri, artist, mbid = false){
return (dispatch, getState) => {
if (mbid){
var params = 'method=artist.getInfo&mbid='+mbid
} else {
artist = encodeURIComponent(artist );
artist = artist.replace("&","and");
artist = encodeURIComponent(artist);
var params = 'method=artist.getInfo&artist='+artist
}
sendRequest(dispatch, getState, params)
@ -240,6 +242,7 @@ export function getArtist(uri, artist, mbid = false){
artist: {
uri: uri,
images: response.artist.image,
mbid: response.artist.mbid,
bio: response.artist.bio,
listeners: parseInt(response.artist.stats.listeners),
play_count: parseInt(response.artist.stats.playcount),

View File

@ -1544,7 +1544,6 @@ const MopidyMiddleware = (function(){
* =============================================================== ARTIST(S) ============
* ======================================================================================
**/
case 'MOPIDY_GET_LIBRARY_ARTISTS':
instruct(socket, store, 'library.browse', { uri: 'local:directory?type=artist' } )
.then(response => {
@ -1555,15 +1554,13 @@ const MopidyMiddleware = (function(){
store.dispatch({
type: 'ARTISTS_LOADED',
artists: response
})
});
store.dispatch({
type: 'MOPIDY_LIBRARY_ARTISTS_LOADED',
uris: uris
})
})
});
});
break;
/**
@ -1571,7 +1568,6 @@ const MopidyMiddleware = (function(){
*
* We can't get specific artist artwork from Mopidy. Perhaps we fetch additional
* artist metadata via LastFM? Their API limits will make this quite slow.
*
case 'MOPIDY_GET_LIBRARY_ARTISTS':
var last_run = store.getState().ui.processes.MOPIDY_LIBRARY_ARTISTS_PROCESSOR
@ -1579,9 +1575,9 @@ const MopidyMiddleware = (function(){
if (!last_run){
instruct(socket, store, 'library.browse', { uri: 'local:directory?type=artist' } )
.then(response => {
if (response.length <= 0) return
if (response.length <= 0) return;
var uris = helpers.arrayOf('uri',response)
var uris = helpers.arrayOf('uri',response);
store.dispatch({
type: 'MOPIDY_LIBRARY_ARTISTS_LOADED',
@ -1589,7 +1585,7 @@ const MopidyMiddleware = (function(){
});
// Start our process to load the full album objects
store.dispatch(uiActions.startProcess('MOPIDY_LIBRARY_ARTISTS_PROCESSOR','Loading '+uris.length+' local artists', {uris: uris}))
store.dispatch(uiActions.startProcess('MOPIDY_LIBRARY_ARTISTS_PROCESSOR','Loading '+uris.length+' local artists', {uris: uris}));
})
} else if (last_run.status == 'cancelled'){
@ -1602,11 +1598,11 @@ const MopidyMiddleware = (function(){
case 'MOPIDY_LIBRARY_ARTISTS_PROCESSOR':
if (store.getState().ui.processes['MOPIDY_LIBRARY_ARTISTS_PROCESSOR'] !== undefined){
var processor = store.getState().ui.processes['MOPIDY_LIBRARY_ARTISTS_PROCESSOR']
var processor = store.getState().ui.processes['MOPIDY_LIBRARY_ARTISTS_PROCESSOR'];
if (processor.status == 'cancelling'){
store.dispatch(uiActions.processCancelled('MOPIDY_LIBRARY_ARTISTS_PROCESSOR'))
return false
store.dispatch(uiActions.processCancelled('MOPIDY_LIBRARY_ARTISTS_PROCESSOR'));
return false;
}
}
@ -1614,15 +1610,14 @@ const MopidyMiddleware = (function(){
var uris_to_load = uris.splice(0,50)
if (uris_to_load.length > 0){
store.dispatch(uiActions.updateProcess('MOPIDY_LIBRARY_ARTISTS_PROCESSOR', 'Loading '+uris.length+' local artists', {uris: uris}))
store.dispatch(mopidyActions.getArtists(uris_to_load, {name: 'MOPIDY_LIBRARY_ARTISTS_PROCESSOR', data: {uris: uris}}))
store.dispatch(uiActions.updateProcess('MOPIDY_LIBRARY_ARTISTS_PROCESSOR', 'Loading '+uris.length+' local artists', {uris: uris}));
store.dispatch(mopidyActions.getArtists(uris_to_load, {name: 'MOPIDY_LIBRARY_ARTISTS_PROCESSOR', data: {uris: uris}}));
} else {
store.dispatch(uiActions.processFinished('MOPIDY_LIBRARY_ARTISTS_PROCESSOR'))
store.dispatch(uiActions.processFinished('MOPIDY_LIBRARY_ARTISTS_PROCESSOR'));
}
break
*/
break;
**/
case 'MOPIDY_GET_ARTIST':
instruct(socket, store, 'library.lookup', action.data )
@ -1665,7 +1660,7 @@ const MopidyMiddleware = (function(){
albums_uris: helpers.arrayOf('uri',albums),
tracks: response.slice(0,10)
}
);
);
store.dispatch({
type: 'ARTIST_LOADED',
key: artist.uri,
@ -1673,11 +1668,12 @@ const MopidyMiddleware = (function(){
});
// load artwork from LastFM
if (!artist.images || artist.images.length <= 0){
var existing_artist = store.getState().core.artists[artist.uri];
if (existing_artist && !existing_artist.images){
if (artist.musicbrainz_id){
store.dispatch(lastfmActions.getArtist(artist.uri, false, artist.musicbrainz_id ) )
store.dispatch(lastfmActions.getArtist(artist.uri, false, artist.musicbrainz_id))
} else {
store.dispatch(lastfmActions.getArtist(artist.uri, artist.name.replace('&','and') ) )
store.dispatch(lastfmActions.getArtist(artist.uri, artist.name))
}
}
})
@ -1698,8 +1694,8 @@ const MopidyMiddleware = (function(){
{
is_mopidy: true
}
)
artists.push(artist)
);
artists.push(artist);
}
}

View File

@ -68,7 +68,7 @@ class Artist extends React.Component{
default:
if (props.mopidy_connected){
if (props.artist && props.artist.images){
if (props.artist && props.artist.images && props.artist.albums_uris){
console.info('Loading local artist from index');
} else {
this.props.mopidyActions.getArtist(props.params.uri);

View File

@ -29,12 +29,12 @@ class LibraryArtists extends React.Component{
}
componentDidMount(){
if (this.props.mopidy_library_artists_status != 'finished' && this.props.mopidy_connected && (this.props.source == 'all' || this.props.source == 'local')){
this.props.mopidyActions.getLibraryArtists()
if (!this.props.mopidy_library_artists && this.props.mopidy_connected && (this.props.source == 'all' || this.props.source == 'local')){
this.props.mopidyActions.getLibraryArtists();
}
if (this.props.mopidy_uri_schemes.includes('spotify:') && this.props.spotify_library_artists_status != 'finished' && this.props.spotify_connected && (this.props.source == 'all' || this.props.source == 'spotify')){
this.props.spotifyActions.getLibraryArtists()
this.props.spotifyActions.getLibraryArtists();
}
}
@ -43,12 +43,12 @@ class LibraryArtists extends React.Component{
// We've just connected
if (!this.props.mopidy_connected){
this.props.mopidyActions.getLibraryArtists()
this.props.mopidyActions.getLibraryArtists();
}
// Filter changed, but we haven't got this provider's library yet
if (this.props.source != 'all' && this.props.source != 'local' && newProps.mopidy_library_artists_status != 'finished'){
this.props.mopidyActions.getLibraryArtists()
if (this.props.source != 'all' && this.props.source != 'local' && !newProps.mopidy_library_artists){
this.props.mopidyActions.getLibraryArtists();
}
}
@ -56,12 +56,12 @@ class LibraryArtists extends React.Component{
// We've just connected
if (!this.props.spotify_connected){
this.props.spotifyActions.getLibraryArtists()
this.props.spotifyActions.getLibraryArtists();
}
// Filter changed, but we haven't got this provider's library yet
if (this.props.source != 'all' && this.props.source != 'spotify' && newProps.spotify_library_artists_status != 'finished'){
this.props.spotifyActions.getLibraryArtists()
this.props.spotifyActions.getLibraryArtists();
}
}
}
@ -76,6 +76,11 @@ class LibraryArtists extends React.Component{
this.props.uiActions.showContextMenu(data)
}
loadMore(){
console.log('Load more');
this.setState({limit: this.state.limit + this.state.per_page});
}
setSort(value){
var reverse = false
if (this.props.sort == value ) reverse = !this.props.sort_reverse
@ -84,11 +89,11 @@ class LibraryArtists extends React.Component{
library_artists_sort_reverse: reverse,
library_artists_sort: value
}
this.props.uiActions.set(data)
this.props.uiActions.set(data);
}
renderView(){
var artists = []
var artists = [];
// Mopidy library items
if (this.props.mopidy_library_artists && (this.props.source == 'all' || this.props.source == 'local')){
@ -154,7 +159,7 @@ class LibraryArtists extends React.Component{
columns={columns}
className="artist-list"
link_prefix={global.baseURL+"artist/"} />
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.loadMore()} />
</section>
)
} else {
@ -163,7 +168,7 @@ class LibraryArtists extends React.Component{
<ArtistGrid
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
artists={artists} />
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.loadMore()} />
</section>
)
}

View File

@ -66,6 +66,11 @@ aside{
}
}
.status {
margin-left: 5px;
float: right;
}
&.active {
background: $turquoise;
color: $white;