User view

This commit is contained in:
James Barnsley
2016-11-18 15:31:06 +13:00
parent 7389cf7fe9
commit 5ddbc48b80
9 changed files with 203 additions and 3 deletions

View File

@ -42,6 +42,7 @@ const mapStateToProps = (state, ownProps) => {
return {
spotify_authorized: state.spotify.authorized,
artist: state.ui.artist,
user: state.ui.user,
album: state.ui.album,
playlist: state.ui.playlist
}

View File

@ -16,6 +16,7 @@ import App from './views/App'
import Album from './views/Album'
import Artist from './views/Artist'
import Playlist from './views/Playlist'
import User from './views/User'
import Queue from './views/Queue'
import Settings from './views/Settings'
import Search from './views/Search'
@ -49,6 +50,7 @@ ReactDOM.render(
<Route path="album/:uri" component={Album} />
<Route path="artist/:uri" component={Artist} />
<Route path="playlist/:uri" component={Playlist} />
<Route path="user/:uri" component={User} />
<Route path="discover" component={Discover} />
<Route path="discover/featured" component={DiscoverFeatured} />

View File

@ -383,6 +383,14 @@ export function following(uri, method = 'GET'){
data = {}
}
break
case 'user':
if( method == 'GET' ){
endpoint = 'me/following/contains?type=user&ids='+ helpers.getFromUri('userid', uri)
}else{
endpoint = 'me/following?type=user&ids='+ helpers.getFromUri('userid', uri)
data = {}
}
break
case 'playlist':
if( method == 'GET' ){
endpoint = 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'/followers/contains?ids='+ getState().spotify.me.id
@ -496,6 +504,43 @@ export function getLibraryArtists(){
/**
* =============================================================== USER(S) ==============
* ======================================================================================
**/
export function getUser( uri ){
return (dispatch, getState) => {
dispatch({ type: 'SPOTIFY_USER_LOADED', data: false });
var user = {};
// get both the artist and the top tracks
$.when(
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) )
.then( response => {
Object.assign(user, response);
}),
sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid', uri) +'/playlists?limit=50' )
.then( response => {
Object.assign(user, { playlists: response.items, playlists_more: response.next, playlists_total: response.total });
})
).then( () => {
dispatch({
type: 'SPOTIFY_USER_LOADED',
data: user
});
});
}
}
/**
* =============================================================== ALBUM(S) =============
* ======================================================================================
@ -715,5 +760,4 @@ export function reorderPlaylistTracks( uri, range_start, range_length, insert_be
});
});
}
}
}

View File

@ -174,6 +174,26 @@ export default function reducer(ui = {}, action){
return Object.assign({}, ui, { artist: artist, following_loading: false });
/**
* User
**/
case 'SPOTIFY_USER_LOADED':
if( !action.data ) return Object.assign({}, ui, { user: false })
return Object.assign({}, ui, { user: action.data })
case 'SPOTIFY_USER_PLAYLISTS_LOADED_MORE':
var user = Object.assign({}, ui.user, {
playlists: [ ...ui.user.playlists, ...action.data.items ],
playlists_more: action.data.next
})
return Object.assign({}, ui, { user: user });
case 'USER_FOLLOWING_LOADED':
var user = Object.assign({}, ui.user, { following: action.is_following })
return Object.assign({}, ui, { user: user, following_loading: false });
/**
* Playlists
**/

View File

@ -127,7 +127,7 @@ class Playlist extends React.Component{
<li>{ this.props.playlist.tracks_total } tracks, <Dater type="total-time" data={this.props.playlist.tracks} /></li>
{ this.props.playlist.last_modified ? <li>Updated <Dater type="ago" data={this.props.playlist.last_modified} /> ago</li> : null }
{ this.props.playlist.followers ? <li>{this.props.playlist.followers.total.toLocaleString()} followers</li> : null }
{ scheme == 'spotify' && this.props.playlist.owner ? <li>By <Link to={'/user/'+this.props.playlist.owner.id}>{this.props.playlist.owner.id}</Link> &nbsp;{ !this.props.playlist.public ? <FontAwesome name="lock" /> : null }</li> : null }
{ scheme == 'spotify' && this.props.playlist.owner ? <li>By <Link to={'/user/'+this.props.playlist.owner.uri}>{this.props.playlist.owner.id}</Link> &nbsp;{ !this.props.playlist.public ? <FontAwesome name="lock" /> : null }</li> : null }
{ scheme == 'spotify' ? <li><FontAwesome name="spotify" /> Spotify playlist</li> : null }
{ scheme == 'm3u' ? <li><FontAwesome name="folder" /> Local playlist</li> : null }
</ul>

95
src/js/views/User.js Executable file
View File

@ -0,0 +1,95 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
import Thumbnail from '../components/Thumbnail'
import PlaylistGrid from '../components/PlaylistGrid'
import FollowButton from '../components/FollowButton'
import LazyLoadListener from '../components/LazyLoadListener'
import * as helpers from '../helpers'
import * as mopidyActions from '../services/mopidy/actions'
import * as spotifyActions from '../services/spotify/actions'
class User extends React.Component{
constructor(props) {
super(props);
}
componentDidMount(){
this.loadUser();
}
componentWillReceiveProps( nextProps ){
if( nextProps.params.uri != this.props.params.uri ) this.loadUser( nextProps )
}
loadUser( props = this.props ){
this.props.spotifyActions.getUser( props.params.uri )
}
loadMore(){
if( !this.props.user.playlists_more ) return
this.props.spotifyActions.getURL( this.props.user.playlists_more, 'SPOTIFY_USER_PLAYLISTS_LOADED_MORE' );
}
renderMeFlag(){
if( !this.props.spotify_authorized ) return null
if( helpers.getFromUri('userid',this.props.params.uri) == this.props.me.id ){
return <span className="flag blue"><FontAwesome name="star" />&nbsp;You</span>
}else{
return null
}
}
render(){
if( !this.props.user ) return null
return (
<div className="view user-view">
<div className="intro">
<Thumbnail circle={true} size="medium" images={ this.props.user.images } />
<h1>{ this.props.user.display_name ? this.props.user.display_name : this.props.user.id }{ this.renderMeFlag() }</h1>
<div className="actions">
<FollowButton uri={this.props.params.uri} addText="Follow" removeText="Unfollow" />
</div>
<ul className="details">
<li>{ this.props.user.playlists_total } playlists</li>
<li>{ this.props.user.followers.total.toLocaleString() } followers</li>
</ul>
</div>
<div className="main">
<section className="grid-wrapper">
{ this.props.user.playlists ? <PlaylistGrid playlists={ this.props.user.playlists } /> : null }
<LazyLoadListener loadMore={ () => this.loadMore() }/>
</section>
</div>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
spotify_authorized: state.spotify.authorized,
me: state.spotify.me,
user: state.ui.user
};
}
const mapDispatchToProps = (dispatch) => {
return {
spotifyActions: bindActionCreators(spotifyActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(User)

View File

@ -20,6 +20,7 @@
@import 'components/header';
@import 'views/artist';
@import 'views/user';
@import 'views/album';
@import 'views/playlist';
@import 'views/queue';

View File

@ -144,3 +144,17 @@ footer {
padding-bottom: 4px;
}
}
.flag {
font-size: 12px;
display: inline-block;
padding: 2px 4px;
border-radius: 2px;
background: $light_grey;
color: $dark_grey;
&.blue {
background: $blue;
color: #FFFFFF;
}
}

23
src/scss/views/_user.scss Executable file
View File

@ -0,0 +1,23 @@
.user-view {
.intro {
@include clearfix();
padding-bottom: 20px;
.thumbnail {
max-width: 200px;
margin: 40px 40px 0 40px;
float: left;
}
h1 {
padding-top: 80px;
padding-bottom: 20px;
}
.actions {
float: left;
margin-right: 20px;
}
}
}