Loaders in Track view; Revisiting URI encoding again...

This commit is contained in:
James Barnsley
2021-01-18 21:06:59 +13:00
parent 499a4800b0
commit 50f106c710
12 changed files with 559 additions and 330 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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 = () => {

View File

@ -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

View File

@ -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,

View File

@ -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 += `&timestamp=${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') {

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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 = () => {