From e93751a702c8e6727eab7db0ab6a6d86a342dcef Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Fri, 27 Oct 2017 10:51:48 +1300 Subject: [PATCH] URILink for encoding all URIs; fixes #220; current_track now just a URI --- src/js/components/AlbumGrid.js | 2 +- src/js/components/ArtistGrid.js | 9 +- src/js/components/ArtistSentence.js | 18 +- src/js/components/CategoryGrid.js | 2 +- src/js/components/FullPlayer.js | 165 ------------- src/js/components/List.js | 7 +- src/js/components/MiniPlayer.js | 80 ------- src/js/components/PlaybackControls.js | 2 +- src/js/components/PlaylistGrid.js | 4 +- src/js/components/ProgressSlider.js | 1 - src/js/components/PusherConnectionList.js | 1 - src/js/components/RelatedArtists.js | 6 +- src/js/components/SearchForm.js | 10 +- .../components/SpotifyAuthenticationFrame.js | 1 - src/js/components/Track.js | 9 +- src/js/components/URILink.js | 53 +++++ src/js/helpers.js | 7 + src/js/services/core/reducer.js | 17 +- src/js/services/mopidy/middleware.js | 51 ++-- src/js/views/Playlist.js | 2 +- src/js/views/Queue.js | 29 ++- src/js/views/library/LibraryBrowse.js | 218 ++++++++---------- 22 files changed, 252 insertions(+), 442 deletions(-) delete mode 100755 src/js/components/FullPlayer.js delete mode 100755 src/js/components/MiniPlayer.js create mode 100755 src/js/components/URILink.js diff --git a/src/js/components/AlbumGrid.js b/src/js/components/AlbumGrid.js index 80188fb6..053d9e5a 100755 --- a/src/js/components/AlbumGrid.js +++ b/src/js/components/AlbumGrid.js @@ -41,7 +41,7 @@ class AlbumGrid extends React.Component{ key={album.uri} type="album" item={album} - onClick={e => {hashHistory.push(global.baseURL+'album/'+album.uri)}} + onClick={e => {hashHistory.push(global.baseURL+'album/'+encodeURIComponent(album.uri))}} onLoad={() => this.handleLoad(album.uri)} onContextMenu={e => this.handleContextMenu(e,album)} /> diff --git a/src/js/components/ArtistGrid.js b/src/js/components/ArtistGrid.js index eb9e65ca..666964dd 100755 --- a/src/js/components/ArtistGrid.js +++ b/src/js/components/ArtistGrid.js @@ -2,10 +2,10 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' import { createStore, bindActionCreators } from 'redux' -import { Link } from 'react-router' import FontAwesome from 'react-fontawesome' import Thumbnail from './Thumbnail' +import URILink from './URILink' import * as helpers from '../helpers' import * as uiActions from '../services/ui/actions' @@ -39,9 +39,10 @@ class ArtistGrid extends React.Component{ this.props.artists.map( (artist, index) => { return ( - this.handleContextMenu(e,artist)}> @@ -53,7 +54,7 @@ class ArtistGrid extends React.Component{ {artist.followers ? artist.followers.total.toLocaleString()+' followers' : null} {artist.albums_uris && !artist.followers ? artist.albums_uris.length+' albums' : null} - + ) } ) diff --git a/src/js/components/ArtistSentence.js b/src/js/components/ArtistSentence.js index 71d42e4a..7e6c0df3 100755 --- a/src/js/components/ArtistSentence.js +++ b/src/js/components/ArtistSentence.js @@ -1,7 +1,7 @@ import React, { PropTypes } from 'react' import FontAwesome from 'react-fontawesome' -import { Link } from 'react-router' +import URILink from './URILink' export default class ArtistSentence extends React.Component{ @@ -10,26 +10,34 @@ export default class ArtistSentence extends React.Component{ } render(){ - if (!this.props.artists ) return - + if (!this.props.artists){ + return -; + } return ( { this.props.artists.map((artist, index) => { - if (!artist) return - + + if (!artist){ + return -; + } + var separator = null; if (index == this.props.artists.length - 2){ separator = ' and '; - }else if (index < this.props.artists.length - 2){ + } else if (index < this.props.artists.length - 2){ separator = ', '; } + if (!artist.name){ var content = - } else if (!artist.uri || this.props.nolinks){ var content = { artist.name } } else { - var content = { artist.name } + var content = { artist.name } } + return ( { content } diff --git a/src/js/components/CategoryGrid.js b/src/js/components/CategoryGrid.js index 4e83f1b4..f8b69d14 100755 --- a/src/js/components/CategoryGrid.js +++ b/src/js/components/CategoryGrid.js @@ -24,7 +24,7 @@ export default class CategoryGrid extends React.Component{ key={category.id} type="category" item={category} - onClick={e => {hashHistory.push(global.baseURL+'discover/categories/'+category.id)}} + onClick={e => {hashHistory.push(global.baseURL+'discover/categories/'+encodeURIComponent(category.id))}} /> ) }) diff --git a/src/js/components/FullPlayer.js b/src/js/components/FullPlayer.js deleted file mode 100755 index 1e54d8ff..00000000 --- a/src/js/components/FullPlayer.js +++ /dev/null @@ -1,165 +0,0 @@ - -import React, { PropTypes } from 'react' -import { connect } from 'react-redux' -import { bindActionCreators } from 'redux' -import { Link } from 'react-router' - -import FontAwesome from 'react-fontawesome' -import ProgressSlider from './ProgressSlider' -import ArtistSentence from './ArtistSentence' -import Thumbnail from './Thumbnail' -import Dater from './Dater' - -import * as uiActions from '../services/ui/actions' -import * as mopidyActions from '../services/mopidy/actions' - -class FullPlayer extends React.Component{ - - constructor(props){ - super(props); - } - - handleContextMenu(e,item){ - e.preventDefault() - var data = { - e: e, - context: 'album', - uris: [item.uri], - items: [item] - } - this.props.uiActions.showContextMenu(data) - } - - renderPlayButton(){ - var button = this.props.mopidyActions.play()}> - if (this.props.play_state == 'playing'){ - button = this.props.mopidyActions.pause()}> - } - return button; - } - - renderConsumeButton(){ - var button = this.props.mopidyActions.instruct('tracklist.setConsume', [true])}>Consume - if (this.props.consume){ - button = this.props.mopidyActions.instruct('tracklist.setConsume', [false])}>Consume - } - return button; - } - - renderRandomButton(){ - var button = this.props.mopidyActions.instruct('tracklist.setRandom', [true])}>Shuffle - if (this.props.random){ - button = this.props.mopidyActions.instruct('tracklist.setRandom', [false])}>Shuffle - } - return button; - } - - renderRepeatButton(){ - var button = this.props.mopidyActions.instruct('tracklist.setRepeat', [true])}>Repeat - if (this.props.repeat){ - button = this.props.mopidyActions.instruct('tracklist.setRepeat', [false])}>Repeat - } - return button; - } - - renderArtwork(){ - if ( - !this.props.current_track || - !this.props.current_track.album || - !this.props.current_track.album.images){ - return ( - - {this.props.radio_enabled ? : null} - - - ) - } - - var images = this.props.current_track.album.images - if (typeof(this.props.tracks[this.props.current_track.uri]) !== 'undefined'){ - images = this.props.tracks[this.props.current_track.uri].album.images - } - - var link = null - if (this.props.current_track.album.uri ) link = '/album/'+this.props.current_track.album.uri - return ( - this.handleContextMenu(e,this.props.current_track.album)}> - {this.props.radio_enabled ? : null} - - - ) - } - - render(){ - return ( -
- - { this.renderArtwork() } - -
-
- {this.props.current_track ? this.props.current_track.name : -} -
- {this.props.current_track ? : } -
- -
- -
- { this.renderConsumeButton() } - { this.renderRandomButton() } - { this.renderRepeatButton() } -
- -
- -
- - { this.props.time_position ? : '-' } - { this.props.current_track ? : '-' } -
- -
- ); - } -} - - -/** - * Export our component - * - * We also integrate our global store, using connect() - **/ - -const mapStateToProps = (state, ownProps) => { - return { - radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false), - tracks: state.core.tracks, - current_track: (typeof(state.core.current_track) !== 'undefined' && typeof(state.core.tracks) !== 'undefined' && typeof(state.core.tracks[state.core.current_track.uri]) !== 'undefined' ? state.core.tracks[state.core.current_track.uri] : null), - play_state: state.mopidy.play_state, - time_position: state.mopidy.time_position, - consume: state.mopidy.consume, - repeat: state.mopidy.repeat, - random: state.mopidy.random - } -} - -const mapDispatchToProps = (dispatch) => { - return { - uiActions: bindActionCreators(uiActions, dispatch), - mopidyActions: bindActionCreators(mopidyActions, dispatch) - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(FullPlayer) \ No newline at end of file diff --git a/src/js/components/List.js b/src/js/components/List.js index c6c2e1b3..29d3b847 100755 --- a/src/js/components/List.js +++ b/src/js/components/List.js @@ -2,11 +2,12 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' import { createStore, bindActionCreators } from 'redux' -import { hashHistory, Link } from 'react-router' +import { hashHistory } from 'react-router' import FontAwesome from 'react-fontawesome' import ArtistSentence from './ArtistSentence' import Dater from './Dater' +import URILink from './URILink' import * as helpers from '../helpers' import * as uiActions from '../services/ui/actions' @@ -21,7 +22,7 @@ class List extends React.Component{ // make sure we haven't clicked a nested link (ie Artist name) if (e.target.tagName.toLowerCase() !== 'a'){ - hashHistory.push((this.props.link_prefix ? this.props.link_prefix : '') + uri ); + hashHistory.push((this.props.link_prefix ? this.props.link_prefix : '') + encodeURIComponent(uri)); } } @@ -62,7 +63,7 @@ class List extends React.Component{ } if (key_string === 'added_at') return ago - if (key_string === 'owner') return {value.id} + if (key_string === 'owner') return {value.id} if (key[0] === 'artists') return if (value === true) return if (typeof(value) === 'number') return {value.toLocaleString()} diff --git a/src/js/components/MiniPlayer.js b/src/js/components/MiniPlayer.js deleted file mode 100755 index a874c61b..00000000 --- a/src/js/components/MiniPlayer.js +++ /dev/null @@ -1,80 +0,0 @@ - -import React, { PropTypes } from 'react' -import { connect } from 'react-redux' -import { bindActionCreators } from 'redux' -import { Link } from 'react-router' -import FontAwesome from 'react-fontawesome' - -import ProgressSlider from './ProgressSlider' -import VolumeControl from './VolumeControl' -import ArtistSentence from './ArtistSentence' -import Thumbnail from './Thumbnail' - -import * as mopidyActions from '../services/mopidy/actions' - -class MiniPlayer extends React.Component{ - - constructor(props){ - super(props); - } - - renderPlayButton(){ - var button = this.props.mopidyActions.play()}> - if (this.props.play_state == 'playing'){ - button = this.props.mopidyActions.pause()}> - } - return button; - } - - render(){ - var images = [] - if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){ - images = this.props.current_track.album.images - } - - return ( -
- - - -
-
{ this.props.current_track ? this.props.current_track.name : - }
- { this.props.current_track ? : } -
- -
- { this.renderPlayButton() } - this.props.mopidyActions.next()}> - -   - -
- - - -
- ); - } -} - - -/** - * Export our component - * - * We also integrate our global store, using connect() - **/ - -const mapStateToProps = (state, ownProps) => { - return { - current_track: (typeof(state.core.current_track) !== 'undefined' && typeof(state.core.tracks) !== 'undefined' && typeof(state.core.tracks[state.core.current_track.uri]) !== 'undefined' ? state.core.tracks[state.core.current_track.uri] : null), - play_state: state.mopidy.play_state - } -} - -const mapDispatchToProps = (dispatch) => { - return { - mopidyActions: bindActionCreators(mopidyActions, dispatch) - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(MiniPlayer) \ No newline at end of file diff --git a/src/js/components/PlaybackControls.js b/src/js/components/PlaybackControls.js index a33ac430..a10c8616 100755 --- a/src/js/components/PlaybackControls.js +++ b/src/js/components/PlaybackControls.js @@ -135,7 +135,7 @@ class PlaybackControls extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - current_track: (state.core.current_track !== undefined && state.core.tracks !== undefined && state.core.tracks[state.core.current_track.uri] !== undefined ? state.core.tracks[state.core.current_track.uri] : null), + current_track: (state.core.current_track !== undefined && state.core.tracks !== undefined && state.core.tracks[state.core.current_track] !== undefined ? state.core.tracks[state.core.current_track] : null), radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false), play_state: state.mopidy.play_state, time_position: state.mopidy.time_position, diff --git a/src/js/components/PlaylistGrid.js b/src/js/components/PlaylistGrid.js index d0b686f1..30d6abe8 100755 --- a/src/js/components/PlaylistGrid.js +++ b/src/js/components/PlaylistGrid.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' import { createStore, bindActionCreators } from 'redux' -import { Link, hashHistory } from 'react-router' +import { hashHistory } from 'react-router' import FontAwesome from 'react-fontawesome' import * as helpers from '../helpers' @@ -41,7 +41,7 @@ class PlaylistGrid extends React.Component{ key={playlist.uri} type="playlist" item={playlist} - onClick={e => {hashHistory.push(global.baseURL+'playlist/'+playlist.uri)}} + onClick={e => {hashHistory.push(global.baseURL+'playlist/'+encodeURIComponent(playlist.uri))}} onContextMenu={e => this.handleContextMenu(e,playlist)} /> )} diff --git a/src/js/components/ProgressSlider.js b/src/js/components/ProgressSlider.js index f432dbd7..71eb9f0e 100755 --- a/src/js/components/ProgressSlider.js +++ b/src/js/components/ProgressSlider.js @@ -2,7 +2,6 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' -import { Link } from 'react-router' import * as mopidyActions from '../services/mopidy/actions' diff --git a/src/js/components/PusherConnectionList.js b/src/js/components/PusherConnectionList.js index ce995c2f..775fe6f4 100755 --- a/src/js/components/PusherConnectionList.js +++ b/src/js/components/PusherConnectionList.js @@ -1,7 +1,6 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' -import { Link } from 'react-router' import { createStore, bindActionCreators } from 'redux' import FontAwesome from 'react-fontawesome' diff --git a/src/js/components/RelatedArtists.js b/src/js/components/RelatedArtists.js index 5d4a0679..943683af 100755 --- a/src/js/components/RelatedArtists.js +++ b/src/js/components/RelatedArtists.js @@ -1,7 +1,7 @@ import React, { PropTypes } from 'react' -import { Link } from 'react-router' import Thumbnail from './Thumbnail' +import URILink from './URILink' export default class RelatedArtists extends React.Component{ @@ -18,10 +18,10 @@ export default class RelatedArtists extends React.Component{ this.props.artists.map((artist, index) => { if (artist.uri){ return ( - + { artist.name } - + ) } else { return ( diff --git a/src/js/components/SearchForm.js b/src/js/components/SearchForm.js index 65ddb899..8be76b4c 100755 --- a/src/js/components/SearchForm.js +++ b/src/js/components/SearchForm.js @@ -1,7 +1,7 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' -import { Link, hashHistory } from 'react-router' +import { hashHistory } from 'react-router' import { createStore, bindActionCreators } from 'redux' import FontAwesome from 'react-fontawesome' @@ -38,19 +38,19 @@ class SearchForm extends React.Component{ switch (helpers.uriType(this.state.query)){ case 'album': - hashHistory.push(global.baseURL+'album/'+this.state.query) + hashHistory.push(global.baseURL+'album/'+encodeURIComponent(this.state.query)) break case 'artist': - hashHistory.push(global.baseURL+'artist/'+this.state.query) + hashHistory.push(global.baseURL+'artist/'+encodeURIComponent(this.state.query)) break case 'playlist': - hashHistory.push(global.baseURL+'playlist/'+this.state.query) + hashHistory.push(global.baseURL+'playlist/'+encodeURIComponent(this.state.query)) break default: - hashHistory.push(global.baseURL+'search/iris:search:'+this.state.query) + hashHistory.push(global.baseURL+'search/iris:search:'+encodeURIComponent(this.state.query)) break } diff --git a/src/js/components/SpotifyAuthenticationFrame.js b/src/js/components/SpotifyAuthenticationFrame.js index 2873fc8e..aefa4aa9 100755 --- a/src/js/components/SpotifyAuthenticationFrame.js +++ b/src/js/components/SpotifyAuthenticationFrame.js @@ -1,7 +1,6 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' -import { Link } from 'react-router' import { createStore, bindActionCreators } from 'redux' import ReactGA from 'react-ga' diff --git a/src/js/components/Track.js b/src/js/components/Track.js index 1d238495..b9f0c4d8 100755 --- a/src/js/components/Track.js +++ b/src/js/components/Track.js @@ -5,6 +5,8 @@ import { Link } from 'react-router' import FontAwesome from 'react-fontawesome' import ArtistSentence from './ArtistSentence' import Dater from './Dater' +import URILink from './URILink' + import * as helpers from '../helpers' export default class Track extends React.Component{ @@ -98,6 +100,7 @@ export default class Track extends React.Component{ var track = this.props.track var className = 'list-item track' if (this.props.selected) className += ' selected' + if (this.props.selected) className += ' selected' if (this.props.can_sort) className += ' can-sort' if (track.type !== undefined) className += ' '+track.type if (track.playing) className += ' playing' @@ -106,7 +109,7 @@ export default class Track extends React.Component{ var album = '-' if (track.album){ if (track.album.uri){ - album = {track.album.name} + album = {track.album.name} } else { album = {track.album.name} } @@ -137,9 +140,9 @@ export default class Track extends React.Component{ if (track.added_from && track.added_by){ var type = (track.added_from ? helpers.uriType(track.added_from) : null) if (type == 'discover'){ - var link = discover + var link = discover } else { - var link = {type} + var link = {type} } var added = {track.added_by} (from {link}) diff --git a/src/js/components/URILink.js b/src/js/components/URILink.js new file mode 100755 index 00000000..09d05333 --- /dev/null +++ b/src/js/components/URILink.js @@ -0,0 +1,53 @@ + +import React from 'react' +import { Link } from 'react-router' + +export default class URILink extends React.Component{ + + constructor(props){ + super(props); + } + + render(){ + var to = null; + var uri = encodeURIComponent(this.props.uri); + + switch (this.props.type){ + + case 'playlist': + to = global.baseURL+'playlist/'+uri; + break; + + case 'artist': + to = global.baseURL+'artist/'+uri; + break; + + case 'album': + to = global.baseURL+'album/'+uri; + break; + + case 'track': + to = global.baseURL+'track/'+uri; + break; + + case 'user': + to = global.baseURL+'user/'+uri; + break; + + case 'recommendations': + to = global.baseURL+'discover/recommendations/'+uri; + break; + + default: + return null; + } + + return ( + + {this.props.children} + + ); + } +} \ No newline at end of file diff --git a/src/js/helpers.js b/src/js/helpers.js index cb942dab..5f063ce6 100755 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -224,6 +224,13 @@ export let formatTracks = function(tracks){ track.date = track.release_date; } + // Copy images from albums (if applicable) + if (track.album && track.album.images){ + if (!track.images || track.images.length > 0){ + track.images = track.album.images; + } + } + formatted.push(track); } diff --git a/src/js/services/core/reducer.js b/src/js/services/core/reducer.js index ddf16503..87b5f2b2 100755 --- a/src/js/services/core/reducer.js +++ b/src/js/services/core/reducer.js @@ -26,12 +26,17 @@ export default function reducer(core = {}, action){ var metadata = {} } + var current_tlid = null; + if (core.current_track && core.tracks && core.tracks[core.current_track] !== undefined && core.tracks[core.current_track].tlid !== undefined){ + current_tlid = core.tracks[core.current_track].tlid; + } + var track = Object.assign( {}, tltrack, metadata, { - playing: (core.current_track && tltrack.tlid == core.current_track.tlid ) + playing: (tltrack.tlid == current_tlid) }) tracklist.push(track) } @@ -53,17 +58,9 @@ export default function reducer(core = {}, action){ ) } - var current_track = Object.assign( - {}, - action.data.track, - { - tlid: action.data.tlid - } - ) - return Object.assign({}, core, { current_tracklist: current_tracklist, - current_track: current_track + current_track: action.data.track.uri }); case 'TRACK_LOADED': diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 4b10a628..e2e624eb 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -1111,7 +1111,7 @@ const MopidyMiddleware = (function(){ {}, response, { - uri: decodeURIComponent(response.uri), + uri: response.uri, type: 'playlist', is_mopidy: true, tracks: (response.tracks ? response.tracks : []), @@ -1602,7 +1602,7 @@ const MopidyMiddleware = (function(){ {}, response[i].album, { - uri: decodeURIComponent(response[i].album.uri), + uri: response[i].album.uri, } ); if (album){ @@ -1630,9 +1630,7 @@ const MopidyMiddleware = (function(){ albums_uris: helpers.arrayOf('uri',albums), tracks: response.slice(0,10) } - ); - artist.uri = decodeURIComponent(artist.uri); - + ); store.dispatch({ type: 'ARTIST_LOADED', key: artist.uri, @@ -1666,7 +1664,6 @@ const MopidyMiddleware = (function(){ is_mopidy: true } ) - artist.uri = decodeURIComponent(artist.uri); artists.push(artist) } } @@ -1698,36 +1695,38 @@ const MopidyMiddleware = (function(){ case 'MOPIDY_CURRENTTLTRACK': if (action.data && action.data.track){ + var track = helpers.formatTracks(action.data); // Fire off our universal track index loader store.dispatch({ type: 'TRACK_LOADED', - key: action.data.track.uri, - track: action.data.track + key: track.uri, + track: track }); // We've got Spotify running, and it's a spotify track - go straight to the source! - if (helpers.uriSource(action.data.track.uri) == 'spotify' && store.getState().spotify.enabled){ - store.dispatch(spotifyActions.getTrack(action.data.track.uri)) + if (helpers.uriSource(track.uri) == 'spotify' && store.getState().spotify.enabled){ + store.dispatch(spotifyActions.getTrack(track.uri)) // Some other source, rely on Mopidy backends to do their work } else { - store.dispatch(mopidyActions.getImages('tracks',[action.data.track.uri])) + store.dispatch(mopidyActions.getImages('tracks',[track.uri])) } } - next(action) - break + next(action); + break; case 'MOPIDY_GET_TRACK': instruct(socket, store, 'library.lookup', action.data ) .then( response => { if (response.length > 0){ + var track = Object.assign({}, response[0]); store.dispatch({ type: 'TRACK_LOADED', - key: decodeURIComponent(action.data.uri), - track: response[0] + key: track.uri, + track: track }); } }, @@ -1753,27 +1752,27 @@ const MopidyMiddleware = (function(){ var records = [] for (var uri in response){ - uri = decodeURIComponent(uri); if (response.hasOwnProperty(uri)){ - var images = response[uri]; images = helpers.digestMopidyImages(store.getState().mopidy, images); - records.push({ - uri: uri, - images: images - }) + if (images && images.length > 0){ + records.push({ + uri: uri, + images: images + }); + } } } var action_data = { type: (action.context+'_LOADED').toUpperCase() } - action_data[action.context] = records - store.dispatch(action_data) - }) + action_data[action.context] = records; + store.dispatch(action_data); + }); - next(action) - break + next(action); + break; /** diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index df257e58..cdaced14 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -180,7 +180,7 @@ class Playlist extends React.Component{ { !this.props.slim_mode ?
  • {helpers.uriSource(this.props.params.uri )} playlist
  • : null } { this.props.playlist.owner && !this.props.slim_mode ?
  • {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.last_modified ?
  • Edited
  • : null }
  • { this.props.playlist.tracks_total ? this.props.playlist.tracks_total : '0'} tracks,  { this.props.playlist.tracks ? : '0 mins' } diff --git a/src/js/views/Queue.js b/src/js/views/Queue.js index 016fc976..54906614 100755 --- a/src/js/views/Queue.js +++ b/src/js/views/Queue.js @@ -9,10 +9,10 @@ import Parallax from '../components/Parallax' import TrackList from '../components/TrackList' import Track from '../components/Track' import Dater from '../components/Dater' -import FullPlayer from '../components/FullPlayer' import ArtistSentence from '../components/ArtistSentence' import Thumbnail from '../components/Thumbnail' import Header from '../components/Header' +import URILink from '../components/URILink' import * as helpers from '../helpers' import * as uiActions from '../services/ui/actions' @@ -68,23 +68,28 @@ class Queue extends React.Component{ ) } - var link = null + var uri = null if (this.props.current_track.album && this.props.current_track.album.uri){ - link = '/album/'+this.props.current_track.album.uri.replace(/[/]/g,'%2F'); + uri = this.props.current_track.album.uri; } return ( - - {this.props.radio_enabled ? : null} - - + + {this.props.radio_enabled ? : null} + + ) } render(){ var image = null - if (this.props.current_track && this.props.current_track.images !== undefined){ - image = helpers.sizedImages(this.props.current_track.images) - image = image.large + if (this.props.current_track){ + if (this.props.current_track.images !== undefined && this.props.current_track.images){ + image = helpers.sizedImages(this.props.current_track.images) + image = image.large + } } var options = ( @@ -118,7 +123,7 @@ class Queue extends React.Component{
    { this.renderArtwork(image) }
    - {this.props.current_track ? {this.props.current_track.name} : -} + {this.props.current_track ? {this.props.current_track.name} : -}
    {this.props.current_track ? : }
    @@ -154,7 +159,7 @@ const mapStateToProps = (state, ownProps) => { radio: state.core.radio, radio_enabled: (state.core.radio && state.core.radio.enabled ? true : false), current_tracklist: state.core.current_tracklist, - current_track: (state.core.current_track !== undefined && state.core.tracks !== undefined && state.core.tracks[state.core.current_track.uri] !== undefined ? state.core.tracks[state.core.current_track.uri] : null) + current_track: (state.core.current_track !== undefined && state.core.tracks !== undefined && state.core.tracks[state.core.current_track] !== undefined ? state.core.tracks[state.core.current_track] : null) } } diff --git a/src/js/views/library/LibraryBrowse.js b/src/js/views/library/LibraryBrowse.js index a7d61ed2..46b22d34 100755 --- a/src/js/views/library/LibraryBrowse.js +++ b/src/js/views/library/LibraryBrowse.js @@ -55,31 +55,11 @@ class LibraryBrowse extends React.Component{ if (directory[i].type && directory[i].type == 'track'){ tracks.push(directory[i] ) } else { - var uri = directory[i].uri - - // If we've navigated to a handled asset type, use our standard views - switch (helpers.uriType(uri)){ - case 'album': - uri = global.baseURL+'album/'+uri - break - - case 'artist': - uri = global.baseURL+'artist/'+uri - break - - case 'playlist': - uri = global.baseURL+'playlist/'+uri - break - - default: - uri = global.baseURL+"library/browse/"+uri.replace(/[/]/g,'%2F') - } - folders.push(Object.assign( {}, directory[i], { - uri: uri + uri: directory[i].uri } )) } @@ -91,119 +71,123 @@ class LibraryBrowse extends React.Component{ } } - render(){ - if (typeof(this.props.params.uri) !== 'undefined' && this.props.params.uri){ - - var title = 'Browse'; - var uri_exploded = this.props.params.uri.split(':'); - if (uri_exploded.length > 0){ - title = uri_exploded[0]; - title = title.charAt(0).toUpperCase() + title.slice(1); - } + renderDirectory(){ + var title = 'Browse'; + var uri_exploded = this.props.params.uri.split(':'); + if (uri_exploded.length > 0){ + title = uri_exploded[0]; + title = title.charAt(0).toUpperCase() + title.slice(1); + } - var options = null; - if (this.props.params.uri != 'local:directory'){ - options = ( - - ) - } - - if (!this.props.directory || helpers.isLoading(this.props.load_queue,['mopidy_browse'])){ - return ( -
    -
    -
    -
    -
    -
    - ) - } - - var items = this.arrange_directory(this.props.directory ) + var options = ( + + ); + if (!this.props.directory || helpers.isLoading(this.props.load_queue,['mopidy_browse'])){ return (
    -
    - - -
    +
    +
    +
    - ); + ) + } - } else { + var items = this.arrange_directory(this.props.directory); - var grid_items = [] - if (this.props.directory){ - for (var i = 0; i < this.props.directory.length; i++){ - var directory = this.props.directory[i] + return ( +
    +
    +
    + + +
    +
    + ); + } - switch (directory.name){ - case 'Files': - directory.icons = ['assets/backgrounds/browse-folders.jpg'] - break + renderIndex(){ + var grid_items = [] + if (this.props.directory){ + for (var i = 0; i < this.props.directory.length; i++){ + var directory = this.props.directory[i] - case 'Local media': - directory.icons = ['assets/backgrounds/browse-folders.jpg'] - break + switch (directory.name){ + case 'Files': + directory.icons = ['assets/backgrounds/browse-folders.jpg'] + break - case 'Spotify': - case 'Spotify Browse': - directory.icons = ['assets/backgrounds/browse-spotify.jpg'] - break + case 'Local media': + directory.icons = ['assets/backgrounds/browse-folders.jpg'] + break - case 'Spotify Tunigo': - case 'Tunigo': - directory.icons = ['assets/backgrounds/browse-tunigo.jpg'] - break + case 'Spotify': + case 'Spotify Browse': + directory.icons = ['assets/backgrounds/browse-spotify.jpg'] + break - case 'TuneIn': - directory.icons = ['assets/backgrounds/browse-tunein.jpg'] - break + case 'Spotify Tunigo': + case 'Tunigo': + directory.icons = ['assets/backgrounds/browse-tunigo.jpg'] + break - default: - directory.icons = ['assets/backgrounds/browse-default.jpg'] - } + case 'TuneIn': + directory.icons = ['assets/backgrounds/browse-tunein.jpg'] + break - grid_items.push({ - name: directory.name, - link: global.baseURL+'library/browse/'+directory.uri.replace(/[/]/g,'%2F'), - icons: directory.icons - }) + default: + directory.icons = ['assets/backgrounds/browse-default.jpg'] } - } - return ( -
    -
    -
    -
    - { - grid_items.map( - (item, index) => { - return ( - hashHistory.push(item.link)} - /> - ) - } - ) - } -
    -
    -
    - ); + grid_items.push({ + name: directory.name, + link: global.baseURL+'library/browse/'+encodeURIComponent(directory.uri), + icons: directory.icons + }) + } + } + + return ( +
    +
    +
    +
    + { + grid_items.map( + (item, index) => { + return ( + hashHistory.push(item.link)} + /> + ) + } + ) + } +
    +
    +
    + ); + } + + render(){ + if (this.props.params.uri !== undefined && this.props.params.uri){ + return this.renderDirectory(); + } else { + return this.renderIndex(); } } }