Localstorage woes again; multiple requests; creating common components
This commit is contained in:
4
src/js/bootstrap.js
vendored
4
src/js/bootstrap.js
vendored
@ -21,6 +21,10 @@ var initialState = {
|
||||
mopidy: {
|
||||
host: window.location.hostname,
|
||||
port: 6680
|
||||
},
|
||||
spotify: {
|
||||
country: 'NZ',
|
||||
locale: 'en_NZ'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
28
src/js/components/AlbumGrid.js
Executable file
28
src/js/components/AlbumGrid.js
Executable file
@ -0,0 +1,28 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import AlbumGridItem from './AlbumGridItem'
|
||||
|
||||
export default class AlbumGrid extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render(){
|
||||
if( this.props.items ){
|
||||
return (
|
||||
<ul className="grid album-grid">
|
||||
{
|
||||
this.props.items.items.map(
|
||||
(album, index) => {
|
||||
return <AlbumGridItem item={album} key={index} />
|
||||
}
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
21
src/js/components/AlbumGridItem.js
Executable file
21
src/js/components/AlbumGridItem.js
Executable file
@ -0,0 +1,21 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
export default class AlbumGridItem extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render(){
|
||||
var link = '/album/' + this.props.item.uri;
|
||||
return (
|
||||
<Link to={link} className="grid-item album-grid-item">
|
||||
<Thumbnail size="medium" images={this.props.item.images} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
26
src/js/components/Thumbnail.js
Executable file
26
src/js/components/Thumbnail.js
Executable file
@ -0,0 +1,26 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
|
||||
export default class Thumbnail extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
sizedImageURL(){
|
||||
var images = this.props.images;
|
||||
switch( this.props.size ){
|
||||
case 'small':
|
||||
break;
|
||||
|
||||
default:
|
||||
return images[images.length-1].url
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<img src={ this.sizedImageURL() } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -14,12 +14,20 @@ const localstorageMiddleware = (function(){
|
||||
|
||||
case 'MOPIDY_SET_CONFIG':
|
||||
var mopidy = {
|
||||
host: action.host,
|
||||
port: action.port
|
||||
host: action.config.host,
|
||||
port: action.config.port
|
||||
};
|
||||
localStorage.setItem('mopidy', JSON.stringify(mopidy));
|
||||
break;
|
||||
|
||||
case 'SPOTIFY_SET_CONFIG':
|
||||
var spotify = {
|
||||
country: action.config.country,
|
||||
locale: action.config.locale
|
||||
};
|
||||
localStorage.setItem('spotify', JSON.stringify(spotify));
|
||||
break;
|
||||
|
||||
case 'SPOTIFY_COMPLETE_AUTHORIZATION':
|
||||
var spotify = {
|
||||
authorized: true,
|
||||
|
||||
@ -3,6 +3,13 @@
|
||||
* Actions and Action Creators
|
||||
**/
|
||||
|
||||
export function setConfig( config ){
|
||||
return {
|
||||
type: 'MOPIDY_SET_CONFIG',
|
||||
config: config
|
||||
}
|
||||
}
|
||||
|
||||
export function connect(){
|
||||
return {
|
||||
type: 'MOPIDY_CONNECT'
|
||||
@ -15,14 +22,6 @@ export function disconnect(){
|
||||
}
|
||||
}
|
||||
|
||||
export function setConfig( host, port ){
|
||||
return {
|
||||
type: 'MOPIDY_SET_CONFIG',
|
||||
host: host,
|
||||
port: port
|
||||
}
|
||||
}
|
||||
|
||||
export function changeTrack( tlid ){
|
||||
return {
|
||||
type: 'MOPIDY_CHANGE_TRACK',
|
||||
|
||||
@ -3,6 +3,13 @@
|
||||
* Actions and Action Creators
|
||||
**/
|
||||
|
||||
export function setConfig( config ){
|
||||
return {
|
||||
type: 'SPOTIFY_SET_CONFIG',
|
||||
config: config
|
||||
}
|
||||
}
|
||||
|
||||
export function startAuthorization(){
|
||||
return {
|
||||
type: 'SPOTIFY_START_AUTHORIZATION'
|
||||
|
||||
@ -33,8 +33,10 @@ const SpotifyMiddleware = (function(){
|
||||
* The actual middleware inteceptor
|
||||
**/
|
||||
return store => next => action => {
|
||||
switch(action.type) {
|
||||
|
||||
var state = store.getState();
|
||||
|
||||
switch(action.type){
|
||||
|
||||
case 'SPOTIFY_LOAD_ALBUM':
|
||||
|
||||
@ -57,21 +59,32 @@ const SpotifyMiddleware = (function(){
|
||||
case 'SPOTIFY_LOAD_ARTIST':
|
||||
|
||||
store.dispatch({ type: 'SPOTIFY_ARTIST_LOADED', data: false })
|
||||
store.dispatch({ type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', data: false })
|
||||
|
||||
var id = action.uri.replace('spotify:artist:','');
|
||||
|
||||
sendRequest( false, 'artists/'+id )
|
||||
.then( (response) => {
|
||||
store.dispatch({ type: 'SPOTIFY_ARTIST_LOADED', data: response })
|
||||
});
|
||||
|
||||
sendRequest( false, 'artists/'+id+'/albums' )
|
||||
.then( (response) => {
|
||||
store.dispatch({ type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', data: response })
|
||||
});
|
||||
|
||||
sendRequest( false, 'artists/'+id+'/top-tracks?country='+state.spotify.country )
|
||||
.then( (response) => {
|
||||
store.dispatch({ type: 'SPOTIFY_ARTIST_TRACKS_LOADED', data: response })
|
||||
});
|
||||
break;
|
||||
|
||||
|
||||
case 'SPOTIFY_LOAD_LIBRARY_ARTISTS':
|
||||
|
||||
store.dispatch({ type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', data: false })
|
||||
var token = store.getState().spotify.access_token;
|
||||
|
||||
sendRequest( token, 'me/following?type=artist' )
|
||||
sendRequest( state.spotify.access_token, 'me/following?type=artist' )
|
||||
.then( (response) => {
|
||||
store.dispatch({ type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', data: response })
|
||||
});
|
||||
|
||||
@ -35,6 +35,12 @@ export default function reducer(spotify = {}, action){
|
||||
case 'SPOTIFY_ARTIST_LOADED':
|
||||
return Object.assign({}, spotify, { artist: action.data });
|
||||
|
||||
case 'SPOTIFY_ARTIST_ALBUMS_LOADED':
|
||||
return Object.assign({}, spotify, { artist_albums: action.data });
|
||||
|
||||
case 'SPOTIFY_ARTIST_TRACKS_LOADED':
|
||||
return Object.assign({}, spotify, { artist_top_tracks: action.data });
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ARTISTS_LOADED':
|
||||
return Object.assign({}, spotify, { libraryArtists: action.data });
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
|
||||
import TrackList from '../components/TrackList'
|
||||
import AlbumGrid from '../components/AlbumGrid'
|
||||
|
||||
import * as spotifyActions from '../services/spotify/actions'
|
||||
|
||||
class Artist extends React.Component{
|
||||
@ -30,6 +32,8 @@ class Artist extends React.Component{
|
||||
<div>
|
||||
<h3>{ this.props.spotify.artist.name }</h3>
|
||||
<p>{ this.props.spotify.artist.followers.total.toLocaleString() } followers</p>
|
||||
{ this.props.spotify.artist_top_tracks ? <TrackList tracks={ this.props.spotify.artist_top_tracks.tracks } /> : null }
|
||||
{ this.props.spotify.artist_albums ? <AlbumGrid items={ this.props.spotify.artist_albums } /> : null }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -17,7 +17,9 @@ class Settings extends React.Component{
|
||||
|
||||
this.state = {
|
||||
mopidy_host: this.props.mopidy.host,
|
||||
mopidy_port: this.props.mopidy.port
|
||||
mopidy_port: this.props.mopidy.port,
|
||||
spotify_country: this.props.spotify.country,
|
||||
spotify_locale: this.props.spotify.locale
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,17 +28,22 @@ class Settings extends React.Component{
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
||||
setConfig(){
|
||||
this.props.mopidyActions.setConfig( this.state.mopidy_host, this.state.mopidy_port );
|
||||
setMopidyConfig(){
|
||||
this.props.mopidyActions.setConfig({ host: this.state.mopidy_host, port: this.state.mopidy_port });
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
||||
setSpotifyConfig(){
|
||||
this.props.spotifyActions.setConfig({ country: this.state.spotify_country, locale: this.state.spotify_locale });
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<h3>Settings</h3>
|
||||
|
||||
<h4>Mopidy</h4>
|
||||
<form onSubmit={() => this.setConfig()}>
|
||||
<form onSubmit={() => this.setMopidyConfig()}>
|
||||
<label>
|
||||
<span className="label">Host</span>
|
||||
<input onChange={ e => this.setState({ mopidy_host: e.target.value })} value={ this.state.mopidy_host } />
|
||||
@ -47,8 +54,22 @@ class Settings extends React.Component{
|
||||
</label>
|
||||
<button type="submit">Apply</button>
|
||||
</form>
|
||||
|
||||
<h4>Spotify</h4>
|
||||
<form onSubmit={() => this.setSpotifyConfig()}>
|
||||
<label>
|
||||
<span className="label">Country</span>
|
||||
<input onChange={ e => this.setState({ spotify_country: e.target.value })} value={ this.state.spotify_country } />
|
||||
</label>
|
||||
<label>
|
||||
<span className="label">Locale</span>
|
||||
<input onChange={ e => this.setState({ spotify_locale: e.target.value })} value={ this.state.spotify_locale } />
|
||||
</label>
|
||||
<button type="submit">Apply</button>
|
||||
</form>
|
||||
|
||||
<SpotifyAuthenticationFrame />
|
||||
|
||||
<ConfirmationButton content="Reset all settings" confirmingContent="Are you sure?" onConfirm={() => this.resetAllSettings()} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
|
||||
@import 'vendor/font-awesome/index';
|
||||
|
||||
@import 'global/reset';
|
||||
@import 'global/forms';
|
||||
|
||||
@import 'components/track';
|
||||
|
||||
|
||||
14
src/scss/global/_forms.scss
Executable file
14
src/scss/global/_forms.scss
Executable file
@ -0,0 +1,14 @@
|
||||
|
||||
input {
|
||||
padding: 5px 8px;
|
||||
border: 1px solid #000000;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="button"],
|
||||
input[type="submit"] {
|
||||
padding: 5px 8px;
|
||||
background: #000000;
|
||||
color: #FFFFFF;
|
||||
border: 0;
|
||||
}
|
||||
7
src/scss/global/_reset.scss
Executable file
7
src/scss/global/_reset.scss
Executable file
@ -0,0 +1,7 @@
|
||||
|
||||
* {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
outline: 0;
|
||||
}
|
||||
Reference in New Issue
Block a user