From 5ddbc48b80128a9db1d2d48bb3f706fd3e9ad9fb Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Fri, 18 Nov 2016 15:31:06 +1300 Subject: [PATCH] User view --- src/js/components/FollowButton.js | 1 + src/js/index.js | 2 + src/js/services/spotify/actions.js | 48 ++++++++++++++- src/js/services/ui/reducer.js | 20 +++++++ src/js/views/Playlist.js | 2 +- src/js/views/User.js | 95 ++++++++++++++++++++++++++++++ src/scss/app.scss | 1 + src/scss/global/_core.scss | 14 +++++ src/scss/views/_user.scss | 23 ++++++++ 9 files changed, 203 insertions(+), 3 deletions(-) create mode 100755 src/js/views/User.js create mode 100755 src/scss/views/_user.scss diff --git a/src/js/components/FollowButton.js b/src/js/components/FollowButton.js index c0d0fb16..cc01a3e5 100755 --- a/src/js/components/FollowButton.js +++ b/src/js/components/FollowButton.js @@ -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 } diff --git a/src/js/index.js b/src/js/index.js index 491fad21..a3785265 100755 --- a/src/js/index.js +++ b/src/js/index.js @@ -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( + diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 022fe6b6..012bef45 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -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 }); }); } -} - +} \ No newline at end of file diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 406b839a..d8758145 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -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 **/ diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index 9f88d3f5..5c8e9000 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -127,7 +127,7 @@ class Playlist extends React.Component{
  • { this.props.playlist.tracks_total } tracks,
  • { this.props.playlist.last_modified ?
  • Updated ago
  • : null } { this.props.playlist.followers ?
  • {this.props.playlist.followers.total.toLocaleString()} followers
  • : null } - { scheme == 'spotify' && this.props.playlist.owner ?
  • By {this.props.playlist.owner.id}  { !this.props.playlist.public ? : null }
  • : null } + { scheme == 'spotify' && this.props.playlist.owner ?
  • By {this.props.playlist.owner.id}  { !this.props.playlist.public ? : null }
  • : null } { scheme == 'spotify' ?
  • Spotify playlist
  • : null } { scheme == 'm3u' ?
  • Local playlist
  • : null } diff --git a/src/js/views/User.js b/src/js/views/User.js new file mode 100755 index 00000000..bdecedb3 --- /dev/null +++ b/src/js/views/User.js @@ -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  You + }else{ + return null + } + } + + render(){ + if( !this.props.user ) return null + + return ( +
    +
    + + +

    { this.props.user.display_name ? this.props.user.display_name : this.props.user.id }{ this.renderMeFlag() }

    + +
    + +
    + +
      +
    • { this.props.user.playlists_total } playlists
    • +
    • { this.props.user.followers.total.toLocaleString() } followers
    • +
    +
    +
    + +
    + { this.props.user.playlists ? : null } + this.loadMore() }/> +
    + +
    +
    + ); + } +} + +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) \ No newline at end of file diff --git a/src/scss/app.scss b/src/scss/app.scss index b13a1157..2000d62d 100755 --- a/src/scss/app.scss +++ b/src/scss/app.scss @@ -20,6 +20,7 @@ @import 'components/header'; @import 'views/artist'; +@import 'views/user'; @import 'views/album'; @import 'views/playlist'; @import 'views/queue'; diff --git a/src/scss/global/_core.scss b/src/scss/global/_core.scss index b13168a6..90d191f4 100755 --- a/src/scss/global/_core.scss +++ b/src/scss/global/_core.scss @@ -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; + } +} diff --git a/src/scss/views/_user.scss b/src/scss/views/_user.scss new file mode 100755 index 00000000..6c1645d7 --- /dev/null +++ b/src/scss/views/_user.scss @@ -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; + } + } +}