Service worker caching, Loader component

This commit is contained in:
James Barnsley
2019-09-10 06:59:44 +12:00
parent a86403fa59
commit c08cc71c47
49 changed files with 16390 additions and 11259 deletions

View File

@ -10,49 +10,56 @@ const helpers = require('../../helpers');
* @param endpoint = String
* @param params = String
* */
const sendRequest = (dispatch, endpoint, params) => new Promise((resolve, reject) => {
const loader_key = helpers.generateGuid();
dispatch(uiActions.startLoading(loader_key, `,discogs_${endpoint}`));
const sendRequest = (dispatch, getState, endpoint, params) => new Promise((resolve, reject) => {
const key = 'CXIwsVMAjrXIVitBWgqd';
const secret = 'KiEUfwKpebxRnEHlKoXnYIftJxeuqjTK';
const url = `https://api.discogs.com/${endpoint}?${params}`;
const loader_key = helpers.generateGuid();
dispatch(uiActions.startLoading(loader_key, `discogs_${endpoint}`));
const checkRateLimit = (request) => {
const rate = {
limit: request.getResponseHeader('X-Discogs-Ratelimit'),
remaining: request.getResponseHeader('X-Discogs-Ratelimit-Remaining'),
used: request.getResponseHeader('X-Discogs-Ratelimit-Used'),
}
if (rate.remaining !== undefined && rate.remaining === 0) {
dispatch(uiActions.createNotification({
key: "discogs_rate_limit",
type: "bad",
content: "Rate limit exceeded",
description: `Discogs rate limit exceeded, try again in a few minutes.`
}));
}
}
const config = {
method: 'GET',
cache: true,
timeout: 30000,
url: `https://api.discogs.com/${endpoint}?key=${key}&secret=${secret}&${params}`,
url: url,
crossDomain: true,
headers: {
'user-agent': 'Iris/1.0',
'User-Agent': 'Iris/1.0',
'Authorization': `Discogs key=${key}, secret=${secret}`
},
};
$.ajax(config).then(
(response) => {
(data, textStatus, request) => {
dispatch(uiActions.stopLoading(loader_key));
if (response.error) {
reject({
config,
error: response,
});
} else {
resolve(response);
}
checkRateLimit(request);
resolve(data);
},
(xhr, status, error) => {
dispatch(uiActions.stopLoading(loader_key));
// Snatch a more meaningful error
let description = null;
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
description = xhr.responseJSON.message;
}
checkRateLimit(xhr);
reject({
config,
error,
description,
status,
xhr,
});
@ -60,45 +67,62 @@ const sendRequest = (dispatch, endpoint, params) => new Promise((resolve, reject
);
});
export function findArtist(name) {
return sendRequest(dispatch, `artists/${artist.mbid}`, 'inc=url-rels')
.then(
(response) => {
if (response) {
const image_relations = response.relations.filter((rel) => rel.type == 'image');
if (image_relations) {
const updated_artist = {
uri,
images: image_relations.map((relation) => relation.url.resource.replace('File:', 'Special:FilePath/')),
};
dispatch(coreActions.artistLoaded(updated_artist));
}
}
},
(error) => {
console.log(`Musicbrainz: No results for ${artist.mbid}`);
},
);
}
export function getArtistImages(uri, artist) {
return (dispatch, getState) => {
sendRequest(dispatch, 'database/search', `query=${artist.name}`)
sendRequest(dispatch, getState, 'database/search', `type=artist&query=${artist.name}`)
.then(
(response) => {
if (response) {
if (response.results && response.results[0].cover_image) {
if (response.results.length > 0 && response.results[0].cover_image !== undefined) {
const updated_artist = {
uri,
images: [response.results[0].cover_image],
};
dispatch(coreActions.artistLoaded(updated_artist));
} else {
console.log(`Discogs: No results for artist ${artist.name}`);
}
}
},
(error) => {
console.log(`Discogs: No results for ${artist.name}`);
console.error(error);
},
);
/*
const finalise = (cover_image, cache) => {
const updated_artist = {
uri,
images: [cover_image],
};
dispatch(coreActions.artistLoaded(updated_artist));
if (cache) {
helpers.cache.set(`discogs_${artist.name}`, cover_image);
}
}
const cached = helpers.cache.get(`discogs_${artist.name}`);
if (cached) {
finalise(cached);
return;
}
sendRequest(dispatch, getState, 'database/search', `type=artist&query=${artist.name}`)
.then(
(response) => {
if (response) {
if (response.results.length > 0 && response.results[0].cover_image !== undefined) {
finalise(response.results[0].cover_image, true);
} else {
console.log(`Discogs: No results for artist ${artist.name}`);
}
}
},
(error) => {
console.error(error);
},
);
*/
};
}

View File

@ -20,7 +20,7 @@ export function set(data) {
* @param data mixed = request payload
* @return Promise
* */
const sendRequest = (dispatch, getState, endpoint, method = 'GET', data = false) => new Promise((resolve, reject) => {
const sendRequest = (dispatch, getState, endpoint, method = 'GET', data = false) => new Promise((resolve, reject) => {
if (endpoint.startsWith('https://') || endpoint.startsWith('http://')) {
var url = endpoint;
} else {
@ -135,7 +135,7 @@ export function getMe() {
* @param path = String, the relative API path for the HTML lyrics
* */
export function getTrackLyrics(uri, path) {
return (dispatch, getState) => {
return (dispatch, getState) => {
dispatch(coreActions.trackLoaded({
uri,
lyrics: null,
@ -160,7 +160,7 @@ export function getTrackLyrics(uri, path) {
if (response && response.result) {
const html = $(response.result);
let lyrics = html.find('.lyrics');
if (lyrics.length > 0) {
if (lyrics.length > 0) {
lyrics = lyrics.first();
lyrics.find('a').replaceWith(function () {
return this.innerHTML;
@ -181,7 +181,7 @@ export function getTrackLyrics(uri, path) {
'Could not get track lyrics',
response.error,
));
}
}
},
(xhr, status, error) => {
dispatch(uiActions.stopLoading(loader_key));
@ -195,7 +195,7 @@ export function getTrackLyrics(uri, path) {
}
export function findTrackLyrics(track) {
return (dispatch, getState) => {
return (dispatch, getState) => {
let query = '';
query += `${track.artists[0].name} `;
query += track.name;

View File

@ -1,4 +1,5 @@
import axios from 'axios';
const coreActions = require('../core/actions');
const uiActions = require('../ui/actions');
const helpers = require('../../helpers');
@ -18,57 +19,47 @@ export function set(data) {
* @param params = string, the url params to send
* @params signed = boolean, whether we've got a signed request with baked-in api_key
* */
const sendRequest = (dispatch, getState, params, signed = false) => new Promise((resolve, reject) => {
const loader_key = helpers.generateGuid();
const sendRequest = (dispatch, getState, params, signed = false) => new Promise((resolve, reject) => {
let url = `https://ws.audioscrobbler.com/2.0/?format=json&${params}`;
let http_method = 'GET';
let method = params.substring(params.indexOf('method=') + 7, params.length);
method = method.substring(0, method.indexOf('&'));
dispatch(uiActions.startLoading(loader_key, `lastfm_${method}`));
const config = {
method: 'GET',
cache: true,
timeout: 30000,
url: `https://ws.audioscrobbler.com/2.0/?format=json&${params}`,
};
// Signed requests don't need our api_key as the proxy has it's own
if (!signed) {
config.url += '&api_key=4320a3ef51c9b3d69de552ac083c55e3';
url += '&api_key=4320a3ef51c9b3d69de552ac083c55e3';
} else {
config.method = 'POST';
http_method = 'POST';
}
$.ajax(config).then(
(response) => {
dispatch(uiActions.stopLoading(loader_key));
if (response.error) {
reject({
config,
error: response,
});
} else {
resolve(response);
}
},
(xhr, status, error) => {
dispatch(uiActions.stopLoading(loader_key));
const config = {
method: http_method,
timeout: 30000,
cache: "force-cache",
};
// Snatch a more meaningful error
let description = null;
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
description = xhr.responseJSON.message;
}
const loader_key = helpers.generateGuid();
dispatch(uiActions.startLoading(loader_key, `lastfm_${method}`));
reject({
config,
error,
description,
status,
xhr,
});
},
);
function status(response) {
dispatch(uiActions.stopLoading(loader_key));
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
fetch(url, config)
.then(status)
.then(response => response.json())
.then(data => {
resolve(data);
})
.catch(error => {
reject(error);
});
});
/**
@ -79,7 +70,7 @@ const sendRequest = (dispatch, getState, params, signed = false) => new Promise(
* @param params = string, the url params to send
* @param signed = boolean
* */
const sendSignedRequest = (dispatch, getState, params) => new Promise((resolve, reject) => {
const sendSignedRequest = (dispatch, getState, params) => new Promise((resolve, reject) => {
// Not authorized
if (!getState().lastfm.authorization) {
reject({
@ -227,7 +218,7 @@ export function getTrack(uri) {
};
}
export function getArtist(uri, artist, mbid = false) {
export function getArtist(uri, artist, mbid = false) {
return (dispatch, getState) => {
if (mbid) {
var params = `method=artist.getInfo&mbid=${mbid}`;
@ -271,7 +262,7 @@ export function getAlbum(uri, artist, album, mbid = false) {
sendRequest(dispatch, getState, params)
.then(
(response) => {
if (response.album) {
if (response.album) {
const existing_album = getState().core.albums[uri];
const album = {
@ -303,10 +294,10 @@ export function getAlbum(uri, artist, album, mbid = false) {
export function getImages(context, uri) {
return (dispatch, getState) => {
return (dispatch, getState) => {
let record = getState().core[context][uri];
if (record) {
switch (context) {
switch (context) {
case 'tracks':
if (record.mbid) {

View File

@ -14,7 +14,7 @@ const googleActions = require('../google/actions.js');
const lastfmActions = require('../lastfm/actions.js');
const discogsActions = require('../discogs/actions.js');
const MopidyMiddleware = (function () {
const MopidyMiddleware = (function () {
// container for the actual Mopidy socket
let socket = null;
@ -23,13 +23,13 @@ const MopidyMiddleware = (function () {
let progress_interval_counter = 0;
// handle all manner of socket messages
const handleMessage = (ws, store, type, data) => {
const handleMessage = (ws, store, type, data) => {
// if debug enabled
if (store.getState().ui.log_mopidy) {
console.log('Mopidy', type, data);
}
switch (type) {
switch (type) {
case 'state:online':
store.dispatch({ type: 'MOPIDY_CONNECTED' });
@ -46,7 +46,7 @@ const MopidyMiddleware = (function () {
// Every 1s update our play position (when playing)
progress_interval = setInterval(() => {
if (store.getState().mopidy.play_state == 'playing') {
if (store.getState().mopidy.play_state == 'playing') {
// Every 10s get real position from server, provided we're in-focus
if (progress_interval_counter % 5 == 0 && store.getState().ui.window_focus === true) {
store.dispatch(mopidyActions.getTimePosition());
@ -221,7 +221,7 @@ const MopidyMiddleware = (function () {
* it just proceeds to the next middleware, or default functionality
* */
return (store) => (next) => (action) => {
switch (action.type) {
switch (action.type) {
case 'MOPIDY_CONNECT':
if (socket != null) {
socket.close();
@ -358,7 +358,7 @@ const MopidyMiddleware = (function () {
store.dispatch({
type: 'MOPIDY_PLAY_STATE',
play_state: 'paused',
});
});
store.dispatch(pusherActions.deliverBroadcast(
'notification',
{
@ -600,7 +600,7 @@ const MopidyMiddleware = (function () {
var playlist = store.getState().core.playlists[action.uri];
// We have the playlist loaded already, and we've got at least 1 track to start playing
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
// We've got all of the tracks, so just play those; no further action required
if (playlist.tracks_total == playlist.tracks_uris.length) {
store.dispatch(mopidyActions.playURIs(playlist.tracks_uris, action.uri, action.shuffle));
@ -648,7 +648,7 @@ const MopidyMiddleware = (function () {
var playlist = store.getState().core.playlists[action.uri];
// We have the playlist loaded already, and we've got at least 1 track to start playing
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
// We've got all of the tracks, so just play those; no further action required
if (playlist.tracks_total == playlist.tracks_uris.length) {
let tracks_uris = Object.assign([], playlist.tracks_uris);
@ -752,7 +752,7 @@ const MopidyMiddleware = (function () {
return;
// make sure we have some uris in the queue
} if (action.data.batches && action.data.batches.length > 0) {
} if (action.data.batches && action.data.batches.length > 0) {
var batches = Object.assign([], action.data.batches);
var batch = batches[0];
let total_uris = 0;
@ -790,7 +790,7 @@ const MopidyMiddleware = (function () {
var params = { uris: batch.uris };
// Play this batch next
if (batch.play_next) {
if (batch.play_next) {
// Make sure we're playing something first
if (current_track_index > -1) {
params.at_position = current_track_index + batch.offset + 1;
@ -864,7 +864,7 @@ const MopidyMiddleware = (function () {
// add our first track
request(socket, store, 'tracklist.add', { uri: first_uri, at_position: 0 })
.then(
(response) => {
(response) => {
// play it (only if we got a successful lookup)
if (response.length > 0) {
store.dispatch(mopidyActions.changeTrack(response[0].tlid));
@ -885,7 +885,7 @@ const MopidyMiddleware = (function () {
action.uris.splice(first_uri_index, 1);
// And add the rest of our uris (if any)
if (action.uris.length > 0) {
if (action.uris.length > 0) {
// Wait a moment so the server can trigger track_changed etc
// this means our UI feels snappier as the first track shows up quickly
setTimeout(
@ -1010,7 +1010,7 @@ const MopidyMiddleware = (function () {
},
));
switch (action.data.context) {
switch (action.data.context) {
// Albums
case 'albums':
@ -1283,7 +1283,7 @@ const MopidyMiddleware = (function () {
case 'all':
default:
var process_tracks = () => {
var process_tracks = () => {
// Quick check to see if we should be cancelling
const last_run = store.getState().ui.processes.MOPIDY_GET_SEARCH_RESULTS_PROCESSOR;
if (last_run && last_run.status == 'cancelling') {
@ -1324,7 +1324,7 @@ const MopidyMiddleware = (function () {
);
};
var process_albums = () => {
var process_albums = () => {
// Quick check to see if we should be cancelling
const last_run = store.getState().ui.processes.MOPIDY_GET_SEARCH_RESULTS_PROCESSOR;
if (last_run && last_run.status == 'cancelling') {
@ -1385,7 +1385,7 @@ const MopidyMiddleware = (function () {
);
};
var process_artists = () => {
var process_artists = () => {
// Quick check to see if we should be cancelling
const last_run = store.getState().ui.processes.MOPIDY_GET_SEARCH_RESULTS_PROCESSOR;
if (last_run && last_run.status == 'cancelling') {
@ -1454,7 +1454,7 @@ const MopidyMiddleware = (function () {
},
);
};
var process_playlists = () => {
var process_playlists = () => {
// Quick check to see if we should be cancelling
const last_run = store.getState().ui.processes.MOPIDY_GET_SEARCH_RESULTS_PROCESSOR;
if (last_run && last_run.status == 'cancelling') {
@ -1473,7 +1473,7 @@ const MopidyMiddleware = (function () {
request(socket, store, 'playlists.asList')
.then(
(response) => {
if (response.length > 0) {
if (response.length > 0) {
let playlists_uris = [];
for (var i = 0; i < response.length; i++) {
const playlist = response[i];
@ -1543,7 +1543,7 @@ const MopidyMiddleware = (function () {
case 'MOPIDY_GET_LIBRARY_PLAYLISTS':
request(socket, store, 'playlists.asList')
.then((response) => {
.then((response) => {
// drop in our URI list
const playlist_uris = helpers.arrayOf('uri', response);
const playlist_uris_filtered = [];
@ -1614,9 +1614,9 @@ const MopidyMiddleware = (function () {
request(socket, store, 'library.lookup', { uris })
.then((response) => {
for (const uri in response) {
if (response.hasOwnProperty(uri)) {
if (response.hasOwnProperty(uri)) {
var track = response[uri][0];
if (track) {
if (track) {
// find the track reference, and drop in the full track data
function getByURI(trackReference) {
return track.uri == trackReference.uri;
@ -1701,11 +1701,11 @@ const MopidyMiddleware = (function () {
// Even though we have the full playlist in our index, our "playlists.save" request
// requires a Mopidy playlist object (with updates)
request(socket, store, 'playlists.lookup', { uri: action.key })
.then((response) => {
.then((response) => {
const mopidy_playlist = { ...response, name: action.name };
request(socket, store, 'playlists.save', { playlist: mopidy_playlist })
.then((response) => {
.then((response) => {
// Overwrite our playlist with the response to our save
// This is essential to get the updated URI from Mopidy
const playlist = {
@ -1717,7 +1717,7 @@ const MopidyMiddleware = (function () {
// When we rename a playlist, the URI also changes to reflect the name change.
// We need to update our index, as well as redirect our current page URL.
if (action.key !== playlist.uri) {
if (action.key !== playlist.uri) {
// Remove old playlist (by old key/uri) from index
// By providing the new key, the old playlist gets replaced with a redirector object
store.dispatch(coreActions.removeFromIndex('playlists', action.key, playlist.uri));
@ -1732,7 +1732,7 @@ const MopidyMiddleware = (function () {
case 'MOPIDY_REORDER_PLAYLIST_TRACKS':
request(socket, store, 'playlists.lookup', { uri: action.key })
.then((response) => {
.then((response) => {
let playlist = { ...response };
const tracks = Object.assign([], playlist.tracks);
const tracks_to_move = [];
@ -1744,7 +1744,7 @@ const MopidyMiddleware = (function () {
if (insert_before > range_start) insert_before -= range_length;
// collate our tracks to be moved
for (var i = 0; i < range_length; i++) {
for (var i = 0; i < range_length; i++) {
// add to FRONT: we work backwards to avoid screwing up our indexes
tracks_to_move.unshift(tracks[range_start + i]);
}
@ -1824,7 +1824,7 @@ const MopidyMiddleware = (function () {
remaining: uris.length,
},
));
});
});
} else if (last_run.status == 'cancelled') {
store.dispatch(uiActions.resumeProcess('MOPIDY_LIBRARY_ALBUMS_PROCESSOR'));
} else if (last_run.status == 'finished') {
@ -1984,7 +1984,7 @@ const MopidyMiddleware = (function () {
if (response.length <= 0) return;
const uris = [];
for (let i = 0; i < response.length; i++) {
for (let i = 0; i < response.length; i++) {
// Convert local URI to actual artist URI
// See https://github.com/mopidy/mopidy-local-sqlite/issues/39
response[i].uri = response[i].uri.replace('local:directory?albumartist=', '');
@ -2058,7 +2058,7 @@ const MopidyMiddleware = (function () {
case 'MOPIDY_GET_ARTIST':
request(socket, store, 'library.lookup', action.data)
.then((response) => {
.then((response) => {
if (response.length <= 0) {
return;
}
@ -2094,7 +2094,7 @@ const MopidyMiddleware = (function () {
// Get the artist object from the track. This is a bit ugly because it's a simplified
// (Mopidy) artist object but gives us enough to fetch their name and artwork.
for (const raw_artist of response[0].artists) {
for (const raw_artist of response[0].artists) {
// We're only interested in the artist we asked for
if (raw_artist.uri === artist.uri) {
artist = { ...raw_artist };
@ -2109,7 +2109,7 @@ const MopidyMiddleware = (function () {
// Load supprting information from LastFM and Discogs
const existing_artist = store.getState().core.artists[artist.uri];
if (existing_artist) {
if (existing_artist) {
if (!existing_artist.images) {
store.dispatch(discogsActions.getArtistImages(artist.uri, artist));
}
@ -2208,7 +2208,7 @@ const MopidyMiddleware = (function () {
store.dispatch(uiActions.setCurrentTrackTransition(false));
var track = helpers.formatTrack(action.tl_track);
if (track.uri) {
if (track.uri) {
// Deliver the data we've got already
store.dispatch({
type: 'CURRENT_TRACK_LOADED',
@ -2217,7 +2217,7 @@ const MopidyMiddleware = (function () {
});
// Now attempt to get supporting images
if (store.getState().core.tracks[track.uri] === undefined || store.getState().core.tracks[track.uri].images === undefined) {
if (store.getState().core.tracks[track.uri] === undefined || store.getState().core.tracks[track.uri].images === undefined) {
// We've got Spotify running, and it's a spotify track - go straight to the source!
if (store.getState().spotify.enabled && helpers.uriSource(track.uri) == 'spotify') {
store.dispatch(spotifyActions.getTrack(track.uri));
@ -2234,7 +2234,7 @@ const MopidyMiddleware = (function () {
request(socket, store, 'tracklist.getNextTlid')
.then(
(response) => {
if (response && response >= 0) {
if (response && response >= 0) {
// Get the full track object from our tracklist
// We know it will be here, as the tlid refers to an item in this list
const track = helpers.applyFilter('tlid', response, store.getState().core.queue, true);
@ -2246,7 +2246,7 @@ const MopidyMiddleware = (function () {
});
// We don't have the track (including images) already in our index
if (store.getState().core.tracks[track.uri] === undefined || store.getState().core.tracks[track.uri].images === undefined) {
if (store.getState().core.tracks[track.uri] === undefined || store.getState().core.tracks[track.uri].images === undefined) {
// We've got Spotify running, and it's a spotify track - go straight to the source!
if (store.getState().spotify.enabled && helpers.uriSource(track.uri) == 'spotify') {
store.dispatch(spotifyActions.getTrack(track.uri));
@ -2300,7 +2300,7 @@ const MopidyMiddleware = (function () {
records.push({
uri,
images,
});
});
} else {
store.dispatch(lastfmActions.getImages(action.context, uri));
}
@ -2330,7 +2330,7 @@ const MopidyMiddleware = (function () {
});
request(socket, store, 'library.browse', action.data)
.then((response) => {
.then((response) => {
const tracks_uris = [];
const subdirectories = [];
@ -2388,7 +2388,7 @@ const MopidyMiddleware = (function () {
default:
return next(action);
}
};
};
}());
export default MopidyMiddleware;