diff --git a/setup.py b/setup.py index b83043da3..2266089a0 100755 --- a/setup.py +++ b/setup.py @@ -22,7 +22,6 @@ def get_version(filename): install_requires=[ 'setuptools >= 3.3', 'pylast >= 1.6.0', - 'spotipy >= 2.3.8', 'Mopidy >= 2.0', 'Mopidy-Local-Images >= 1.0', 'ConfigObj >= 5.0.6' diff --git a/src/js/App.js b/src/js/App.js index cecedc3c7..0a4b00bf7 100755 --- a/src/js/App.js +++ b/src/js/App.js @@ -15,6 +15,7 @@ import Notifications from './components/Notifications' import DebugInfo from './components/DebugInfo' import * as helpers from './helpers' +import * as coreActions from './services/core/actions' import * as uiActions from './services/ui/actions' import * as pusherActions from './services/pusher/actions' import * as mopidyActions from './services/mopidy/actions' @@ -46,10 +47,8 @@ class App extends React.Component{ } componentDidMount(){ - this.props.pusherActions.connect() - this.props.mopidyActions.connect() - this.props.spotifyActions.connect() - this.props.uiActions.getBroadcasts() + this.props.coreActions.startServices() + this.props.coreActions.getBroadcasts() if (this.props.spotify_authorized){ @@ -295,6 +294,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch) => { return { + coreActions: bindActionCreators(coreActions, dispatch), uiActions: bindActionCreators(uiActions, dispatch), pusherActions: bindActionCreators(pusherActions, dispatch), mopidyActions: bindActionCreators(mopidyActions, dispatch), diff --git a/src/js/bootstrap.js b/src/js/bootstrap.js index 59a0a12a3..871ab2d1a 100755 --- a/src/js/bootstrap.js +++ b/src/js/bootstrap.js @@ -1,6 +1,7 @@ import { createStore, applyMiddleware, combineReducers } from 'redux' +import core from './services/core/reducer' import ui from './services/ui/reducer' import pusher from './services/pusher/reducer' import mopidy from './services/mopidy/reducer' @@ -8,6 +9,7 @@ import lastfm from './services/lastfm/reducer' import spotify from './services/spotify/reducer' import thunk from 'redux-thunk' +import coreMiddleware from './services/core/middleware' import uiMiddleware from './services/ui/middleware' import pusherMiddleware from './services/pusher/middleware' import mopidyMiddleware from './services/mopidy/middleware' @@ -15,6 +17,7 @@ import spotifyMiddleware from './services/spotify/middleware' import localstorageMiddleware from './services/localstorage/middleware' let reducers = combineReducers({ + core, ui, pusher, mopidy, @@ -25,6 +28,15 @@ let reducers = combineReducers({ // set application defaults // TODO: Look at using propTypes in the component for these falsy initial states var initialState = { + core: { + current_tracklist: [], + current_tltrack: false + }, + ui: { + slim_mode: false, + selected_tracks: [], + notifications: [] + }, mopidy: { connected: false, host: window.location.hostname, @@ -50,20 +62,23 @@ var initialState = { spotify: { connected: false, me: false, - autocomplete_results: {} - }, - ui: { - slim_mode: false, - current_tracklist: [], - current_tltrack: false, - selected_tracks: [], - notifications: [], - config: { - authorization_url: 'https://jamesbarnsley.co.nz/auth_v2.php' - } + autocomplete_results: {}, + authorization_url: 'https://jamesbarnsley.co.nz/auth_v2.php' } }; +// if we've got a stored version of spotify state, load and merge +if( localStorage.getItem('core') ){ + var storedCore = JSON.parse( localStorage.getItem('core') ); + initialState.core = Object.assign(initialState.core, storedCore ); +} + +// if we've got a stored version of spotify state, load and merge +if( localStorage.getItem('ui') ){ + var storedUi = JSON.parse( localStorage.getItem('ui') ); + initialState.ui = Object.assign(initialState.ui, storedUi ); +} + // if we've got a stored version of mopidy state, load and merge if( localStorage.getItem('mopidy') ){ var storedMopidy = JSON.parse( localStorage.getItem('mopidy') ); @@ -82,18 +97,12 @@ if( localStorage.getItem('spotify') ){ initialState.spotify = Object.assign(initialState.spotify, storedSpotify ); } -// if we've got a stored version of spotify state, load and merge -if( localStorage.getItem('ui') ){ - var storedUi = JSON.parse( localStorage.getItem('ui') ); - initialState.ui = Object.assign(initialState.ui, storedUi ); -} - console.log('Bootstrapping', initialState) let store = createStore( reducers, initialState, - applyMiddleware( thunk, localstorageMiddleware, uiMiddleware, mopidyMiddleware, pusherMiddleware, spotifyMiddleware ) + applyMiddleware( thunk, localstorageMiddleware, coreMiddleware, uiMiddleware, mopidyMiddleware, pusherMiddleware, spotifyMiddleware ) ); export default store; diff --git a/src/js/components/ContextMenu.js b/src/js/components/ContextMenu.js index 98feeb888..f6a38d45d 100755 --- a/src/js/components/ContextMenu.js +++ b/src/js/components/ContextMenu.js @@ -575,12 +575,12 @@ class ContextMenu extends React.Component{ const mapStateToProps = (state, ownProps) => { return { menu: state.ui.context_menu, - current_track: state.ui.current_track, - current_tracklist: state.ui.current_tracklist, - library_artists: state.ui.library_artists, - library_albums: state.ui.library_albums, - library_playlists: state.ui.library_playlists, - playlists: state.ui.playlists, + current_track: state.core.current_track, + current_tracklist: state.core.current_tracklist, + library_artists: state.core.library_artists, + library_albums: state.core.library_albums, + library_playlists: state.core.library_playlists, + playlists: state.core.playlists, spotify_authorized: state.spotify.authorized } } diff --git a/src/js/components/DebugInfo.js b/src/js/components/DebugInfo.js index d584b1d36..0fb97126b 100755 --- a/src/js/components/DebugInfo.js +++ b/src/js/components/DebugInfo.js @@ -66,7 +66,7 @@ class DebugInfo extends React.Component{ Playlists: {this.props.ui.playlists ? Object.keys(this.props.ui.playlists).length : '0'}
- Tracks: {this.props.ui.tracks ? Object.keys(this.props.ui.tracks).length : '0'} + Tracks: {this.props.core.tracks ? Object.keys(this.props.core.tracks).length : '0'}
Users: {this.props.ui.users ? Object.keys(this.props.ui.users).length : '0'} diff --git a/src/js/components/FullPlayer.js b/src/js/components/FullPlayer.js index 761bed13f..eee97ba2e 100755 --- a/src/js/components/FullPlayer.js +++ b/src/js/components/FullPlayer.js @@ -145,8 +145,8 @@ class FullPlayer extends React.Component{ const mapStateToProps = (state, ownProps) => { return { radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false), - tracks: state.ui.tracks, - current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null), + 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, diff --git a/src/js/components/MiniPlayer.js b/src/js/components/MiniPlayer.js index d4bb7e116..3ec87d865 100755 --- a/src/js/components/MiniPlayer.js +++ b/src/js/components/MiniPlayer.js @@ -66,7 +66,7 @@ class MiniPlayer extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null), + 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 } } diff --git a/src/js/components/Modal/Modal.js b/src/js/components/Modal/Modal.js index 7626df838..5c805c9b9 100755 --- a/src/js/components/Modal/Modal.js +++ b/src/js/components/Modal/Modal.js @@ -66,14 +66,14 @@ class Modal extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null), + 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), uri_schemes: (state.mopidy.uri_schemes ? state.mopidy.uri_schemes : null), search_settings: (state.ui.search_settings ? state.ui.search_settings : null), volume: state.mopidy.volume, mute: state.mopidy.mute, modal: state.ui.modal, radio: state.ui.radio, - tracks: state.ui.tracks, + tracks: state.core.tracks, artists: state.ui.artists, playlists: state.ui.playlists, context_menu: state.ui.context_menu, diff --git a/src/js/components/PlaybackControls.js b/src/js/components/PlaybackControls.js index f1f9a7c1e..d251af8a5 100755 --- a/src/js/components/PlaybackControls.js +++ b/src/js/components/PlaybackControls.js @@ -143,7 +143,7 @@ class PlaybackControls extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null), + 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), 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/ProgressSlider.js b/src/js/components/ProgressSlider.js index 00c36e9af..48c502a64 100755 --- a/src/js/components/ProgressSlider.js +++ b/src/js/components/ProgressSlider.js @@ -53,7 +53,7 @@ class ProgressSlider extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null), + 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), connected: state.mopidy.connected, time_position: state.mopidy.time_position, play_state: state.mopidy.play_state diff --git a/src/js/components/SpotifyAuthenticationFrame.js b/src/js/components/SpotifyAuthenticationFrame.js index ecac652bb..f4c2e0410 100755 --- a/src/js/components/SpotifyAuthenticationFrame.js +++ b/src/js/components/SpotifyAuthenticationFrame.js @@ -146,7 +146,7 @@ class SpotifyAuthenticationFrame extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - authorization_url: state.ui.config.authorization_url, + authorization_url: state.spotify.authorization_url, authorized: state.spotify.authorized, authorizing: state.spotify.authorizing, refreshing_token: state.spotify.refreshing_token diff --git a/src/js/components/TrackList.js b/src/js/components/TrackList.js index 46a9646b0..0f8def6d3 100755 --- a/src/js/components/TrackList.js +++ b/src/js/components/TrackList.js @@ -421,7 +421,7 @@ const mapStateToProps = (state, ownProps) => { selected_tracks: state.ui.selected_tracks, slim_mode: state.ui.slim_mode, dragger: state.ui.dragger, - current_track: state.ui.current_track, + current_track: state.core.current_track, context_menu: state.ui.context_menu } } diff --git a/src/js/helpers.js b/src/js/helpers.js index ac7c99ec9..8acd92d59 100755 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -97,8 +97,8 @@ export let getTrackIcon = function(current_track = false, ui = false){ if (!ui) return false if (!current_track) return false if (typeof(current_track.uri) == 'undefined') return false - if (typeof(ui.tracks[current_track.uri]) === 'undefined') return false - var track = ui.tracks[current_track.uri] + if (typeof(core.tracks[current_track.uri]) === 'undefined') return false + var track = core.tracks[current_track.uri] if (!track.album) return false if (!track.album.images) return false return sizedImages(track.album.images).small diff --git a/src/js/services/core/actions.js b/src/js/services/core/actions.js new file mode 100755 index 000000000..58e42a81a --- /dev/null +++ b/src/js/services/core/actions.js @@ -0,0 +1,210 @@ + +import * as helpers from '../../helpers' + +export function getBroadcasts(){ + return (dispatch, getState) => { + var config = { + method: 'GET', + url: 'https://gist.githubusercontent.com/jaedb/b677dccf80daf3ccb2ef12e96e495677/raw' + } + $.ajax(config).then( + response => { + dispatch({ + type: 'BROADCASTS_LOADED', + broadcasts: JSON.parse(response) + }) + }, + (xhr, status, error) => { + console.error('Could not fetch broadcasts from GitHub') + } + ) + } +} + +export function startSearch(search_type, query, only_mopidy = false){ + return { + type: 'SEARCH_STARTED', + search_type: search_type, + query: query, + only_mopidy: only_mopidy + } +} + +export function debugResponse( response ){ + return { + type: 'DEBUG', + response: response + } +} + + +export function startServices(){ + return { + type: 'CORE_START_SERVICES' + } +} + + + + +/** + * Playlist manipulation + **/ + +export function reorderPlaylistTracks( uri, indexes, insert_before, snapshot_id = false ){ + var range = helpers.createRange( indexes ); + switch( helpers.uriSource( uri ) ){ + + case 'spotify': + return { + type: 'SPOTIFY_REORDER_PLAYLIST_TRACKS', + key: uri, + range_start: range.start, + range_length: range.length, + insert_before: insert_before, + snapshot_id: snapshot_id + } + + case 'm3u': + return { + type: 'MOPIDY_REORDER_PLAYLIST_TRACKS', + key: uri, + range_start: range.start, + range_length: range.length, + insert_before: insert_before + } + } +} + +export function savePlaylist(uri, name, description = '', is_public = false, is_collaborative = false){ + switch( helpers.uriSource( uri ) ){ + + case 'spotify': + return { + type: 'SPOTIFY_SAVE_PLAYLIST', + key: uri, + name: name, + description: (description == '' ? null : description), + is_public: is_public, + is_collaborative: is_collaborative + } + + case 'm3u': + return { + type: 'MOPIDY_SAVE_PLAYLIST', + key: uri, + name: name + } + } + return false +} + +export function createPlaylist(scheme, name, description = '', is_public = false, is_collaborative = false){ + switch( scheme ){ + + case 'spotify': + return { + type: 'SPOTIFY_CREATE_PLAYLIST', + name: name, + description: (description == '' ? null : description), + is_public: is_public, + is_collaborative: is_collaborative + } + + case 'm3u': + return { + type: 'MOPIDY_CREATE_PLAYLIST', + scheme: scheme, + name: name + } + } + return false +} + +export function removeTracksFromPlaylist( uri, tracks_indexes ){ + switch( helpers.uriSource( uri ) ){ + + case 'spotify': + return { + type: 'SPOTIFY_REMOVE_PLAYLIST_TRACKS', + key: uri, + tracks_indexes: tracks_indexes + } + + case 'm3u': + return { + type: 'MOPIDY_REMOVE_PLAYLIST_TRACKS', + key: uri, + tracks_indexes: tracks_indexes + } + } +} + +export function addTracksToPlaylist( uri, tracks_uris ){ + switch( helpers.uriSource( uri ) ){ + + case 'spotify': + return { + type: 'SPOTIFY_ADD_PLAYLIST_TRACKS', + key: uri, + tracks_uris: tracks_uris + } + + case 'm3u': + return { + type: 'MOPIDY_ADD_PLAYLIST_TRACKS', + key: uri, + tracks_uris: tracks_uris + } + } +} + + +/** + * Assets loaded + **/ + +export function albumLoaded(key,album){ + return { + type: 'ALBUM_LOADED', + key: key, + album: album + } +} + +export function albumsLoaded(albums){ + return { + type: 'ALBUMS_LOADED', + albums: albums + } +} + +export function artistLoaded(key,artist){ + return { + type: 'ARTIST_LOADED', + key: key, + artist: artist + } +} + +export function artistsLoaded(artists){ + return { + type: 'ALBUMS_LOADED', + artists: artists + } +} + +export function trackLoaded(key,track){ + return { + type: 'TRACK_LOADED', + key: key, + track: track + } +} + +export function tracksLoaded(tracks){ + return { + type: 'TRACKS_LOADED', + tracks: tracks + } +} diff --git a/src/js/services/core/middleware.js b/src/js/services/core/middleware.js new file mode 100755 index 000000000..5e377476c --- /dev/null +++ b/src/js/services/core/middleware.js @@ -0,0 +1,281 @@ + +import ReactGA from 'react-ga' + +var coreActions = require('./actions.js') +var uiActions = require('../ui/actions.js') +var pusherActions = require('../pusher/actions.js') +var mopidyActions = require('../mopidy/actions.js') +var spotifyActions = require('../spotify/actions.js') +var helpers = require('../../helpers.js') + +const CoreMiddleware = (function(){ + + /** + * The actual middleware inteceptor + **/ + return store => next => action => { + + switch(action.type){ + + case 'CORE_START_SERVICES': + store.dispatch(mopidyActions.connect()) + store.dispatch(pusherActions.connect()) + store.dispatch(spotifyActions.connect()) + next(action) + break + + case 'MOPIDY_CONNECTED': + ReactGA.event({ category: 'Mopidy', action: 'Connected', label: window.location.hostname }) + next(action) + break + + case 'PUSHER_CONNECTED': + ReactGA.event({ category: 'Pusher', action: 'Connected', label: action.username }) + next(action) + break + + case 'ALBUM_LOADED': + if (action.data) ReactGA.event({ category: 'Album', action: 'Load', label: action.album.uri }) + + // make sure our images use mopidy host:port + if (action.album.images && action.album.images.length > 0){ + var images = Object.assign([], action.album.images) + for (var i = 0; i < images.length; i++){ + if (typeof(images[i]) === 'string' && images[i].startsWith('/images/')){ + images[i] = '//'+store.getState().mopidy.host+':'+store.getState().mopidy.port+images[i] + } + } + action.album.images = images + } + + next(action) + break + + case 'ALBUMS_LOADED': + if (action.data) ReactGA.event({ category: 'Albums', action: 'Load', label: action.albums.length+' items' }) + + for (var i = 0; i < action.albums.length; i++){ + // make sure our images use mopidy host:port + if (action.albums[i].images && action.albums[i].images.length > 0){ + var images = Object.assign([], action.albums[i].images) + for (var j = 0; j < images.length; j++){ + if (typeof(images[j]) === 'string' && images[j].startsWith('/images/')){ + images[j] = '//'+store.getState().mopidy.host+':'+store.getState().mopidy.port+images[j] + } + } + action.albums[i].images = images + } + } + + next(action) + break + + case 'ARTIST_LOADED': + if (action.data) ReactGA.event({ category: 'Artist', action: 'Load', label: action.artist.uri }) + next(action) + break + + case 'MOPIDY_DIRECTORY': + if (action.data) ReactGA.event({ category: 'Directory', action: 'Load', label: action.data.uri }) + next(action) + break + + case 'PLAY_PLAYLIST': + ReactGA.event({ category: 'Playlist', action: 'Play', label: action.uri }) + next(action) + break + + case 'SAVE_PLAYLIST': + ReactGA.event({ category: 'Playlist', action: 'Save', label: action.key }) + next(action) + break + + case 'CREATE_PLAYLIST': + ReactGA.event({ category: 'Playlist', action: 'Create', label: +action.name }) + next(action) + break + + case 'REORDER_PLAYLIST_TRACKS': + ReactGA.event({ category: 'Playlist', action: 'Reorder tracks', label: action.key }) + next(action) + break + + case 'ADD_PLAYLIST_TRACKS': + ReactGA.event({ category: 'Playlist', action: 'Add tracks', label: action.playlist_uri }) + next(action) + break + + case 'REMOVE_PLAYLIST_TRACKS': + ReactGA.event({ category: 'Playlist', action: 'Remove tracks', label: action.playlist_uri }) + next(action) + break + + case 'DELETE_PLAYLIST': + ReactGA.event({ category: 'Playlist', action: 'Delete', label: action.uri }) + next(action) + break + + case 'SEARCH_STARTED': + ReactGA.event({ category: 'Search', action: 'Started', label: action.type+': '+action.query }) + + var state = store.getState() + if (state.ui.search_settings){ + var uri_schemes = state.ui.search_settings.uri_schemes + } else { + var uri_schemes = state.mopidy.uri_schemes + } + + // backends that can handle more than just track results + // make sure they are available and respect our settings + var available_full_uri_schemes = ['local:','file:','gmusic:'] + var full_uri_schemes = [] + for (var i = 0; i < available_full_uri_schemes.length; i++){ + var index = uri_schemes.indexOf(available_full_uri_schemes[i]) + if (index > -1){ + full_uri_schemes.push(available_full_uri_schemes[i]) + } + } + + // initiate spotify searching + if (!action.only_mopidy){ + if (!state.ui.search_settings || state.ui.search_settings.spotify){ + store.dispatch(spotifyActions.getSearchResults(action.query)) + } + } + + // backend searching (mopidy) + if (state.mopidy.connected){ + switch (action.search_type){ + case 'playlists': + for (var i = 0; i < full_uri_schemes.length; i++){ + store.dispatch(mopidyActions.getPlaylistSearchResults(action.query,100,full_uri_schemes[i])) + } + break + + case 'artists': + for (var i = 0; i < full_uri_schemes.length; i++){ + store.dispatch(mopidyActions.getArtistSearchResults(action.query,100,full_uri_schemes[i])) + } + break + + case 'albums': + for (var i = 0; i < full_uri_schemes.length; i++){ + store.dispatch(mopidyActions.getAlbumSearchResults(action.query,100,full_uri_schemes[i])) + } + break + + case 'tracks': + for (var i = 0; i < uri_schemes.length; i++){ + store.dispatch(mopidyActions.getTrackSearchResults(action.query,100,uri_schemes[i])) + } + break + + default: + for (var i = 0; i < full_uri_schemes.length; i++){ + store.dispatch(mopidyActions.getPlaylistSearchResults(action.query,6,full_uri_schemes[i])) + store.dispatch(mopidyActions.getArtistSearchResults(action.query,6,full_uri_schemes[i])) + store.dispatch(mopidyActions.getAlbumSearchResults(action.query,6,full_uri_schemes[i])) + } + + for (var i = 0; i < uri_schemes.length; i++){ + store.dispatch(mopidyActions.getTrackSearchResults(action.query,20,uri_schemes[i])) + } + } + } + + next(action) + break + + case 'PLAYLIST_TRACKS_ADDED': + store.dispatch(uiActions.createNotification('Added '+action.tracks_uris.length+' tracks to playlist')) + switch(helpers.uriSource(action.key)){ + case 'spotify': + store.dispatch(spotifyActions.getPlaylist(action.key)) + break + case 'm3u': + if( store.getState().mopidy.connected ) store.dispatch(mopidyActions.getPlaylist(action.key)) + break + } + next(action) + break + + case 'PLAYLIST_LOADED': + if (action.data) ReactGA.event({ category: 'Playlist', action: 'Load', label: action.playlist.uri }) + + var playlist = Object.assign({}, action.playlist) + switch (helpers.uriSource(playlist.uri)){ + + case 'm3u': + playlist.can_edit = true + break + + case 'spotify': + if (store.getState().spotify.authorized && store.getState().spotify.me){ + playlist.can_edit = (helpers.getFromUri('playlistowner',playlist.uri) == store.getState().spotify.me.id) + } + } + + // proceed as usual + action.playlist = playlist + next(action) + break + + case 'PLAYLISTS_LOADED': + if (action.data) ReactGA.event({ category: 'Playlists', action: 'Load', label: action.playlists.length+' items' }) + + var playlists = [] + for (var i = 0; i < action.playlists.length; i++){ + var playlist = Object.assign({}, action.playlists[i]) + + switch (helpers.uriSource(playlist.uri)){ + + case 'm3u': + playlist.can_edit = true + break + + case 'spotify': + if (store.getState().spotify.authorized && store.getState().spotify.me){ + playlist.can_edit = (helpers.getFromUri('playlistowner',playlist.uri) == store.getState().spotify.me.id) + } + } + + playlists.push(playlist) + } + + // proceed as usual + action.playlists = playlists + next(action) + break + + case 'MOPIDY_CURRENTTLTRACK': + if (action.data && action.data.track ){ + helpers.setWindowTitle(action.data.track, store.getState().mopidy.play_state) + + // make sure our images use mopidy host:port + if (action.data.track.album && action.data.track.album.images && action.data.track.album.images.length > 0){ + var images = Object.assign([], action.data.track.album.images) + for (var i = 0; i < images.length; i++){ + if (typeof(images[i]) === 'string' && images[i].startsWith('/images/')){ + images[i] = '//'+store.getState().mopidy.host+':'+store.getState().mopidy.port+images[i] + } + } + action.data.track.album.images = images + } + } + + next(action) + break + + case 'RESTART': + location.reload() + break + + // This action is irrelevant to us, pass it on to the next middleware + default: + return next(action) + } + } + +})(); + +export default CoreMiddleware \ No newline at end of file diff --git a/src/js/services/core/reducer.js b/src/js/services/core/reducer.js new file mode 100755 index 000000000..a32912812 --- /dev/null +++ b/src/js/services/core/reducer.js @@ -0,0 +1,651 @@ + +import * as helpers from '../../helpers' + +export default function reducer(core = {}, action){ + switch (action.type) { + + /** + * Current track and tracklist + **/ + + case 'MOPIDY_TLTRACKS': + if( !action.data ) return core + + var tracklist = [] + for( var i = 0; i < action.data.length; i++ ){ + + var tltrack = action.data[i] + + // load our metadata (if we have any for that tlid) + if (core.queue_metadata !== undefined && core.queue_metadata['tlid_'+tltrack.tlid] !== undefined){ + var metadata = core.queue_metadata['tlid_'+tltrack.tlid] + } else { + var metadata = {} + } + + var track = Object.assign( + {}, + tltrack.track, + metadata, + { + tlid: tltrack.tlid, + playing: ( core.current_track && tltrack.tlid == core.current_track.tlid ) + }) + tracklist.push( track ) + } + + return Object.assign({}, core, { current_tracklist: tracklist }); + + case 'MOPIDY_CURRENTTLTRACK': + if (!action.data) return core + + var current_tracklist = [] + Object.assign(current_tracklist, core.current_tracklist) + + for (var i = 0; i < current_tracklist.length; i++){ + Object.assign( + current_tracklist[i], + { playing: ( current_tracklist[i].tlid == action.data.tlid ) } + ) + } + + var current_track = Object.assign( + {}, + action.data.track, + { + tlid: action.data.tlid + } + ) + + return Object.assign({}, core, { + current_tracklist: current_tracklist, + current_track: current_track + }); + + case 'TRACK_LOADED': + if (!action.key || !action.track) return core + + var tracks = Object.assign({}, core.tracks) + if (tracks[action.key]){ + var track = Object.assign({}, tracks[action.key], action.track) + }else{ + var track = Object.assign({}, action.track) + } + + tracks[action.key] = track + return Object.assign({}, core, { tracks: tracks }); + + case 'TRACKS_LOADED': + var tracks = Object.assign({}, core.tracks) + + for (var i = 0; i < action.tracks.length; i++){ + var track = action.tracks[i] + if (typeof(tracks[track.uri]) !== 'undefined'){ + track = Object.assign({}, tracks[track.uri], track) + } + tracks[track.uri] = track + } + + return Object.assign({}, core, { tracks: tracks }); + + case 'PUSHER_QUEUE_METADATA': + case 'PUSHER_QUEUE_METADATA_CHANGED': + var tracklist = Object.assign([], core.current_tracklist) + for( var i = 0; i < tracklist.length; i++ ){ + + // load our metadata (if we have any for that tlid) + if (typeof(action.queue_metadata['tlid_'+tracklist[i].tlid]) !== 'undefined'){ + tracklist[i] = Object.assign( + {}, + tracklist[i], + action.queue_metadata['tlid_'+tracklist[i].tlid], + ) + } + } + return Object.assign({}, core, { current_tracklist: tracklist, queue_metadata: action.queue_metadata }); + + case 'PUSHER_RADIO': + case 'PUSHER_RADIO_STARTED': + case 'PUSHER_RADIO_CHANGED': + case 'PUSHER_RADIO_STOPPED': + return Object.assign({}, core, { seeds_resolved: false }, { radio: action.radio }) + + case 'RADIO_SEEDS_RESOLVED': + var radio = Object.assign({}, core.radio, { resolved_seeds: action.resolved_seeds }) + return Object.assign({}, core, { radio: radio }) + + + + + /** + * Categories + **/ + + case 'CATEGORY_LOADED': + var categories = Object.assign([], core.categories) + + if (categories[action.key]){ + var category = Object.assign({}, categories[action.key], action.category) + }else{ + var category = Object.assign({}, action.category) + } + + categories[action.key] = category + return Object.assign({}, core, { categories: categories }); + + case 'CATEGORIES_LOADED': + var categories = Object.assign([], core.categories) + + for (var i = 0; i < action.categories.length; i++){ + var key = 'category:'+action.categories[i].id + if (categories[key]){ + var category = Object.assign({}, categories[key], action.categories[i]) + }else{ + var category = Object.assign({}, action.categories[i]) + } + categories[key] = category + } + + return Object.assign({}, core, { categories: categories }); + + case 'CATEGORY_PLAYLISTS_LOADED': + var categories = Object.assign([], core.categories) + var playlists_uris = [] + if (categories[action.key].playlists_uris) playlists_uris = categories[action.key].playlists_uris + + var category = Object.assign( + {}, + categories[action.key], + { + playlists_uris: [...playlists_uris, ...action.uris], + playlists_more: action.more, + playlists_total: action.total + } + ) + categories[action.key] = category + return Object.assign({}, core, { categories: categories }); + + + + + /** + * Albums + **/ + + case 'ALBUM_LOADED': + var albums = Object.assign([], core.albums) + + if (albums[action.key]){ + var album = Object.assign({}, albums[action.key], action.album) + }else{ + var album = Object.assign({}, action.album) + } + + albums[action.key] = album + return Object.assign({}, core, { albums: albums }); + + case 'ALBUMS_LOADED': + var albums = Object.assign([], core.albums) + + for (var i = 0; i < action.albums.length; i++){ + var album = action.albums[i] + if (albums[album.uri]){ + album = Object.assign({}, albums[album.uri], album) + } + albums[album.uri] = album + } + + return Object.assign({}, core, { albums: albums }); + + case 'LIBRARY_ALBUMS_LOADED': + var library_albums = [] + if (core.library_albums) library_albums = Object.assign([], core.library_albums) + + return Object.assign({}, core, { + library_albums: helpers.removeDuplicates([...library_albums, ...action.uris]), + library_albums_more: action.more, + library_albums_total: action.total, + library_albums_started: true + }); + + case 'ALBUM_LIBRARY_CHECK': + var items = Object.assign([], core.library_albums) + + // add/remove library reference + var index = items.indexOf(action.key) + + // removing existing + if (index > -1 && !action.in_library){ + items.splice(index, 1) + } else if (index < 0 && action.in_library){ + items.push(action.key) + } + + return Object.assign({}, core, { library_albums: items }); + + case 'LOCAL_ALBUMS_LOADED': + if (!action.uris) return Object.assign({}, core, { local_albums: null }); + return Object.assign({}, core, { local_albums: action.uris }); + + case 'NEW_RELEASES_LOADED': + if (!action.uris){ + return Object.assign({}, core, { + new_releases: null, + new_releases_more: null, + new_releases_total: null + }); + } + + var new_releases = [] + if (core.new_releases) new_releases = Object.assign([], core.new_releases) + + return Object.assign({}, core, { + new_releases: [...new_releases, ...action.uris], + new_releases_more: action.more, + new_releases_total: action.total + }); + + + + /** + * Artists + **/ + + case 'ARTIST_LOADED': + var artists = Object.assign([], core.artists) + + if (artists[action.key]){ + + // if we've already got images, remove and add as additional_images + // this is to prevent LastFM overwriting Spotify images + if (artists[action.key].images){ + action.artist.images_additional = action.artist.images + delete action.artist.images + } + + var artist = Object.assign({}, artists[action.key], action.artist) + }else{ + var artist = Object.assign({}, action.artist) + } + + artists[action.key] = artist + return Object.assign({}, core, { artists: artists }); + + case 'ARTISTS_LOADED': + var artists = Object.assign([], core.artists) + + for (var i = 0; i < action.artists.length; i++){ + var artist = action.artists[i] + if (typeof(artists[artist.uri]) !== 'undefined'){ + artist = Object.assign({}, artists[artist.uri], artist) + } + artists[artist.uri] = artist + } + + return Object.assign({}, core, { artists: artists }); + + case 'ARTIST_ALBUMS_LOADED': + var artists = Object.assign([], core.artists) + var albums_uris = [] + if (artists[action.key].albums_uris) albums_uris = artists[action.key].albums_uris + + var artist = Object.assign( + {}, + artists[action.key], + { + albums_uris: [...albums_uris, ...action.uris], + albums_more: action.more, + albums_total: action.total + } + ) + artists[action.key] = artist + return Object.assign({}, core, { artists: artists }); + + case 'LIBRARY_ARTISTS_LOADED': + var library_artists = [] + if (core.library_artists) library_artists = Object.assign([], core.library_artists) + + return Object.assign({}, core, { + library_artists: helpers.removeDuplicates([...library_artists, ...action.uris]), + library_artists_more: action.more, + library_artists_total: action.total, + library_artists_started: true + }); + + case 'ARTIST_LIBRARY_CHECK': + var items = Object.assign([], core.library_artists) + + // add/remove library reference + var index = items.indexOf(action.key) + + // removing existing + if (index > -1 && !action.in_library){ + items.splice(index, 1) + } else if (index < 0 && action.in_library){ + items.push(action.key) + } + + return Object.assign({}, core, { library_artists: items }); + + case 'LOCAL_ARTISTS_LOADED': + if (!action.uris) return Object.assign({}, core, { local_artists: null }); + return Object.assign({}, core, { local_artists: action.uris }); + + + /** + * User profiles + **/ + + case 'USER_LOADED': + var users = Object.assign([], core.users) + + if (users[action.key]){ + var user = Object.assign({}, users[action.key], action.user) + }else{ + var user = Object.assign({}, action.user) + } + + users[action.key] = user + return Object.assign({}, core, { users: users }); + + case 'USER_PLAYLISTS_LOADED': + var users = Object.assign([], core.users) + var playlists_uris = [] + if (users[action.key] && users[action.key].playlists_uris) playlists_uris = users[action.key].playlists_uris + + var artist = Object.assign( + {}, + users[action.key], + { + playlists_uris: [...playlists_uris, ...action.uris], + playlists_more: action.more, + playlists_total: action.total + } + ) + users[action.key] = artist + return Object.assign({}, core, { users: users }); + + + + + /** + * Playlists + **/ + + case 'PLAYLIST_LOADED': + case 'PLAYLIST_UPDATED': + var playlists = Object.assign([], core.playlists) + + if (typeof(playlists[action.key]) !== 'undefined'){ + var existing_playlist = Object.assign({}, playlists[action.key]) + + if (existing_playlist.tracks && action.playlist.tracks){ + var tracks = [...existing_playlist.tracks, ...action.playlist.tracks] + } else if (existing_playlist.tracks){ + var tracks = existing_playlist.tracks + } else if (action.playlist.tracks){ + var tracks = action.playlist.tracks + } else { + var tracks = [] + } + + var merged_playlist = Object.assign( + {}, + existing_playlist, + action.playlist, + { + tracks: tracks + } + ) + }else{ + var merged_playlist = Object.assign({}, action.playlist) + } + + playlists[action.key] = merged_playlist + return Object.assign({}, core, { playlists: playlists }) + + case 'PLAYLIST_KEY_UPDATED': + var playlists = Object.assign([], core.playlists) + + // URI not in our index? No change needed then + if (typeof(playlists[action.key]) === 'undefined'){ + return core + } + + // Delete our old playlist by key, and add by new key + var playlist = Object.assign({}, playlists[action.key]) + delete playlists[action.key] + playlists[playlist.uri] = playlist + + return Object.assign({}, core, { playlists: playlists }) + + case 'PLAYLISTS_LOADED': + var playlists = Object.assign([], core.playlists) + + for (var i = 0; i < action.playlists.length; i++){ + var loaded_playlist = action.playlists[i] + + if (typeof(playlists[loaded_playlist.uri]) !== 'undefined'){ + var existing_playlist = Object.assign({}, playlists[loaded_playlist.uri]) + + if (existing_playlist.tracks && loaded_playlist.tracks){ + var tracks = [...existing_playlist.tracks, ...loaded_playlist.tracks] + } else if (existing_playlist.tracks){ + var tracks = existing_playlist.tracks + } else if (loaded_playlist.tracks){ + var tracks = loaded_playlist.tracks + } else { + var tracks = [] + } + + var merged_playlist = Object.assign( + {}, + existing_playlist, + loaded_playlist, + { + tracks: tracks + } + ) + + } else { + var merged_playlist = loaded_playlist + } + + playlists[merged_playlist.uri] = merged_playlist + } + + return Object.assign({}, core, { playlists: playlists }); + + case 'PLAYLIST_LOADED_MORE_TRACKS': + var playlists = Object.assign([], core.playlists) + var playlist = Object.assign( + {}, + playlists[action.key], + { + tracks: [...playlists[action.key].tracks, ...helpers.flattenTracks(action.data.items)], + tracks_more: action.data.next, + tracks_total: action.data.total + } + ) + + playlists[action.key] = playlist + return Object.assign({}, core, { playlists: playlists }); + + case 'PLAYLIST_TRACKS_REMOVED': + var playlists = Object.assign([], core.playlists) + var playlist = Object.assign({}, playlists[action.key]) + var tracks = Object.assign([], playlist.tracks) + var indexes = action.tracks_indexes.reverse() + for( var i = 0; i < indexes.length; i++ ){ + tracks.splice( indexes[i], 1 ) + } + var snapshot_id = null + if( action.snapshot_id ) snapshot_id = action.snapshot_id + Object.assign(playlist, { tracks: tracks, snapshot_id: snapshot_id }) + playlists[action.key] = playlist + return Object.assign({}, core, { playlists: playlists }); + + case 'PLAYLIST_TRACKS': + var playlists = Object.assign([], core.playlists) + var playlist = Object.assign({}, playlists[action.key], { tracks: action.tracks }) + + playlists[action.key] = playlist + return Object.assign({}, core, { playlists: playlists }); + + case 'PLAYLIST_TRACKS_REORDERED': + var playlists = Object.assign([], core.playlists) + var playlist = Object.assign({}, playlists[action.key]) + var tracks = Object.assign([], playlist.tracks) + + // handle insert_before offset if we're moving BENEATH where we're slicing tracks + var insert_before = action.insert_before + if( insert_before > action.range_start ) insert_before = insert_before - action.range_length + + // cut our moved tracks into a new array + var tracks_to_move = tracks.splice(action.range_start, action.range_length) + tracks_to_move.reverse() + + for( i = 0; i < tracks_to_move.length; i++ ){ + tracks.splice(insert_before, 0, tracks_to_move[i]) + } + + var snapshot_id = null + if( action.snapshot_id ) snapshot_id = action.snapshot_id + Object.assign(playlist, { tracks: tracks, snapshot_id: snapshot_id }) + playlists[action.key] = playlist + return Object.assign({}, core, { playlists: playlists }); + + case 'LIBRARY_PLAYLISTS_LOADED': + if (core.library_playlists){ + var library_playlists = [...core.library_playlists, ...action.uris] + }else{ + var library_playlists = action.uris + } + + library_playlists = helpers.removeDuplicates(library_playlists) + + return Object.assign({}, core, { + library_playlists: library_playlists, + library_playlists_started: true + }); + + case 'PLAYLIST_LIBRARY_CHECK': + var items = Object.assign([], core.library_playlists) + + // add/remove library reference + var index = items.indexOf(action.key) + + // removing existing + if (index > -1 && !action.in_library){ + items.splice(index, 1) + } else if (index < 0 && action.in_library){ + items.push(action.key) + } + + return Object.assign({}, core, { library_playlists: items }); + + + /** + * Genres + **/ + + case 'SPOTIFY_GENRES_LOADED': + return Object.assign({}, core, {genres: action.genres}) + + + /** + * Search results + **/ + + case 'SEARCH_STARTED': + return Object.assign({}, core, { + search_results: { + artists_more: null, + artists_uris: [], + albums_more: null, + albums_uris: [], + playlists_more: null, + playlists_uris: [], + tracks: [], + tracks_more: null, + } + }); + + case 'SEARCH_RESULTS_LOADED': + + // artists + if (core.search_results && core.search_results.artists_uris){ + var artists_uris = core.search_results.artists_uris + }else{ + var artists_uris = [] + } + if (action.artists_uris) artists_uris = [...artists_uris, ...action.artists_uris] + + // more tracks + if (typeof(action.artists_more) !== 'undefined') var artists_more = action.artists_more + else if (core.search_results && core.search_results.artists_more) var artists_more = core.search_results.artists_more + else var artists_more = null + + + // albums + if (core.search_results && core.search_results.albums_uris){ + var albums_uris = core.search_results.albums_uris + }else{ + var albums_uris = [] + } + if (action.albums_uris) albums_uris = [...albums_uris, ...action.albums_uris] + + // more tracks + if (typeof(action.albums_more) !== 'undefined') var albums_more = action.albums_more + else if (core.search_results && core.search_results.albums_more) var albums_more = core.search_results.albums_more + else var albums_more = null + + + // playlists + if (core.search_results && core.search_results.playlists_uris){ + var playlists_uris = core.search_results.playlists_uris + }else{ + var playlists_uris = [] + } + if (action.playlists_uris) playlists_uris = [...playlists_uris, ...action.playlists_uris] + + // more tracks + if (typeof(action.playlists_more) !== 'undefined') var playlists_more = action.playlists_more + else if (core.search_results && core.search_results.playlists_more) var playlists_more = core.search_results.playlists_more + else var playlists_more = null + + + // tracks + if (core.search_results && core.search_results.tracks){ + var tracks = core.search_results.tracks + }else{ + var tracks = [] + } + if (action.tracks) tracks = [...tracks, ...action.tracks] + + // more tracks + if (typeof(action.tracks_more) !== 'undefined') var tracks_more = action.tracks_more + else if (core.search_results && core.search_results.tracks_more) var tracks_more = core.search_results.tracks_more + else var tracks_more = null + + return Object.assign({}, core, { + search_results: { + artists_more: artists_more, + artists_uris: helpers.removeDuplicates(artists_uris), + albums_more: albums_more, + albums_uris: helpers.removeDuplicates(albums_uris), + playlists_more: playlists_more, + playlists_uris: helpers.removeDuplicates(playlists_uris), + tracks: tracks, + tracks_more: tracks_more + } + }); + + + default: + return core + } +} + + + diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index fd4990ab9..5b32d4ace 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -238,7 +238,7 @@ const MopidyMiddleware = (function(){ type: 'browser_notification', title: 'Track skipped', body: store.getState().pusher.username +' skipped this track', - icon: (store.getState().ui.current_track ? helpers.getTrackIcon(store.getState().ui.current_track, store.getState().ui) : false) + icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().ui) : false) } store.dispatch( pusherActions.deliverBroadcast(data) ) break @@ -248,7 +248,7 @@ const MopidyMiddleware = (function(){ type: 'browser_notification', title: 'Playback stopped', body: store.getState().pusher.username +' stopped playback', - icon: (store.getState().ui.current_track ? helpers.getTrackIcon(store.getState().ui.current_track, store.getState().ui) : false) + icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().ui) : false) } store.dispatch( pusherActions.deliverBroadcast(data) ) break @@ -343,8 +343,8 @@ const MopidyMiddleware = (function(){ break } - var current_track = store.getState().ui.current_track - var current_tracklist = store.getState().ui.current_tracklist + var current_track = store.getState().core.current_track + var current_tracklist = store.getState().core.current_tracklist var current_track_index = -1 if (typeof(current_track) !== 'undefined'){ diff --git a/src/js/services/pusher/middleware.js b/src/js/services/pusher/middleware.js index e9216d114..8c21883c1 100755 --- a/src/js/services/pusher/middleware.js +++ b/src/js/services/pusher/middleware.js @@ -1,4 +1,6 @@ +import ReactGA from 'react-ga' + var helpers = require('../../helpers.js') var uiActions = require('../ui/actions.js') var pusherActions = require('./actions.js') @@ -185,6 +187,7 @@ const PusherMiddleware = (function(){ break; case 'PUSHER_START_UPGRADE': + ReactGA.event({ category: 'Pusher', action: 'Upgrade', label: '' }) request(store, 'upgrade') .then( response => { @@ -244,6 +247,7 @@ const PusherMiddleware = (function(){ case 'PUSHER_START_RADIO': case 'PUSHER_UPDATE_RADIO': + ReactGA.event({ category: 'Pusher', action: 'Start radio', label: action.uris.join() }) // start our UI process notification if (action.type == 'PUSHER_UPDATE_RADIO'){ @@ -284,6 +288,7 @@ const PusherMiddleware = (function(){ case 'PUSHER_STOP_RADIO': store.dispatch(uiActions.createNotification('Stopping radio')) + ReactGA.event({ category: 'Pusher', action: 'Stop radio' }) var data = { seed_artists: [], @@ -295,6 +300,14 @@ const PusherMiddleware = (function(){ request(store, 'stop_radio', data) break + case 'PUSHER_RADIO_STARTED': + case 'PUSHER_RADIO_CHANGED': + if (action.radio.enabled){ + store.dispatch(spotifyActions.resolveRadioSeeds(action.radio)) + } + next(action) + break + case 'PUSHER_BROWSER_NOTIFICATION': store.dispatch(uiActions.createBrowserNotification(action)) break @@ -305,6 +318,15 @@ const PusherMiddleware = (function(){ window.location.reload(true); break + case 'PUSHER_VERSION': + ReactGA.event({ category: 'Pusher', action: 'Version', label: action.version.current }) + + if (action.version.upgrade_available){ + store.dispatch( uiActions.createNotification( 'Version '+action.version.latest+' is available. See settings to upgrade.' ) ) + } + next( action ) + break + case 'PUSHER_DEBUG': request(store, action.message.method, action.message.data ) .then( @@ -316,7 +338,8 @@ const PusherMiddleware = (function(){ case 'PUSHER_ERROR': store.dispatch(uiActions.createNotification(action.message, 'bad')) - break; + ReactGA.event({ category: 'Pusher', action: 'Error', label: action.message }) + break // This action is irrelevant to us, pass it on to the next middleware default: diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index ab9bbf738..4304b07db 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -116,7 +116,7 @@ function refreshToken( dispatch, getState ){ $.ajax({ method: 'GET', - url: getState().ui.config.authorization_url+'?action=refresh&refresh_token='+getState().spotify.refresh_token, + url: getState().spotify.authorization_url+'?action=refresh&refresh_token='+getState().spotify.refresh_token, dataType: "json", timeout: 10000 }) diff --git a/src/js/services/spotify/middleware.js b/src/js/services/spotify/middleware.js index 7c46ed8fb..b8bd1533b 100755 --- a/src/js/services/spotify/middleware.js +++ b/src/js/services/spotify/middleware.js @@ -1,4 +1,6 @@ +import ReactGA from 'react-ga' + var helpers = require('./../../helpers') var spotifyActions = require('./actions') var uiActions = require('../ui/actions') @@ -14,6 +16,46 @@ const SpotifyMiddleware = (function(){ switch(action.type){ + case 'SPOTIFY_CONNECTED': + var label = null + if (store.getState().spotify.me) label = store.getState().spotify.me.id + ReactGA.event({ category: 'Spotify', action: 'Connected', label: label }) + next(action) + break + + case 'SPOTIFY_AUTHORIZATION_GRANTED': + ReactGA.event({ category: 'Spotify', action: 'Authorization granted' }) + next(action) + break + + case 'SPOTIFY_AUTHORIZATION_REVOKED': + var label = null + if (store.getState().spotify.me) label = store.getState().spotify.me.id + ReactGA.event({ category: 'Spotify', action: 'Authorization revoked', label: label }) + next(action) + break + + case 'SPOTIFY_IMPORT_AUTHORIZATION': + var label = null + if (action.me && action.me.id){ + label = action.me.id + } + ReactGA.event({ category: 'Spotify', action: 'Authorization imported', label: label }) + next(action) + break + + case 'SPOTIFY_RECOMMENDATIONS_LOADED': + if (action.seeds_uris){ + ReactGA.event({ category: 'Spotify', action: 'Recommendations', label: action.seeds_uris.join(',') }) + } + next(action) + break + + case 'SPOTIFY_USER_LOADED': + if (action.data) ReactGA.event({ category: 'User', action: 'Load', label: action.data.uri }) + next(action) + break + case 'SPOTIFY_CONNECT': store.dispatch( spotifyActions.getMe() ) break @@ -75,18 +117,6 @@ const SpotifyMiddleware = (function(){ store.dispatch( spotifyActions.savePlaylist( action.key, action.name, action.description, action.is_public, action.is_collaborative )) break - // when radio returns - case 'PUSHER_RADIO_STARTED': - case 'PUSHER_RADIO_CHANGED': - - next(action) - - // only resolve if radio is enabled - if( action.radio.enabled ){ - store.dispatch(spotifyActions.resolveRadioSeeds(action.radio)) - } - break - case 'SPOTIFY_NEW_RELEASES_LOADED': store.dispatch({ type: 'ALBUMS_LOADED', @@ -362,6 +392,7 @@ const SpotifyMiddleware = (function(){ // Use 'me' name as my Pusher username store.dispatch(pusherActions.setUsername(name)) } + ReactGA.event({ category: 'Spotify', action: 'Authorization verified', label: action.data.id }) next(action) break; diff --git a/src/js/services/ui/actions.js b/src/js/services/ui/actions.js index 81361a816..260a312f1 100755 --- a/src/js/services/ui/actions.js +++ b/src/js/services/ui/actions.js @@ -1,26 +1,6 @@ import * as helpers from '../../helpers' -export function getBroadcasts(){ - return (dispatch, getState) => { - var config = { - method: 'GET', - url: 'https://gist.githubusercontent.com/jaedb/b677dccf80daf3ccb2ef12e96e495677/raw' - } - $.ajax(config).then( - response => { - dispatch({ - type: 'BROADCASTS_LOADED', - broadcasts: JSON.parse(response) - }) - }, - (xhr, status, error) => { - console.error('Could not fetch broadcasts from GitHub') - } - ) - } -} - export function setSelectedTracks(keys = []){ if (typeof(keys) === 'string'){ keys = [keys] @@ -66,22 +46,6 @@ export function hideTouchContextMenu(){ } } -export function startSearch(search_type, query, only_mopidy = false){ - return { - type: 'SEARCH_STARTED', - search_type: search_type, - query: query, - only_mopidy: only_mopidy - } -} - -export function debugResponse( response ){ - return { - type: 'DEBUG', - response: response - } -} - export function lazyLoading( start ){ return { type: 'LAZY_LOADING', @@ -122,164 +86,6 @@ export function set( data ){ } } -export function reorderPlaylistTracks( uri, indexes, insert_before, snapshot_id = false ){ - var range = helpers.createRange( indexes ); - switch( helpers.uriSource( uri ) ){ - - case 'spotify': - return { - type: 'SPOTIFY_REORDER_PLAYLIST_TRACKS', - key: uri, - range_start: range.start, - range_length: range.length, - insert_before: insert_before, - snapshot_id: snapshot_id - } - - case 'm3u': - return { - type: 'MOPIDY_REORDER_PLAYLIST_TRACKS', - key: uri, - range_start: range.start, - range_length: range.length, - insert_before: insert_before - } - } -} - -export function savePlaylist(uri, name, description = '', is_public = false, is_collaborative = false){ - switch( helpers.uriSource( uri ) ){ - - case 'spotify': - return { - type: 'SPOTIFY_SAVE_PLAYLIST', - key: uri, - name: name, - description: (description == '' ? null : description), - is_public: is_public, - is_collaborative: is_collaborative - } - - case 'm3u': - return { - type: 'MOPIDY_SAVE_PLAYLIST', - key: uri, - name: name - } - } - return false -} - -export function createPlaylist(scheme, name, description = '', is_public = false, is_collaborative = false){ - switch( scheme ){ - - case 'spotify': - return { - type: 'SPOTIFY_CREATE_PLAYLIST', - name: name, - description: (description == '' ? null : description), - is_public: is_public, - is_collaborative: is_collaborative - } - - case 'm3u': - return { - type: 'MOPIDY_CREATE_PLAYLIST', - scheme: scheme, - name: name - } - } - return false -} - -export function removeTracksFromPlaylist( uri, tracks_indexes ){ - switch( helpers.uriSource( uri ) ){ - - case 'spotify': - return { - type: 'SPOTIFY_REMOVE_PLAYLIST_TRACKS', - key: uri, - tracks_indexes: tracks_indexes - } - - case 'm3u': - return { - type: 'MOPIDY_REMOVE_PLAYLIST_TRACKS', - key: uri, - tracks_indexes: tracks_indexes - } - } -} - -export function addTracksToPlaylist( uri, tracks_uris ){ - switch( helpers.uriSource( uri ) ){ - - case 'spotify': - return { - type: 'SPOTIFY_ADD_PLAYLIST_TRACKS', - key: uri, - tracks_uris: tracks_uris - } - - case 'm3u': - return { - type: 'MOPIDY_ADD_PLAYLIST_TRACKS', - key: uri, - tracks_uris: tracks_uris - } - } -} - - -/** - * Assets loaded - **/ - -export function albumLoaded(key,album){ - return { - type: 'ALBUM_LOADED', - key: key, - album: album - } -} - -export function albumsLoaded(albums){ - return { - type: 'ALBUMS_LOADED', - albums: albums - } -} - -export function artistLoaded(key,artist){ - return { - type: 'ARTIST_LOADED', - key: key, - artist: artist - } -} - -export function artistsLoaded(artists){ - return { - type: 'ALBUMS_LOADED', - artists: artists - } -} - -export function trackLoaded(key,track){ - return { - type: 'TRACK_LOADED', - key: key, - track: track - } -} - -export function tracksLoaded(tracks){ - return { - type: 'TRACKS_LOADED', - tracks: tracks - } -} - /** diff --git a/src/js/services/ui/middleware.js b/src/js/services/ui/middleware.js index 4218771ac..2a36af7b1 100755 --- a/src/js/services/ui/middleware.js +++ b/src/js/services/ui/middleware.js @@ -15,251 +15,11 @@ const UIMiddleware = (function(){ switch(action.type){ - case 'MOPIDY_CONNECTED': - ReactGA.event({ category: 'Mopidy', action: 'Connected', label: window.location.hostname }) - next(action) - break - - case 'PUSHER_CONNECTED': - ReactGA.event({ category: 'Pusher', action: 'Connected', label: action.username }) - next(action) - break - - case 'SPOTIFY_CONNECTED': - var label = null - if (store.getState().spotify.me) label = store.getState().spotify.me.id - ReactGA.event({ category: 'Spotify', action: 'Connected', label: label }) - next(action) - break - - case 'SPOTIFY_AUTHORIZATION_GRANTED': - ReactGA.event({ category: 'Spotify', action: 'Authorization granted' }) - next(action) - break - - case 'SPOTIFY_AUTHORIZATION_REVOKED': - var label = null - if (store.getState().spotify.me) label = store.getState().spotify.me.id - ReactGA.event({ category: 'Spotify', action: 'Authorization revoked', label: label }) - next(action) - break - - case 'SPOTIFY_ME_LOADED': - ReactGA.event({ category: 'Spotify', action: 'Authorization verified', label: action.data.id }) - next(action) - break - - case 'SPOTIFY_IMPORT_AUTHORIZATION': - var label = null - if (action.me && action.me.id){ - label = action.me.id - } - ReactGA.event({ category: 'Spotify', action: 'Authorization imported', label: label }) - next(action) - break - - case 'SPOTIFY_RECOMMENDATIONS_LOADED': - if (action.seeds_uris){ - ReactGA.event({ category: 'Spotify', action: 'Recommendations', label: action.seeds_uris.join(',') }) - } - next(action) - break - - case 'ALBUM_LOADED': - if (action.data) ReactGA.event({ category: 'Album', action: 'Load', label: action.album.uri }) - - // make sure our images use mopidy host:port - if (action.album.images && action.album.images.length > 0){ - var images = Object.assign([], action.album.images) - for (var i = 0; i < images.length; i++){ - if (typeof(images[i]) === 'string' && images[i].startsWith('/images/')){ - images[i] = '//'+store.getState().mopidy.host+':'+store.getState().mopidy.port+images[i] - } - } - action.album.images = images - } - - next(action) - break - - case 'ALBUMS_LOADED': - if (action.data) ReactGA.event({ category: 'Albums', action: 'Load', label: action.albums.length+' items' }) - - for (var i = 0; i < action.albums.length; i++){ - // make sure our images use mopidy host:port - if (action.albums[i].images && action.albums[i].images.length > 0){ - var images = Object.assign([], action.albums[i].images) - for (var j = 0; j < images.length; j++){ - if (typeof(images[j]) === 'string' && images[j].startsWith('/images/')){ - images[j] = '//'+store.getState().mopidy.host+':'+store.getState().mopidy.port+images[j] - } - } - action.albums[i].images = images - } - } - - next(action) - break - - case 'ARTIST_LOADED': - if (action.data) ReactGA.event({ category: 'Artist', action: 'Load', label: action.artist.uri }) - next(action) - break - - case 'SPOTIFY_USER_LOADED': - if (action.data) ReactGA.event({ category: 'User', action: 'Load', label: action.data.uri }) - next(action) - break - - case 'MOPIDY_DIRECTORY': - if (action.data) ReactGA.event({ category: 'Directory', action: 'Load', label: action.data.uri }) - next(action) - break - - case 'MOPIDY_PLAY_PLAYLIST': - ReactGA.event({ category: 'Playlist', action: 'Play', label: action.uri }) - next(action) - break - - case 'MOPIDY_SAVE_PLAYLIST': - case 'SPOTIFY_SAVE_PLAYLIST': - ReactGA.event({ category: 'Playlist', action: 'Save', label: action.key }) - next(action) - break - - case 'MOPIDY_CREATE_PLAYLIST': - ReactGA.event({ category: 'Playlist', action: 'Create', label: 'Mopidy,'+action.name }) - next(action) - break - - case 'SPOTIFY_CREATE_PLAYLIST': - ReactGA.event({ category: 'Playlist', action: 'Create', label: 'Spotify,'+action.name }) - next(action) - break - - case 'MOPIDY_REORDER_PLAYLIST_TRACKS': - case 'SPOTIFY_REORDER_PLAYLIST_TRACKS': - ReactGA.event({ category: 'Playlist', action: 'Reorder tracks', label: action.key }) - next(action) - break - - case 'MOPIDY_ADD_PLAYLIST_TRACKS': - case 'SPOTIFY_ADD_PLAYLIST_TRACKS': - ReactGA.event({ category: 'Playlist', action: 'Add tracks', label: action.playlist_uri }) - next(action) - break - - case 'MOPIDY_REMOVE_PLAYLIST_TRACKS': - case 'SPOTIFY_REMOVE_PLAYLIST_TRACKS': - ReactGA.event({ category: 'Playlist', action: 'Remove tracks', label: action.playlist_uri }) - next(action) - break - - case 'MOPIDY_DELETE_PLAYLIST': - ReactGA.event({ category: 'Playlist', action: 'Delete', label: action.uri }) - next(action) - break - - case 'PUSHER_ERROR': - ReactGA.event({ category: 'Pusher', action: 'Error', label: action.message }) - next(action) - break - - case 'SEARCH_STARTED': - ReactGA.event({ category: 'Search', action: 'Started', label: action.type+': '+action.query }) - - var state = store.getState() - if (state.ui.search_settings){ - var uri_schemes = state.ui.search_settings.uri_schemes - } else { - var uri_schemes = state.mopidy.uri_schemes - } - - // backends that can handle more than just track results - // make sure they are available and respect our settings - var available_full_uri_schemes = ['local:','file:','gmusic:'] - var full_uri_schemes = [] - for (var i = 0; i < available_full_uri_schemes.length; i++){ - var index = uri_schemes.indexOf(available_full_uri_schemes[i]) - if (index > -1){ - full_uri_schemes.push(available_full_uri_schemes[i]) - } - } - - // initiate spotify searching - if (!action.only_mopidy){ - if (!state.ui.search_settings || state.ui.search_settings.spotify){ - store.dispatch(spotifyActions.getSearchResults(action.query)) - } - } - - // backend searching (mopidy) - if (state.mopidy.connected){ - switch (action.search_type){ - case 'playlists': - for (var i = 0; i < full_uri_schemes.length; i++){ - store.dispatch(mopidyActions.getPlaylistSearchResults(action.query,100,full_uri_schemes[i])) - } - break - - case 'artists': - for (var i = 0; i < full_uri_schemes.length; i++){ - store.dispatch(mopidyActions.getArtistSearchResults(action.query,100,full_uri_schemes[i])) - } - break - - case 'albums': - for (var i = 0; i < full_uri_schemes.length; i++){ - store.dispatch(mopidyActions.getAlbumSearchResults(action.query,100,full_uri_schemes[i])) - } - break - - case 'tracks': - for (var i = 0; i < uri_schemes.length; i++){ - store.dispatch(mopidyActions.getTrackSearchResults(action.query,100,uri_schemes[i])) - } - break - - default: - for (var i = 0; i < full_uri_schemes.length; i++){ - store.dispatch(mopidyActions.getPlaylistSearchResults(action.query,6,full_uri_schemes[i])) - store.dispatch(mopidyActions.getArtistSearchResults(action.query,6,full_uri_schemes[i])) - store.dispatch(mopidyActions.getAlbumSearchResults(action.query,6,full_uri_schemes[i])) - } - - for (var i = 0; i < uri_schemes.length; i++){ - store.dispatch(mopidyActions.getTrackSearchResults(action.query,20,uri_schemes[i])) - } - } - } - - next(action) - break - - case 'PUSHER_START_RADIO': - ReactGA.event({ category: 'Pusher', action: 'Start radio', label: action.uris.join() }) - next(action) - break - - case 'PUSHER_UPDATE_RADIO': - ReactGA.event({ category: 'Pusher', action: 'Update radio', label: action.uris.join() }) - next(action) - break - - case 'PUSHER_STOP_RADIO': - ReactGA.event({ category: 'Pusher', action: 'Stop radio' }) - next(action) - break - - case 'PUSHER_UPGRADING': - ReactGA.event({ category: 'Pusher', action: 'Upgrade', label: action.data }) + case 'MOPIDY_STATE': + helpers.setWindowTitle(store.getState().core.current_track, action.data) next(action) break - case 'RESTART': - location.reload() - break - case 'OPEN_MODAL': ReactGA.event({ category: 'Modal', action: 'Opened', label: action.modal.name }) $('body').addClass('modal-open') @@ -363,101 +123,6 @@ const UIMiddleware = (function(){ next(action) break - case 'PLAYLIST_TRACKS_ADDED': - store.dispatch(uiActions.createNotification('Added '+action.tracks_uris.length+' tracks to playlist')) - switch(helpers.uriSource(action.key)){ - case 'spotify': - store.dispatch(spotifyActions.getPlaylist(action.key)) - break - case 'm3u': - if( store.getState().mopidy.connected ) store.dispatch(mopidyActions.getPlaylist(action.key)) - break - } - next(action) - break - - case 'PLAYLIST_LOADED': - if (action.data) ReactGA.event({ category: 'Playlist', action: 'Load', label: action.playlist.uri }) - - var playlist = Object.assign({}, action.playlist) - switch (helpers.uriSource(playlist.uri)){ - - case 'm3u': - playlist.can_edit = true - break - - case 'spotify': - if (store.getState().spotify.authorized && store.getState().spotify.me){ - playlist.can_edit = (helpers.getFromUri('playlistowner',playlist.uri) == store.getState().spotify.me.id) - } - } - - // proceed as usual - action.playlist = playlist - next(action) - break - - case 'PLAYLISTS_LOADED': - if (action.data) ReactGA.event({ category: 'Playlists', action: 'Load', label: action.playlists.length+' items' }) - - var playlists = [] - for (var i = 0; i < action.playlists.length; i++){ - var playlist = Object.assign({}, action.playlists[i]) - - switch (helpers.uriSource(playlist.uri)){ - - case 'm3u': - playlist.can_edit = true - break - - case 'spotify': - if (store.getState().spotify.authorized && store.getState().spotify.me){ - playlist.can_edit = (helpers.getFromUri('playlistowner',playlist.uri) == store.getState().spotify.me.id) - } - } - - playlists.push(playlist) - } - - // proceed as usual - action.playlists = playlists - next(action) - break - - - case 'MOPIDY_STATE': - helpers.setWindowTitle(store.getState().ui.current_track, action.data) - next(action) - break - - case 'MOPIDY_CURRENTTLTRACK': - if (action.data && action.data.track ){ - helpers.setWindowTitle(action.data.track, store.getState().mopidy.play_state) - - // make sure our images use mopidy host:port - if (action.data.track.album && action.data.track.album.images && action.data.track.album.images.length > 0){ - var images = Object.assign([], action.data.track.album.images) - for (var i = 0; i < images.length; i++){ - if (typeof(images[i]) === 'string' && images[i].startsWith('/images/')){ - images[i] = '//'+store.getState().mopidy.host+':'+store.getState().mopidy.port+images[i] - } - } - action.data.track.album.images = images - } - } - - next(action) - break - - case 'PUSHER_VERSION': - ReactGA.event({ category: 'Pusher', action: 'Version', label: action.version.current }) - - if (action.version.upgrade_available){ - store.dispatch( uiActions.createNotification( 'Version '+action.version.latest+' is available. See settings to upgrade.' ) ) - } - next( action ) - break - // This action is irrelevant to us, pass it on to the next middleware default: return next(action) diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 9b0c1ae52..efd55e243 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -16,9 +16,6 @@ export default function reducer(ui = {}, action){ case 'UI_SET': return Object.assign({}, ui, action.data) - case 'PUSHER_CONFIG': - return Object.assign({}, ui, { config: action.config }) - case 'TOGGLE_SIDEBAR': var new_state = !ui.sidebar_open if( typeof(action.new_state) !== 'undefined' ) new_state = action.new_state @@ -79,643 +76,6 @@ export default function reducer(ui = {}, action){ - /** - * Current track and tracklist - **/ - - case 'MOPIDY_TLTRACKS': - if( !action.data ) return ui - - var tracklist = [] - for( var i = 0; i < action.data.length; i++ ){ - - var tltrack = action.data[i] - - // load our metadata (if we have any for that tlid) - if (typeof(ui.queue_metadata) !== 'undefined' && typeof(ui.queue_metadata['tlid_'+tltrack.tlid]) !== 'undefined'){ - var metadata = ui.queue_metadata['tlid_'+tltrack.tlid] - } else { - var metadata = {} - } - - var track = Object.assign( - {}, - tltrack.track, - metadata, - { - tlid: tltrack.tlid, - playing: ( ui.current_track && tltrack.tlid == ui.current_track.tlid ) - }) - tracklist.push( track ) - } - - return Object.assign({}, ui, { current_tracklist: tracklist }); - - case 'MOPIDY_CURRENTTLTRACK': - if (!action.data) return ui - - var current_tracklist = [] - Object.assign(current_tracklist, ui.current_tracklist) - - for (var i = 0; i < current_tracklist.length; i++){ - Object.assign( - current_tracklist[i], - { playing: ( current_tracklist[i].tlid == action.data.tlid ) } - ) - } - - var current_track = Object.assign( - {}, - action.data.track, - { - tlid: action.data.tlid - } - ) - - return Object.assign({}, ui, { - current_tracklist: current_tracklist, - current_track: current_track - }); - - case 'TRACK_LOADED': - if (!action.key || !action.track) return ui - - var tracks = Object.assign({}, ui.tracks) - if (tracks[action.key]){ - var track = Object.assign({}, tracks[action.key], action.track) - }else{ - var track = Object.assign({}, action.track) - } - - tracks[action.key] = track - return Object.assign({}, ui, { tracks: tracks }); - - case 'TRACKS_LOADED': - var tracks = Object.assign({}, ui.tracks) - - for (var i = 0; i < action.tracks.length; i++){ - var track = action.tracks[i] - if (typeof(tracks[track.uri]) !== 'undefined'){ - track = Object.assign({}, tracks[track.uri], track) - } - tracks[track.uri] = track - } - - return Object.assign({}, ui, { tracks: tracks }); - - case 'PUSHER_QUEUE_METADATA': - case 'PUSHER_QUEUE_METADATA_CHANGED': - var tracklist = Object.assign([], ui.current_tracklist) - for( var i = 0; i < tracklist.length; i++ ){ - - // load our metadata (if we have any for that tlid) - if (typeof(action.queue_metadata['tlid_'+tracklist[i].tlid]) !== 'undefined'){ - tracklist[i] = Object.assign( - {}, - tracklist[i], - action.queue_metadata['tlid_'+tracklist[i].tlid], - ) - } - } - return Object.assign({}, ui, { current_tracklist: tracklist, queue_metadata: action.queue_metadata }); - - case 'PUSHER_RADIO': - case 'PUSHER_RADIO_STARTED': - case 'PUSHER_RADIO_CHANGED': - case 'PUSHER_RADIO_STOPPED': - return Object.assign({}, ui, { seeds_resolved: false }, { radio: action.radio }) - - case 'RADIO_SEEDS_RESOLVED': - var radio = Object.assign({}, ui.radio, { resolved_seeds: action.resolved_seeds }) - return Object.assign({}, ui, { radio: radio }) - - - - - /** - * Categories - **/ - - case 'CATEGORY_LOADED': - var categories = Object.assign([], ui.categories) - - if (categories[action.key]){ - var category = Object.assign({}, categories[action.key], action.category) - }else{ - var category = Object.assign({}, action.category) - } - - categories[action.key] = category - return Object.assign({}, ui, { categories: categories }); - - case 'CATEGORIES_LOADED': - var categories = Object.assign([], ui.categories) - - for (var i = 0; i < action.categories.length; i++){ - var key = 'category:'+action.categories[i].id - if (categories[key]){ - var category = Object.assign({}, categories[key], action.categories[i]) - }else{ - var category = Object.assign({}, action.categories[i]) - } - categories[key] = category - } - - return Object.assign({}, ui, { categories: categories }); - - case 'CATEGORY_PLAYLISTS_LOADED': - var categories = Object.assign([], ui.categories) - var playlists_uris = [] - if (categories[action.key].playlists_uris) playlists_uris = categories[action.key].playlists_uris - - var category = Object.assign( - {}, - categories[action.key], - { - playlists_uris: [...playlists_uris, ...action.uris], - playlists_more: action.more, - playlists_total: action.total - } - ) - categories[action.key] = category - return Object.assign({}, ui, { categories: categories }); - - - - - /** - * Albums - **/ - - case 'ALBUM_LOADED': - var albums = Object.assign([], ui.albums) - - if (albums[action.key]){ - var album = Object.assign({}, albums[action.key], action.album) - }else{ - var album = Object.assign({}, action.album) - } - - albums[action.key] = album - return Object.assign({}, ui, { albums: albums }); - - case 'ALBUMS_LOADED': - var albums = Object.assign([], ui.albums) - - for (var i = 0; i < action.albums.length; i++){ - var album = action.albums[i] - if (albums[album.uri]){ - album = Object.assign({}, albums[album.uri], album) - } - albums[album.uri] = album - } - - return Object.assign({}, ui, { albums: albums }); - - case 'LIBRARY_ALBUMS_LOADED': - var library_albums = [] - if (ui.library_albums) library_albums = Object.assign([], ui.library_albums) - - return Object.assign({}, ui, { - library_albums: helpers.removeDuplicates([...library_albums, ...action.uris]), - library_albums_more: action.more, - library_albums_total: action.total, - library_albums_started: true - }); - - case 'ALBUM_LIBRARY_CHECK': - var items = Object.assign([], ui.library_albums) - - // add/remove library reference - var index = items.indexOf(action.key) - - // removing existing - if (index > -1 && !action.in_library){ - items.splice(index, 1) - } else if (index < 0 && action.in_library){ - items.push(action.key) - } - - return Object.assign({}, ui, { library_albums: items }); - - case 'LOCAL_ALBUMS_LOADED': - if (!action.uris) return Object.assign({}, ui, { local_albums: null }); - return Object.assign({}, ui, { local_albums: action.uris }); - - case 'NEW_RELEASES_LOADED': - if (!action.uris){ - return Object.assign({}, ui, { - new_releases: null, - new_releases_more: null, - new_releases_total: null - }); - } - - var new_releases = [] - if (ui.new_releases) new_releases = Object.assign([], ui.new_releases) - - return Object.assign({}, ui, { - new_releases: [...new_releases, ...action.uris], - new_releases_more: action.more, - new_releases_total: action.total - }); - - - - /** - * Artists - **/ - - case 'ARTIST_LOADED': - var artists = Object.assign([], ui.artists) - - if (artists[action.key]){ - - // if we've already got images, remove and add as additional_images - // this is to prevent LastFM overwriting Spotify images - if (artists[action.key].images){ - action.artist.images_additional = action.artist.images - delete action.artist.images - } - - var artist = Object.assign({}, artists[action.key], action.artist) - }else{ - var artist = Object.assign({}, action.artist) - } - - artists[action.key] = artist - return Object.assign({}, ui, { artists: artists }); - - case 'ARTISTS_LOADED': - var artists = Object.assign([], ui.artists) - - for (var i = 0; i < action.artists.length; i++){ - var artist = action.artists[i] - if (typeof(artists[artist.uri]) !== 'undefined'){ - artist = Object.assign({}, artists[artist.uri], artist) - } - artists[artist.uri] = artist - } - - return Object.assign({}, ui, { artists: artists }); - - case 'ARTIST_ALBUMS_LOADED': - var artists = Object.assign([], ui.artists) - var albums_uris = [] - if (artists[action.key].albums_uris) albums_uris = artists[action.key].albums_uris - - var artist = Object.assign( - {}, - artists[action.key], - { - albums_uris: [...albums_uris, ...action.uris], - albums_more: action.more, - albums_total: action.total - } - ) - artists[action.key] = artist - return Object.assign({}, ui, { artists: artists }); - - case 'LIBRARY_ARTISTS_LOADED': - var library_artists = [] - if (ui.library_artists) library_artists = Object.assign([], ui.library_artists) - - return Object.assign({}, ui, { - library_artists: helpers.removeDuplicates([...library_artists, ...action.uris]), - library_artists_more: action.more, - library_artists_total: action.total, - library_artists_started: true - }); - - case 'ARTIST_LIBRARY_CHECK': - var items = Object.assign([], ui.library_artists) - - // add/remove library reference - var index = items.indexOf(action.key) - - // removing existing - if (index > -1 && !action.in_library){ - items.splice(index, 1) - } else if (index < 0 && action.in_library){ - items.push(action.key) - } - - return Object.assign({}, ui, { library_artists: items }); - - case 'LOCAL_ARTISTS_LOADED': - if (!action.uris) return Object.assign({}, ui, { local_artists: null }); - return Object.assign({}, ui, { local_artists: action.uris }); - - - /** - * User profiles - **/ - - case 'USER_LOADED': - var users = Object.assign([], ui.users) - - if (users[action.key]){ - var user = Object.assign({}, users[action.key], action.user) - }else{ - var user = Object.assign({}, action.user) - } - - users[action.key] = user - return Object.assign({}, ui, { users: users }); - - case 'USER_PLAYLISTS_LOADED': - var users = Object.assign([], ui.users) - var playlists_uris = [] - if (users[action.key] && users[action.key].playlists_uris) playlists_uris = users[action.key].playlists_uris - - var artist = Object.assign( - {}, - users[action.key], - { - playlists_uris: [...playlists_uris, ...action.uris], - playlists_more: action.more, - playlists_total: action.total - } - ) - users[action.key] = artist - return Object.assign({}, ui, { users: users }); - - - - - /** - * Playlists - **/ - - case 'PLAYLIST_LOADED': - case 'PLAYLIST_UPDATED': - var playlists = Object.assign([], ui.playlists) - - if (typeof(playlists[action.key]) !== 'undefined'){ - var existing_playlist = Object.assign({}, playlists[action.key]) - - if (existing_playlist.tracks && action.playlist.tracks){ - var tracks = [...existing_playlist.tracks, ...action.playlist.tracks] - } else if (existing_playlist.tracks){ - var tracks = existing_playlist.tracks - } else if (action.playlist.tracks){ - var tracks = action.playlist.tracks - } else { - var tracks = [] - } - - var merged_playlist = Object.assign( - {}, - existing_playlist, - action.playlist, - { - tracks: tracks - } - ) - }else{ - var merged_playlist = Object.assign({}, action.playlist) - } - - playlists[action.key] = merged_playlist - return Object.assign({}, ui, { playlists: playlists }) - - case 'PLAYLIST_KEY_UPDATED': - var playlists = Object.assign([], ui.playlists) - - // URI not in our index? No change needed then - if (typeof(playlists[action.key]) === 'undefined'){ - return ui - } - - // Delete our old playlist by key, and add by new key - var playlist = Object.assign({}, playlists[action.key]) - delete playlists[action.key] - playlists[playlist.uri] = playlist - - return Object.assign({}, ui, { playlists: playlists }) - - case 'PLAYLISTS_LOADED': - var playlists = Object.assign([], ui.playlists) - - for (var i = 0; i < action.playlists.length; i++){ - var loaded_playlist = action.playlists[i] - - if (typeof(playlists[loaded_playlist.uri]) !== 'undefined'){ - var existing_playlist = Object.assign({}, playlists[loaded_playlist.uri]) - - if (existing_playlist.tracks && loaded_playlist.tracks){ - var tracks = [...existing_playlist.tracks, ...loaded_playlist.tracks] - } else if (existing_playlist.tracks){ - var tracks = existing_playlist.tracks - } else if (loaded_playlist.tracks){ - var tracks = loaded_playlist.tracks - } else { - var tracks = [] - } - - var merged_playlist = Object.assign( - {}, - existing_playlist, - loaded_playlist, - { - tracks: tracks - } - ) - - } else { - var merged_playlist = loaded_playlist - } - - playlists[merged_playlist.uri] = merged_playlist - } - - return Object.assign({}, ui, { playlists: playlists }); - - case 'PLAYLIST_LOADED_MORE_TRACKS': - var playlists = Object.assign([], ui.playlists) - var playlist = Object.assign( - {}, - playlists[action.key], - { - tracks: [...playlists[action.key].tracks, ...helpers.flattenTracks(action.data.items)], - tracks_more: action.data.next, - tracks_total: action.data.total - } - ) - - playlists[action.key] = playlist - return Object.assign({}, ui, { playlists: playlists }); - - case 'PLAYLIST_TRACKS_REMOVED': - var playlists = Object.assign([], ui.playlists) - var playlist = Object.assign({}, playlists[action.key]) - var tracks = Object.assign([], playlist.tracks) - var indexes = action.tracks_indexes.reverse() - for( var i = 0; i < indexes.length; i++ ){ - tracks.splice( indexes[i], 1 ) - } - var snapshot_id = null - if( action.snapshot_id ) snapshot_id = action.snapshot_id - Object.assign(playlist, { tracks: tracks, snapshot_id: snapshot_id }) - playlists[action.key] = playlist - return Object.assign({}, ui, { playlists: playlists }); - - case 'PLAYLIST_TRACKS': - var playlists = Object.assign([], ui.playlists) - var playlist = Object.assign({}, playlists[action.key], { tracks: action.tracks }) - - playlists[action.key] = playlist - return Object.assign({}, ui, { playlists: playlists }); - - case 'PLAYLIST_TRACKS_REORDERED': - var playlists = Object.assign([], ui.playlists) - var playlist = Object.assign({}, playlists[action.key]) - var tracks = Object.assign([], playlist.tracks) - - // handle insert_before offset if we're moving BENEATH where we're slicing tracks - var insert_before = action.insert_before - if( insert_before > action.range_start ) insert_before = insert_before - action.range_length - - // cut our moved tracks into a new array - var tracks_to_move = tracks.splice(action.range_start, action.range_length) - tracks_to_move.reverse() - - for( i = 0; i < tracks_to_move.length; i++ ){ - tracks.splice(insert_before, 0, tracks_to_move[i]) - } - - var snapshot_id = null - if( action.snapshot_id ) snapshot_id = action.snapshot_id - Object.assign(playlist, { tracks: tracks, snapshot_id: snapshot_id }) - playlists[action.key] = playlist - return Object.assign({}, ui, { playlists: playlists }); - - case 'LIBRARY_PLAYLISTS_LOADED': - if (ui.library_playlists){ - var library_playlists = [...ui.library_playlists, ...action.uris] - }else{ - var library_playlists = action.uris - } - - library_playlists = helpers.removeDuplicates(library_playlists) - - return Object.assign({}, ui, { - library_playlists: library_playlists, - library_playlists_started: true - }); - - case 'PLAYLIST_LIBRARY_CHECK': - var items = Object.assign([], ui.library_playlists) - - // add/remove library reference - var index = items.indexOf(action.key) - - // removing existing - if (index > -1 && !action.in_library){ - items.splice(index, 1) - } else if (index < 0 && action.in_library){ - items.push(action.key) - } - - return Object.assign({}, ui, { library_playlists: items }); - - - /** - * Genres - **/ - - case 'SPOTIFY_GENRES_LOADED': - return Object.assign({}, ui, {genres: action.genres}) - - - /** - * Search results - **/ - - case 'SEARCH_STARTED': - return Object.assign({}, ui, { - search_results: { - artists_more: null, - artists_uris: [], - albums_more: null, - albums_uris: [], - playlists_more: null, - playlists_uris: [], - tracks: [], - tracks_more: null, - } - }); - - case 'SEARCH_RESULTS_LOADED': - - // artists - if (ui.search_results && ui.search_results.artists_uris){ - var artists_uris = ui.search_results.artists_uris - }else{ - var artists_uris = [] - } - if (action.artists_uris) artists_uris = [...artists_uris, ...action.artists_uris] - - // more tracks - if (typeof(action.artists_more) !== 'undefined') var artists_more = action.artists_more - else if (ui.search_results && ui.search_results.artists_more) var artists_more = ui.search_results.artists_more - else var artists_more = null - - - // albums - if (ui.search_results && ui.search_results.albums_uris){ - var albums_uris = ui.search_results.albums_uris - }else{ - var albums_uris = [] - } - if (action.albums_uris) albums_uris = [...albums_uris, ...action.albums_uris] - - // more tracks - if (typeof(action.albums_more) !== 'undefined') var albums_more = action.albums_more - else if (ui.search_results && ui.search_results.albums_more) var albums_more = ui.search_results.albums_more - else var albums_more = null - - - // playlists - if (ui.search_results && ui.search_results.playlists_uris){ - var playlists_uris = ui.search_results.playlists_uris - }else{ - var playlists_uris = [] - } - if (action.playlists_uris) playlists_uris = [...playlists_uris, ...action.playlists_uris] - - // more tracks - if (typeof(action.playlists_more) !== 'undefined') var playlists_more = action.playlists_more - else if (ui.search_results && ui.search_results.playlists_more) var playlists_more = ui.search_results.playlists_more - else var playlists_more = null - - - // tracks - if (ui.search_results && ui.search_results.tracks){ - var tracks = ui.search_results.tracks - }else{ - var tracks = [] - } - if (action.tracks) tracks = [...tracks, ...action.tracks] - - // more tracks - if (typeof(action.tracks_more) !== 'undefined') var tracks_more = action.tracks_more - else if (ui.search_results && ui.search_results.tracks_more) var tracks_more = ui.search_results.tracks_more - else var tracks_more = null - - return Object.assign({}, ui, { - search_results: { - artists_more: artists_more, - artists_uris: helpers.removeDuplicates(artists_uris), - albums_more: albums_more, - albums_uris: helpers.removeDuplicates(albums_uris), - playlists_more: playlists_more, - playlists_uris: helpers.removeDuplicates(playlists_uris), - tracks: tracks, - tracks_more: tracks_more - } - }); - /** * Modals diff --git a/src/js/views/Album.js b/src/js/views/Album.js index cf82040a1..5ddaa78b2 100755 --- a/src/js/views/Album.js +++ b/src/js/views/Album.js @@ -169,10 +169,10 @@ const mapStateToProps = (state, ownProps) => { return { slim_mode: state.ui.slim_mode, load_queue: state.ui.load_queue, - artists: state.ui.artists, - album: (state.ui.albums && typeof(state.ui.albums[ownProps.params.uri]) !== 'undefined' ? state.ui.albums[ownProps.params.uri] : false ), - albums: state.ui.albums, - library_albums: state.ui.library_albums, + artists: state.core.artists, + album: (state.core.albums && typeof(state.core.albums[ownProps.params.uri]) !== 'undefined' ? state.core.albums[ownProps.params.uri] : false ), + albums: state.core.albums, + library_albums: state.core.library_albums, spotify_authorized: state.spotify.authorized, mopidy_connected: state.mopidy.connected }; diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 3756cadd0..7d0d35fcb 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -261,10 +261,10 @@ const mapStateToProps = (state, ownProps) => { return { slim_mode: state.ui.slim_mode, load_queue: state.ui.load_queue, - artist: (state.ui.artists && typeof(state.ui.artists[ownProps.params.uri]) !== 'undefined' ? state.ui.artists[ownProps.params.uri] : false ), - artists: (state.ui.artists ? state.ui.artists : []), - library_artists: (state.ui.library_artists ? state.ui.library_artists : []), - albums: (state.ui.albums ? state.ui.albums : []), + artist: (state.core.artists && typeof(state.core.artists[ownProps.params.uri]) !== 'undefined' ? state.core.artists[ownProps.params.uri] : false ), + artists: (state.core.artists ? state.core.artists : []), + library_artists: (state.core.library_artists ? state.core.library_artists : []), + albums: (state.core.albums ? state.core.albums : []), spotify_authorized: state.spotify.authorized, mopidy_connected: state.mopidy.connected } diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index 55bd3b513..a876395f5 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -213,8 +213,8 @@ const mapStateToProps = (state, ownProps) => { return { slim_mode: state.ui.slim_mode, load_queue: state.ui.load_queue, - playlist: (state.ui.playlists && typeof(state.ui.playlists[uri]) !== 'undefined' ? state.ui.playlists[uri] : false ), - library_playlists: state.ui.library_playlists, + playlist: (state.core.playlists && typeof(state.core.playlists[uri]) !== 'undefined' ? state.core.playlists[uri] : false ), + library_playlists: state.core.library_playlists, mopidy_connected: state.mopidy.connected, spotify_authorized: state.spotify.authorized, spotify_userid: state.spotify.me.id diff --git a/src/js/views/Queue.js b/src/js/views/Queue.js index ebcea3c26..6bda7fb77 100755 --- a/src/js/views/Queue.js +++ b/src/js/views/Queue.js @@ -152,8 +152,8 @@ const mapStateToProps = (state, ownProps) => { return { radio: state.ui.radio, radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false), - current_tracklist: state.ui.current_tracklist, - current_track: (state.ui.current_track !== undefined && state.ui.tracks !== undefined && state.ui.tracks[state.ui.current_track.uri] !== undefined ? state.ui.tracks[state.ui.current_track.uri] : null) + 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) } } diff --git a/src/js/views/Search.js b/src/js/views/Search.js index 1f9d3a6b5..d2ea4fe69 100755 --- a/src/js/views/Search.js +++ b/src/js/views/Search.js @@ -262,13 +262,13 @@ const mapStateToProps = (state, ownProps) => { search_settings: (state.ui.search_settings ? state.ui.search_settings : null), tracks: (state.ui.search_results ? state.ui.search_results.tracks : []), tracks_more: (state.ui.search_results && state.ui.search_results.tracks_more ? state.ui.search_results.tracks_more : null), - artists: (state.ui.artists ? state.ui.artists : []), + artists: (state.core.artists ? state.core.artists : []), artists_uris: (state.ui.search_results ? state.ui.search_results.artists_uris : []), artists_more: (state.ui.search_results ? state.ui.search_results.artists_more : null), - albums: (state.ui.albums ? state.ui.albums : []), + albums: (state.core.albums ? state.core.albums : []), albums_uris: (state.ui.search_results ? state.ui.search_results.albums_uris : []), albums_more: (state.ui.search_results ? state.ui.search_results.albums_more : null), - playlists: (state.ui.playlists ? state.ui.playlists : []), + playlists: (state.core.playlists ? state.core.playlists : []), playlists_uris: (state.ui.search_results ? state.ui.search_results.playlists_uris : []), playlists_more: (state.ui.search_results ? state.ui.search_results.playlists_more : null) } diff --git a/src/js/views/User.js b/src/js/views/User.js index b6d2f1a13..46a639f45 100755 --- a/src/js/views/User.js +++ b/src/js/views/User.js @@ -110,9 +110,9 @@ const mapStateToProps = (state, ownProps) => { load_queue: state.ui.load_queue, spotify_authorized: state.spotify.authorized, me: state.spotify.me, - playlists: state.ui.playlists, - user: (state.ui.users && typeof(state.ui.users[ownProps.params.uri]) !== 'undefined' ? state.ui.users[ownProps.params.uri] : false ), - users: state.ui.users + playlists: state.core.playlists, + user: (state.core.users && state.core.users[ownProps.params.uri] !== undefined ? state.core.users[ownProps.params.uri] : false), + users: state.core.users }; } diff --git a/src/js/views/discover/DiscoverCategory.js b/src/js/views/discover/DiscoverCategory.js index 1ba89baa7..db2a6988e 100755 --- a/src/js/views/discover/DiscoverCategory.js +++ b/src/js/views/discover/DiscoverCategory.js @@ -83,7 +83,7 @@ class DiscoverCategory extends React.Component{ const mapStateToProps = (state, ownProps) => { return { load_queue: state.ui.load_queue, - playlists: state.ui.playlists, + playlists: state.core.playlists, category: (state.ui.categories && typeof(state.ui.categories['category:'+ownProps.params.id]) !== 'undefined' ? state.ui.categories['category:'+ownProps.params.id] : false ) } } diff --git a/src/js/views/discover/DiscoverFeatured.js b/src/js/views/discover/DiscoverFeatured.js index 0350ec7ae..c98871cdf 100755 --- a/src/js/views/discover/DiscoverFeatured.js +++ b/src/js/views/discover/DiscoverFeatured.js @@ -120,7 +120,7 @@ const mapStateToProps = (state, ownProps) => { return { load_queue: state.ui.load_queue, featured_playlists: state.spotify.featured_playlists, - playlists: state.ui.playlists + playlists: state.core.playlists } } diff --git a/src/js/views/discover/DiscoverNewReleases.js b/src/js/views/discover/DiscoverNewReleases.js index c38484ca6..a10681cc3 100755 --- a/src/js/views/discover/DiscoverNewReleases.js +++ b/src/js/views/discover/DiscoverNewReleases.js @@ -128,7 +128,7 @@ class DiscoverNewReleases extends React.Component{ const mapStateToProps = (state, ownProps) => { return { load_queue: state.ui.load_queue, - albums: state.ui.albums, + albums: state.core.albums, new_releases: state.ui.new_releases, new_releases_more: state.ui.new_releases_more, new_releases_total: state.ui.new_releases_total diff --git a/src/js/views/discover/DiscoverRecommendations.js b/src/js/views/discover/DiscoverRecommendations.js index a35f3cc02..5fd9cdf36 100755 --- a/src/js/views/discover/DiscoverRecommendations.js +++ b/src/js/views/discover/DiscoverRecommendations.js @@ -264,9 +264,9 @@ class Discover extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - albums: (state.ui.albums ? state.ui.albums : []), - artists: (state.ui.artists ? state.ui.artists : []), - tracks: (state.ui.tracks ? state.ui.tracks : []), + albums: (state.core.albums ? state.core.albums : []), + artists: (state.core.artists ? state.core.artists : []), + tracks: (state.core.tracks ? state.core.tracks : []), genres: (state.ui.genres ? state.ui.genres : []), authorized: state.spotify.authorized, load_queue: state.ui.load_queue, diff --git a/src/js/views/library/LibraryAlbums.js b/src/js/views/library/LibraryAlbums.js index 4ec16204e..3995525e0 100755 --- a/src/js/views/library/LibraryAlbums.js +++ b/src/js/views/library/LibraryAlbums.js @@ -219,13 +219,13 @@ class LibraryAlbums extends React.Component{ const mapStateToProps = (state, ownProps) => { return { load_queue: state.ui.load_queue, + albums: state.core.albums, view: state.ui.library_albums_view, - albums: state.ui.albums, sort: (state.ui.library_albums_sort ? state.ui.library_albums_sort : 'name'), sort_reverse: (state.ui.library_albums_sort_reverse ? true : false), - library_albums: state.ui.library_albums, - library_albums_started: state.ui.library_albums_started, - library_albums_more: state.ui.library_albums_more + library_albums: state.core.library_albums, + library_albums_started: state.core.library_albums_started, + library_albums_more: state.core.library_albums_more } } diff --git a/src/js/views/library/LibraryArtists.js b/src/js/views/library/LibraryArtists.js index effde4e98..d87d82af6 100755 --- a/src/js/views/library/LibraryArtists.js +++ b/src/js/views/library/LibraryArtists.js @@ -168,14 +168,14 @@ class LibraryArtists extends React.Component{ const mapStateToProps = (state, ownProps) => { return { load_queue: state.ui.load_queue, - artists: state.ui.artists, + artists: state.core.artists, + library_artists: state.core.library_artists, + library_artists_started: state.core.library_artists_started, + library_artists_more: state.core.library_artists_more, + library_artists_started: state.core.library_artists_started, sort: (state.ui.library_artists_sort ? state.ui.library_artists_sort : 'name'), sort_reverse: (state.ui.library_artists_sort_reverse ? true : false), - library_artists: state.ui.library_artists, - library_artists_started: state.ui.library_artists_started, - library_artists_more: state.ui.library_artists_more, - library_artists_started: state.ui.library_artists_started, - view: state.ui.library_artists_view + view: state.core.library_artists_view } } diff --git a/src/js/views/library/LibraryLocalAlbums.js b/src/js/views/library/LibraryLocalAlbums.js index 1ba8aeb17..029a30522 100755 --- a/src/js/views/library/LibraryLocalAlbums.js +++ b/src/js/views/library/LibraryLocalAlbums.js @@ -184,8 +184,8 @@ const mapStateToProps = (state, ownProps) => { return { load_queue: state.ui.load_queue, mopidy_connected: state.mopidy.connected, - albums: state.ui.albums, - local_albums: state.ui.local_albums, + albums: state.core.albums, + local_albums: state.core.local_albums, view: state.ui.library_local_albums_view, sort: (state.ui.library_local_albums_sort ? state.ui.library_local_albums_sort : 'name'), sort_reverse: (state.ui.library_local_albums_sort_reverse ? true : false) diff --git a/src/js/views/library/LibraryLocalArtists.js b/src/js/views/library/LibraryLocalArtists.js index 00c8106ac..97f508ef5 100755 --- a/src/js/views/library/LibraryLocalArtists.js +++ b/src/js/views/library/LibraryLocalArtists.js @@ -94,8 +94,8 @@ const mapStateToProps = (state, ownProps) => { return { load_queue: state.ui.load_queue, mopidy_connected: state.mopidy.connected, - local_artists: state.ui.local_artists, - artists: state.ui.artists + local_artists: state.core.local_artists, + artists: state.core.artists } } diff --git a/src/js/views/library/LibraryPlaylists.js b/src/js/views/library/LibraryPlaylists.js index 3288c6feb..1ad8e194d 100755 --- a/src/js/views/library/LibraryPlaylists.js +++ b/src/js/views/library/LibraryPlaylists.js @@ -231,12 +231,12 @@ const mapStateToProps = (state, ownProps) => { slim_mode: state.ui.slim_mode, load_queue: state.ui.load_queue, me_id: (state.spotify.me ? state.spotify.me.id : (state.ui.config && state.ui.config.spotify_username ? state.ui.config.spotify_username : false)), - view: state.ui.library_playlists_view, - filter: (state.ui.library_playlists_filter ? state.ui.library_playlists_filter : 'all'), - sort: (state.ui.library_playlists_sort ? state.ui.library_playlists_sort : 'name'), - sort_reverse: (state.ui.library_playlists_sort_reverse ? true : false), - library_playlists: state.ui.library_playlists, - playlists: state.ui.playlists + view: state.core.library_playlists_view, + filter: (state.core.library_playlists_filter ? state.core.library_playlists_filter : 'all'), + sort: (state.core.library_playlists_sort ? state.core.library_playlists_sort : 'name'), + sort_reverse: (state.core.library_playlists_sort_reverse ? true : false), + library_playlists: state.core.library_playlists, + playlists: state.core.playlists } }