From 658879047689dcd1535b240b56bc6fb6674cc222 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Mon, 21 Nov 2016 16:26:47 +1300 Subject: [PATCH] View toggling; Enhanced List component --- src/js/bootstrap.js | 6 ++ src/js/components/AlbumGrid.js | 8 -- src/js/components/List.js | 26 ++++-- .../components/SpotifyAuthenticationFrame.js | 2 +- src/js/services/localstorage/middleware.js | 9 +- src/js/services/spotify/reducer.js | 24 +++++- src/js/views/library/LibraryAlbums.js | 82 +++++++++++++++++-- src/js/views/library/LibraryArtists.js | 4 +- src/js/views/library/LibraryPlaylists.js | 77 +++++++++++++---- src/scss/components/_dropdown-field.scss | 1 + 10 files changed, 197 insertions(+), 42 deletions(-) diff --git a/src/js/bootstrap.js b/src/js/bootstrap.js index 666350df..32bbb4c4 100755 --- a/src/js/bootstrap.js +++ b/src/js/bootstrap.js @@ -87,6 +87,12 @@ if( localStorage.getItem('spotify') ){ initialState.spotify = Object.assign(initialState.spotify, storedSpotify ); } +// if we've got a stored version of spotify state, load and merge +if( localStorage.getItem('ui') ){ + var storedUi = JSON.parse( localStorage.getItem('ui') ); + initialState.ui = Object.assign(initialState.ui, storedUi ); +} + console.log('Bootstrapping', initialState) let store = createStore( diff --git a/src/js/components/AlbumGrid.js b/src/js/components/AlbumGrid.js index c4e608f7..ebb3bd7c 100755 --- a/src/js/components/AlbumGrid.js +++ b/src/js/components/AlbumGrid.js @@ -17,14 +17,6 @@ export default class AlbumGrid extends React.Component{ { this.props.albums.map( (album, index) => { - - // handle nested album objects (as in Album Library) - if( album.album ){ - var flatAlbum = album.album; - flatAlbum.added_at = album.added_at; - album = flatAlbum; - } - return } ) diff --git a/src/js/components/List.js b/src/js/components/List.js index c666b6a5..ea60c297 100755 --- a/src/js/components/List.js +++ b/src/js/components/List.js @@ -1,9 +1,12 @@ import React, { PropTypes } from 'react' import { Link } from 'react-router' - import FontAwesome from 'react-fontawesome' +import ArtistSentence from './ArtistSentence' + +import * as helpers from '../helpers' + export default class List extends React.Component{ constructor(props) { @@ -17,16 +20,28 @@ export default class List extends React.Component{
{ this.props.columns.map( (col, col_index) => { - return
{ col.name }
+ return
{ col.label ? col.label : col.name }
}) }
) } - renderValue( value ){ - if( typeof(value) === 'undefined' ) return - + renderValue( row, key ){ + var key = key.split('.') + var value = row + + for( var i = 0; i < key.length; i++ ){ + if( typeof(value[key[i]]) !== 'undefined' ){ + value = value[key[i]] + }else{ + return - + } + } + + if( key[0] === 'artists' ) return if( value === true ) return + if( typeof(value) === 'number' ) return {value.toLocaleString()} return {value} } @@ -44,11 +59,12 @@ export default class List extends React.Component{ this.props.columns.map( (col, col_index) => { return (
- { this.renderValue(row[col.name]) } + { this.renderValue(row, col.name) }
) }) } + { this.props.show_source_icon ? : null } ) }) diff --git a/src/js/components/SpotifyAuthenticationFrame.js b/src/js/components/SpotifyAuthenticationFrame.js index 4a0600cb..1fd38684 100755 --- a/src/js/components/SpotifyAuthenticationFrame.js +++ b/src/js/components/SpotifyAuthenticationFrame.js @@ -68,7 +68,7 @@ class SpotifyAuthenticationFrame extends React.Component{
Logged in as {this.props.spotify.me.display_name ? this.props.spotify.me.display_name : null } -  ({ this.props.spotify.me.id }) +  ({ this.props.spotify.me.id })
diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js index 68da3581..87d5ca69 100755 --- a/src/js/services/localstorage/middleware.js +++ b/src/js/services/localstorage/middleware.js @@ -12,7 +12,7 @@ const localstorageMiddleware = (function(){ // append our state to a global variable. This gives us access to debug the store at any point window._store = store - console.log(action) + //console.log(action) switch( action.type ){ @@ -114,6 +114,13 @@ const localstorageMiddleware = (function(){ ); localStorage.setItem('spotify', JSON.stringify(spotify)); break; + + case 'SET_VIEW': + var ui = JSON.parse( localStorage.getItem('ui') ); + if( !ui ) ui = {}; + Object.assign( ui, action.view ); + localStorage.setItem('ui', JSON.stringify(ui)); + break; } } diff --git a/src/js/services/spotify/reducer.js b/src/js/services/spotify/reducer.js index b39d5c07..19bd84f9 100755 --- a/src/js/services/spotify/reducer.js +++ b/src/js/services/spotify/reducer.js @@ -79,14 +79,34 @@ export default function reducer(spotify = {}, action){ case 'SPOTIFY_LIBRARY_ALBUMS_LOADED': if( !action.data ) return Object.assign({}, spotify) + var albums = [] + for( var i = 0; i < action.data.items.length; i++ ){ + albums.push( Object.assign( + {}, + action.data.items[i].album, + { + added_at: action.data.items[i].added_at + } + )) + } return Object.assign({}, spotify, { - library_albums: action.data.items, + library_albums: albums, library_albums_more: action.data.next }); case 'SPOTIFY_LIBRARY_ALBUMS_LOADED_MORE': + var albums = [] + for( var i = 0; i < action.data.items.length; i++ ){ + albums.push( Object.assign( + {}, + action.data.items[i].album, + { + added_at: action.data.items[i].added_at + } + )) + } return Object.assign({}, spotify, { - library_albums: [...spotify.library_albums, ...action.data.items ], + library_albums: [...spotify.library_albums, ...albums ], library_albums_more: action.data.next }); diff --git a/src/js/views/library/LibraryAlbums.js b/src/js/views/library/LibraryAlbums.js index e19799a7..fd473493 100755 --- a/src/js/views/library/LibraryAlbums.js +++ b/src/js/views/library/LibraryAlbums.js @@ -5,9 +5,12 @@ import { bindActionCreators } from 'redux' import { Link } from 'react-router' import AlbumGrid from '../../components/AlbumGrid' +import List from '../../components/List' import Header from '../../components/Header' +import DropdownField from '../../components/DropdownField' import LazyLoadListener from '../../components/LazyLoadListener' +import * as uiActions from '../../services/ui/actions' import * as mopidyActions from '../../services/mopidy/actions' import * as spotifyActions from '../../services/spotify/actions' @@ -26,16 +29,77 @@ class LibraryAlbums extends React.Component{ this.props.spotifyActions.getURL( this.props.albums_more, 'SPOTIFY_LIBRARY_ALBUMS_LOADED_MORE' ); } - render(){ - return ( -
-
+ renderView(){ + if( !this.props.albums ) return null + + if( this.props.view == 'list' ){ + var columns = [ + { + width: 35, + label: 'Name', + name: 'name' + }, + { + width: 35, + label: 'Artists', + name: 'artists' + }, + { + width: 15, + label: 'Added', + name: 'added_at' + }, + { + width: 15, + label: 'Tracks', + name: 'tracks.total' + } + ] + return ( +
+ +
+ ) + }else if( this.props.view == 'thumbnails' ){ + return (
{ this.props.albums ? : null } -
+ + ) + }else{ + return ( +
+ { this.props.albums ? : null } +
+ ) + } + } + + render(){ + + var view_options = [ + { + value: 'detail', + label: 'Detail' + }, + { + value: 'thumbnails', + label: 'Thumbnails' + }, + { + value: 'list', + label: 'List' + } + ] + + var actions = ( + this.props.uiActions.setView({ library_albums_view: value }) } /> + ) + + return ( +
+
+ { this.renderView() } this.loadMore() }/>
); @@ -51,6 +115,7 @@ class LibraryAlbums extends React.Component{ const mapStateToProps = (state, ownProps) => { return { + view: state.ui.library_albums_view, albums: state.spotify.library_albums, albums_more: state.spotify.library_albums_more, } @@ -58,6 +123,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch) => { return { + uiActions: bindActionCreators(uiActions, dispatch), mopidyActions: bindActionCreators(mopidyActions, dispatch), spotifyActions: bindActionCreators(spotifyActions, dispatch) } diff --git a/src/js/views/library/LibraryArtists.js b/src/js/views/library/LibraryArtists.js index 44581da0..d3fd13da 100755 --- a/src/js/views/library/LibraryArtists.js +++ b/src/js/views/library/LibraryArtists.js @@ -37,16 +37,18 @@ class LibraryArtists extends React.Component{ var columns = [ { width: 30, + label: 'Name', name: 'name' }, { width: 10, + label: 'Followers', name: 'followers.total' } ] return (
- +
) }else{ diff --git a/src/js/views/library/LibraryPlaylists.js b/src/js/views/library/LibraryPlaylists.js index eddb5c84..424aebdf 100755 --- a/src/js/views/library/LibraryPlaylists.js +++ b/src/js/views/library/LibraryPlaylists.js @@ -6,6 +6,8 @@ import { Link } from 'react-router' import FontAwesome from 'react-fontawesome' import PlaylistGrid from '../../components/PlaylistGrid' +import List from '../../components/List' +import DropdownField from '../../components/DropdownField' import Header from '../../components/Header' import * as uiActions from '../../services/ui/actions' @@ -18,31 +20,73 @@ class LibraryPlaylists extends React.Component{ super(props); } - render(){ + renderView(){ if( !this.props.playlists ) return null - var columns = [ - { name: 'name', width: '30'}, - { name: 'tracks.total', width: '15'}, - { name: 'can_edit', width: '15'}, - { name: 'uri', width: '40'} + + if( this.props.view == 'list' ){ + var columns = [ + { + width: 30, + label: 'Name', + name: 'name' + }, + { + width: 30, + label: 'Owner', + name: 'owner.id' + }, + { + width: 10, + label: 'Can edit', + name: 'can_edit' + }, + { + width: 10, + label: 'Tracks', + name: 'tracks.total' + } + ] + return ( +
+ +
+ ) + }else{ + return ( +
+ +
+ ) + } + } + + render(){ + + var view_options = [ + { + value: 'thumbnails', + label: 'Thumbnails' + }, + { + value: 'list', + label: 'List' + } ] var actions = ( - +
+ this.props.uiActions.setView({ library_playlists_view: value }) } /> + +
) return (
-
- -
- -
- + { this.renderView() }
) } @@ -57,6 +101,7 @@ class LibraryPlaylists extends React.Component{ const mapStateToProps = (state, ownProps) => { return { + view: state.ui.library_playlists_view, playlists: state.ui.playlists } } diff --git a/src/scss/components/_dropdown-field.scss b/src/scss/components/_dropdown-field.scss index 8d02aa2b..618321d5 100755 --- a/src/scss/components/_dropdown-field.scss +++ b/src/scss/components/_dropdown-field.scss @@ -4,6 +4,7 @@ font-weight: 600; text-transform: uppercase; position: relative; + display: inline-block; .label { padding: 18px 10px;