Search fully functional; Loading images properly, and dependent URIs
This commit is contained in:
@ -55,6 +55,21 @@ class DebugInfo extends React.Component {
|
||||
|
||||
render = () => {
|
||||
const localStorageUsage = this.localStorageSize();
|
||||
const {
|
||||
core: {
|
||||
items = {},
|
||||
},
|
||||
ui: {
|
||||
notifications = {},
|
||||
processes = {},
|
||||
slim_mode,
|
||||
test_mode,
|
||||
selected_tracks = [],
|
||||
},
|
||||
mopidy: {
|
||||
enqueue_uris_batches = [],
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className="debug-info">
|
||||
@ -75,95 +90,45 @@ class DebugInfo extends React.Component {
|
||||
<div className="debug-info-section">
|
||||
<h5>State</h5>
|
||||
<div className="debug-info-item">
|
||||
Items:
|
||||
{' '}
|
||||
{this.props.core.items ? Object.keys(this.props.core.items).length : '0'}
|
||||
{`Items: ${Object.keys(items).length}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Coldstore items:
|
||||
{' '}
|
||||
{this.state.localForageLength}
|
||||
{`Coldstore items: ${this.state.localForageLength}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Albums:
|
||||
{' '}
|
||||
{this.props.core.albums ? Object.keys(this.props.core.albums).length : '0'}
|
||||
{`Notifications: ${Object.keys(notifications).length}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Artists:
|
||||
{' '}
|
||||
{this.props.core.artists ? Object.keys(this.props.core.artists).length : '0'}
|
||||
{`Processes: ${Object.keys(processes).length}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Playlists:
|
||||
{' '}
|
||||
{this.props.core.playlists ? Object.keys(this.props.core.playlists).length : '0'}
|
||||
{`Enqueue batches: ${enqueue_uris_batches.length}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Tracks:
|
||||
{' '}
|
||||
{this.props.core.tracks ? Object.keys(this.props.core.tracks).length : '0'}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Users:
|
||||
{' '}
|
||||
{this.props.core.users ? Object.keys(this.props.core.users).length : '0'}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Notifications:
|
||||
{' '}
|
||||
{this.props.ui.notifications ? Object.keys(this.props.ui.notifications).length : '0'}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Processes:
|
||||
{' '}
|
||||
{this.props.ui.processes ? Object.keys(this.props.ui.processes).length : '0'}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Enqueue batches:
|
||||
{' '}
|
||||
{this.props.mopidy.enqueue_uris_batches ? this.props.mopidy.enqueue_uris_batches.length : '0'}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Cached URLs:
|
||||
{' '}
|
||||
{Object.keys(getStorage('cache')).length}
|
||||
{`Cached URLs: ${Object.keys(getStorage('cache')).length}`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="debug-info-section">
|
||||
<h5>Config</h5>
|
||||
<div className="debug-info-item">
|
||||
Slim mode:
|
||||
{' '}
|
||||
{this.props.ui.slim_mode ? 'on' : 'off'}
|
||||
{`Slim mode: ${slim_mode ? 'on' : 'off'}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Test mode:
|
||||
{' '}
|
||||
{this.props.ui.test_mode ? 'on' : 'off'}
|
||||
{`Test mode: ${test_mode ? 'on' : 'off'}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Touch:
|
||||
{' '}
|
||||
{isTouchDevice() ? 'on' : 'off'}
|
||||
{`Touch: ${isTouchDevice() ? 'on' : 'off'}`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
LocalStorage usage:
|
||||
{' '}
|
||||
{localStorageUsage.used}
|
||||
kb (~
|
||||
{localStorageUsage.percent}
|
||||
%)
|
||||
{`LocalStorage usage: ${localStorageUsage.used}kb (~${localStorageUsage.percent}%)`}
|
||||
</div>
|
||||
<div className="debug-info-item">
|
||||
Selected tracks:
|
||||
{' '}
|
||||
{this.props.ui.selected_tracks.length}
|
||||
{`Selected tracks: ${selected_tracks.length}`}
|
||||
<br />
|
||||
{
|
||||
this.props.ui.selected_tracks.map((track_key, index) => (
|
||||
<div key={`${track_key}_${index}`}>{track_key}</div>
|
||||
selected_tracks.map((track_key, index) => (
|
||||
<div key={`${track_key}_${index}`}>{track_key}</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
@ -9,11 +9,6 @@ import {
|
||||
formatTracks,
|
||||
formatTrack,
|
||||
formatSimpleObject,
|
||||
formatAlbum,
|
||||
formatArtist,
|
||||
formatPlaylist,
|
||||
formatUser,
|
||||
formatSimpleObjects,
|
||||
} from '../../util/format';
|
||||
import { handleException } from './actions';
|
||||
|
||||
@ -66,10 +61,12 @@ const ensureLoaded = ({
|
||||
if (!parent) return [];
|
||||
|
||||
return [...dependents, ...fullDependents].reduce(
|
||||
(acc, dependent) => [
|
||||
...acc,
|
||||
...(dependent.match(new RegExp('/(.*)_uri(.*)')) ? parent[dependent] : []),
|
||||
],
|
||||
(acc, dependent) => {
|
||||
return [
|
||||
...acc,
|
||||
...(dependent.match(new RegExp('(.*)_uri(.*)')) ? parent[dependent] : []),
|
||||
];
|
||||
},
|
||||
[],
|
||||
);
|
||||
};
|
||||
@ -86,10 +83,7 @@ const ensureLoaded = ({
|
||||
if (getMissingDependents(item).length === 0) {
|
||||
console.info(`"${uri}" and all dependents already in index`);
|
||||
store.dispatch(uiActions.stopLoading(uri));
|
||||
|
||||
getDependentUris(item).forEach(
|
||||
(dependent) => store.dispatch(coreActions.loadItems(item[dependent])),
|
||||
);
|
||||
coreActions.loadItems(getDependentUris(item));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -327,14 +321,6 @@ const CoreMiddleware = (function () {
|
||||
...(term === prevTerm && type === prevType ? allResults : {}),
|
||||
};
|
||||
|
||||
console.log({
|
||||
term,
|
||||
type,
|
||||
prevTerm,
|
||||
prevType,
|
||||
allResults,
|
||||
});
|
||||
|
||||
// Merge our new results with the existing (if any)
|
||||
search_results[resultType] = [
|
||||
...(search_results[resultType] || []),
|
||||
@ -352,98 +338,89 @@ const CoreMiddleware = (function () {
|
||||
location.reload();
|
||||
break;
|
||||
|
||||
/**
|
||||
* Playlist manipulation
|
||||
* */
|
||||
case 'PLAYLIST_TRACKS':
|
||||
const playlist_tracks = formatTracks(action.tracks);
|
||||
|
||||
store.dispatch({
|
||||
type: 'TRACKS_LOADED',
|
||||
tracks: playlist_tracks,
|
||||
});
|
||||
|
||||
next({ ...action, tracks_uris: arrayOf('uri', playlist_tracks) });
|
||||
break;
|
||||
|
||||
case 'PLAYLIST_TRACKS_ADDED':
|
||||
const asset = store.getState().core.playlists[action.key];
|
||||
case 'PLAYLIST_TRACKS_ADDED': {
|
||||
const {
|
||||
key,
|
||||
tracks_uris,
|
||||
} = action;
|
||||
const {
|
||||
core: {
|
||||
items: {
|
||||
[key]: asset,
|
||||
},
|
||||
},
|
||||
} = store.getState();
|
||||
store.dispatch(uiActions.createNotification({
|
||||
content: <span>Added {action.tracks_uris.length} tracks to <URILink uri={action.key}>{asset ? asset.name : 'playlist'}</URILink></span>,
|
||||
content: (
|
||||
<span>
|
||||
Added {tracks_uris.length} tracks to <URILink uri={key}>{asset ? asset.name : 'playlist'}</URILink>
|
||||
</span>
|
||||
),
|
||||
}));
|
||||
switch (uriSource(action.key)) {
|
||||
|
||||
switch (uriSource(key)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getPlaylist(action.key));
|
||||
store.dispatch(spotifyActions.getPlaylist(key));
|
||||
break;
|
||||
case 'm3u':
|
||||
store.dispatch(mopidyActions.getPlaylist(action.key));
|
||||
store.dispatch(mopidyActions.getPlaylist(key));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PLAYLIST_TRACKS_REORDERED':
|
||||
var playlists = { ...core.playlists };
|
||||
var playlist = { ...playlists[action.key] };
|
||||
var tracks_uris = Object.assign([], playlist.tracks_uris);
|
||||
case 'PLAYLIST_TRACKS_REORDERED': {
|
||||
const {
|
||||
key,
|
||||
snapshot_id,
|
||||
} = action;
|
||||
let { insert_before } = action;
|
||||
const playlist = { ...core.items[key] };
|
||||
const tracks = Object.assign([], playlist.tracks);
|
||||
|
||||
// handle insert_before offset if we're moving BENEATH where we're slicing tracks
|
||||
var { insert_before } = action;
|
||||
if (insert_before > action.range_start) {
|
||||
insert_before -= action.range_length;
|
||||
}
|
||||
|
||||
// cut our moved tracks into a new array
|
||||
var tracks_to_move = tracks_uris.splice(action.range_start, action.range_length);
|
||||
const tracks_to_move = tracks.splice(action.range_start, action.range_length);
|
||||
tracks_to_move.reverse();
|
||||
|
||||
for (i = 0; i < tracks_to_move.length; i++) {
|
||||
tracks_uris.splice(insert_before, 0, tracks_to_move[i]);
|
||||
for (let i = 0; i < tracks_to_move.length; i++) {
|
||||
tracks.splice(insert_before, 0, tracks_to_move[i]);
|
||||
}
|
||||
|
||||
var snapshot_id = null;
|
||||
if (action.snapshot_id) {
|
||||
snapshot_id = action.snapshot_id;
|
||||
}
|
||||
|
||||
// Update our playlist
|
||||
playlist.tracks_uris = tracks_uris;
|
||||
playlist.snapshot_id = snapshot_id;
|
||||
|
||||
// Trigger normal playlist updating
|
||||
store.dispatch({
|
||||
type: 'PLAYLISTS_LOADED',
|
||||
playlists: [playlist],
|
||||
});
|
||||
store.dispatch(coreActions.itemLoaded({
|
||||
...playlist,
|
||||
tracks,
|
||||
snapshot_id,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PLAYLIST_TRACKS_REMOVED':
|
||||
var playlists = { ...core.playlists };
|
||||
var playlist = { ...playlists[action.key] };
|
||||
var tracks_uris = Object.assign([], playlist.tracks_uris);
|
||||
|
||||
var indexes = action.tracks_indexes.reverse();
|
||||
for (var i = 0; i < indexes.length; i++) {
|
||||
tracks_uris.splice(indexes[i], 1);
|
||||
case 'PLAYLIST_TRACKS_REMOVED': {
|
||||
const {
|
||||
key,
|
||||
snapshot_id,
|
||||
} = action;
|
||||
const playlist = { ...core.items[key] };
|
||||
const tracks = Object.assign([], playlist.tracks);
|
||||
const indexes = action.tracks_indexes.reverse();
|
||||
for (let i = 0; i < indexes.length; i++) {
|
||||
tracks.splice(indexes[i], 1);
|
||||
}
|
||||
|
||||
var snapshot_id = null;
|
||||
if (action.snapshot_id) {
|
||||
snapshot_id = action.snapshot_id;
|
||||
}
|
||||
|
||||
// Update our playlist
|
||||
playlist.tracks_uris = tracks_uris;
|
||||
playlist.snapshot_id = snapshot_id;
|
||||
|
||||
// Trigger normal playlist updating
|
||||
store.dispatch({
|
||||
type: 'PLAYLISTS_LOADED',
|
||||
playlists: [playlist],
|
||||
});
|
||||
store.dispatch(coreActions.itemLoaded({
|
||||
...playlist,
|
||||
tracks,
|
||||
snapshot_id,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asset Load commands
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import ReactGA from 'react-ga';
|
||||
import Mopidy from 'mopidy';
|
||||
import { sha256 } from 'js-sha256';
|
||||
import { sampleSize, uniqBy } from 'lodash';
|
||||
import { sampleSize, compact } from 'lodash';
|
||||
import { i18n } from '../../locale';
|
||||
import {
|
||||
generateGuid,
|
||||
@ -1147,18 +1147,23 @@ const MopidyMiddleware = (function () {
|
||||
term: query.term,
|
||||
requestType: type,
|
||||
uri_scheme,
|
||||
data: {
|
||||
uris: [uri_scheme],
|
||||
},
|
||||
};
|
||||
switch (type) {
|
||||
case 'tracks':
|
||||
item.data = { query: { track_name: [query.term] }, uris: [uri_scheme] };
|
||||
item.data.query = { any: [query.term] };
|
||||
break;
|
||||
case 'artists':
|
||||
item.data = { query: { artist: [query.term] }, uris: [uri_scheme] };
|
||||
item.data.query = { artist: [query.term] };
|
||||
break;
|
||||
case 'albums':
|
||||
item.data = { query: { album: [query.term] }, uris: [uri_scheme] };
|
||||
item.data.query = { album: [query.term] };
|
||||
break;
|
||||
case 'playlists':
|
||||
// Searching for playlists is not supported, so we get a simple
|
||||
// list of names and perform a client-side regex match
|
||||
item.method = 'playlists.asList';
|
||||
item.data = {};
|
||||
break;
|
||||
@ -1489,22 +1494,19 @@ const MopidyMiddleware = (function () {
|
||||
);
|
||||
break;
|
||||
|
||||
case 'MOPIDY_CURRENT_TRACK_LOADED':
|
||||
|
||||
// Let the UI know we're finished transition
|
||||
case 'MOPIDY_CURRENT_TRACK_LOADED': {
|
||||
store.dispatch(uiActions.setCurrentTrackTransition(false));
|
||||
|
||||
var track = formatTrack(action.tl_track);
|
||||
const track = formatTrack(action.tl_track);
|
||||
if (track.uri) {
|
||||
store.dispatch({
|
||||
type: 'CURRENT_TRACK_LOADED',
|
||||
track,
|
||||
uri: track.uri,
|
||||
});
|
||||
|
||||
store.dispatch(coreActions.loadItem(track.uri, { full: true }));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'MOPIDY_GET_NEXT_TRACK':
|
||||
request(store, 'tracklist.getNextTlid')
|
||||
@ -1539,10 +1541,11 @@ const MopidyMiddleware = (function () {
|
||||
.then(
|
||||
(_response) => {
|
||||
if (!_response) return;
|
||||
const tracks = compact(indexToArray(_response).map(
|
||||
(results) => (results.length ? formatTrack(results[0]) : null),
|
||||
));
|
||||
|
||||
const tracks = indexToArray(_response);
|
||||
|
||||
store.dispatch(coreActions.itemsLoaded(formatTracks(tracks)));
|
||||
store.dispatch(coreActions.itemsLoaded(tracks));
|
||||
|
||||
if (action.get_images) {
|
||||
store.dispatch(mopidyActions.getImages(arrayOf('uri', tracks)));
|
||||
@ -1588,15 +1591,10 @@ const MopidyMiddleware = (function () {
|
||||
);
|
||||
break;
|
||||
|
||||
|
||||
/**
|
||||
* =============================================================== IMAGES ===============
|
||||
* ======================================================================================
|
||||
* */
|
||||
|
||||
case 'MOPIDY_GET_IMAGES':
|
||||
case 'MOPIDY_GET_IMAGES': {
|
||||
const { uris } = action;
|
||||
if (action.uris) {
|
||||
request(store, 'library.getImages', { uris: action.uris })
|
||||
request(store, 'library.getImages', { uris })
|
||||
.then((response) => {
|
||||
const itemsWithImages = [];
|
||||
Object.keys(response).forEach((uri) => {
|
||||
@ -1620,6 +1618,7 @@ const MopidyMiddleware = (function () {
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@ -1509,23 +1509,28 @@ export function addTracksToPlaylist(uri, tracks_uris) {
|
||||
|
||||
export function deleteTracksFromPlaylist(uri, snapshot_id, tracks_indexes) {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, `playlists/${getFromUri('playlistid', uri)}/tracks`, 'DELETE', { snapshot_id, positions: tracks_indexes })
|
||||
.then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_REMOVED',
|
||||
key: uri,
|
||||
tracks_indexes,
|
||||
snapshot_id: response.snapshot_id,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not remove tracks from playlist',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
request(
|
||||
dispatch,
|
||||
getState,
|
||||
`playlists/${getFromUri('playlistid', uri)}/tracks`,
|
||||
'DELETE',
|
||||
{ snapshot_id, positions: tracks_indexes },
|
||||
).then(
|
||||
(response) => {
|
||||
dispatch({
|
||||
type: 'PLAYLIST_TRACKS_REMOVED',
|
||||
key: uri,
|
||||
tracks_indexes,
|
||||
snapshot_id: response.snapshot_id,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
dispatch(coreActions.handleException(
|
||||
'Could not remove tracks from playlist',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -89,11 +89,11 @@ const SpotifyMiddleware = (function () {
|
||||
store.dispatch(spotifyActions.createPlaylist(action.name, action.description, action.is_private, action.is_collaborative));
|
||||
break;
|
||||
|
||||
case 'SPOTIFY_REMOVE_PLAYLIST_TRACKS':
|
||||
var playlist = { ...store.getState().core.playlists[action.key] };
|
||||
store.dispatch(spotifyActions.deleteTracksFromPlaylist(playlist.uri, playlist.snapshot_id, action.tracks_indexes));
|
||||
case 'SPOTIFY_REMOVE_PLAYLIST_TRACKS': {
|
||||
const { snapshot_id } = store.getState().core.items[action.key] || {};
|
||||
store.dispatch(spotifyActions.deleteTracksFromPlaylist(action.key, snapshot_id, action.tracks_indexes));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 'SPOTIFY_ADD_PLAYLIST_TRACKS':
|
||||
store.dispatch(spotifyActions.addTracksToPlaylist(action.key, action.tracks_uris));
|
||||
|
||||
@ -96,7 +96,7 @@ class Search extends React.Component {
|
||||
}
|
||||
|
||||
onSourceClose = () => {
|
||||
this.search();
|
||||
this.search(true);
|
||||
};
|
||||
|
||||
digestUri = () => {
|
||||
@ -125,7 +125,7 @@ class Search extends React.Component {
|
||||
this.setState({ term: '' });
|
||||
}
|
||||
|
||||
search = () => {
|
||||
search = (force = false) => {
|
||||
const {
|
||||
coreActions: {
|
||||
startSearch,
|
||||
@ -133,6 +133,10 @@ class Search extends React.Component {
|
||||
uiActions: {
|
||||
setWindowTitle,
|
||||
},
|
||||
search_results_query: {
|
||||
type: existingType,
|
||||
term: existingTerm,
|
||||
},
|
||||
} = this.props;
|
||||
const {
|
||||
type,
|
||||
@ -141,27 +145,11 @@ class Search extends React.Component {
|
||||
|
||||
setWindowTitle(i18n('search.title_window', { term: decodeURIComponent(term) }));
|
||||
|
||||
/**
|
||||
* TODO: Searches are being triggered when navigating to type subviews, despite already having
|
||||
* results. This might be a consequence of having providers merging their results into one array
|
||||
*/
|
||||
|
||||
if (type && term) {
|
||||
if ((type && term && (force || existingType !== type || existingTerm !== term))) {
|
||||
startSearch({ type, term });
|
||||
/*
|
||||
mopidyActions.getSearchResults(type, term);
|
||||
|
||||
if (uri_schemes_search_enabled.includes('spotify:')) {
|
||||
spotifyActions.getSearchResults(type, term);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
loadMore = (type) => {
|
||||
alert(`load more: ${type}`);
|
||||
// this.props.spotifyActions.getURL(this.props['spotify_'+type+'_more'], 'SPOTIFY_SEARCH_RESULTS_LOADED_MORE_'+type.toUpperCase());
|
||||
}
|
||||
|
||||
setSort = (value) => {
|
||||
const {
|
||||
sort,
|
||||
@ -291,14 +279,41 @@ class Search extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
type: ownProps.match.params.type,
|
||||
term: ownProps.match.params.term,
|
||||
uri_schemes_search_enabled: state.ui.uri_schemes_search_enabled || [],
|
||||
uri_schemes: state.mopidy.uri_schemes || [],
|
||||
sort: state.ui.search_results_sort || 'followers.total',
|
||||
sort_reverse: (!!state.ui.search_results_sort_reverse),
|
||||
});
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const {
|
||||
match: {
|
||||
params: {
|
||||
type,
|
||||
term,
|
||||
},
|
||||
},
|
||||
} = ownProps;
|
||||
const {
|
||||
mopidy: {
|
||||
uri_schemes = [],
|
||||
},
|
||||
ui: {
|
||||
uri_schemes_search_enabled = [],
|
||||
search_results_sort: sort = 'followers.total',
|
||||
search_results_sort_reverse,
|
||||
},
|
||||
core: {
|
||||
search_results: {
|
||||
query: search_results_query = {},
|
||||
} = {},
|
||||
},
|
||||
} = state;
|
||||
|
||||
return {
|
||||
type,
|
||||
term,
|
||||
uri_schemes,
|
||||
uri_schemes_search_enabled,
|
||||
sort,
|
||||
sort_reverse: !!search_results_sort_reverse,
|
||||
search_results_query,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
|
||||
Reference in New Issue
Block a user