@@ -53,7 +53,11 @@ class PusherConnectionList extends React.Component{
}
const mapStateToProps = (state, ownProps) => {
- return state;
+ return {
+ connected: state.pusher.connected,
+ connectionid: state.pusher.connectionid,
+ connections: state.pusher.connections
+ }
}
const mapDispatchToProps = (dispatch) => {
diff --git a/src/js/components/SpotifyAuthenticationFrame.js b/src/js/components/SpotifyAuthenticationFrame.js
index e731fa67..d70369bf 100755
--- a/src/js/components/SpotifyAuthenticationFrame.js
+++ b/src/js/components/SpotifyAuthenticationFrame.js
@@ -59,20 +59,6 @@ class SpotifyAuthenticationFrame extends React.Component{
})
}
- renderMe(){
- if( !this.props.authorized || !this.props.me ) return null;
-
- return (
-
- )
- }
-
renderAuthorizeButton(){
if( this.state.authorizing ){
return (
@@ -114,7 +100,6 @@ class SpotifyAuthenticationFrame extends React.Component{
render(){
return (
- { this.renderMe() }
{ this.renderAuthorizeButton() }
{ this.renderRefreshButton() }
@@ -128,8 +113,7 @@ const mapStateToProps = (state, ownProps) => {
return {
authorized: state.spotify.authorized,
authorizing: state.spotify.authorizing,
- refreshing_token: state.spotify.refreshing_token,
- me: state.spotify.me
+ refreshing_token: state.spotify.refreshing_token
}
}
diff --git a/src/js/components/Thumbnail.js b/src/js/components/Thumbnail.js
index 616e4609..66dc631c 100755
--- a/src/js/components/Thumbnail.js
+++ b/src/js/components/Thumbnail.js
@@ -20,10 +20,14 @@ export default class Thumbnail extends React.Component{
this.mapImageSizes( nextProps );
}
- mapImageSizes( props = this.props ){
+ mapImageSizes( props = this.props ){
+
+ // no images
+ if( !this.props.image && !this.props.images ){
+ this.setState({ url: require('../../assets/no-image.svg') })
// single image
- if( this.props.image ){
+ }else if( this.props.image ){
this.setState({ url: this.props.image })
// multiple images
@@ -36,9 +40,7 @@ export default class Thumbnail extends React.Component{
}
render(){
- var style = {
- backgroundImage: 'url("'+this.state.url+'")'
- }
+ var style = { backgroundImage: 'url("'+this.state.url+'")' }
var className = 'thumbnail '+this.props.size;
if( this.props.circle ) className += ' circle';
diff --git a/src/js/helpers.js b/src/js/helpers.js
index f1b3b804..790fb625 100755
--- a/src/js/helpers.js
+++ b/src/js/helpers.js
@@ -80,6 +80,19 @@ export let getCurrentPusherConnection = function( connections, connectionid ){
}
+/**
+ * Get a track's icon
+ * @param track object
+ * @return string
+ **/
+export let getTrackIcon = function( track = false ){
+ if( !track ) return false
+ if( !track.album ) return false
+ if( !track.album.images ) return false
+ return sizedImages( track.album.images ).small
+}
+
+
/**
diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js
index 87d5ca69..7180f5c3 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 ){
diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js
index cd744138..5b135963 100755
--- a/src/js/services/mopidy/middleware.js
+++ b/src/js/services/mopidy/middleware.js
@@ -161,6 +161,47 @@ const MopidyMiddleware = (function(){
instruct( socket, store, action.call, action.value )
break;
+ // send an instruction to the websocket
+ case 'MOPIDY_NEXT':
+ var icon = false
+ if( store.getState().ui.current_track ){
+ icon = helpers.getTrackIcon( store.getState().ui.current_track )
+ }
+ store.dispatch({
+ type: 'PUSHER_SEND_BROADCAST',
+ action: 'broadcast',
+ ignore_self: true,
+ data: {
+ type: 'notification',
+ data: {
+ title: 'Track skipped',
+ body: store.getState().pusher.username +' skipped this track',
+ icon: icon
+ }
+ }
+ })
+ break;
+
+ case 'MOPIDY_STOP':
+ var icon = false
+ if( store.getState().ui.current_track ){
+ icon = helpers.getTrackIcon( store.getState().ui.current_track )
+ }
+ store.dispatch({
+ type: 'PUSHER_SEND_BROADCAST',
+ action: 'broadcast',
+ ignore_self: true,
+ data: {
+ type: 'notification',
+ data: {
+ title: 'Playback stopped',
+ body: store.getState().pusher.username +' stopped playback',
+ icon: icon
+ }
+ }
+ })
+ break;
+
// send an instruction to the websocket
case 'MOPIDY_URISCHEMES':
var uri_schemes = action.data
diff --git a/src/js/services/pusher/middleware.js b/src/js/services/pusher/middleware.js
index d3cecb59..d3015351 100755
--- a/src/js/services/pusher/middleware.js
+++ b/src/js/services/pusher/middleware.js
@@ -13,7 +13,7 @@ const PusherMiddleware = (function(){
default:
var name = 'unspecified'
if( message.action ) name = message.action
- name = name.replace('GET_','').toUpperCase()
+ name = name.replace('get_','').toUpperCase()
store.dispatch({ type: 'PUSHER_'+name, data: message.data })
}
}
@@ -24,6 +24,12 @@ const PusherMiddleware = (function(){
socket.send( JSON.stringify(data) );
}
+ const broadcast = (data) => {
+ data.type = 'broadcast';
+ socket.send( JSON.stringify(data) );
+ }
+
+
/**
* Middleware
*
@@ -84,6 +90,38 @@ const PusherMiddleware = (function(){
makeRequest({ action: action.action, data: action.data });
break;
+ case 'PUSHER_SEND_BROADCAST':
+ broadcast({ action: action.action, data: action.data });
+ break;
+
+ case 'PUSHER_NOTIFICATION':
+ case 'PUSHER_BROADCAST':
+
+ var notification = window.Notification || window.mozNotification || window.webkitNotification;
+ if ('undefined' === typeof notification) return false;
+ if ('undefined' !== typeof notification) notification.requestPermission(function(permission){});
+
+ // handle nested data objects
+ var data = {}
+ if( typeof(action.data) ) data = action.data
+ if( typeof(data.data) ) data = Object.assign({}, data, data.data)
+
+ // construct our browser notification
+ var title = '';
+ var options = {
+ body: '',
+ dir: 'auto',
+ lang: 'EN',
+ tag: 'iris'
+ };
+ if( data.title ) title = data.title;
+ if( data.body ) options.body = data.body;
+ if( data.icon ) options.icon = data.icon;
+
+ // make it so
+ var notification = new notification( title, options );
+ break;
+
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);
diff --git a/src/js/views/Queue.js b/src/js/views/Queue.js
index a9a277c9..ff27091e 100755
--- a/src/js/views/Queue.js
+++ b/src/js/views/Queue.js
@@ -45,7 +45,7 @@ class Queue extends React.Component{
return (
-
+
diff --git a/src/js/views/Settings.js b/src/js/views/Settings.js
index d7a933de..c7d549fd 100755
--- a/src/js/views/Settings.js
+++ b/src/js/views/Settings.js
@@ -1,15 +1,17 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
+import { Link } from 'react-router'
import { bindActionCreators } from 'redux'
-
import FontAwesome from 'react-fontawesome'
+
import SpotifyAuthenticationFrame from '../components/SpotifyAuthenticationFrame'
import ConfirmationButton from '../components/ConfirmationButton'
import PusherConnectionList from '../components/PusherConnectionList'
import URISchemesList from '../components/URISchemesList'
import VersionManager from '../components/VersionManager'
import Header from '../components/Header'
+import Thumbnail from '../components/Thumbnail'
import * as pusherActions from '../services/pusher/actions'
import * as mopidyActions from '../services/mopidy/actions'
@@ -84,6 +86,28 @@ class Settings extends React.Component{
}
}
+ renderSpotifyUser(){
+ if( this.props.spotify.me && this.props.spotify.authorized ){
+ return (
+
+
+
+ { this.props.spotify.me.display_name ? this.props.spotify.me.display_name : this.props.spotify.me.id }
+
+
+ )
+ }else{
+ return (
+
+
+
+ Default user (As defined in mopidy.config)
+
+
+ )
+ }
+ }
+
render(){
return (
@@ -178,6 +202,14 @@ class Settings extends React.Component{
value={ this.state.spotify_locale } />
+
+
Current user
+
+
+ { this.renderSpotifyUser() }
+
+
+
Authentication
@@ -188,7 +220,7 @@ class Settings extends React.Component{
Advanced
-
+
Connections
diff --git a/src/scss/views/_album.scss b/src/scss/views/_album.scss
index de6f964e..f2bab1db 100755
--- a/src/scss/views/_album.scss
+++ b/src/scss/views/_album.scss
@@ -73,4 +73,20 @@
}
}
}
+
+ @include responsive( $bp_medium ){
+
+ .main {
+ padding-left: 0;
+ }
+
+ .intro {
+ position: static;
+ width: auto;
+
+ .artist-grid {
+ display: none;
+ }
+ }
+ }
}
diff --git a/src/scss/views/_artist.scss b/src/scss/views/_artist.scss
index b45b6501..d86261e4 100755
--- a/src/scss/views/_artist.scss
+++ b/src/scss/views/_artist.scss
@@ -91,8 +91,7 @@
.parallax {
height: 100%;
canvas {
- @include blur();
- margin: -20px;
+ @include blur( 30px );
}
}
diff --git a/src/scss/views/_settings.scss b/src/scss/views/_settings.scss
index 6a4e620c..5406955d 100755
--- a/src/scss/views/_settings.scss
+++ b/src/scss/views/_settings.scss
@@ -4,18 +4,41 @@
padding: 40px;
}
- .me {
- display: block;
- float: left;
+ .field.current-user {
+ .text {
+ padding: 14px 14px 14px 0px;
- .thumbnail {
- float: left;
- margin-right: 10px;
- max-width: 40px;
+ .user {
+ border: 0;
+ position: relative;
+
+ .thumbnail {
+ @include animate();
+ position: absolute;
+ display: block;
+ top: -14px;
+ left: 0;
+ max-width: 40px;
+ border: 2px solid transparent;
+ }
+
+ .user-name {
+ padding: 2px 14px 2px 55px;
+ }
+
+ &[href]:hover {
+ .thumbnail {
+ cursor: pointer;
+ border-color: $turquoise;
+ }
+ }
+ }
}
+ }
- .user-name {
- padding: 2px 14px;
+ .field.pusher-connections {
+ .connection.me {
+ font-weight: bold;
}
}