User view
This commit is contained in:
@ -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
|
||||
}
|
||||
|
||||
@ -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} />
|
||||
|
||||
@ -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
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
**/
|
||||
|
||||
@ -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> { !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> { !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
95
src/js/views/User.js
Executable 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" /> 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)
|
||||
@ -20,6 +20,7 @@
|
||||
@import 'components/header';
|
||||
|
||||
@import 'views/artist';
|
||||
@import 'views/user';
|
||||
@import 'views/album';
|
||||
@import 'views/playlist';
|
||||
@import 'views/queue';
|
||||
|
||||
@ -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
23
src/scss/views/_user.scss
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user