From 4bb52070f8fd01f6385730ea37cf057ecd9d0aeb Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Thu, 23 Feb 2017 22:06:43 +1300 Subject: [PATCH] Kiosk mode; Placeholder for unloaded albums/artists/playlists; Gmusic integration part 1 --- src/js/components/Modal/KioskModeModal.js | 62 +++++++++++++++ src/js/components/Modal/Modal.js | 2 + src/js/components/Thumbnail.js | 1 + src/js/helpers.js | 3 + src/js/services/mopidy/middleware.js | 5 +- src/js/views/Album.js | 95 ++++++++++++++--------- src/js/views/Artist.js | 57 +++++++++----- src/js/views/Playlist.js | 92 ++++++++++++++-------- src/js/views/Queue.js | 6 +- src/scss/components/_modal.scss | 41 ++++++++++ 10 files changed, 272 insertions(+), 92 deletions(-) create mode 100755 src/js/components/Modal/KioskModeModal.js diff --git a/src/js/components/Modal/KioskModeModal.js b/src/js/components/Modal/KioskModeModal.js new file mode 100755 index 00000000..48ce8d39 --- /dev/null +++ b/src/js/components/Modal/KioskModeModal.js @@ -0,0 +1,62 @@ + +import React, { PropTypes } from 'react' +import { connect } from 'react-redux' +import { Link } from 'react-router' +import { createStore, bindActionCreators } from 'redux' + +import * as uiActions from '../../services/ui/actions' +import * as helpers from '../../helpers' + +import Icon from '../Icon' +import Thumbnail from '../Thumbnail' +import ArtistSentence from '../ArtistSentence' + +class KioskModeModal extends React.Component{ + + constructor(props){ + super(props) + } + + handleClick(e, connectionid){ + e.preventDefault() + this.props.uiActions.closeModal() + return false; + } + + render(){ + if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){ + var images = this.props.current_track.album.images + } else { + var images = [] + } + + return ( +
+ + + + +
+
+
{ this.props.current_track ? this.props.current_track.name : - }
+ { this.props.current_track ? : } +
+
+
+ ) + } +} + +const mapStateToProps = (state, ownProps) => { + return { + current_track: state.ui.current_track + } +} + +const mapDispatchToProps = (dispatch) => { + return { + uiActions: bindActionCreators(uiActions, dispatch) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(KioskModeModal) \ No newline at end of file diff --git a/src/js/components/Modal/Modal.js b/src/js/components/Modal/Modal.js index f150a217..2a5d3c20 100755 --- a/src/js/components/Modal/Modal.js +++ b/src/js/components/Modal/Modal.js @@ -11,6 +11,7 @@ import EditPlaylistModal from './EditPlaylistModal' import SendAuthorizationModal from './SendAuthorizationModal' import EditRadioModal from './EditRadioModal' import ImageZoomModal from './ImageZoomModal' +import KioskModeModal from './KioskModeModal' import * as uiActions from '../../services/ui/actions' import * as mopidyActions from '../../services/mopidy/actions' @@ -39,6 +40,7 @@ class Modal extends React.Component{ { this.props.modal.name == 'send_authorization' ? : null } { this.props.modal.name == 'edit_radio' ? : null } { this.props.modal.name == 'image_zoom' ? : null } + { this.props.modal.name == 'kiosk_mode' ? : null } diff --git a/src/js/components/Thumbnail.js b/src/js/components/Thumbnail.js index f6e08e18..b9bf5c6d 100755 --- a/src/js/components/Thumbnail.js +++ b/src/js/components/Thumbnail.js @@ -68,6 +68,7 @@ class Thumbnail extends React.Component{ var style = { backgroundImage: 'url("'+image+'")' } var class_name = 'thumbnail '+this.props.size; if( this.props.circle ) class_name += ' circle'; + if( this.props.className ) class_name += ' '+this.props.className; var zoom_icon = null if (this.props.canZoom){ diff --git a/src/js/helpers.js b/src/js/helpers.js index 79156842..2505b232 100755 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -141,6 +141,9 @@ export let sourceIcon = function(uri,source = null){ case 'm3u': return 'folder' break + case 'gmusic': + return 'google' + break default: return source } diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 04b15dee..b1d0a596 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -360,13 +360,14 @@ const MopidyMiddleware = (function(){ for (var i = 0; i < response.length; i++ ){ instruct( socket, store, 'playlists.lookup', { uri: response[i].uri }) .then( response => { + var source = helpers.uriSource(response.uri) var playlist = Object.assign( {}, { type: 'playlist', name: response.name, uri: response.uri, - source: 'local', + source: (source == 'spotify' ? 'local' : source), last_modified: response.last_modified, can_edit: (response.uri.startsWith('m3u:')), tracks_total: ( response.tracks ? response.tracks.length : 0 ) @@ -739,7 +740,7 @@ const MopidyMiddleware = (function(){ var artist = Object.assign( {}, - response[0].artists[0], + (response ? response[0].artists[0] : {}), { albums_uris: helpers.asURIs(albums), tracks: response.slice(0,10) diff --git a/src/js/views/Album.js b/src/js/views/Album.js index 0cb6ce98..36b8d750 100755 --- a/src/js/views/Album.js +++ b/src/js/views/Album.js @@ -60,7 +60,7 @@ class Album extends React.Component{ } break; - case 'local': + default: if (props.mopidy_connected){ if (props.album && props.album.tracks){ console.info('Loading album from index') @@ -81,53 +81,76 @@ class Album extends React.Component{ } render(){ - if( !this.props.album ) return null - - var artists = [] - if (this.props.album.artists_uris && this.props.artists){ - for (var i = 0; i < this.props.album.artists_uris.length; i++){ - var uri = this.props.album.artists_uris[i] - if (this.props.artists.hasOwnProperty(uri)){ - artists.push(this.props.artists[uri]) + if (this.props.album){ + var artists = [] + if (this.props.album.artists_uris && this.props.artists){ + for (var i = 0; i < this.props.album.artists_uris.length; i++){ + var uri = this.props.album.artists_uris[i] + if (this.props.artists.hasOwnProperty(uri)){ + artists.push(this.props.artists[uri]) + } } } - } + return ( +
+ - return ( -
- + - +
+
+ {helpers.uriSource( this.props.params.uri )} album +
-
-
- {helpers.uriSource( this.props.params.uri )} album +

{ this.props.album.name }

+ +
    + { artists.length > 0 ?
  • : null } + { this.props.album.release_date ?
  • : null } +
  • + { this.props.album.tracks_total ? this.props.album.tracks_total : '0' } tracks,  + { this.props.album.tracks ? : '0 mins' } +
  • +
-

{ this.props.album.name }

+
+ + { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null } +
+ +
+ { this.props.album.tracks ? : null } + this.loadMore() }/> +
-
    - { artists.length > 0 ?
  • : null } - { this.props.album.release_date ?
  • : null } -
  • - { this.props.album.tracks_total ? this.props.album.tracks_total : '0' } tracks,  - { this.props.album.tracks ? : '0 mins' } -
  • -
+ ); -
- - { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null } + } else { + return ( +
+ + +
+
+ {helpers.uriSource( this.props.params.uri )} album +
+

{ this.props.params.uri }

+
    +
  • + 0 tracks, + 0 mins +
  • +
+
+
+ +
+ ); -
- { this.props.album.tracks ? : null } - this.loadMore() }/> -
- -
- ); + } } } diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 6b396b86..09628b36 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -65,7 +65,7 @@ class Artist extends React.Component{ } break - case 'local': + default: if (props.mopidy_connected){ if (props.artist && props.artist.images){ console.info('Loading local artist from index') @@ -177,33 +177,52 @@ class Artist extends React.Component{ } render(){ - if( !this.props.artist ) return null var scheme = helpers.uriSource( this.props.params.uri ); - var image = null - if( this.props.artist.images ) image = helpers.sizedImages( this.props.artist.images ).huge + if ( this.props.artist && this.props.artist.images ){ + var image = helpers.sizedImages( this.props.artist.images ).huge + } else { + var image = null + } - return ( -
+ if (this.props.artist){ + return ( +
- + -
+
- + -
- -

{ this.props.artist.name }

- { scheme == 'spotify' ?
: null} - { this.renderSubViewMenu() } +
+ +

{this.props.artist ? this.props.artist.name : null}

+ { scheme == 'spotify' ?
: null} + { this.renderSubViewMenu() } +
+
+ + {this.props.artist ? this.renderBody() : null} + +
+ ); + + } else { + return ( +
+ +
+ +
+ +

{this.props.params.uri}

+ { this.renderSubViewMenu() } +
- - { this.renderBody() } - -
- ); + ); + } } } diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index 3fabbb0d..7ead6218 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -46,7 +46,7 @@ class Playlist extends React.Component{ this.props.spotifyActions.getPlaylist( props.params.uri ); break - case 'm3u': + default: if( props.mopidy_connected ) this.props.mopidyActions.getPlaylist( props.params.uri ); break @@ -128,48 +128,72 @@ class Playlist extends React.Component{ } render(){ - if( !this.props.playlist || !this.props.playlist.name ) return null - var scheme = helpers.uriSource( this.props.playlist.uri ) - + var scheme = helpers.uriSource( this.props.params.uri ) var context = 'playlist-track' - if (this.props.playlist.can_edit) context = 'editable-playlist-track' - return ( -
- - + if (this.props.playlist){ + if (this.props.playlist.can_edit) context = 'editable-playlist-track' - + return ( +
+ + -
-
- {helpers.uriSource( this.props.params.uri )} playlist + + +
+
+ {helpers.uriSource( this.props.params.uri )} playlist +
+

{ this.props.playlist.name }

+ { this.props.playlist.description ?
: null } + +
    + { this.props.playlist.owner ?
  • {this.props.playlist.owner.id}
  • : null } + + { this.props.playlist.followers ?
  • {this.props.playlist.followers.total.toLocaleString()} followers
  • : null } + + { this.props.playlist.last_modified ?
  • : null } + +
  • + { this.props.playlist.tracks_total ? this.props.playlist.tracks_total : '0'} tracks,  + { this.props.playlist.tracks ? : '0 mins' } +
  • +
-

{ this.props.playlist.name }

- { this.props.playlist.description ?
: null } -
    - { this.props.playlist.owner ?
  • {this.props.playlist.owner.id}
  • : null } + { this.renderActions() } - { this.props.playlist.followers ?
  • {this.props.playlist.followers.total.toLocaleString()} followers
  • : null } - - { this.props.playlist.last_modified ?
  • : null } - -
  • - { this.props.playlist.tracks_total ? this.props.playlist.tracks_total : '0'} tracks,  - { this.props.playlist.tracks ? : '0 mins' } -
  • -
+
+ { this.props.playlist.tracks ? this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } /> : null } + this.loadMore() }/> +
+ ) - { this.renderActions() } - -
- { this.props.playlist.tracks ? this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } /> : null } - this.loadMore() }/> -
-
- ) + } else { + return ( +
+ + +
+
+ {helpers.uriSource( this.props.params.uri )} playlist +
+

{ this.props.params.uri }

+
    +
  • + 0 tracks, + 0 mins +
  • +
+
+
+ +
+
+ ) + } } } diff --git a/src/js/views/Queue.js b/src/js/views/Queue.js index e74b7f03..3833950f 100755 --- a/src/js/views/Queue.js +++ b/src/js/views/Queue.js @@ -21,7 +21,7 @@ import * as mopidyActions from '../services/mopidy/actions' class Queue extends React.Component{ constructor(props) { - super(props); + super(props) } removeTracks( tracks_indexes ){ @@ -59,6 +59,10 @@ class Queue extends React.Component{ render(){ var actions = ( +