Loaders in Track view; Revisiting URI encoding again...
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
14
mopidy_iris/static/app.min.js
vendored
14
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 = "1610772376";
|
||||
var build = "1610955379";
|
||||
var version = "3.55.5";
|
||||
|
||||
// Construct the script tag
|
||||
|
||||
@ -552,7 +552,7 @@ class ContextMenu extends React.Component {
|
||||
hideContextMenu();
|
||||
|
||||
// note: we can only go to one artist (even if this item has multiple artists, just go to the first one)
|
||||
push(buildLink(items[0].artists[0].uri));
|
||||
push(buildLink(items[0].artists[0].uri, 'artist'));
|
||||
}
|
||||
|
||||
goToUser = () => {
|
||||
@ -570,7 +570,7 @@ class ContextMenu extends React.Component {
|
||||
|
||||
if (!items || items.length <= 0 || !items[0].user) return null;
|
||||
hideContextMenu();
|
||||
push(buildLink(items[0].user.uri));
|
||||
push(buildLink(items[0].user.uri, 'user'));
|
||||
}
|
||||
|
||||
goToTrack = () => {
|
||||
@ -588,7 +588,7 @@ class ContextMenu extends React.Component {
|
||||
|
||||
if (!uris) return null;
|
||||
hideContextMenu();
|
||||
push(buildLink(uris[0]));
|
||||
push(buildLink(uris[0], 'track'));
|
||||
}
|
||||
|
||||
copyURIs = () => {
|
||||
|
||||
@ -339,6 +339,7 @@ track:
|
||||
track_number: 'Track %{number}'
|
||||
unknown_album: Unknown album
|
||||
want_lyrics: 'Want track lyrics? Authorize Genius under '
|
||||
lyrics: Lyrics
|
||||
settings:
|
||||
title: Settings
|
||||
help: Help
|
||||
|
||||
@ -166,7 +166,7 @@ export function getTrackLyrics(uri, path) {
|
||||
|
||||
// add reference to loader queue
|
||||
const loader_key = generateGuid();
|
||||
dispatch(uiActions.startLoading(loader_key, 'genius_get_lyrics'));
|
||||
dispatch(uiActions.startLoading(loader_key, `genius_get_lyrics_${uri}`));
|
||||
|
||||
function status(response) {
|
||||
dispatch(uiActions.stopLoading(loader_key));
|
||||
@ -221,14 +221,12 @@ export function findTrackLyrics(uri) {
|
||||
const selector = makeItemSelector(uri);
|
||||
const track = selector(getState());
|
||||
if (!track || !track.artists) {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not get Genius lyrics',
|
||||
{},
|
||||
'Not in index or has no artists',
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
const loader_key = generateGuid();
|
||||
dispatch(uiActions.startLoading(loader_key, `genius_find_lyrics_${uri}`));
|
||||
|
||||
let query = '';
|
||||
query += `${track.artists[0].name} `;
|
||||
query += track.name;
|
||||
@ -257,9 +255,11 @@ export function findTrackLyrics(uri) {
|
||||
// Immediately go and get the first result's lyrics
|
||||
const lyrics_result = lyrics_results[0];
|
||||
dispatch(getTrackLyrics(track.uri, lyrics_result.path));
|
||||
}
|
||||
};
|
||||
dispatch(uiActions.stopLoading(loader_key));
|
||||
},
|
||||
(error) => {
|
||||
dispatch(uiActions.stopLoading(loader_key));
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not search for track lyrics',
|
||||
error,
|
||||
|
||||
@ -41,12 +41,20 @@ const geniusActions = require('../../services/genius/actions');
|
||||
* @param cache boolean
|
||||
* @return Promise
|
||||
* */
|
||||
const request = (dispatch, getState, endpoint, method = 'GET', data = false, cache = false) => {
|
||||
const request = ({
|
||||
dispatch,
|
||||
getState,
|
||||
endpoint,
|
||||
method = 'GET',
|
||||
data,
|
||||
uri,
|
||||
}) => {
|
||||
// Add reference to loader queue
|
||||
// We do this straight away so that even if we're refreshing the token, it still registers as
|
||||
// loading said endpoint
|
||||
const loader_key = generateGuid();
|
||||
dispatch(uiActions.startLoading(loader_key, `spotify_${endpoint}`));
|
||||
const loaderId = generateGuid();
|
||||
const loaderKey = uri ? `spotify_uri_${uri}` : `spotify_${endpoint}`;
|
||||
dispatch(uiActions.startLoading(loaderId, loaderKey));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getToken(dispatch, getState)
|
||||
@ -78,7 +86,7 @@ const request = (dispatch, getState, endpoint, method = 'GET', data = false, cac
|
||||
}
|
||||
|
||||
function status(response) {
|
||||
dispatch(uiActions.stopLoading(loader_key));
|
||||
dispatch(uiActions.stopLoading(loaderId));
|
||||
|
||||
// TODO: Rate limiting
|
||||
if (response.status === 429) {
|
||||
@ -306,7 +314,7 @@ export function importAuthorization(authorization) {
|
||||
* */
|
||||
export function getMe() {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, 'me')
|
||||
request({ dispatch, getState, endpoint: 'me' })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
@ -329,7 +337,7 @@ export function getTrack(uri, { forceRefetch, full }) {
|
||||
let endpoint = `tracks/${getFromUri('trackid', uri)}`;
|
||||
if (forceRefetch) endpoint += `?refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint, uri })
|
||||
.then(
|
||||
(response) => {
|
||||
const track = formatTrack(response);
|
||||
@ -372,7 +380,7 @@ export function getFeaturedPlaylists(forceRefetch = false) {
|
||||
endpoint += `×tamp=${timestamp}`;
|
||||
if (forceRefetch) endpoint += `&refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
const playlists = response.playlists.items.map(
|
||||
@ -410,7 +418,7 @@ export function getCategories() {
|
||||
endpoint += `&country=${getState().spotify.country}`;
|
||||
endpoint += `&locale=${getState().spotify.locale}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
@ -442,14 +450,17 @@ export function getCategory(uri, { forceRefetch } = {}) {
|
||||
plEndpoint += `&locale=${getState().spotify.locale}`;
|
||||
if (forceRefetch) plEndpoint += `&refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
const category = formatCategory(response);
|
||||
|
||||
let playlists = [];
|
||||
const fetchPlaylists = (plEndpoint) => request(dispatch, getState, plEndpoint)
|
||||
.then((response) => {
|
||||
const fetchPlaylists = (plEndpoint) => request({
|
||||
dispatch,
|
||||
getState,
|
||||
endpoint: plEndpoint,
|
||||
}).then((response) => {
|
||||
playlists = [...playlists, ...formatPlaylists(response.playlists.items)];
|
||||
if (response.playlists.next) {
|
||||
fetchPlaylists(response.playlists.next);
|
||||
@ -481,7 +492,7 @@ export function getNewReleases(forceRefetch = false) {
|
||||
endpoint += `&country=${getState().spotify.country}`;
|
||||
if (forceRefetch) endpoint += `&refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
@ -499,9 +510,9 @@ export function getNewReleases(forceRefetch = false) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getURL(url, action_name, key = false) {
|
||||
export function getURL(endpoint, action_name, key = false) {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, url)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
@ -520,9 +531,9 @@ export function getURL(url, action_name, key = false) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getMore(url, core_action = null, custom_action = null, extra_data = {}) {
|
||||
export function getMore(endpoint, core_action = null, custom_action = null, extra_data = {}) {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, url)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
if (core_action) {
|
||||
@ -569,13 +580,13 @@ export function getSearchResults({ type, term }, limit = 50, offset = 0) {
|
||||
typeString = 'album,artist,playlist,track';
|
||||
}
|
||||
|
||||
let url = `search?q=${term}`;
|
||||
url += `&type=${typeString}`;
|
||||
url += `&country=${getState().spotify.country}`;
|
||||
url += `&limit=${limit}`;
|
||||
url += `&offset=${offset}`;
|
||||
let endpoint = `search?q=${term}`;
|
||||
endpoint += `&type=${typeString}`;
|
||||
endpoint += `&country=${getState().spotify.country}`;
|
||||
endpoint += `&limit=${limit}`;
|
||||
endpoint += `&offset=${offset}`;
|
||||
|
||||
request(dispatch, getState, url)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
if (response.tracks !== undefined) {
|
||||
@ -640,7 +651,7 @@ export function getAutocompleteResults(field_id, query, types = ['album', 'artis
|
||||
endpoint += `&type=${types.join(',')}`;
|
||||
endpoint += `&country=${getState().spotify.country}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
const genres = [];
|
||||
@ -767,7 +778,7 @@ export function following(uri, method = 'GET') {
|
||||
break;
|
||||
}
|
||||
|
||||
request(dispatch, getState, endpoint, method, data)
|
||||
request({ dispatch, getState, endpoint, method, data })
|
||||
.then(
|
||||
(response) => {
|
||||
if (Array.isArray(response) && response.length > 0) {
|
||||
@ -812,7 +823,7 @@ export function resolveRadioSeeds(radio) {
|
||||
artist_ids += getFromUri('artistid', radio.seed_artists[i]);
|
||||
}
|
||||
|
||||
request(dispatch, getState, `artists?ids=${artist_ids}`)
|
||||
request({ dispatch, getState, endpoint: `artists?ids=${artist_ids}` })
|
||||
.then(
|
||||
(response) => {
|
||||
if (response && response.artists) {
|
||||
@ -840,7 +851,7 @@ export function resolveRadioSeeds(radio) {
|
||||
track_ids += getFromUri('trackid', radio.seed_tracks[i]);
|
||||
}
|
||||
|
||||
request(dispatch, getState, `tracks?ids=${track_ids}`)
|
||||
request({ dispatch, getState, endpoint: `tracks?ids=${track_ids}` })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
@ -918,7 +929,7 @@ export function getRecommendations(uris = [], limit = 20, tunabilities = null) {
|
||||
}
|
||||
}
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
const tracks = Object.assign([], formatTracks(response.tracks));
|
||||
@ -991,7 +1002,7 @@ export function getRecommendations(uris = [], limit = 20, tunabilities = null) {
|
||||
* */
|
||||
export function getGenres() {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, 'recommendations/available-genre-seeds')
|
||||
request({ dispatch, getState, endpoint: 'recommendations/available-genre-seeds' })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
@ -1026,7 +1037,7 @@ export function getArtist(uri, { full, forceRefetch }) {
|
||||
let endpoint = `artists/${getFromUri('artistid', uri)}`;
|
||||
if (forceRefetch) endpoint += `?refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint, 'GET', false, true)
|
||||
request({ dispatch, getState, endpoint, uri })
|
||||
.then(
|
||||
(response) => {
|
||||
const artist = formatArtist(response);
|
||||
@ -1040,7 +1051,7 @@ export function getArtist(uri, { full, forceRefetch }) {
|
||||
|
||||
// All albums (gets all pages, may take some time to iterate them all)
|
||||
let albums = [];
|
||||
const fetchAlbums = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetchAlbums = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
albums = [...albums, ...formatAlbums(response.items)];
|
||||
if (response.next) {
|
||||
@ -1056,7 +1067,10 @@ export function getArtist(uri, { full, forceRefetch }) {
|
||||
fetchAlbums(`artists/${getFromUri('artistid', uri)}/albums?limit=50&include_groups=album,single&market=${getState().spotify.country}`);
|
||||
|
||||
// Get top tracks
|
||||
request(dispatch, getState, `artists/${getFromUri('artistid', uri)}/top-tracks?country=${getState().spotify.country}`)
|
||||
let tracksEndpoint = `artists/${getFromUri('artistid', uri)}`;
|
||||
tracksEndpoint += `/top-tracks?country=${getState().spotify.country}`;
|
||||
if (forceRefetch) tracksEndpoint += `&refetch=${Date.now()}`;
|
||||
request({ dispatch, getState, endpoint: tracksEndpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch(coreActions.itemLoaded({
|
||||
@ -1067,7 +1081,9 @@ export function getArtist(uri, { full, forceRefetch }) {
|
||||
);
|
||||
|
||||
// Related artists
|
||||
request(dispatch, getState, `artists/${getFromUri('artistid', uri)}/related-artists`)
|
||||
let relatedEndpoint = `artists/${getFromUri('artistid', uri)}/related-artists`;
|
||||
if (forceRefetch) relatedEndpoint += `?refetch=${Date.now()}`;
|
||||
request({ dispatch, getState, endpoint: relatedEndpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch(coreActions.itemLoaded({
|
||||
@ -1083,7 +1099,7 @@ export function getArtist(uri, { full, forceRefetch }) {
|
||||
// Used to get images for non-Spotify artists
|
||||
export function getArtistImages(artist) {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, `search?q=${artist.name}&type=artist`)
|
||||
request({ dispatch, getState, endpoint: `search?q=${artist.name}&type=artist` })
|
||||
.then(response => {
|
||||
if (response.artists.items.length > 0) {
|
||||
dispatch(coreActions.itemLoaded({
|
||||
@ -1108,16 +1124,15 @@ export function playArtistTopTracks(uri) {
|
||||
const uris = arrayOf('uri', artist.tracks);
|
||||
dispatch(mopidyActions.playURIs(uris, uri));
|
||||
} else {
|
||||
request(
|
||||
dispatch,
|
||||
getState,
|
||||
`artists/${getFromUri('artistid', uri)}/top-tracks?country=${getState().spotify.country}`,
|
||||
).then(
|
||||
(response) => {
|
||||
const uris = arrayOf('uri', response.tracks);
|
||||
dispatch(mopidyActions.playURIs(uris, uri));
|
||||
},
|
||||
);
|
||||
let endpoint = `artists/${getFromUri('artistid', uri)}`;
|
||||
endpoint += `/top-tracks?country=${getState().spotify.country}`;
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
const uris = arrayOf('uri', response.tracks);
|
||||
dispatch(mopidyActions.playURIs(uris, uri));
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1134,7 +1149,7 @@ export function getUser(uri, { full, forceRefetch }) {
|
||||
let endpoint = `users/${userId}`;
|
||||
if (forceRefetch) endpoint += `?refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint, 'GET', false, true)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
const user = formatUser(response);
|
||||
@ -1144,7 +1159,7 @@ export function getUser(uri, { full, forceRefetch }) {
|
||||
|
||||
if (full) {
|
||||
let playlists = [];
|
||||
const fetchPlaylists = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetchPlaylists = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
playlists = [...playlists, ...formatPlaylists(response.items)];
|
||||
if (response.next) {
|
||||
@ -1180,7 +1195,7 @@ export function getAlbum(uri, { full, forceRefetch }) {
|
||||
let endpoint = `albums/${getFromUri('albumid', uri)}`;
|
||||
if (forceRefetch) endpoint += `?refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch(coreActions.itemLoaded({
|
||||
@ -1189,7 +1204,7 @@ export function getAlbum(uri, { full, forceRefetch }) {
|
||||
|
||||
if (full) {
|
||||
let tracks = formatTracks(response.tracks.items);
|
||||
const fetchTracks = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetchTracks = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
tracks = [...tracks, ...formatTracks(response.items)];
|
||||
if (response.next) {
|
||||
@ -1239,7 +1254,7 @@ export function createPlaylist(name, description, is_public, is_collaborative) {
|
||||
},
|
||||
} = getState();
|
||||
|
||||
request(dispatch, getState, `users/${meId}/playlists/`, 'POST', data)
|
||||
request({ dispatch, getState, endpoint: `users/${meId}/playlists/`, method: 'POST', data })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch(coreActions.itemLoaded({
|
||||
@ -1291,28 +1306,32 @@ export function savePlaylist(uri, name, description, is_public, is_collaborative
|
||||
|
||||
// Save the image
|
||||
if (image) {
|
||||
request(dispatch, getState, `users/${meId}/playlists/${getFromUri('playlistid', uri)}/images`, 'PUT', image)
|
||||
.then(
|
||||
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_UPDATED',
|
||||
key: uri,
|
||||
playlist: {
|
||||
name,
|
||||
public: is_public,
|
||||
collaborative: is_collaborative,
|
||||
description,
|
||||
},
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not save image',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
request({
|
||||
dispatch,
|
||||
getState,
|
||||
endpoint: `users/${meId}/playlists/${getFromUri('playlistid', uri)}/images`,
|
||||
method: 'PUT',
|
||||
data: image,
|
||||
}).then(
|
||||
() => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_UPDATED',
|
||||
key: uri,
|
||||
playlist: {
|
||||
name,
|
||||
public: is_public,
|
||||
collaborative: is_collaborative,
|
||||
description,
|
||||
},
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not save image',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
|
||||
// No image, so we're done here
|
||||
} else {
|
||||
@ -1346,7 +1365,7 @@ export function getPlaylistTracks(uri, { forceRefetch, callbackAction } = {}) {
|
||||
|
||||
let tracks = [];
|
||||
|
||||
const fetchTracks = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetchTracks = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
tracks = [...tracks, ...formatTracks(response.items)];
|
||||
if (response.next) {
|
||||
@ -1401,7 +1420,7 @@ export function getPlaylist(uri, options) {
|
||||
endpoint += `?market=${getState().spotify.country}`;
|
||||
if (forceRefetch) endpoint += `&refetch=${Date.now()}`;
|
||||
|
||||
request(dispatch, getState, endpoint)
|
||||
request({ dispatch, getState, endpoint })
|
||||
.then(
|
||||
(response) => {
|
||||
let description = null;
|
||||
@ -1438,35 +1457,40 @@ export function getPlaylist(uri, options) {
|
||||
|
||||
export function addTracksToPlaylist(uri, tracks_uris) {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, `playlists/${getFromUri('playlistid', uri)}/tracks`, 'POST', { uris: tracks_uris })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_ADDED',
|
||||
key: uri,
|
||||
tracks_uris,
|
||||
snapshot_id: response.snapshot_id,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not add tracks to playlist',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
request({
|
||||
dispatch,
|
||||
getState,
|
||||
endpoint: `playlists/${getFromUri('playlistid', uri)}/tracks`,
|
||||
method: 'POST',
|
||||
data: { uris: tracks_uris },
|
||||
}).then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_ADDED',
|
||||
key: uri,
|
||||
tracks_uris,
|
||||
snapshot_id: response.snapshot_id,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not add tracks to playlist',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteTracksFromPlaylist(uri, snapshot_id, tracks_indexes) {
|
||||
return (dispatch, getState) => {
|
||||
request(
|
||||
request({
|
||||
dispatch,
|
||||
getState,
|
||||
`playlists/${getFromUri('playlistid', uri)}/tracks`,
|
||||
'DELETE',
|
||||
{ snapshot_id, positions: tracks_indexes },
|
||||
).then(
|
||||
endpoint: `playlists/${getFromUri('playlistid', uri)}/tracks`,
|
||||
method: 'DELETE',
|
||||
data: { snapshot_id, positions: tracks_indexes },
|
||||
}).then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_REMOVED',
|
||||
@ -1487,31 +1511,32 @@ export function deleteTracksFromPlaylist(uri, snapshot_id, tracks_indexes) {
|
||||
|
||||
export function reorderPlaylistTracks(uri, range_start, range_length, insert_before, snapshot_id) {
|
||||
return (dispatch, getState) => {
|
||||
request(
|
||||
request({
|
||||
dispatch,
|
||||
getState,
|
||||
`playlists/${getFromUri('playlistid', uri)}/tracks`, 'PUT',
|
||||
{
|
||||
endpoint: `playlists/${getFromUri('playlistid', uri)}/tracks`,
|
||||
method: 'PUT',
|
||||
data: {
|
||||
uri, range_start, range_length, insert_before, snapshot_id,
|
||||
},
|
||||
).then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_REORDERED',
|
||||
key: uri,
|
||||
range_start,
|
||||
range_length,
|
||||
insert_before,
|
||||
snapshot_id: response.snapshot_id,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not reorder playlist tracks',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
}).then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_REORDERED',
|
||||
key: uri,
|
||||
range_start,
|
||||
range_length,
|
||||
insert_before,
|
||||
snapshot_id: response.snapshot_id,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not reorder playlist tracks',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1540,7 +1565,7 @@ export function getLibraryPlaylists(forceRefetch) {
|
||||
dispatch(uiActions.startProcess(processKey, { notification: false }));
|
||||
|
||||
let libraryItems = [];
|
||||
const fetch = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetch = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
const processor = getState().ui.processes[processKey];
|
||||
if (processor && processor.status === 'cancelling') {
|
||||
@ -1584,7 +1609,7 @@ export function getLibraryAlbums(forceRefetch) {
|
||||
dispatch(uiActions.startProcess(processKey, { notification: false }));
|
||||
|
||||
let libraryItems = [];
|
||||
const fetch = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetch = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
const processor = getState().ui.processes[processKey];
|
||||
if (processor && processor.status === 'cancelling') {
|
||||
@ -1628,7 +1653,7 @@ export function getLibraryArtists(forceRefetch) {
|
||||
dispatch(uiActions.startProcess(processKey, { notification: false }));
|
||||
|
||||
let libraryItems = [];
|
||||
const fetch = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetch = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
const processor = getState().ui.processes[processKey];
|
||||
if (processor && processor.status === 'cancelling') {
|
||||
@ -1652,8 +1677,8 @@ export function getLibraryArtists(forceRefetch) {
|
||||
}),
|
||||
);
|
||||
libraryItems = [...libraryItems, ...items];
|
||||
if (response.next) {
|
||||
fetch(`${response.next}${forceRefetch ? `&refetch=${Date.now()}` : ''}`);
|
||||
if (response.artists.next) {
|
||||
fetch(`${response.artists.next}${forceRefetch ? `&refetch=${Date.now()}` : ''}`);
|
||||
} else {
|
||||
dispatch(uiActions.processFinished(processKey));
|
||||
dispatch(coreActions.itemsLoaded(libraryItems));
|
||||
@ -1674,7 +1699,7 @@ export function getLibraryTracks(forceRefetch) {
|
||||
dispatch(uiActions.startProcess(processKey, { notification: false }));
|
||||
|
||||
let libraryItems = [];
|
||||
const fetch = (endpoint) => request(dispatch, getState, endpoint)
|
||||
const fetch = (endpoint) => request({ dispatch, getState, endpoint })
|
||||
.then((response) => {
|
||||
const processor = getState().ui.processes[processKey];
|
||||
if (processor && processor.status === 'cancelling') {
|
||||
|
||||
@ -300,10 +300,24 @@ const encodeUri = (uri) => {
|
||||
* @param {String} rawUri
|
||||
*/
|
||||
const decodeUri = (rawUri) => {
|
||||
const uri = decodeURIComponent(rawUri);
|
||||
let uri = decodeURIComponent(rawUri);
|
||||
const source = uriSource(uri);
|
||||
const type = uriType(uri);
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* Why the hell is this so difficult?
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Reinstate slashes for the Mopidy-Local structure
|
||||
//uri = uri.replace(/%2F/g, '/');
|
||||
|
||||
// Ensure all ':' are uri encoded to lowercase
|
||||
//uri = uri.replace(/%3A/g, '%3a');
|
||||
return uri;
|
||||
|
||||
// Escape unreserved characters (RFC 3986)
|
||||
// https://stackoverflow.com/questions/18251399/why-doesnt-encodeuricomponent-encode-single-quotes-apostrophes
|
||||
let id = getFromUri(`${type}id`, uri);
|
||||
|
||||
@ -200,6 +200,7 @@ const sourceIcon = function (uri, source = null) {
|
||||
switch (source) {
|
||||
case 'local':
|
||||
case 'm3u':
|
||||
case 'file':
|
||||
return 'folder';
|
||||
|
||||
case 'gmusic':
|
||||
@ -329,11 +330,11 @@ const getFromUri = (element, uri) => {
|
||||
* to direct the user (eg /track/local:track:1235.mp3)
|
||||
*
|
||||
* @param $uri = String
|
||||
* @param $type = String, optional
|
||||
* @return String
|
||||
* */
|
||||
const buildLink = (uri) => {
|
||||
const type = uriType(uri);
|
||||
let link = `/${type}/`;
|
||||
const buildLink = (uri, type = null) => {
|
||||
let link = `/${type || uriType(uri)}/`;
|
||||
|
||||
// Encode the whole URI as though it's a component. This makes it URL friendly for
|
||||
// all Mopidy backends (some use URIs like local:track:http://rss.com/stuff.mp3) which
|
||||
@ -404,7 +405,6 @@ const isLoading = function (load_queue = {}, keys = []) {
|
||||
return matches.length > 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Is this app running from the hosted instance?
|
||||
* For example the GitHub-hosted UI
|
||||
|
||||
@ -124,10 +124,13 @@ class Track extends React.Component {
|
||||
renderLyricsSelector = () => {
|
||||
const {
|
||||
track,
|
||||
geniusActions: { getTrackLyrics },
|
||||
geniusActions: {
|
||||
getTrackLyrics,
|
||||
},
|
||||
genius_authorized,
|
||||
} = this.props;
|
||||
|
||||
if (track.lyrics_results === undefined || track.lyrics_results === null) {
|
||||
if (!genius_authorized || track.lyrics_results === undefined || track.lyrics_results === null) {
|
||||
return null;
|
||||
} if (track.lyrics_results.length <= 0) {
|
||||
return (
|
||||
@ -165,38 +168,30 @@ class Track extends React.Component {
|
||||
|
||||
renderLyrics = () => {
|
||||
const {
|
||||
load_queue,
|
||||
track: {
|
||||
lyrics,
|
||||
lyrics_path,
|
||||
} = {},
|
||||
genius_authorized,
|
||||
loadingLyrics,
|
||||
} = this.props;
|
||||
|
||||
if (isLoading(load_queue, ['genius_'])) {
|
||||
return (
|
||||
<div className="lyrics">
|
||||
<Loader body loading />
|
||||
</div>
|
||||
);
|
||||
} if (lyrics) {
|
||||
return (
|
||||
<div className="lyrics">
|
||||
<div className="content" dangerouslySetInnerHTML={{ __html: lyrics }} />
|
||||
<div className="origin mid_grey-text">
|
||||
<I18n path="track.lyrics_origin" />
|
||||
<a
|
||||
href={`https://genius.com${lyrics_path}`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{`https://genius.com${lyrics_path}`}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!lyrics || !genius_authorized || loadingLyrics) return null;
|
||||
|
||||
return (
|
||||
<ErrorMessage type="not-found" title={i18n('errors.no_results')} />
|
||||
<div className="lyrics">
|
||||
<div className="content" dangerouslySetInnerHTML={{ __html: lyrics }} />
|
||||
<div className="origin mid_grey-text">
|
||||
<I18n path="track.lyrics_origin" />
|
||||
<a
|
||||
href={`https://genius.com${lyrics_path}`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{`https://genius.com${lyrics_path}`}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -208,6 +203,7 @@ class Track extends React.Component {
|
||||
slim_mode,
|
||||
uiActions,
|
||||
genius_authorized,
|
||||
loadingLyrics,
|
||||
} = this.props;
|
||||
|
||||
if (loading) {
|
||||
@ -292,6 +288,11 @@ class Track extends React.Component {
|
||||
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
|
||||
</div>
|
||||
|
||||
<h4>
|
||||
<I18n path="track.lyrics" />
|
||||
{loadingLyrics && <Loader loading mini />}
|
||||
</h4>
|
||||
|
||||
{!genius_authorized && (
|
||||
<p className="no-results">
|
||||
<I18n path="track.want_lyrics" />
|
||||
@ -301,8 +302,8 @@ class Track extends React.Component {
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
{genius_authorized && this.renderLyricsSelector()}
|
||||
{genius_authorized && this.renderLyrics()}
|
||||
{this.renderLyricsSelector()}
|
||||
{this.renderLyrics()}
|
||||
|
||||
</div>
|
||||
);
|
||||
@ -311,13 +312,15 @@ class Track extends React.Component {
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const uri = decodeUri(ownProps.match.params.uri);
|
||||
const loadingSelector = makeLoadingSelector([`(.*)${uri}(.*)`]);
|
||||
const loadingSelector = makeLoadingSelector([`^(?!genius)(.*)${uri}(.*)$`]);
|
||||
const loadingLyricsSelector = makeLoadingSelector([`^genius_(.*)lyrics_${uri}$`]);
|
||||
const trackSelector = makeItemSelector(uri);
|
||||
|
||||
return {
|
||||
uri,
|
||||
slim_mode: state.ui.slim_mode,
|
||||
loading: loadingSelector(state),
|
||||
loadingLyrics: loadingLyricsSelector(state),
|
||||
track: trackSelector(state),
|
||||
spotify_library_albums: state.spotify.library_albums,
|
||||
local_library_albums: state.mopidy.library_albums,
|
||||
|
||||
@ -51,13 +51,13 @@ class DiscoverCategory extends React.Component {
|
||||
const {
|
||||
uri,
|
||||
category,
|
||||
coreActions: {
|
||||
loadCategory,
|
||||
spotifyActions: {
|
||||
getCategory,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
if (!category) {
|
||||
loadCategory(uri);
|
||||
getCategory(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,13 +67,13 @@ class DiscoverCategory extends React.Component {
|
||||
uiActions: {
|
||||
hideContextMenu,
|
||||
},
|
||||
coreActions: {
|
||||
loadCategory,
|
||||
spotifyActions: {
|
||||
getCategory,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
hideContextMenu();
|
||||
loadCategory(uri, { forceRefetch: true });
|
||||
getCategory(uri, { forceRefetch: true });
|
||||
}
|
||||
|
||||
render = () => {
|
||||
|
||||
Reference in New Issue
Block a user