diff --git a/mopidy_iris/frontend.py b/mopidy_iris/frontend.py index 9ec657c7..da07748b 100755 --- a/mopidy_iris/frontend.py +++ b/mopidy_iris/frontend.py @@ -98,6 +98,7 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener): self.load_more_tracks() except RuntimeError: + pusher.broadcast('error', {'source': 'check_for_radio_update', 'message': 'Could not fetch tracklist length'}) logger.warning('IrisFrontend: Could not fetch tracklist length') pass @@ -118,6 +119,7 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener): token = token['access_token'] except: logger.error('IrisFrontend: access_token missing or invalid') + pusher.broadcast('error', {'source': 'load_more_tracks', 'message': 'access_token missing or invalid'}) try: spotify = Spotify( auth = token ) @@ -129,7 +131,8 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener): self.core.tracklist.add( uris = uris ) except: - logger.error('IrisFrontend: Failed to fetch recommendations from Spotify') + pusher.broadcast('error', {'source': 'load_more_tracks', 'message': 'Failed to fetch Spotify recommendations'}) + logger.error('IrisFrontend: Failed to fetch Spotify recommendations') ## @@ -154,7 +157,7 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener): self.core.playback.play() # notify clients - pusher.broadcast( 'radio', { 'radio': self.radio }) + pusher.broadcast('radio', { 'radio': self.radio }) # return new radio state to initial call return self.radio diff --git a/src/js/components/Modal/AddToPlaylistModal.js b/src/js/components/Modal/AddToPlaylistModal.js index 8ae15da4..0eb131a2 100755 --- a/src/js/components/Modal/AddToPlaylistModal.js +++ b/src/js/components/Modal/AddToPlaylistModal.js @@ -27,11 +27,11 @@ export default class AddToPlaylistModal extends React.Component{ return (

Add to playlist

