Files
Iris/src/js/util/library.js
James Barnsley 39accaf362 Play/Enqueue partially-loaded albums
- When in index, but tracks are not, our callbackAction is not triggered
- Send and consume our `callbackAction` as per Playlists
- Added support to our `ensureLoaded` handler; This will need some observation and testing to ensure there is no knock-on issues (possibly double-ups?)
2023-06-25 18:12:39 +12:00

202 lines
4.6 KiB
JavaScript

import { compact } from 'lodash';
import { formatContext } from './format';
import localForage from 'localforage';
import { stopLoading } from '../services/ui/actions';
import { enqueueURIs, playURIs } from '../services/mopidy/actions';
import {
setLoading,
loadItems,
restoreItemsFromColdStore,
} from '../services/core/actions';
/**
* Inspect object to check for missing dependent properties
*
* @param {Object} = item, dependents, fullDependents, full
*/
const getMissingDependents = ({
item,
dependents = [],
fullDependents = [],
full,
}) => {
const allDependents = [
...dependents,
...fullDependents,
];
if (!item) return allDependents;
if (full) {
return allDependents.filter((dep) => item[dep] === undefined || item[dep] === null);
}
return dependents.filter((dep) => item[dep] === undefined || item[dep] === null);
};
/**
* Pluck all the URIs out of our dependent properties.
*
* @param {Object} = item, dependents, fullDependents
*/
const getDependentUris = ({
item,
dependents = [],
fullDependents = [],
}) => {
if (!item) return [];
const getUrisOfDependent = (dep) => {
if (!dep.match(new RegExp('(.*)_uri(.*)'))) return [];
const uris = item[dep];
if (uris && Array.isArray(uris)) return uris;
return [];
};
return [...dependents, ...fullDependents].reduce(
(acc, dep) => [
...acc,
...getUrisOfDependent(dep),
],
[],
);
};
/**
* Ensure we have an item in our index
* If it's not there, attempt to fetch it from our cold storage
* If it's not their either, call the provided fetch()
*
* @param {*} Object { store, action, fetch, dependents}
*/
const ensureLoaded = ({
store,
containerName = 'items',
action,
fetch,
dependents = [],
fullDependents = [],
type,
}) => {
const {
uri,
options: {
forceRefetch,
full,
callbackAction,
},
} = action;
const {
core: {
[containerName]: {
[uri]: item,
} = {},
} = {},
} = store.getState();
const dispatch = store.dispatch;
// Forced refetch bypasses everything
if (forceRefetch) {
console.info(`Force-refetching "${uri}"`);
store.dispatch(setLoading(uri, true));
fetch();
return;
}
const missingDependents = (itemToCheck) => getMissingDependents({
item: itemToCheck,
dependents,
fullDependents,
full,
});
const dependentUris = (itemToCheck) => getDependentUris({
item: itemToCheck,
dependents,
fullDependents,
});
const runCallback = (item) => {
switch (callbackAction.name) {
case 'enqueue':
dispatch(enqueueURIs({
uris: [item.uri],
from: formatContext(item),
...callbackAction,
}));
break;
case 'play':
dispatch(playURIs({
uris: [item.uri],
from: formatContext(item),
...callbackAction,
}));
break;
default:
break;
}
}
// Item already in our index?
if (item) {
if (missingDependents(item).length === 0) {
store.dispatch(stopLoading(uri));
console.info(`"${uri}" already in index`);
const uris = dependentUris(item);
if (uris.length) {
console.info(`Loading ${uris.length} dependents`, { uris });
store.dispatch(loadItems(type, uris));
}
if (callbackAction) runCallback(item);
return;
}
}
// What about in the coldstore?
localForage.getItem(uri).then((restoredItem) => {
if (!restoredItem || missingDependents(restoredItem).length > 0) {
store.dispatch(setLoading(uri, true));
fetch();
return;
}
console.info(`Restoring "${uri}" from database`);
store.dispatch(restoreItemsFromColdStore([restoredItem]));
if (callbackAction) runCallback(restoredItem);
// We already have the dependents of our restored item, so restore them.
// We assume that because THIS item is in the coldstore, its dependents
// are as well.
const uris = dependentUris(restoredItem);
if (uris.length > 0) {
console.info(`Restoring ${uris.length} dependents from database`);
const restoreAllDependents = uris.map(
(dependentUri) => localForage.getItem(dependentUri),
);
Promise.all(restoreAllDependents).then(
(dependentItems) => {
store.dispatch(
restoreItemsFromColdStore(
compact(dependentItems), // Squash nulls (ie items not found in coldstore)
),
);
},
);
}
});
};
export {
getMissingDependents,
getDependentUris,
ensureLoaded,
};
export default {
getMissingDependents,
getDependentUris,
ensureLoaded,
};