Upgrading other playlist views to use playlist providers selector
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
18
mopidy_iris/static/app.min.js
vendored
18
mopidy_iris/static/app.min.js
vendored
File diff suppressed because one or more lines are too long
@ -115,7 +115,7 @@
|
|||||||
|
|
||||||
// Release details
|
// Release details
|
||||||
// These are automatically injected to built HTML
|
// These are automatically injected to built HTML
|
||||||
var build = "1619945300";
|
var build = "1620206341";
|
||||||
var version = "3.57.6";
|
var version = "3.57.6";
|
||||||
|
|
||||||
// Construct the script tag
|
// Construct the script tag
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
arrayOf,
|
arrayOf,
|
||||||
sortItems,
|
sortItems,
|
||||||
indexToArray,
|
|
||||||
} from '../util/arrays';
|
} from '../util/arrays';
|
||||||
import Link from './Link';
|
import Link from './Link';
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
@ -28,8 +27,18 @@ import * as pusherActions from '../services/pusher/actions';
|
|||||||
import * as mopidyActions from '../services/mopidy/actions';
|
import * as mopidyActions from '../services/mopidy/actions';
|
||||||
import * as lastfmActions from '../services/lastfm/actions';
|
import * as lastfmActions from '../services/lastfm/actions';
|
||||||
import * as spotifyActions from '../services/spotify/actions';
|
import * as spotifyActions from '../services/spotify/actions';
|
||||||
import { I18n, i18n } from '../locale';
|
import { I18n } from '../locale';
|
||||||
import { collate, encodeUri } from '../util/format';
|
import { encodeUri } from '../util/format';
|
||||||
|
import {
|
||||||
|
makeProcessProgressSelector,
|
||||||
|
makeProvidersSelector,
|
||||||
|
makeLibrarySelector,
|
||||||
|
} from '../util/selectors';
|
||||||
|
|
||||||
|
const processKeys = [
|
||||||
|
'MOPIDY_GET_LIBRARY_PLAYLISTS',
|
||||||
|
'SPOTIFY_GET_LIBRARY_PLAYLISTS',
|
||||||
|
];
|
||||||
|
|
||||||
class ContextMenu extends React.Component {
|
class ContextMenu extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -750,28 +759,21 @@ class ContextMenu extends React.Component {
|
|||||||
|
|
||||||
setSubmenu = (name) => {
|
setSubmenu = (name) => {
|
||||||
const { submenu } = this.state;
|
const { submenu } = this.state;
|
||||||
|
if (submenu !== name && name === 'add-to-playlist') {
|
||||||
|
this.getLibraries();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ submenu: name });
|
||||||
|
}
|
||||||
|
|
||||||
|
getLibraries = () => {
|
||||||
const {
|
const {
|
||||||
spotify_available,
|
providers,
|
||||||
spotify_library_playlists: {
|
|
||||||
items_uris: spotify_library,
|
|
||||||
},
|
|
||||||
mopidy_library_playlists: {
|
|
||||||
items_uris: mopidy_library,
|
|
||||||
},
|
|
||||||
coreActions: {
|
coreActions: {
|
||||||
loadLibrary,
|
loadLibrary,
|
||||||
},
|
},
|
||||||
} = this.props;
|
} = this.props;
|
||||||
if (submenu !== name && name === 'add-to-playlist') {
|
providers.forEach((provider) => loadLibrary(provider.uri, 'playlists'));
|
||||||
if (spotify_available && !spotify_library.length) {
|
|
||||||
loadLibrary('spotify:library:playlists', 'playlists');
|
|
||||||
}
|
|
||||||
if (!mopidy_library.length) {
|
|
||||||
loadLibrary('mopidy:library:playlists', 'playlists');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ submenu: name });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeSubmenu = () => this.setState({ submenu: null });
|
closeSubmenu = () => this.setState({ submenu: null });
|
||||||
@ -779,20 +781,13 @@ class ContextMenu extends React.Component {
|
|||||||
renderSubmenu = () => {
|
renderSubmenu = () => {
|
||||||
const { submenu } = this.state;
|
const { submenu } = this.state;
|
||||||
const {
|
const {
|
||||||
items,
|
playlists: allPlaylists,
|
||||||
spotify_library_playlists,
|
loading_progress,
|
||||||
mopidy_library_playlists,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let list = null;
|
let list = null;
|
||||||
const isLoading = false;
|
|
||||||
|
|
||||||
if (submenu === 'add-to-playlist') {
|
if (submenu === 'add-to-playlist') {
|
||||||
let playlists = [
|
let playlists = compact(allPlaylists.map((playlist) => {
|
||||||
...(collate(spotify_library_playlists, { items }).items || []),
|
|
||||||
...(collate(mopidy_library_playlists, { items }).items || []),
|
|
||||||
];
|
|
||||||
playlists = compact(playlists.map((playlist) => {
|
|
||||||
if (!playlist.can_edit) return null;
|
if (!playlist.can_edit) return null;
|
||||||
return {
|
return {
|
||||||
...playlist,
|
...playlist,
|
||||||
@ -847,7 +842,7 @@ class ContextMenu extends React.Component {
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{isLoading ? (
|
{loading_progress ? (
|
||||||
<div className="context-menu__item context-menu__item--loader">
|
<div className="context-menu__item context-menu__item--loader">
|
||||||
<Loader className="context-menu__item" mini loading />
|
<Loader className="context-menu__item" mini loading />
|
||||||
</div>
|
</div>
|
||||||
@ -1345,13 +1340,23 @@ class ContextMenu extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const librarySelector = makeLibrarySelector('playlists');
|
||||||
|
const providersSelector = makeProvidersSelector('playlists');
|
||||||
|
const processProgressSelector = makeProcessProgressSelector(processKeys);
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
|
providers: providersSelector(state),
|
||||||
|
playlists: librarySelector(state, 'playlists'),
|
||||||
|
loading_progress: processProgressSelector(state),
|
||||||
menu: state.ui.context_menu,
|
menu: state.ui.context_menu,
|
||||||
load_queue: state.ui.load_queue,
|
load_queue: state.ui.load_queue,
|
||||||
processes: state.ui.processes,
|
processes: state.ui.processes,
|
||||||
current_track: state.core.current_track,
|
current_track: state.core.current_track,
|
||||||
current_tracklist: state.core.current_tracklist,
|
current_tracklist: state.core.current_tracklist,
|
||||||
queue_metadata: state.core.queue_metadata,
|
queue_metadata: state.core.queue_metadata,
|
||||||
|
pinned: state.pusher.pinned,
|
||||||
|
lastfm_authorized: state.lastfm.authorization,
|
||||||
|
// TODO: Delete all of the below
|
||||||
spotify_available: state.spotify.access_token,
|
spotify_available: state.spotify.access_token,
|
||||||
spotify_library_playlists: state.core.libraries['spotify:library:playlists'] || { items_uris: [] },
|
spotify_library_playlists: state.core.libraries['spotify:library:playlists'] || { items_uris: [] },
|
||||||
spotify_library_artists: state.core.libraries['spotify:library:artists'] || { items_uris: [] },
|
spotify_library_artists: state.core.libraries['spotify:library:artists'] || { items_uris: [] },
|
||||||
@ -1361,8 +1366,6 @@ const mapStateToProps = (state) => ({
|
|||||||
mopidy_library_artists: state.core.libraries['mopidy:library:artists'] || { items_uris: [] },
|
mopidy_library_artists: state.core.libraries['mopidy:library:artists'] || { items_uris: [] },
|
||||||
mopidy_library_albums: state.core.libraries['mopidy:library:albums'] || { items_uris: [] },
|
mopidy_library_albums: state.core.libraries['mopidy:library:albums'] || { items_uris: [] },
|
||||||
items: state.core.items,
|
items: state.core.items,
|
||||||
pinned: state.pusher.pinned,
|
|
||||||
lastfm_authorized: state.lastfm.authorization,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
|||||||
@ -149,6 +149,8 @@ services:
|
|||||||
unlove: Unlove
|
unlove: Unlove
|
||||||
youtube:
|
youtube:
|
||||||
title: YouTube
|
title: YouTube
|
||||||
|
tidal:
|
||||||
|
title: Tidal
|
||||||
|
|
||||||
##
|
##
|
||||||
# Components
|
# Components
|
||||||
|
|||||||
@ -437,8 +437,11 @@ export function deletePlaylist(uri) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLibraryPlaylists() {
|
export function getLibraryPlaylists(uri) {
|
||||||
return { type: 'MOPIDY_GET_LIBRARY_PLAYLISTS' };
|
return {
|
||||||
|
type: 'MOPIDY_GET_LIBRARY_PLAYLISTS',
|
||||||
|
uri,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPlaylist(uri, options) {
|
export function getPlaylist(uri, options) {
|
||||||
|
|||||||
@ -1668,9 +1668,19 @@ const MopidyMiddleware = (function () {
|
|||||||
subdirectories.push(formatArtist({ ...item, loading: true }));
|
subdirectories.push(formatArtist({ ...item, loading: true }));
|
||||||
subdirectoryImagesToLoad.push(item.uri);
|
subdirectoryImagesToLoad.push(item.uri);
|
||||||
} else if (item.__model__ === 'Ref' && item.type === 'playlist') {
|
} else if (item.__model__ === 'Ref' && item.type === 'playlist') {
|
||||||
console.debug('playlist', { item })
|
// Tidal moods and genres incorrectly marked as Playlist type
|
||||||
subdirectories.push(formatPlaylist({ ...item, loading: true }));
|
if (
|
||||||
subdirectoryImagesToLoad.push(item.uri);
|
item.uri.indexOf('tidal:mood') > -1 ||
|
||||||
|
item.uri.indexOf('tidal:genre') > -1
|
||||||
|
) {
|
||||||
|
subdirectories.push({
|
||||||
|
...item,
|
||||||
|
type: 'directory',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
subdirectories.push(formatPlaylist({ ...item, loading: true }));
|
||||||
|
subdirectoryImagesToLoad.push(item.uri);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
subdirectories.push(item);
|
subdirectories.push(item);
|
||||||
}
|
}
|
||||||
@ -1770,7 +1780,7 @@ const MopidyMiddleware = (function () {
|
|||||||
if (!raw_response.length) {
|
if (!raw_response.length) {
|
||||||
store.dispatch(coreActions.libraryLoaded({
|
store.dispatch(coreActions.libraryLoaded({
|
||||||
uri: 'mopidy:library:artists',
|
uri: 'mopidy:library:artists',
|
||||||
type: action.uriType,
|
type: 'artists',
|
||||||
items_uris: [],
|
items_uris: [],
|
||||||
}));
|
}));
|
||||||
store.dispatch(uiActions.processFinished(action.type));
|
store.dispatch(uiActions.processFinished(action.type));
|
||||||
@ -1796,70 +1806,134 @@ const MopidyMiddleware = (function () {
|
|||||||
case 'MOPIDY_GET_LIBRARY_PLAYLISTS': {
|
case 'MOPIDY_GET_LIBRARY_PLAYLISTS': {
|
||||||
store.dispatch(uiActions.startProcess(action.type, { notification: false }));
|
store.dispatch(uiActions.startProcess(action.type, { notification: false }));
|
||||||
|
|
||||||
request(store, 'playlists.asList')
|
// Built-in playlist support works differently to other providers
|
||||||
.then((listResponse) => {
|
if (action.uri === 'm3u:playlists') {
|
||||||
// Remove any Spotify playlists. These will be handled by our Spotify API
|
request(store, 'playlists.asList')
|
||||||
const playlist_uris = arrayOf('uri', listResponse).filter(
|
.then((listResponse) => {
|
||||||
(playlistUri) => uriSource(playlistUri) !== 'spotify',
|
const libraryPlaylists = [];
|
||||||
);
|
const playlist_uris = arrayOf('uri', listResponse).filter(
|
||||||
const libraryPlaylists = [];
|
(pUri) => (pUri.indexOf('m3u') > -1),
|
||||||
|
);
|
||||||
|
store.dispatch(
|
||||||
|
uiActions.updateProcess(
|
||||||
|
action.type,
|
||||||
|
{
|
||||||
|
total: playlist_uris.length,
|
||||||
|
remaining: playlist_uris.length,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
store.dispatch(
|
if (playlist_uris.length) {
|
||||||
uiActions.updateProcess(
|
playlist_uris.forEach((uri, index) => {
|
||||||
action.type,
|
request(store, 'playlists.lookup', { uri })
|
||||||
{
|
.then((response) => {
|
||||||
total: playlist_uris.length,
|
if (response) {
|
||||||
remaining: playlist_uris.length,
|
libraryPlaylists.push(
|
||||||
},
|
formatPlaylist({
|
||||||
),
|
name: response.name,
|
||||||
);
|
uri: response.uri,
|
||||||
|
can_edit: uriSource(response.uri) === 'm3u',
|
||||||
|
last_modified: response.last_modified,
|
||||||
|
// By not including actual tracks they will be fetched when needed. We don't
|
||||||
|
// want these simple tracks because they don't contain duration, artist, etc.
|
||||||
|
tracks_total: response.tracks ? response.tracks.length : null,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (playlist_uris.length) {
|
store.dispatch(
|
||||||
playlist_uris.forEach((uri, index) => {
|
uiActions.updateProcess(
|
||||||
request(store, 'playlists.lookup', { uri })
|
action.type,
|
||||||
.then((response) => {
|
{
|
||||||
libraryPlaylists.push(
|
remaining: playlist_uris.length - index - 1,
|
||||||
formatPlaylist({
|
},
|
||||||
name: response.name,
|
),
|
||||||
uri: response.uri,
|
);
|
||||||
can_edit: uriSource(response.uri) === 'm3u',
|
|
||||||
last_modified: response.last_modified,
|
|
||||||
// By not including actual tracks they will be fetched when needed. We don't
|
|
||||||
// want these simple tracks because they don't contain duration, artist, etc.
|
|
||||||
tracks_total: response.tracks ? response.tracks.length : null,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
store.dispatch(
|
if (index === playlist_uris.length - 1) {
|
||||||
uiActions.updateProcess(
|
store.dispatch(coreActions.itemsLoaded(libraryPlaylists));
|
||||||
action.type,
|
store.dispatch(coreActions.libraryLoaded({
|
||||||
{
|
uri: action.uri,
|
||||||
remaining: playlist_uris.length - index - 1,
|
type: 'playlists',
|
||||||
},
|
items_uris: arrayOf('uri', libraryPlaylists),
|
||||||
),
|
}));
|
||||||
);
|
store.dispatch(uiActions.processFinished(action.type));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
store.dispatch(coreActions.libraryLoaded({
|
||||||
|
uri: action.uri,
|
||||||
|
type: 'playlists',
|
||||||
|
items_uris: [],
|
||||||
|
}));
|
||||||
|
store.dispatch(uiActions.stopLoading('mopidy:library:playlists'));
|
||||||
|
store.dispatch(uiActions.processFinished(action.type));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
request(store, 'library.browse', { uri: action.uri })
|
||||||
|
.then((browseResponse) => {
|
||||||
|
const libraryPlaylists = [];
|
||||||
|
|
||||||
if (index === playlist_uris.length - 1) {
|
store.dispatch(
|
||||||
store.dispatch(coreActions.itemsLoaded(libraryPlaylists));
|
uiActions.updateProcess(
|
||||||
store.dispatch(coreActions.libraryLoaded({
|
action.type,
|
||||||
uri: 'mopidy:library:playlists',
|
{
|
||||||
type: action.uriType,
|
total: browseResponse.length,
|
||||||
items_uris: arrayOf('uri', libraryPlaylists),
|
remaining: browseResponse.length,
|
||||||
}));
|
},
|
||||||
store.dispatch(uiActions.processFinished(action.type));
|
),
|
||||||
}
|
);
|
||||||
});
|
|
||||||
});
|
if (browseResponse.length) {
|
||||||
} else {
|
browseResponse.forEach((playlist, index) => {
|
||||||
store.dispatch(coreActions.libraryLoaded({
|
request(store, 'library.lookup', { uris: [playlist.uri] })
|
||||||
uri: action.uri,
|
.then((response) => {
|
||||||
type: 'playlists',
|
if (response) {
|
||||||
items_uris: [],
|
libraryPlaylists.push(
|
||||||
}));
|
formatPlaylist({
|
||||||
store.dispatch(uiActions.stopLoading('mopidy:library:playlists'));
|
name: playlist.name,
|
||||||
store.dispatch(uiActions.processFinished(action.type));
|
uri: playlist.uri,
|
||||||
}
|
can_edit: uriSource(playlist.uri) === 'm3u',
|
||||||
});
|
last_modified: playlist.last_modified,
|
||||||
|
tracks: formatTracks(response[playlist.uri]),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
uiActions.updateProcess(
|
||||||
|
action.type,
|
||||||
|
{
|
||||||
|
remaining: browseResponse.length - index - 1,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index === browseResponse.length - 1) {
|
||||||
|
store.dispatch(coreActions.itemsLoaded(libraryPlaylists));
|
||||||
|
store.dispatch(coreActions.libraryLoaded({
|
||||||
|
uri: action.uri,
|
||||||
|
type: 'playlists',
|
||||||
|
items_uris: arrayOf('uri', libraryPlaylists),
|
||||||
|
}));
|
||||||
|
store.dispatch(uiActions.processFinished(action.type));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
store.dispatch(coreActions.libraryLoaded({
|
||||||
|
uri: action.uri,
|
||||||
|
type: 'playlists',
|
||||||
|
items_uris: [],
|
||||||
|
}));
|
||||||
|
store.dispatch(uiActions.stopLoading('mopidy:library:playlists'));
|
||||||
|
store.dispatch(uiActions.processFinished(action.type));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1868,7 +1942,6 @@ const MopidyMiddleware = (function () {
|
|||||||
|
|
||||||
request(store, 'library.browse', { uri: action.uri })
|
request(store, 'library.browse', { uri: action.uri })
|
||||||
.then((browseResponse) => {
|
.then((browseResponse) => {
|
||||||
console.debug({ action, browseResponse })
|
|
||||||
const allUris = arrayOf('uri', browseResponse);
|
const allUris = arrayOf('uri', browseResponse);
|
||||||
|
|
||||||
store.dispatch(uiActions.updateProcess(
|
store.dispatch(uiActions.updateProcess(
|
||||||
|
|||||||
@ -99,6 +99,23 @@ const makeProcessProgressSelector = (keys) => createSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const providers = {
|
const providers = {
|
||||||
|
playlists: [
|
||||||
|
{
|
||||||
|
scheme: 'm3u:',
|
||||||
|
uri: 'm3u:playlists',
|
||||||
|
title: i18n('services.mopidy.local'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scheme: 'spotify:',
|
||||||
|
uri: 'spotify:library:playlists',
|
||||||
|
title: i18n('services.spotify.title'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scheme: 'tidal:',
|
||||||
|
uri: 'tidal:my_playlists',
|
||||||
|
title: i18n('services.tidal.title'),
|
||||||
|
},
|
||||||
|
],
|
||||||
albums: [
|
albums: [
|
||||||
{
|
{
|
||||||
scheme: 'local:',
|
scheme: 'local:',
|
||||||
@ -115,6 +132,11 @@ const providers = {
|
|||||||
uri: 'spotify:library:albums',
|
uri: 'spotify:library:albums',
|
||||||
title: i18n('services.spotify.title'),
|
title: i18n('services.spotify.title'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scheme: 'tidal:',
|
||||||
|
uri: 'tidal:my_albums',
|
||||||
|
title: i18n('services.tidal.title'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scheme: 'ytmusic:',
|
scheme: 'ytmusic:',
|
||||||
uri: 'ytmusic:album',
|
uri: 'ytmusic:album',
|
||||||
@ -137,6 +159,11 @@ const providers = {
|
|||||||
uri: 'spotify:library:artists',
|
uri: 'spotify:library:artists',
|
||||||
title: i18n('services.spotify.title'),
|
title: i18n('services.spotify.title'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scheme: 'tidal:',
|
||||||
|
uri: 'tidal:my_artists',
|
||||||
|
title: i18n('services.tidal.title'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scheme: 'ytmusic:',
|
scheme: 'ytmusic:',
|
||||||
uri: 'ytmusic:artist',
|
uri: 'ytmusic:artist',
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import Loader from '../../components/Loader';
|
|||||||
import {
|
import {
|
||||||
makeLibrarySelector,
|
makeLibrarySelector,
|
||||||
makeProcessProgressSelector,
|
makeProcessProgressSelector,
|
||||||
|
makeProvidersSelector,
|
||||||
getLibrarySource,
|
getLibrarySource,
|
||||||
} from '../../util/selectors';
|
} from '../../util/selectors';
|
||||||
|
|
||||||
@ -44,16 +45,14 @@ class LibraryPlaylists extends React.Component {
|
|||||||
|
|
||||||
setWindowTitle(i18n('library.playlists.title'));
|
setWindowTitle(i18n('library.playlists.title'));
|
||||||
|
|
||||||
this.getMopidyLibrary();
|
this.getLibraries();
|
||||||
this.getSpotifyLibrary();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh = () => {
|
refresh = () => {
|
||||||
const { uiActions: { hideContextMenu } } = this.props;
|
const { uiActions: { hideContextMenu } } = this.props;
|
||||||
|
|
||||||
hideContextMenu();
|
hideContextMenu();
|
||||||
this.getMopidyLibrary(true);
|
this.getLibraries(true);
|
||||||
this.getSpotifyLibrary(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelRefresh = () => {
|
cancelRefresh = () => {
|
||||||
@ -63,46 +62,35 @@ class LibraryPlaylists extends React.Component {
|
|||||||
cancelProcess(processKeys);
|
cancelProcess(processKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMopidyLibrary = (forceRefetch = false) => {
|
|
||||||
const {
|
|
||||||
source,
|
|
||||||
coreActions: {
|
|
||||||
loadLibrary,
|
|
||||||
},
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (source !== 'local' && source !== 'all') return;
|
|
||||||
|
|
||||||
loadLibrary('mopidy:library:playlists', 'playlists', { forceRefetch });
|
|
||||||
};
|
|
||||||
|
|
||||||
getSpotifyLibrary = (forceRefetch = false) => {
|
|
||||||
const {
|
|
||||||
source,
|
|
||||||
spotify_available,
|
|
||||||
coreActions: {
|
|
||||||
loadLibrary,
|
|
||||||
},
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!spotify_available) return;
|
|
||||||
if (source !== 'spotify' && source !== 'all') return;
|
|
||||||
|
|
||||||
loadLibrary('spotify:library:playlists', 'playlists', { forceRefetch });
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidUpdate = ({ source: prevSource }) => {
|
componentDidUpdate = ({ source: prevSource }) => {
|
||||||
const { source } = this.props;
|
const { source } = this.props;
|
||||||
|
|
||||||
if (source !== prevSource) {
|
if (source !== prevSource) {
|
||||||
this.getMopidyLibrary();
|
this.getLibraries();
|
||||||
this.getSpotifyLibrary();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLibraries = (forceRefetch = false) => {
|
||||||
|
const {
|
||||||
|
source,
|
||||||
|
providers,
|
||||||
|
coreActions: {
|
||||||
|
loadLibrary,
|
||||||
|
},
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let uris = [];
|
||||||
|
if (source === 'all') {
|
||||||
|
uris = providers.map((p) => p.uri);
|
||||||
|
} else {
|
||||||
|
uris.push(source);
|
||||||
|
}
|
||||||
|
uris.forEach((uri) => loadLibrary(uri, 'playlists', { forceRefetch }));
|
||||||
|
};
|
||||||
|
|
||||||
setSort(value) {
|
setSort(value) {
|
||||||
let reverse = false;
|
let reverse = false;
|
||||||
if (this.props.sort == value) reverse = !this.props.sort_reverse;
|
if (this.props.sort === value) reverse = !this.props.sort_reverse;
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
library_playlists_sort_reverse: reverse,
|
library_playlists_sort_reverse: reverse,
|
||||||
@ -169,7 +157,7 @@ class LibraryPlaylists extends React.Component {
|
|||||||
render = () => {
|
render = () => {
|
||||||
const {
|
const {
|
||||||
uiActions,
|
uiActions,
|
||||||
spotify_available,
|
providers,
|
||||||
source,
|
source,
|
||||||
sort,
|
sort,
|
||||||
sort_reverse,
|
sort_reverse,
|
||||||
@ -180,24 +168,6 @@ class LibraryPlaylists extends React.Component {
|
|||||||
filter,
|
filter,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const source_options = [
|
|
||||||
{
|
|
||||||
value: 'all',
|
|
||||||
label: i18n('fields.filters.all'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'local',
|
|
||||||
label: i18n('services.mopidy.local'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (spotify_available) {
|
|
||||||
source_options.push({
|
|
||||||
value: 'spotify',
|
|
||||||
label: i18n('services.spotify.title'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const view_options = [
|
const view_options = [
|
||||||
{
|
{
|
||||||
value: 'thumbnails',
|
value: 'thumbnails',
|
||||||
@ -269,7 +239,13 @@ class LibraryPlaylists extends React.Component {
|
|||||||
name={i18n('fields.source')}
|
name={i18n('fields.source')}
|
||||||
valueAsLabel
|
valueAsLabel
|
||||||
value={source}
|
value={source}
|
||||||
options={source_options}
|
options={[
|
||||||
|
{
|
||||||
|
value: 'all',
|
||||||
|
label: i18n('fields.filters.all'),
|
||||||
|
},
|
||||||
|
...providers.map((p) => ({ value: p.uri, label: p.title })),
|
||||||
|
]}
|
||||||
handleChange={(value) => { uiActions.set({ library_playlists_source: value }); uiActions.hideContextMenu(); }}
|
handleChange={(value) => { uiActions.set({ library_playlists_source: value }); uiActions.hideContextMenu(); }}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@ -307,6 +283,7 @@ class LibraryPlaylists extends React.Component {
|
|||||||
|
|
||||||
const librarySelector = makeLibrarySelector('playlists');
|
const librarySelector = makeLibrarySelector('playlists');
|
||||||
const processProgressSelector = makeProcessProgressSelector(processKeys);
|
const processProgressSelector = makeProcessProgressSelector(processKeys);
|
||||||
|
const providersSelector = makeProvidersSelector('playlists');
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const {
|
const {
|
||||||
spotify: {
|
spotify: {
|
||||||
@ -318,8 +295,7 @@ const mapStateToProps = (state) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
slim_mode: state.ui.slim_mode,
|
slim_mode: state.ui.slim_mode,
|
||||||
mopidy_uri_schemes: state.mopidy.uri_schemes,
|
providers: providersSelector(state),
|
||||||
spotify_available: state.spotify.access_token,
|
|
||||||
playlists: librarySelector(state, 'playlists'),
|
playlists: librarySelector(state, 'playlists'),
|
||||||
loading_progress: processProgressSelector(state),
|
loading_progress: processProgressSelector(state),
|
||||||
source: getLibrarySource(state, 'playlists'),
|
source: getLibrarySource(state, 'playlists'),
|
||||||
|
|||||||
@ -12,8 +12,12 @@ import * as spotifyActions from '../../services/spotify/actions';
|
|||||||
import { sourceIcon } from '../../util/helpers';
|
import { sourceIcon } from '../../util/helpers';
|
||||||
import { sortItems } from '../../util/arrays';
|
import { sortItems } from '../../util/arrays';
|
||||||
import { I18n, i18n } from '../../locale';
|
import { I18n, i18n } from '../../locale';
|
||||||
import { collate, decodeUri } from '../../util/format';
|
import { decodeUri } from '../../util/format';
|
||||||
import { makeProcessProgressSelector } from '../../util/selectors';
|
import {
|
||||||
|
makeProcessProgressSelector,
|
||||||
|
makeProvidersSelector,
|
||||||
|
makeLibrarySelector,
|
||||||
|
} from '../../util/selectors';
|
||||||
|
|
||||||
const processKeys = [
|
const processKeys = [
|
||||||
'MOPIDY_GET_LIBRARY_PLAYLISTS',
|
'MOPIDY_GET_LIBRARY_PLAYLISTS',
|
||||||
@ -23,32 +27,26 @@ const processKeys = [
|
|||||||
class AddToPlaylist extends React.Component {
|
class AddToPlaylist extends React.Component {
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
const {
|
const {
|
||||||
spotify_library: {
|
|
||||||
items_uris: spotify_library,
|
|
||||||
},
|
|
||||||
mopidy_library: {
|
|
||||||
items_uris: mopidy_library,
|
|
||||||
},
|
|
||||||
spotify_available,
|
|
||||||
coreActions: {
|
|
||||||
loadLibrary,
|
|
||||||
},
|
|
||||||
uiActions: {
|
uiActions: {
|
||||||
setWindowTitle,
|
setWindowTitle,
|
||||||
},
|
},
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!spotify_library.length && spotify_available) {
|
this.getLibraries();
|
||||||
loadLibrary('spotify:library:playlists', 'playlists');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mopidy_library.length) {
|
|
||||||
loadLibrary('mopidy:library:playlists', 'playlists');
|
|
||||||
}
|
|
||||||
|
|
||||||
setWindowTitle(i18n('modal.add_to_playlist.title'));
|
setWindowTitle(i18n('modal.add_to_playlist.title'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLibraries = () => {
|
||||||
|
const {
|
||||||
|
providers,
|
||||||
|
coreActions: {
|
||||||
|
loadLibrary,
|
||||||
|
},
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
providers.forEach((provider) => loadLibrary(provider.uri, 'playlists'));
|
||||||
|
};
|
||||||
|
|
||||||
playlistSelected = (playlist_uri) => {
|
playlistSelected = (playlist_uri) => {
|
||||||
const {
|
const {
|
||||||
coreActions: {
|
coreActions: {
|
||||||
@ -62,9 +60,7 @@ class AddToPlaylist extends React.Component {
|
|||||||
|
|
||||||
renderList = () => {
|
renderList = () => {
|
||||||
const {
|
const {
|
||||||
items,
|
playlists: allPlaylists,
|
||||||
spotify_library = { items_uris: [] },
|
|
||||||
mopidy_library = { items_uris: [] },
|
|
||||||
loading_progress,
|
loading_progress,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -72,11 +68,7 @@ class AddToPlaylist extends React.Component {
|
|||||||
return <Loader body loading progress={loading_progress} />;
|
return <Loader body loading progress={loading_progress} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let playlists = [
|
let playlists = allPlaylists.filter((playlist) => playlist.can_edit);
|
||||||
...(collate(spotify_library, { items }).items),
|
|
||||||
...(collate(mopidy_library, { items }).items),
|
|
||||||
].filter((playlist) => playlist.can_edit);
|
|
||||||
|
|
||||||
playlists = sortItems(playlists, 'name');
|
playlists = sortItems(playlists, 'name');
|
||||||
|
|
||||||
if (playlists.length > 0) {
|
if (playlists.length > 0) {
|
||||||
@ -128,6 +120,9 @@ class AddToPlaylist extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const librarySelector = makeLibrarySelector('playlists');
|
||||||
|
const processProgressSelector = makeProcessProgressSelector(processKeys);
|
||||||
|
const providersSelector = makeProvidersSelector('playlists');
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
const {
|
const {
|
||||||
spotify: {
|
spotify: {
|
||||||
@ -136,29 +131,18 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
} = {},
|
} = {},
|
||||||
access_token: spotify_available,
|
access_token: spotify_available,
|
||||||
},
|
},
|
||||||
core: {
|
|
||||||
playlists,
|
|
||||||
items,
|
|
||||||
libraries,
|
|
||||||
},
|
|
||||||
mopidy: {
|
|
||||||
uri_schemes: mopidy_uri_schemes,
|
|
||||||
},
|
|
||||||
} = state;
|
} = state;
|
||||||
const processProgressSelector = makeProcessProgressSelector(processKeys);
|
|
||||||
const unencodedUris = ownProps.match.params?.uris.split(',');
|
const unencodedUris = ownProps.match.params?.uris;
|
||||||
const uris = unencodedUris ? unencodedUris.map((uri) => decodeUri(uri)) : [];
|
const uris = unencodedUris ? decodeUri(unencodedUris).split(',') : [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uris,
|
uris,
|
||||||
mopidy_uri_schemes,
|
providers: providersSelector(state),
|
||||||
items,
|
playlists: librarySelector(state, 'playlists'),
|
||||||
mopidy_library: libraries['mopidy:library:playlists'] || { items_uris: [] },
|
|
||||||
spotify_library: libraries['spotify:library:playlists'] || { items_uris: [] },
|
|
||||||
spotify_available,
|
spotify_available,
|
||||||
loading_progress: processProgressSelector(state),
|
loading_progress: processProgressSelector(state),
|
||||||
me_id,
|
me_id,
|
||||||
playlists,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user