-
+
{ playlists.map( playlist => { return ( -
this.playlistSelected(playlist.uri) }> +
this.playlistSelected(playlist.uri) }>   { playlist.name } diff --git a/src/js/components/Modal/EditRadioModal.js b/src/js/components/Modal/EditRadioModal.js index 8f764d00..215f563e 100755 --- a/src/js/components/Modal/EditRadioModal.js +++ b/src/js/components/Modal/EditRadioModal.js @@ -10,31 +10,67 @@ export default class EditRadioModal extends React.Component{ constructor(props){ super(props) this.state = { - seed_tracks: [], - seed_genres: [], - seed_artists: [], - uri: '' + enabled: false, + seeds: [], + uri: '', + uri_validated: false } } componentDidMount(){ - this.setState(this.props.radio) + if (!this.props.radio || !this.props.radio.enabled) return null + var seeds = [...this.props.radio.seed_tracks, ...this.props.radio.seed_artists, ...this.props.radio.seed_genres] + this.setState({seeds: seeds, enabled: this.props.radio.enabled}) + } + + handleChange(uri){ + this.setState({uri: uri}) + + var allowed_types = ['artist','track'] + if (allowed_types.indexOf(helpers.uriType(uri)) > -1){ + this.setState({uri_validated: true}) + } } save(){ + this.props.pusherActions.startRadio(this.state.seeds) this.props.uiActions.closeModal() } - addSeed(seed){ - this.setState({uri: ''}) + stop(){ + this.props.pusherActions.stopRadio() + this.props.uiActions.closeModal() } - removeSeed(type,seed){ - console.log('remove', type, seed) + addSeed(){ + if (!this.state.uri_validated || this.state.uri == '') return null + + var seeds = Object.assign([],this.state.seeds) + + if (seeds.indexOf(this.state.uri) <= -1){ + seeds.push(this.state.uri) + } else { + this.props.uiActions.createNotification('Seed already exists','bad') + } + + // commit to state + this.setState({ + seeds: seeds, + uri: '', + uri_validated: false + }) + } + + removeSeed(uri){ + var seeds = Object.assign([],this.state.seeds) + var index = seeds.indexOf(uri) + if (index > -1){ + delete seeds[index] + this.setState({seeds: seeds}) + } } renderSeeds(){ - if (!this.props.radio || !this.props.radio.enabled) return null /* var seeds = this.props.radio.resolved_seeds var uri @@ -56,31 +92,11 @@ export default class EditRadioModal extends React.Component{ return (
{ - this.state.seed_tracks.map(seed => { + this.state.seeds.map((seed,index) => { return ( -
+
{seed} - this.removeSeed('track',seed)} /> -
- ) - }) - } - { - this.state.seed_genres.map(seed => { - return ( -
- {seed} - this.removeSeed('genre',seed)} /> -
- ) - }) - } - { - this.state.seed_artists.map(seed => { - return ( -
- {seed} - this.removeSeed('artist',seed)} /> + this.removeSeed(seed)} />
) }) @@ -89,21 +105,43 @@ export default class EditRadioModal extends React.Component{ ) } + renderActions(){ + if (this.state.enabled){ + return ( + + + + + ) + }else{ + return ( + + + + ) + } + } + render(){ return (

Edit radio

- {this.renderSeeds()} +
this.setState({uri: e.target.value})} + placeholder="Spotify URI" + onChange={e => this.handleChange(e.target.value)} value={this.state.uri} /> + +
+ + {this.renderSeeds()} + +
+ {this.renderActions()}
- -
) diff --git a/src/js/components/Modal/SendAuthorizationModal.js b/src/js/components/Modal/SendAuthorizationModal.js index 1c02e304..7950f3ad 100755 --- a/src/js/components/Modal/SendAuthorizationModal.js +++ b/src/js/components/Modal/SendAuthorizationModal.js @@ -27,7 +27,7 @@ class SendAuthorizationModal extends React.Component{

Share Spotify authentication

Send your authentication tokens to another client. When the recipient client imports this, their Iris will have full access to your Spotify account ({this.props.me.id}).

-
+
{ this.props.connections.map( (connection, index) => { @@ -35,7 +35,7 @@ class SendAuthorizationModal extends React.Component{ if (connection.connectionid == this.props.connectionid) return null return ( -
this.handleClick(e, connection.connectionid) }> +
this.handleClick(e, connection.connectionid) }> { connection.username }   ({ connection.ip }) diff --git a/src/js/services/pusher/middleware.js b/src/js/services/pusher/middleware.js index eb8082fe..00fb9715 100755 --- a/src/js/services/pusher/middleware.js +++ b/src/js/services/pusher/middleware.js @@ -128,6 +128,10 @@ const PusherMiddleware = (function(){ return next(action); break; + case 'ERROR': + store.dispatch( uiActions.createNotification(action.data.source+': '+action.data.message,'bad') ) + break; + case 'START_UPGRADE': request({ action: 'upgrade' }) .then( @@ -233,6 +237,9 @@ const PusherMiddleware = (function(){ case 'track': data.seed_tracks.push( action.uris[i] ); break; + case 'genre': + data.seed_genres.push( action.uris[i] ); + break; } } @@ -256,7 +263,6 @@ const PusherMiddleware = (function(){ } ) - store.dispatch(uiActions.createNotification('Stopping radio')) var data = { action: 'stop_radio', seed_artists: [], diff --git a/src/js/views/Queue.js b/src/js/views/Queue.js index 30398717..f9e46f87 100755 --- a/src/js/views/Queue.js +++ b/src/js/views/Queue.js @@ -57,27 +57,18 @@ class Queue extends React.Component{ render(){ var actions = ( - + + + + ) - if (this.props.radio && this.props.radio.enabled){ - var actions = ( - - - - - ) - } - return (
diff --git a/src/scss/components/_modal.scss b/src/scss/components/_modal.scss index c4f7bbce..3d356e33 100755 --- a/src/scss/components/_modal.scss +++ b/src/scss/components/_modal.scss @@ -68,6 +68,7 @@ .list { @include clearfix(); + padding-top: 30px; .list-item { box-sizing: border-box; @@ -81,20 +82,6 @@ border-top: 1px solid $mid_grey; } - &.small { - padding-left: 30px; - width: 49%; - float: left; - - &:nth-child(2){ - border-top: 1px solid $mid_grey; - } - - &:nth-child(2n){ - margin-left: 2%; - } - } - .source { position: absolute; top: 18px; @@ -107,8 +94,25 @@ } } - &.pusher-connection-list { - padding-top: 50px; + &.playlists { + padding-top: 0; + + .list-item { + padding-left: 30px; + } + } + + &.small .list-item { + width: 49%; + float: left; + + &:nth-child(2){ + border-top: 1px solid $mid_grey; + } + + &:nth-child(2n){ + margin-left: 2%; + } } } } @@ -127,6 +131,27 @@ } } + &.edit_radio { + .field { + position: relative; + button { + position: absolute; + top: 5px; + right: 0; + } + } + } + + .actions { + padding-top: 50px; + text-align: center; + + .button, + button { + margin: 0 3px; + } + } + @include responsive( $bp_medium ){ .content { diff --git a/src/scss/global/_core.scss b/src/scss/global/_core.scss index 8049a27f..7664675b 100755 --- a/src/scss/global/_core.scss +++ b/src/scss/global/_core.scss @@ -166,6 +166,7 @@ h4 { .red-text { color: $red !important; } .green-text { color: $green !important; } .dark-text { color: $dark_grey !important; } +.centred-text { text-align: center; } footer { display: block; diff --git a/src/scss/global/_forms.scss b/src/scss/global/_forms.scss index 07944c46..d957b67d 100755 --- a/src/scss/global/_forms.scss +++ b/src/scss/global/_forms.scss @@ -96,17 +96,16 @@ input[type="submit"] { } } - &.centered { - display: block; + &.wide { + display: inline-block; min-width: 200px; - margin: 0 auto; text-align: center; padding-left: 30px; padding-right: 30px; } &[disabled], - &[disabled="disabled"] { + &[disabled="disabled"]{ background: rgba(200,200,200,0.4) !important; color: $mid_grey !important; cursor: not-allowed !important;