Hosing out old code, standardizing item loader
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
30
mopidy_iris/static/app.min.js
vendored
30
mopidy_iris/static/app.min.js
vendored
File diff suppressed because one or more lines are too long
@ -106,7 +106,7 @@
|
||||
|
||||
// Release details
|
||||
// These are automatically injected to built HTML
|
||||
var build = "1601711067";
|
||||
var build = "1601884219";
|
||||
var version = "3.52.4";
|
||||
|
||||
// Construct the script tag
|
||||
|
||||
@ -321,7 +321,7 @@ export class App extends React.Component {
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/discover/categories/:id"
|
||||
path="/discover/categories/:uri"
|
||||
component={DiscoverCategory}
|
||||
/>
|
||||
<Route
|
||||
|
||||
@ -14,10 +14,10 @@ export default memo(({
|
||||
{
|
||||
categories.map((category) => (
|
||||
<GridItem
|
||||
key={category.id}
|
||||
key={category.uri}
|
||||
type="category"
|
||||
item={category}
|
||||
link={`/discover/categories/${encodeURIComponent(category.id)}`}
|
||||
link={`/discover/categories/${encodeURIComponent(category.uri)}`}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
@ -204,6 +204,12 @@ export function libraryLoaded(library) {
|
||||
library,
|
||||
};
|
||||
}
|
||||
export function unloadLibrary(uri) {
|
||||
return {
|
||||
type: 'UNLOAD_LIBRARY',
|
||||
uri,
|
||||
};
|
||||
}
|
||||
export function itemsLoaded(items) {
|
||||
return {
|
||||
type: 'ITEMS_LOADED',
|
||||
@ -213,6 +219,12 @@ export function itemsLoaded(items) {
|
||||
export function itemLoaded(item) {
|
||||
return itemsLoaded([item]);
|
||||
}
|
||||
export function unloadItem(uri) {
|
||||
return {
|
||||
type: 'UNLOAD_ITEM',
|
||||
uri,
|
||||
};
|
||||
}
|
||||
|
||||
export function tracksLoaded(tracks) {
|
||||
return {
|
||||
|
||||
@ -23,6 +23,97 @@ const mopidyActions = require('../mopidy/actions.js');
|
||||
const googleActions = require('../google/actions.js');
|
||||
const spotifyActions = require('../spotify/actions.js');
|
||||
|
||||
/**
|
||||
* Ensure we have an item in our index
|
||||
* If it's not there, attempt to fetch it from our cold storage
|
||||
* If it's not their either, call the provided fetch()
|
||||
*
|
||||
* @param {*} Object { store, action, fetch, dependents}
|
||||
*/
|
||||
const ensureItemLoaded = ({
|
||||
store,
|
||||
action,
|
||||
fetch,
|
||||
dependents = [],
|
||||
}) => {
|
||||
const {
|
||||
uri,
|
||||
options: {
|
||||
forceRefetch,
|
||||
full,
|
||||
},
|
||||
} = action;
|
||||
const {
|
||||
core: {
|
||||
items: {
|
||||
[uri]: item,
|
||||
} = {},
|
||||
} = {},
|
||||
} = store.getState();
|
||||
|
||||
// Forced refetch bypasses everything
|
||||
if (forceRefetch) {
|
||||
console.info(`Force-refetching "${uri}"`);
|
||||
fetch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Already-loaded asset; check we have all of it's dependents
|
||||
if (item) {
|
||||
const loadableDependents = dependents.filter((k) => item[k] && item[k].length > 0);
|
||||
if (!full || (loadableDependents.length === dependents.length)) {
|
||||
console.info(`"${uri}" already in index`);
|
||||
loadableDependents.forEach(
|
||||
(dependent) => store.dispatch(coreActions.loadItems(item[dependent])),
|
||||
);
|
||||
store.dispatch(uiActions.stopLoading(uri));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
localForage.getItem(uri).then((restoredItem) => {
|
||||
if (!restoredItem) {
|
||||
fetch();
|
||||
return;
|
||||
}
|
||||
|
||||
const loadableDependents = dependents.filter(
|
||||
(k) => restoredItem[k] && restoredItem[k].length > 0,
|
||||
);
|
||||
console.info(`Restoring "${uri}" from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([restoredItem]));
|
||||
|
||||
if (full) {
|
||||
// We already have the dependents of our restored item, so restore them.
|
||||
// We assume that because THIS item is in the coldstore, its dependents
|
||||
// are as well.
|
||||
if (dependents.length && loadableDependents.length === dependents.length) {
|
||||
const dependentUris = loadableDependents.reduce(
|
||||
(acc, dependent) => [...acc, ...restoredItem[dependent]],
|
||||
[],
|
||||
);
|
||||
|
||||
console.info(`Restoring ${dependentUris.length} dependents from database`);
|
||||
|
||||
const restoreAllDependents = dependentUris.map(
|
||||
(dependentUri) => localForage.getItem(dependentUri),
|
||||
);
|
||||
Promise.all(restoreAllDependents).then(
|
||||
(dependentItems) => {
|
||||
store.dispatch(
|
||||
coreActions.restoreItemsFromColdStore(
|
||||
compact(dependentItems), // Squash nulls (ie items not found in coldstore)
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
fetch();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const CoreMiddleware = (function () {
|
||||
return (store) => (next) => (action = {}) => {
|
||||
const {
|
||||
@ -304,7 +395,7 @@ const CoreMiddleware = (function () {
|
||||
break;
|
||||
|
||||
case 'LOAD_TRACK': {
|
||||
const fetchTrack = () => {
|
||||
const fetch = () => {
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getTrack(action.uri, action.options));
|
||||
@ -319,154 +410,67 @@ const CoreMiddleware = (function () {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (action.options.forceRefetch) {
|
||||
console.info(`Force-refetching "${action.uri}"`);
|
||||
fetchTrack();
|
||||
break;
|
||||
}
|
||||
if (
|
||||
store.getState().core.items[action.uri]
|
||||
&& store.getState().core.items[action.uri].images
|
||||
) {
|
||||
console.info(`${action.uri}" already in index`);
|
||||
store.dispatch(uiActions.stopLoading(action.uri));
|
||||
break;
|
||||
}
|
||||
|
||||
localForage.getItem(action.uri).then((result) => {
|
||||
if (result) {
|
||||
console.info(`Loading "${action.uri}" from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
||||
|
||||
if (!result.images) {
|
||||
fetchTrack();
|
||||
}
|
||||
} else {
|
||||
fetchTrack();
|
||||
}
|
||||
ensureItemLoaded({
|
||||
store,
|
||||
action,
|
||||
fetch,
|
||||
dependents: ['images'],
|
||||
});
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'LOAD_ALBUM':
|
||||
const fetchAlbum = () => {
|
||||
case 'LOAD_ALBUM': {
|
||||
const fetch = () => {
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getAlbum(action.uri, action.options));
|
||||
|
||||
if (spotify.me) {
|
||||
store.dispatch(spotifyActions.following(action.uri));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
store.dispatch(mopidyActions.getAlbum(action.uri, action.options));
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
if (action.options.forceRefetch) {
|
||||
console.info(`Force-refetching "${action.uri}"`);
|
||||
fetchAlbum();
|
||||
break;
|
||||
}
|
||||
if (
|
||||
store.getState().core.items[action.uri]
|
||||
&& (!action.options.full || store.getState().core.items[action.uri].tracks)
|
||||
) {
|
||||
console.info(`${action.uri}" already in index`);
|
||||
store.dispatch(uiActions.stopLoading(action.uri));
|
||||
break;
|
||||
}
|
||||
|
||||
localForage.getItem(action.uri).then((result) => {
|
||||
if (result) {
|
||||
console.info(`Loading "${action.uri}" from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
||||
|
||||
// We don't have the complete Album, so refetch
|
||||
if (!result.tracks) {
|
||||
fetchAlbum();
|
||||
}
|
||||
} else {
|
||||
fetchAlbum();
|
||||
}
|
||||
ensureItemLoaded({
|
||||
store,
|
||||
action,
|
||||
fetch,
|
||||
dependents: ['tracks'],
|
||||
});
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'LOAD_ARTIST':
|
||||
const fetchArtist = () => {
|
||||
case 'LOAD_ARTIST': {
|
||||
const fetch = () => {
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getArtist(action.uri, action.options));
|
||||
|
||||
if (spotify.me) {
|
||||
store.dispatch(spotifyActions.following(action.uri));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
store.dispatch(mopidyActions.getArtist(action.uri, action.options));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (action.options.forceRefetch) {
|
||||
console.info(`Force-refetching "${action.uri}"`);
|
||||
fetchArtist();
|
||||
break;
|
||||
}
|
||||
if (
|
||||
store.getState().core.items[action.uri]
|
||||
&& (
|
||||
!action.options.full
|
||||
|| (
|
||||
store.getState().core.items[action.uri].tracks
|
||||
&& store.getState().core.items[action.uri].albums_uris
|
||||
&& store.getState().core.items[action.uri].images
|
||||
)
|
||||
)
|
||||
) {
|
||||
console.info(`${action.uri}" already in index`);
|
||||
store.dispatch(coreActions.loadItems(store.getState().core.items[action.uri].albums_uris));
|
||||
store.dispatch(uiActions.stopLoading(action.uri));
|
||||
break;
|
||||
}
|
||||
|
||||
localForage.getItem(action.uri).then((artist) => {
|
||||
if (artist) {
|
||||
console.info(`Restoring "${action.uri}" and ${artist.albums_uris ? artist.albums_uris.length : 0} albums from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([artist]));
|
||||
|
||||
if (artist.albums_uris) {
|
||||
const promises = artist.albums_uris.map((albumUri) => localForage.getItem(albumUri));
|
||||
console.time('restoring')
|
||||
Promise.all(promises).then(
|
||||
(albums) => {
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore(compact(albums)));
|
||||
console.timeEnd('restoring')
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (action.options.full && (!artist.tracks || !artist.albums_uris || !artist.images)) {
|
||||
fetchArtist();
|
||||
}
|
||||
} else {
|
||||
fetchArtist();
|
||||
}
|
||||
ensureItemLoaded({
|
||||
store,
|
||||
action,
|
||||
fetch,
|
||||
dependents: ['tracks', 'albums_uris'],
|
||||
});
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'LOAD_PLAYLIST':
|
||||
const fetchPlaylist = () => {
|
||||
case 'LOAD_PLAYLIST': {
|
||||
const fetch = () => {
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getPlaylist(action.uri, action.options));
|
||||
@ -481,121 +485,57 @@ const CoreMiddleware = (function () {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (action.options.forceRefetch) {
|
||||
console.info(`Force-refetching "${action.uri}"`);
|
||||
fetchPlaylist();
|
||||
break;
|
||||
}
|
||||
if (
|
||||
store.getState().core.items[action.uri]
|
||||
&& (
|
||||
!action.options.full
|
||||
|| store.getState().core.items[action.uri].tracks
|
||||
)
|
||||
) {
|
||||
console.info(`${action.uri}" already in index`);
|
||||
store.dispatch(uiActions.stopLoading(action.uri));
|
||||
break;
|
||||
}
|
||||
|
||||
localForage.getItem(action.uri).then((result) => {
|
||||
if (result) {
|
||||
console.info(`Restoring "${action.uri}" from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
||||
if (!action.options.full || !result.tracks) {
|
||||
fetchPlaylist();
|
||||
}
|
||||
} else {
|
||||
fetchPlaylist();
|
||||
}
|
||||
ensureItemLoaded({
|
||||
store,
|
||||
action,
|
||||
fetch,
|
||||
dependents: ['tracks'],
|
||||
});
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'LOAD_USER':
|
||||
const fetchUser = () => {
|
||||
case 'LOAD_USER': {
|
||||
const fetch = () => {
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getUser(action.uri, action.options));
|
||||
|
||||
if (spotify.me) {
|
||||
store.dispatch(spotifyActions.following(action.uri));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// No mopidy user model
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (action.options.forceRefetch) {
|
||||
console.info(`Force-refetching "${action.uri}"`);
|
||||
fetchUser();
|
||||
break;
|
||||
}
|
||||
if (
|
||||
store.getState().core.items[action.uri]
|
||||
&& (
|
||||
!action.options.full
|
||||
|| store.getState().core.items[action.uri].playlists_uris
|
||||
)
|
||||
) {
|
||||
console.info(`${action.uri}" already in index`);
|
||||
store.dispatch(coreActions.loadItems(store.getState().core.items[action.uri].playlists_uris));
|
||||
store.dispatch(uiActions.stopLoading(action.uri));
|
||||
break;
|
||||
}
|
||||
|
||||
localForage.getItem(action.uri).then((user) => {
|
||||
if (user) {
|
||||
console.info(`Restoring "${action.uri}" and ${user.playlists_uris ? user.playlists_uris.length : 0} playlists from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([user]));
|
||||
|
||||
if (user.playlists_uris) {
|
||||
const promises = user.playlists_uris.map((playlistUri) => localForage.getItem(playlistUri));
|
||||
Promise.all(promises).then(
|
||||
(playlists) => {
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore(compact(playlists)));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (action.options.full && !user.playlists_uris) {
|
||||
fetchUser();
|
||||
}
|
||||
} else {
|
||||
fetchUser();
|
||||
}
|
||||
ensureItemLoaded({
|
||||
store,
|
||||
dependents: ['playlists_uris'],
|
||||
action,
|
||||
fetch,
|
||||
});
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'LOAD_USERXXX':
|
||||
if (
|
||||
!action.options.forceRefetch
|
||||
&& store.getState().core.users[action.uri]
|
||||
&& store.getState().core.users[action.uri].playlists_uris) {
|
||||
console.info(`Loading "${action.uri}" from index`);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getUser(action.uri, action.options));
|
||||
|
||||
if (spotify.me) {
|
||||
store.dispatch(spotifyActions.following(action.uri));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// No Mopidy mechanism for users
|
||||
break;
|
||||
}
|
||||
case 'LOAD_CATEGORY':
|
||||
const fetch = () => {
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getCategory(action.uri, action.options));
|
||||
break;
|
||||
default:
|
||||
// No mopidy category model
|
||||
break;
|
||||
}
|
||||
};
|
||||
ensureItemLoaded({
|
||||
store,
|
||||
dependents: ['playlists_uris'],
|
||||
action,
|
||||
fetch,
|
||||
});
|
||||
|
||||
next(action);
|
||||
break;
|
||||
@ -644,7 +584,6 @@ const CoreMiddleware = (function () {
|
||||
store.dispatch(coreActions.restoreLibraryFromColdStore(library));
|
||||
},
|
||||
);
|
||||
|
||||
} else {
|
||||
fetchLibrary();
|
||||
}
|
||||
@ -653,13 +592,19 @@ const CoreMiddleware = (function () {
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'UNLOAD_LIBRARY': {
|
||||
localForage.removeItem(action.uri);
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ADD_TO_LIBRARY': {
|
||||
const library = store.getState().core.libraries[action.uri];
|
||||
if (library) {
|
||||
library.items_uris.push(action.item.uri);
|
||||
store.dispatch(coreActions.libraryLoaded(library));
|
||||
} else {
|
||||
// Clear our stored library. This prevents the next call to possibly restore a stale
|
||||
// Clear our stored library. This prevents the next call from possibly restoring a stale
|
||||
// library listing.
|
||||
localForage.removeItem(action.uri);
|
||||
}
|
||||
@ -706,91 +651,6 @@ const CoreMiddleware = (function () {
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'TRACKS_LOADED':
|
||||
var tracks_index = { ...core.tracks };
|
||||
var artists_index = core.artists;
|
||||
var albums_index = core.albums;
|
||||
var tracks_loaded = [];
|
||||
var artists_loaded = [];
|
||||
var albums_loaded = [];
|
||||
|
||||
for (const raw_track of action.tracks) {
|
||||
var track = formatTrack(raw_track);
|
||||
|
||||
if (tracks_index[track.uri] !== undefined) {
|
||||
track = { ...tracks_index[track.uri], ...track };
|
||||
}
|
||||
|
||||
if (raw_track.album) {
|
||||
track.album = formatSimpleObject(raw_track.album);
|
||||
|
||||
if (!albums_index[raw_track.album.uri]) {
|
||||
albums_loaded.push(raw_track.album);
|
||||
}
|
||||
}
|
||||
|
||||
if (raw_track.artists && raw_track.artists.length > 0) {
|
||||
track.artists = [];
|
||||
|
||||
for (var artist of raw_track.artists) {
|
||||
track.artists.push(formatSimpleObject(artist));
|
||||
|
||||
// Not already in our index, so let's add it
|
||||
if (!artists_index[artist.uri]) {
|
||||
artists_loaded.push(artist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tracks_loaded.push(track);
|
||||
}
|
||||
|
||||
action.tracks = tracks_loaded;
|
||||
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'ALBUMS_LOADED':
|
||||
var albums_index = { ...core.albums };
|
||||
var albums_loaded = [];
|
||||
var artists_loaded = [];
|
||||
var tracks_loaded = [];
|
||||
|
||||
for (const raw_album of action.albums) {
|
||||
let album = formatAlbum(raw_album);
|
||||
|
||||
if (albums_index[album.uri]) {
|
||||
album = { ...albums_index[album.uri], ...album };
|
||||
}
|
||||
|
||||
if (raw_album.tracks) {
|
||||
album.tracks = raw_album.tracks.map((track) => ({
|
||||
...formatTrack(track),
|
||||
album: formatSimpleObject(album)
|
||||
}));
|
||||
}
|
||||
|
||||
if (raw_album.artists) {
|
||||
album.artists = formatSimpleObjects(raw_album.artists);
|
||||
}
|
||||
|
||||
albums_loaded.push(album);
|
||||
}
|
||||
|
||||
action.albums = albums_loaded;
|
||||
|
||||
if (artists_loaded.length > 0) {
|
||||
store.dispatch(coreActions.items(artists_loaded));
|
||||
}
|
||||
if (tracks_loaded.length > 0) {
|
||||
store.dispatch(coreActions.items(tracks_loaded));
|
||||
}
|
||||
|
||||
store.dispatch(coreActions.updateColdStore(albums_loaded));
|
||||
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'ITEMS_LOADED':
|
||||
const mergedItems = [];
|
||||
action.items.forEach((item) => {
|
||||
@ -814,100 +674,6 @@ const CoreMiddleware = (function () {
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'ARTISTS_LOADED':
|
||||
var artists_index = { ...core.artists };
|
||||
var artists_loaded = [];
|
||||
var tracks_loaded = [];
|
||||
|
||||
for (const raw_artist of action.artists) {
|
||||
var artist = formatArtist(raw_artist);
|
||||
artist = { ...artists_index[artist.uri], ...artist };
|
||||
artists_loaded.push(artist);
|
||||
}
|
||||
|
||||
store.dispatch(coreActions.updateColdStore(artists_loaded));
|
||||
next({
|
||||
...action,
|
||||
artists: artists_loaded,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'PLAYLISTS_LOADED':
|
||||
var playlists_index = { ...core.playlists };
|
||||
var playlists_loaded = [];
|
||||
var tracks_loaded = [];
|
||||
|
||||
for (var playlist of action.playlists) {
|
||||
playlist = formatPlaylist(playlist);
|
||||
|
||||
// Detect editability
|
||||
switch (uriSource(playlist.uri)) {
|
||||
case 'm3u':
|
||||
playlist.can_edit = true;
|
||||
break;
|
||||
|
||||
case 'spotify':
|
||||
if (spotify.authorization && spotify.me) {
|
||||
playlist.can_edit = (playlist.owner && playlist.owner.id == spotify.me.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Already have this playlist partially in our index
|
||||
if (playlists_index[playlist.uri]) {
|
||||
playlist = { ...playlists_index[playlist.uri], ...playlist };
|
||||
|
||||
// Setup placeholder tracks_uris
|
||||
if (playlist.tracks_uris === undefined) {
|
||||
playlist.tracks_uris = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Load our tracks
|
||||
if (playlist.tracks) {
|
||||
var tracks = formatTracks(playlist.tracks);
|
||||
var tracks_uris = arrayOf('uri', tracks);
|
||||
playlist.tracks_uris = tracks_uris;
|
||||
delete playlist.tracks;
|
||||
tracks_loaded = [...tracks_loaded, ...tracks];
|
||||
}
|
||||
|
||||
// Update index
|
||||
playlists_loaded.push(playlist);
|
||||
}
|
||||
|
||||
action.playlists = playlists_loaded;
|
||||
|
||||
if (tracks_loaded.length > 0) {
|
||||
store.dispatch(coreActions.tracksLoaded(tracks_loaded));
|
||||
}
|
||||
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'USERS_LOADED':
|
||||
var users_index = { ...core.users };
|
||||
var users_loaded = [];
|
||||
|
||||
for (let user of action.users) {
|
||||
user = formatUser(user);
|
||||
|
||||
if (users_index[user.uri]) {
|
||||
user = { ...users_index[user.uri], ...user };
|
||||
}
|
||||
|
||||
users_loaded.push(user);
|
||||
}
|
||||
|
||||
action.users = users_loaded;
|
||||
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'USER_PLAYLISTS_LOADED':
|
||||
store.dispatch(coreActions.playlistsLoaded(action.playlists));
|
||||
next(action);
|
||||
break;
|
||||
|
||||
/**
|
||||
* Loaded more linked assets
|
||||
* Often fired during lazy-loading or async asset grabbing.
|
||||
|
||||
@ -97,6 +97,15 @@ export default function reducer(core = {}, action) {
|
||||
},
|
||||
};
|
||||
|
||||
case 'UNLOAD_LIBRARY': {
|
||||
const libraries = { ...core.libraries };
|
||||
delete libraries[action.uri];
|
||||
return {
|
||||
...core,
|
||||
libraries,
|
||||
};
|
||||
}
|
||||
|
||||
case 'USER_PLAYLISTS_LOADED':
|
||||
var users = { ...core.users };
|
||||
var existing_playlists_uris = [];
|
||||
|
||||
@ -1863,7 +1863,7 @@ const MopidyMiddleware = (function () {
|
||||
uri: track.uri,
|
||||
});
|
||||
|
||||
store.dispatch(coreActions.loadItem(track.uri));
|
||||
store.dispatch(coreActions.loadItem(track.uri, { full: true }));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ import {
|
||||
upgradeSpotifyPlaylistUris,
|
||||
} from '../../util/helpers';
|
||||
import {
|
||||
formatCategory,
|
||||
formatCategories,
|
||||
formatTracks,
|
||||
formatPlaylist,
|
||||
formatPlaylists,
|
||||
@ -424,7 +426,7 @@ export function getCategories() {
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'SPOTIFY_CATEGORIES_LOADED',
|
||||
categories: response.categories.items,
|
||||
categories: formatCategories(response.categories.items),
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
@ -437,24 +439,40 @@ export function getCategories() {
|
||||
};
|
||||
}
|
||||
|
||||
export function getCategory(id, forceRefetch = false) {
|
||||
export function getCategory(uri, { forceRefetch } = {}) {
|
||||
return (dispatch, getState) => {
|
||||
const id = getFromUri('categoryid', uri);
|
||||
let endpoint = `browse/categories/${id}`;
|
||||
endpoint += `?country=${getState().spotify.country}`;
|
||||
endpoint += `&locale=${getState().spotify.locale}`;
|
||||
if (forceRefetch) endpoint += `&refetch=${Date.now()}`;
|
||||
|
||||
let plEndpoint = `browse/categories/${id}/playlists`;
|
||||
plEndpoint += '?limit=50';
|
||||
plEndpoint += `&country=${getState().spotify.country}`;
|
||||
plEndpoint += `&locale=${getState().spotify.locale}`;
|
||||
if (forceRefetch) plEndpoint += `&refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'SPOTIFY_CATEGORY_LOADED',
|
||||
category: {
|
||||
uri: `category:${response.id}`,
|
||||
playlist_uris: null,
|
||||
...response,
|
||||
},
|
||||
});
|
||||
const category = formatCategory(response);
|
||||
|
||||
let playlists = [];
|
||||
const fetchPlaylists = (plEndpoint) => request(dispatch, getState, plEndpoint)
|
||||
.then((response) => {
|
||||
playlists = [...playlists, ...formatPlaylists(response.playlists.items)];
|
||||
if (response.playlists.next) {
|
||||
fetchPlaylists(response.playlists.next);
|
||||
} else {
|
||||
dispatch(coreActions.itemLoaded({
|
||||
...category,
|
||||
playlists_uris: arrayOf('uri', playlists),
|
||||
}));
|
||||
dispatch(coreActions.itemsLoaded(playlists));
|
||||
}
|
||||
});
|
||||
fetchPlaylists(plEndpoint);
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
@ -466,33 +484,6 @@ export function getCategory(id, forceRefetch = false) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getCategoryPlaylists(id, forceRefetch = false) {
|
||||
return (dispatch, getState) => {
|
||||
let endpoint = `browse/categories/${id}/playlists`;
|
||||
endpoint += '?limit=50';
|
||||
endpoint += `&country=${getState().spotify.country}`;
|
||||
endpoint += `&locale=${getState().spotify.locale}`;
|
||||
if (forceRefetch) endpoint += `&refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED',
|
||||
uri: `category:${id}`,
|
||||
playlists: response.playlists,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not load category playlists',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function getNewReleases(forceRefetch = false) {
|
||||
return (dispatch, getState) => {
|
||||
let endpoint = 'browse/new-releases';
|
||||
@ -1510,134 +1501,6 @@ export function getLibraryTracksAndPlayProcessor(data) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tracks for a playlist
|
||||
*
|
||||
* Recursively get .next until we have all tracks
|
||||
* */
|
||||
|
||||
export function getAllPlaylistTracks(
|
||||
uri,
|
||||
shuffle = false,
|
||||
callback_action = null,
|
||||
play_next = false,
|
||||
at_position = null,
|
||||
offset = 0,
|
||||
) {
|
||||
return (dispatch, getState) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (data.callback_action == 'enqueue') {
|
||||
dispatch(mopidyActions.enqueueURIs(uris, data.uri, data.play_next, data.at_position, data.offset));
|
||||
} else {
|
||||
dispatch(mopidyActions.playURIs(uris, data.uri));
|
||||
}
|
||||
|
||||
dispatch(uiActions.startProcess(
|
||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||
'Loading playlist tracks',
|
||||
{
|
||||
uri,
|
||||
next: `playlists/${getFromUri('playlistid', uri)}/tracks?market=${getState().spotify.country}`,
|
||||
shuffle,
|
||||
play_next,
|
||||
at_position,
|
||||
offset,
|
||||
callback_action,
|
||||
},
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
export function getAllPlaylistTracksProcessor(data) {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, data.next)
|
||||
.then(
|
||||
(response) => {
|
||||
// Check to see if we've been cancelled
|
||||
if (getState().ui.processes.SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR !== undefined) {
|
||||
const processor = getState().ui.processes.SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR;
|
||||
|
||||
if (processor.status == 'cancelling') {
|
||||
dispatch(uiActions.processCancelled('SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add on our new batch of loaded tracks
|
||||
let tracks = [];
|
||||
const new_tracks = [];
|
||||
for (const item of response.items) {
|
||||
if (item.track) {
|
||||
new_tracks.push(item.track);
|
||||
}
|
||||
}
|
||||
if (data.tracks) {
|
||||
tracks = [...data.tracks, ...new_tracks];
|
||||
} else {
|
||||
tracks = new_tracks;
|
||||
}
|
||||
|
||||
// We got a next link, so we've got more work to be done
|
||||
if (response.next) {
|
||||
dispatch(uiActions.updateProcess(
|
||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||
`Loading ${response.total - tracks.length} playlist tracks`,
|
||||
{
|
||||
...data,
|
||||
next: response.next,
|
||||
total: response.total,
|
||||
remaining: response.total - tracks.length,
|
||||
},
|
||||
));
|
||||
dispatch(uiActions.runProcess(
|
||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||
{
|
||||
...data,
|
||||
next: response.next,
|
||||
tracks,
|
||||
},
|
||||
));
|
||||
} else {
|
||||
// Seeing as we now have all the playlist's tracks, add them to the playlist we have
|
||||
// in our index for quicker reuse next time
|
||||
dispatch(coreActions.loadedMore(
|
||||
'playlist',
|
||||
data.uri,
|
||||
'track',
|
||||
{ tracks },
|
||||
));
|
||||
|
||||
let uris = arrayOf('uri', tracks);
|
||||
|
||||
if (data.shuffle) {
|
||||
uris = shuffle(uris);
|
||||
}
|
||||
|
||||
// We don't bother "finishing", we just want it "finished" immediately
|
||||
// This bypasses the fade transition for a more smooth transition between two
|
||||
// processes that flow together
|
||||
dispatch(uiActions.removeProcess('SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR'));
|
||||
|
||||
if (data.callback_action == 'enqueue') {
|
||||
dispatch(mopidyActions.enqueueURIs(uris, data.uri, data.play_next, data.at_position, data.offset));
|
||||
} else {
|
||||
dispatch(mopidyActions.playURIs(uris, data.uri));
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not load tracks to play playlist',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function addTracksToPlaylist(uri, tracks_uris) {
|
||||
return (dispatch, getState) => {
|
||||
|
||||
@ -120,109 +120,13 @@ const SpotifyMiddleware = (function () {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: This can go
|
||||
case 'SPOTIFY_ARTIST_ALBUMS_LOADED':
|
||||
store.dispatch(coreActions.albumsLoaded(action.data.items));
|
||||
store.dispatch({
|
||||
type: 'ARTIST_ALBUMS_LOADED',
|
||||
artist_uri: action.artist_uri,
|
||||
albums_uris: arrayOf('uri', action.data.items),
|
||||
more: action.data.next,
|
||||
total: action.data.total,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'SPOTIFY_USER_PLAYLISTS_LOADED':
|
||||
var playlists = [];
|
||||
for (var i = 0; i < action.data.items.length; i++) {
|
||||
var playlist = {
|
||||
|
||||
...action.data.items[i],
|
||||
tracks_total: action.data.items[i].tracks.total,
|
||||
};
|
||||
|
||||
// remove our tracklist. It'll overwrite any full records otherwise
|
||||
delete playlist.tracks;
|
||||
|
||||
playlists.push(playlist);
|
||||
}
|
||||
|
||||
store.dispatch({
|
||||
type: 'PLAYLISTS_LOADED',
|
||||
playlists,
|
||||
});
|
||||
|
||||
store.dispatch({
|
||||
type: 'USER_PLAYLISTS_LOADED',
|
||||
key: action.key,
|
||||
uris: arrayOf('uri', playlists),
|
||||
more: action.data.next,
|
||||
total: action.data.total,
|
||||
});
|
||||
break;
|
||||
|
||||
/*
|
||||
|
||||
case 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED':
|
||||
var playlists = []
|
||||
for (var i = 0; i < action.data.playlists.items.length; i++){
|
||||
var playlist = Object.assign(
|
||||
{},
|
||||
action.data.playlists.items[i],
|
||||
{
|
||||
tracks_total: action.data.playlists.items[i].tracks.total
|
||||
}
|
||||
)
|
||||
|
||||
// remove our tracklist. It'll overwrite any full records otherwise
|
||||
delete playlist.tracks
|
||||
|
||||
playlists.push(playlist)
|
||||
}
|
||||
|
||||
store.dispatch({
|
||||
type: 'PLAYLISTS_LOADED',
|
||||
playlists: playlists
|
||||
});
|
||||
|
||||
store.dispatch({
|
||||
type: 'CATEGORY_PLAYLISTS_LOADED',
|
||||
key: action.key,
|
||||
uris: arrayOf('uri',playlists),
|
||||
more: action.data.playlists.next,
|
||||
total: action.data.playlists.total
|
||||
});
|
||||
break
|
||||
*/
|
||||
|
||||
case 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED':
|
||||
store.dispatch(coreActions.playlistsLoaded(action.playlists.items));
|
||||
|
||||
action.uris = arrayOf('uri', action.playlists.items);
|
||||
action.more = action.playlists.next;
|
||||
action.total = action.playlists.total;
|
||||
delete action.playlists;
|
||||
|
||||
// Upgrade our URIs
|
||||
action.uris = upgradeSpotifyPlaylistUris(action.uris);
|
||||
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED_MORE':
|
||||
store.dispatch({
|
||||
type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED',
|
||||
uri: action.uri,
|
||||
playlists: action.data.playlists,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'SPOTIFY_CATEGORY_LOADED':
|
||||
store.dispatch({
|
||||
type: 'SPOTIFY_CATEGORIES_LOADED',
|
||||
categories: [action.category],
|
||||
});
|
||||
case 'SPOTIFY_FLUSH_LIBRARY': {
|
||||
store.dispatch(coreActions.unloadLibrary('spotify:library:artists'));
|
||||
store.dispatch(coreActions.unloadLibrary('spotify:library:albums'));
|
||||
store.dispatch(coreActions.unloadLibrary('spotify:library:playlists'));
|
||||
store.dispatch(coreActions.unloadLibrary('spotify:library:tracks'));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'SPOTIFY_CATEGORIES_LOADED':
|
||||
var categories_index = { ...spotify.categories };
|
||||
|
||||
@ -78,7 +78,7 @@ export default function reducer(spotify = {}, action) {
|
||||
case 'SPOTIFY_NEW_RELEASES_LOADED':
|
||||
var new_releases = [];
|
||||
if (spotify.new_releases) {
|
||||
new_releases = Object.assign([], spotify.new_releases);
|
||||
new_releases = Object.assign([], spotify.new_releases);
|
||||
}
|
||||
return {
|
||||
...spotify,
|
||||
@ -96,7 +96,6 @@ export default function reducer(spotify = {}, action) {
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
||||
...spotify,
|
||||
discover: [...spotify.discover, ...[action.data]],
|
||||
};
|
||||
@ -106,7 +105,6 @@ export default function reducer(spotify = {}, action) {
|
||||
|
||||
case 'SPOTIFY_RECOMMENDATIONS_LOADED':
|
||||
return {
|
||||
|
||||
...spotify,
|
||||
recommendations: {
|
||||
artists_uris: action.artists_uris,
|
||||
@ -117,7 +115,6 @@ export default function reducer(spotify = {}, action) {
|
||||
|
||||
case 'SPOTIFY_FAVORITES_LOADED':
|
||||
return {
|
||||
|
||||
...spotify,
|
||||
favorite_artists: action.artists_uris,
|
||||
favorite_tracks: action.tracks_uris,
|
||||
@ -127,7 +124,6 @@ export default function reducer(spotify = {}, action) {
|
||||
var { autocomplete_results } = spotify;
|
||||
autocomplete_results[action.field_id] = { loading: true };
|
||||
return {
|
||||
|
||||
...spotify,
|
||||
autocomplete_results,
|
||||
};
|
||||
@ -137,7 +133,6 @@ export default function reducer(spotify = {}, action) {
|
||||
autocomplete_results[action.field_id] = action.results;
|
||||
autocomplete_results[action.field_id].loading = false;
|
||||
return {
|
||||
|
||||
...spotify,
|
||||
autocomplete_results,
|
||||
};
|
||||
@ -148,14 +143,12 @@ export default function reducer(spotify = {}, action) {
|
||||
delete autocomplete_results[action.field_id];
|
||||
}
|
||||
return {
|
||||
|
||||
...spotify,
|
||||
autocomplete_results,
|
||||
};
|
||||
|
||||
case 'SPOTIFY_GENRES_LOADED':
|
||||
return {
|
||||
|
||||
...spotify,
|
||||
genres: action.genres,
|
||||
};
|
||||
@ -172,69 +165,6 @@ export default function reducer(spotify = {}, action) {
|
||||
}
|
||||
return { ...spotify, categories };
|
||||
|
||||
case 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED':
|
||||
var categories = { ...spotify.categories };
|
||||
var playlists_uris = [];
|
||||
|
||||
if (categories[action.uri] && categories[action.uri].playlists_uris) {
|
||||
playlists_uris = categories[action.uri].playlists_uris;
|
||||
}
|
||||
|
||||
var category = {
|
||||
...categories[action.uri],
|
||||
playlists_uris: [...playlists_uris, ...action.uris],
|
||||
playlists_more: action.more,
|
||||
playlists_total: action.total,
|
||||
};
|
||||
categories[action.uri] = category;
|
||||
return { ...spotify, categories };
|
||||
|
||||
|
||||
/**
|
||||
* Library
|
||||
* */
|
||||
|
||||
case 'SPOTIFY_FLUSH_LIBRARY':
|
||||
return {
|
||||
...spotify,
|
||||
library_playlists: null,
|
||||
library_playlists_loaded_all: null,
|
||||
library_playlists_status: null,
|
||||
library_albums: null,
|
||||
library_albums_status: null,
|
||||
library_albums_loaded_all: null,
|
||||
library_artists: null,
|
||||
library_artists_status: null,
|
||||
library_artists_loaded_all: null,
|
||||
library_tracks: null,
|
||||
library_tracks_status: null,
|
||||
library_tracks_loaded_all: null,
|
||||
};
|
||||
|
||||
case 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED':
|
||||
if (spotify.library_playlists) {
|
||||
var uris = [...spotify.library_playlists, ...action.uris];
|
||||
} else {
|
||||
var { uris } = action;
|
||||
}
|
||||
return { ...spotify, library_playlists: removeDuplicates(uris) };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ARTISTS_LOADED':
|
||||
if (spotify.library_artists) {
|
||||
var uris = [...spotify.library_artists, ...action.uris];
|
||||
} else {
|
||||
var { uris } = action;
|
||||
}
|
||||
return { ...spotify, library_artists: removeDuplicates(uris) };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ALBUMS_LOADED':
|
||||
if (spotify.library_albums) {
|
||||
var uris = [...spotify.library_albums, ...action.uris];
|
||||
} else {
|
||||
var { uris } = action;
|
||||
}
|
||||
return { ...spotify, library_albums: removeDuplicates(uris) };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_TRACKS_LOADED':
|
||||
case 'SPOTIFY_LIBRARY_TRACKS_LOADED_MORE':
|
||||
var tracks = action.data.items;
|
||||
@ -254,65 +184,6 @@ export default function reducer(spotify = {}, action) {
|
||||
library_tracks_more: action.data.next,
|
||||
};
|
||||
|
||||
|
||||
case 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED_ALL':
|
||||
return { ...spotify, library_playlists_loaded_all: true };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_PLAYLISTS_CLEAR':
|
||||
return { ...spotify, library_playlists: [] };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ARTISTS_CLEAR':
|
||||
return { ...spotify, library_artists: [] };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ALBUMS_CLEAR':
|
||||
return { ...spotify, library_albums: [] };
|
||||
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ALBUM_CHECK':
|
||||
var items = Object.assign([], spotify.library_albums);
|
||||
var index = items.indexOf(action.key);
|
||||
if (index > -1 && !action.in_library) {
|
||||
items.splice(index, 1);
|
||||
} else if (index < 0 && action.in_library) {
|
||||
items.push(action.key);
|
||||
}
|
||||
return { ...spotify, library_albums: items };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ARTIST_CHECK':
|
||||
var items = Object.assign([], spotify.library_artists);
|
||||
var index = items.indexOf(action.key);
|
||||
if (index > -1 && !action.in_library) {
|
||||
items.splice(index, 1);
|
||||
} else if (index < 0 && action.in_library) {
|
||||
items.push(action.key);
|
||||
}
|
||||
return { ...spotify, library_artists: items };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_PLAYLIST_CHECK':
|
||||
var items = Object.assign([], spotify.library_playlists);
|
||||
var index = items.indexOf(action.key);
|
||||
if (index > -1 && !action.in_library) {
|
||||
items.splice(index, 1);
|
||||
} else if (index < 0 && action.in_library) {
|
||||
items.push(action.key);
|
||||
}
|
||||
return { ...spotify, library_playlists: items };
|
||||
|
||||
case 'SPOTIFY_LIBRARY_TRACK_CHECK':
|
||||
var items = Object.assign([], spotify.library_tracks);
|
||||
var index = items.indexOf(action.key);
|
||||
if (index > -1 && !action.in_library) {
|
||||
items.splice(index, 1);
|
||||
} else if (index < 0 && action.in_library) {
|
||||
items.push(action.key);
|
||||
}
|
||||
return { ...spotify, library_tracks: items };
|
||||
|
||||
|
||||
/**
|
||||
* Searching
|
||||
* */
|
||||
|
||||
case 'SPOTIFY_CLEAR_SEARCH_RESULTS':
|
||||
return { ...spotify, search_results: {} };
|
||||
|
||||
|
||||
@ -258,6 +258,13 @@ const formatUsers = function (records = []) {
|
||||
}
|
||||
return formatted;
|
||||
};
|
||||
const formatCategories = function (records = []) {
|
||||
const formatted = [];
|
||||
for (const record of records) {
|
||||
formatted.push(formatCategory(record));
|
||||
}
|
||||
return formatted;
|
||||
};
|
||||
const formatSimpleObjects = function (records = []) {
|
||||
const formatted = [];
|
||||
for (const record of records) {
|
||||
@ -725,6 +732,38 @@ const formatClient = function (data) {
|
||||
return client;
|
||||
};
|
||||
|
||||
/**
|
||||
* Spotify playlists category
|
||||
*
|
||||
* @param data obj
|
||||
* @return obj
|
||||
* */
|
||||
const formatCategory = function (data) {
|
||||
const category = {};
|
||||
const fields = [
|
||||
'id',
|
||||
'uri',
|
||||
'name',
|
||||
'playlists_uris',
|
||||
];
|
||||
|
||||
for (const field of fields) {
|
||||
if (data.hasOwnProperty(field)) {
|
||||
category[field] = data[field];
|
||||
}
|
||||
}
|
||||
|
||||
if (!category.uri && data.id) {
|
||||
category.uri = `spotify:category:${data.id}`;
|
||||
}
|
||||
|
||||
if (data.icons) {
|
||||
category.images = formatImages(data.icons);
|
||||
}
|
||||
|
||||
return category;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a snapcast client object into a universal format
|
||||
*
|
||||
@ -909,6 +948,8 @@ export {
|
||||
formatTracks,
|
||||
formatClient,
|
||||
formatGroup,
|
||||
formatCategory,
|
||||
formatCategories,
|
||||
collate,
|
||||
collateLibrary,
|
||||
};
|
||||
@ -932,6 +973,8 @@ export default {
|
||||
formatTracks,
|
||||
formatClient,
|
||||
formatGroup,
|
||||
formatCategory,
|
||||
formatCategories,
|
||||
collate,
|
||||
collateLibrary,
|
||||
};
|
||||
|
||||
@ -131,10 +131,9 @@ const getCurrentPusherConnection = function (connections, connectionid) {
|
||||
* @param uri = string
|
||||
* */
|
||||
let uriSource = function (uri) {
|
||||
if (!uri) {
|
||||
return false;
|
||||
}
|
||||
const exploded = uri.split(':');
|
||||
if (!uri) return false;
|
||||
|
||||
const exploded = `${uri}`.split(':');
|
||||
return exploded[0];
|
||||
};
|
||||
/**
|
||||
@ -146,7 +145,7 @@ let uriSource = function (uri) {
|
||||
const uriType = function (uri) {
|
||||
if (!uri) return null;
|
||||
|
||||
const exploded = uri.split(':');
|
||||
const exploded = `${uri}`.split(':');
|
||||
|
||||
if (exploded[0] === 'm3u') {
|
||||
return 'playlist';
|
||||
@ -226,9 +225,9 @@ const sourceIcon = function (uri, source = null) {
|
||||
* @param element = string, the element we wish to extract
|
||||
* @param uri = string
|
||||
* */
|
||||
const getFromUri = function (element, uri = '') {
|
||||
const exploded = uri.split(':');
|
||||
const namespace = exploded[0];
|
||||
const getFromUri = function (element, uri) {
|
||||
if (!uri) return null;
|
||||
const exploded = `${uri}`.split(':');
|
||||
|
||||
switch (element) {
|
||||
case 'mbid':
|
||||
@ -281,6 +280,12 @@ const getFromUri = function (element, uri = '') {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'categoryid':
|
||||
if (exploded[1] == 'category') {
|
||||
return exploded[2];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'seeds':
|
||||
if (exploded[1] == 'discover') {
|
||||
return exploded[2];
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import Header from '../../components/Header';
|
||||
import Icon from '../../components/Icon';
|
||||
import PlaylistGrid from '../../components/PlaylistGrid';
|
||||
import LazyLoadListener from '../../components/LazyLoadListener';
|
||||
import Loader from '../../components/Loader';
|
||||
import ErrorMessage from '../../components/ErrorMessage';
|
||||
import Button from '../../components/Button';
|
||||
import * as uiActions from '../../services/ui/actions';
|
||||
import * as coreActions from '../../services/core/actions';
|
||||
import * as spotifyActions from '../../services/spotify/actions';
|
||||
import { isLoading } from '../../util/helpers';
|
||||
import { collate } from '../../util/format';
|
||||
import { i18n } from '../../locale';
|
||||
import { I18n, i18n } from '../../locale';
|
||||
import {
|
||||
makeItemSelector,
|
||||
makeLoadingSelector,
|
||||
} from '../../util/selectors';
|
||||
|
||||
class DiscoverCategory extends React.Component {
|
||||
componentDidMount() {
|
||||
@ -20,23 +23,15 @@ class DiscoverCategory extends React.Component {
|
||||
}
|
||||
|
||||
componentDidUpdate = ({
|
||||
match: {
|
||||
params: {
|
||||
id: prevId,
|
||||
},
|
||||
},
|
||||
uri: prevUri,
|
||||
category: prevCategory,
|
||||
}) => {
|
||||
const {
|
||||
match: {
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
uri,
|
||||
category,
|
||||
} = this.props;
|
||||
|
||||
if (prevId !== id) this.loadCategory();
|
||||
if (prevUri !== uri) this.loadCategory();
|
||||
if (!prevCategory && category) this.setWindowTitle(category);
|
||||
}
|
||||
|
||||
@ -54,105 +49,104 @@ class DiscoverCategory extends React.Component {
|
||||
|
||||
loadCategory = () => {
|
||||
const {
|
||||
uri,
|
||||
category,
|
||||
match: {
|
||||
params: { id },
|
||||
},
|
||||
spotifyActions: {
|
||||
getCategory,
|
||||
getCategoryPlaylists,
|
||||
coreActions: {
|
||||
loadItem,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
if (!category) {
|
||||
getCategory(id);
|
||||
}
|
||||
|
||||
if (!category.playlists_uris) {
|
||||
getCategoryPlaylists(id);
|
||||
loadItem(uri);
|
||||
}
|
||||
}
|
||||
|
||||
loadMore = () => {
|
||||
refresh = () => {
|
||||
const {
|
||||
spotifyActions: {
|
||||
getMore,
|
||||
uri,
|
||||
uiActions: {
|
||||
hideContextMenu,
|
||||
},
|
||||
category: {
|
||||
playlists_more,
|
||||
},
|
||||
match: {
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
coreActions: {
|
||||
loadItem,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
getMore(
|
||||
playlists_more,
|
||||
null,
|
||||
{
|
||||
type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED_MORE',
|
||||
uri: `category:${id}`,
|
||||
},
|
||||
);
|
||||
hideContextMenu();
|
||||
loadItem(uri, { forceRefetch: true });
|
||||
}
|
||||
|
||||
render = () => {
|
||||
const {
|
||||
category: categoryProp,
|
||||
category,
|
||||
playlists,
|
||||
load_queue,
|
||||
loading,
|
||||
uiActions,
|
||||
uri,
|
||||
} = this.props;
|
||||
|
||||
if (isLoading(load_queue, ['spotify_browse/categories/'])) {
|
||||
if (loading) {
|
||||
return <Loader body loading />;
|
||||
}
|
||||
if (!category) {
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header>
|
||||
<Icon name="mood" type="material" />
|
||||
{(categoryProp ? categoryProp.name : i18n('discover.category.category'))}
|
||||
</Header>
|
||||
<Loader body loading />
|
||||
</div>
|
||||
<ErrorMessage type="not-found" title="Not found">
|
||||
<p>
|
||||
<I18n path="errors.uri_not_found" uri={uri} />
|
||||
</p>
|
||||
</ErrorMessage>
|
||||
);
|
||||
}
|
||||
|
||||
if (!categoryProp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const category = collate(categoryProp, { playlists });
|
||||
const options = (
|
||||
<Button
|
||||
noHover
|
||||
onClick={this.refresh}
|
||||
tracking={{ category: 'DiscoverCategory', action: 'Refresh' }}
|
||||
>
|
||||
<Icon name="refresh" />
|
||||
<I18n path="actions.refresh" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header uiActions={uiActions}>
|
||||
<Header uiActions={uiActions} options={options}>
|
||||
<Icon name="mood" type="material" />
|
||||
{category.name}
|
||||
</Header>
|
||||
<div className="content-wrapper">
|
||||
<section className="grid-wrapper">
|
||||
<PlaylistGrid playlists={category.playlists} />
|
||||
<PlaylistGrid playlists={playlists} />
|
||||
</section>
|
||||
<LazyLoadListener
|
||||
loadKey={category.playlists_more}
|
||||
showLoader={category.playlists_more}
|
||||
loadMore={this.loadMore}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
load_queue: state.ui.load_queue,
|
||||
playlists: state.core.playlists,
|
||||
category: (state.spotify.categories && state.spotify.categories[`category:${ownProps.match.params.id}`] !== undefined ? state.spotify.categories[`category:${ownProps.match.params.id}`] : false),
|
||||
});
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const uri = decodeURIComponent(ownProps.match.params.uri);
|
||||
const loadingSelector = makeLoadingSelector([`(.*)${uri}(.*)`]);
|
||||
const categorySelector = makeItemSelector(uri);
|
||||
const category = categorySelector(state);
|
||||
let playlists = null;
|
||||
if (category && category.playlists_uris) {
|
||||
const playlistsSelector = makeItemSelector(category.playlists_uris);
|
||||
playlists = playlistsSelector(state);
|
||||
}
|
||||
|
||||
return {
|
||||
uri,
|
||||
loading: loadingSelector(state),
|
||||
playlists,
|
||||
category,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
uiActions: bindActionCreators(uiActions, dispatch),
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user