diff --git a/src/js/App.js b/src/js/App.js
index d097b168f..38ca038ed 100755
--- a/src/js/App.js
+++ b/src/js/App.js
@@ -54,7 +54,7 @@ const Content = () => (
} />
} />
} />
- } />
+ } />
} />
} />
} />
diff --git a/src/js/components/SearchResults.js b/src/js/components/SearchResults.js
index 01cd95fd2..d1ef8cb4d 100755
--- a/src/js/components/SearchResults.js
+++ b/src/js/components/SearchResults.js
@@ -16,12 +16,14 @@ const SearchResults = ({
type,
all,
}) => {
- const { term } = useParams();
+ const { term, providers = '' } = useParams();
const [sortField, sortReverse] = useSelector(
(state) => getSortSelector(state, SORT_KEY, 'name'),
);
- const searchResultsSelector = makeSearchResultsSelector(term, type);
+ const providersArray = providers.split(',');
+ const searchResultsSelector = makeSearchResultsSelector(providersArray, term, type);
const rawResults = useSelector(searchResultsSelector);
+ console.debug(rawResults, providersArray, term, type)
const encodedTerm = encodeURIComponent(term);
let results = [...rawResults];
@@ -43,7 +45,7 @@ const SearchResults = ({
{!all && (
-
+
{' '}
@@ -53,7 +55,7 @@ const SearchResults = ({
)}
{all && (
-
+
)}
@@ -66,7 +68,7 @@ const SearchResults = ({
{type === 'tracks' && (
i !== 'spotify:'), // Omit Spotify; handled above
+ providers.filter((i) => i !== 'spotify:'), // Omit Spotify; handled above
));
break;
}
- case 'SEARCH_RESULTS_LOADED': {
- const {
- query: {
- term,
- type,
- },
- resultType,
- results,
- } = action;
- const {
- core: {
- search_results: {
- query: {
- term: prevTerm,
- type: prevType,
- } = {},
- ...allResults
- } = {},
- } = {},
- } = store.getState();
-
- // Add to our existing results, so long as the search term is the same
- const search_results = {
- query: { term, type },
- ...(term === prevTerm && type === prevType ? allResults : {}),
- };
-
- // Merge our new results with the existing (if any)
- search_results[resultType] = [
- ...(search_results[resultType] || []),
- ...results,
- ];
-
- next({
- ...action,
- search_results,
- });
- break;
- }
-
case 'PLAYLIST_TRACKS_ADDED': {
const {
key,
diff --git a/src/js/services/core/reducer.js b/src/js/services/core/reducer.js
index 7b771b077..9f424d36c 100755
--- a/src/js/services/core/reducer.js
+++ b/src/js/services/core/reducer.js
@@ -176,23 +176,15 @@ export default function reducer(core = {}, action) {
/**
* Search results
* */
- case 'START_SEARCH':
- return {
- ...core,
- search_results: {
- query: action.query,
- artists: [],
- albums: [],
- playlists: [],
- tracks: [],
- },
- };
case 'SEARCH_RESULTS_LOADED': {
- const { search_results } = action;
+ console.debug(action)
return {
...core,
- search_results,
+ search_results: {
+ ...core?.search_results || {},
+ [action.key]: action.results,
+ },
};
}
diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js
index 48c883157..bbd06dddb 100755
--- a/src/js/services/mopidy/actions.js
+++ b/src/js/services/mopidy/actions.js
@@ -591,12 +591,12 @@ export function clearSearchResults() {
};
}
-export function getSearchResults(query, limit = 100, uri_schemes) {
+export function getSearchResults(query, providers, limit = 100) {
return {
type: 'MOPIDY_GET_SEARCH_RESULTS',
query,
+ providers,
limit,
- uri_schemes,
};
}
diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js
index 310019dfe..bc04c878e 100755
--- a/src/js/services/mopidy/middleware.js
+++ b/src/js/services/mopidy/middleware.js
@@ -8,6 +8,7 @@ import {
uriSource,
setFavicon,
titleCase,
+ getSearchResultKey,
} from '../../util/helpers';
import {
digestMopidyImages,
@@ -263,10 +264,11 @@ const MopidyMiddleware = (function () {
type,
term,
requestType,
- uri_scheme,
+ provider,
method = 'library.search',
data,
} = queue.shift();
+ const resultKey = getSearchResultKey({ provider, type, term });
const processKey = 'MOPIDY_GET_SEARCH_RESULTS';
const processor = store.getState().ui.processes[processKey];
@@ -281,7 +283,7 @@ const MopidyMiddleware = (function () {
content: i18n(
'services.mopidy.searching',
{
- provider: titleCase(uri_scheme.replace(':', '')),
+ provider: titleCase(provider.replace(':', '')),
type: requestType,
},
),
@@ -348,7 +350,7 @@ const MopidyMiddleware = (function () {
playlists: (response) => {
const playlists = response.filter(
(item) => {
- if (!item.uri.includes(uri_scheme)) return false;
+ if (!item.uri.includes(provider)) return false;
return item.name.toLowerCase().includes(term.toLowerCase());
},
);
@@ -371,8 +373,7 @@ const MopidyMiddleware = (function () {
(response) => {
if (response.length > 0) {
store.dispatch(coreActions.searchResultsLoaded(
- { term, type },
- requestType,
+ resultKey,
processResults[requestType](response),
));
}
@@ -1221,35 +1222,35 @@ const MopidyMiddleware = (function () {
case 'MOPIDY_GET_SEARCH_RESULTS': {
const {
- uri_schemes = [],
- query = {},
+ query: { term, type: queryType } = {},
+ providers,
} = action;
- const types = query.type === 'all'
+ const types = queryType === 'all'
? ['artists', 'albums', 'tracks', 'playlists']
- : [query.type];
+ : [queryType];
const queue = [];
- uri_schemes.forEach(
- (uri_scheme) => types.forEach(
+ providers.forEach(
+ (provider) => types.forEach(
(type) => {
const item = {
- type: query.type,
- term: query.term,
+ type,
+ term,
+ provider,
requestType: type,
- uri_scheme,
data: {
- uris: [uri_scheme],
+ uris: [provider],
},
};
switch (type) {
case 'tracks':
- item.data.query = { any: [query.term] };
+ item.data.query = { any: [term] };
break;
case 'artists':
- item.data.query = { artist: [query.term] };
+ item.data.query = { artist: [term] };
break;
case 'albums':
- item.data.query = { album: [query.term] };
+ item.data.query = { album: [term] };
break;
case 'playlists':
// Searching for playlists is not supported, so we get a simple
diff --git a/src/js/services/mopidy/reducer.js b/src/js/services/mopidy/reducer.js
index 9ab621311..ba6239ca9 100755
--- a/src/js/services/mopidy/reducer.js
+++ b/src/js/services/mopidy/reducer.js
@@ -103,33 +103,6 @@ export default function reducer(mopidy = {}, action) {
directory: { ...mopidy.directory, ...action.directory },
};
- /**
- * Searching
- * */
- case 'MOPIDY_CLEAR_SEARCH_RESULTS':
- return { ...mopidy, search_results: {} };
-
- case 'MOPIDY_SEARCH_RESULTS_LOADED':
- // Fetch or create our container
- if (mopidy.search_results) {
- var search_results = { ...mopidy.search_results };
- } else {
- var search_results = {};
- }
-
- search_results = {
- ...search_results,
- query: action.query,
- };
-
- if (search_results[action.context]) {
- search_results[action.context] = [...search_results[action.context], ...action.results];
- } else {
- search_results[action.context] = action.results;
- }
-
- return { ...mopidy, search_results };
-
default:
return mopidy;
}
diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js
index e34a8c711..0d47a00ed 100755
--- a/src/js/services/spotify/actions.js
+++ b/src/js/services/spotify/actions.js
@@ -6,6 +6,7 @@ import {
getFromUri,
uriType,
upgradeSpotifyPlaylistUris,
+ getSearchResultKey,
} from '../../util/helpers';
import {
formatPlaylistGroup,
@@ -613,7 +614,8 @@ export function getMore(endpoint, core_action = null, custom_action = null, extr
};
}
-export function getSearchResults({ type, term }, limit = 50, offset = 0) {
+export function getSearchResults(query, limit = 50, offset = 0) {
+ const { type, term } = query;
const processKey = 'SPOTIFY_GET_SEARCH_RESULTS';
return (dispatch, getState) => {
const {
@@ -641,24 +643,21 @@ export function getSearchResults({ type, term }, limit = 50, offset = 0) {
(response) => {
if (response.tracks !== undefined) {
dispatch(coreActions.searchResultsLoaded(
- { term, type },
- 'tracks',
+ getSearchResultKey({ provider: 'spotify', type: 'tracks', term }),
formatTracks(response.tracks.items),
));
}
if (response.artists !== undefined) {
dispatch(coreActions.searchResultsLoaded(
- { term, type },
- 'artists',
+ getSearchResultKey({ provider: 'spotify', type: 'artists', term }),
formatArtists(response.artists.items),
));
}
if (response.albums !== undefined) {
dispatch(coreActions.searchResultsLoaded(
- { term, type },
- 'albums',
+ getSearchResultKey({ provider: 'spotify', type: 'albums', term }),
formatAlbums(response.albums.items),
));
}
@@ -669,8 +668,7 @@ export function getSearchResults({ type, term }, limit = 50, offset = 0) {
can_edit: (meId === item.owner.id),
}));
dispatch(coreActions.searchResultsLoaded(
- { term, type },
- 'playlists',
+ getSearchResultKey({ provider: 'spotify', type: 'playlists', term }),
playlists,
));
}
diff --git a/src/js/store/index.js b/src/js/store/index.js
index eb520b929..cd861b384 100755
--- a/src/js/store/index.js
+++ b/src/js/store/index.js
@@ -38,6 +38,7 @@ let initialState = {
tracks: {},
items: {},
libraries: {},
+ search_results: {},
},
ui: {
language: 'en',
diff --git a/src/js/util/helpers.js b/src/js/util/helpers.js
index 20f7aa24f..860b106e4 100755
--- a/src/js/util/helpers.js
+++ b/src/js/util/helpers.js
@@ -603,6 +603,9 @@ const upgradeSpotifyPlaylistUri = function (uri) {
return upgradeSpotifyPlaylistUris([uri])[0];
};
+const getSearchResultKey = ({ provider, type, term }) =>
+ [provider.replace(':', ''), type, term].join(':');
+
export {
debounce,
throttle,
@@ -626,6 +629,7 @@ export {
upgradeSpotifyPlaylistUris,
upgradeSpotifyPlaylistUri,
iconFromKeyword,
+ getSearchResultKey,
};
export default {
@@ -651,4 +655,5 @@ export default {
upgradeSpotifyPlaylistUris,
upgradeSpotifyPlaylistUri,
iconFromKeyword,
+ getSearchResultKey,
};
diff --git a/src/js/util/selectors.js b/src/js/util/selectors.js
index 03782f690..a3f707cce 100755
--- a/src/js/util/selectors.js
+++ b/src/js/util/selectors.js
@@ -1,6 +1,6 @@
import { createSelector } from 'reselect';
import { indexToArray } from './arrays';
-import { isLoading } from './helpers';
+import { getSearchResultKey, isLoading } from './helpers';
import { i18n } from '../locale';
const getItem = (state, uri) => state.core.items[uri];
@@ -68,13 +68,14 @@ const makeLibrarySelector = (name, filtered = true) => createSelector(
},
);
-const makeSearchResultsSelector = (term, type) => createSelector(
+const makeSearchResultsSelector = (providers = [], term, type) => createSelector(
[getSearchResults],
- (searchResults) => {
- if (!searchResults || searchResults.query.term !== term) return [];
- return searchResults[type] || [];
- },
-);
+ (searchResults) => providers.reduce(
+ (acc, curr) => {[
+ ...acc,
+ ...searchResults[getSearchResultKey({ provider: curr, term, type })] || [],
+ ]}, []),
+ );
const makeProcessProgressSelector = (keys) => createSelector(
[getProcesses],
diff --git a/src/js/views/Search.js b/src/js/views/Search.js
index a15ecea2d..cf50da9b5 100755
--- a/src/js/views/Search.js
+++ b/src/js/views/Search.js
@@ -18,13 +18,21 @@ import { getSortSelector } from '../util/selectors';
const SORT_KEY = 'search_results';
const Search = () => {
- const { term, type = 'all' } = useParams();
+ const {
+ term,
+ providers: providersString = 'all',
+ type = 'all',
+ } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const lastQuery = useSelector((state) => state.core?.search_results?.query);
const [sortField, sortReverse] = useSelector(
(state) => getSortSelector(state, SORT_KEY, 'name'),
);
+ const allProviders = useSelector((state) => state.mopidy?.uri_schemes || []);
+ const providers = providersString == 'all'
+ ? [...allProviders]
+ : providersString.split(',').filter((str) => allProviders.indexOf(str) > -1);
useEffect(() => {
dispatch(setWindowTitle('Search'));
@@ -32,25 +40,38 @@ const Search = () => {
}, []);
useEffect(() => {
- if (term && type && term !== lastQuery?.term) {
+ console.debug(providers, lastQuery?.providers)
+ if (term && type && (term !== lastQuery?.term || providers.length !== lastQuery?.providers?.length)) {
+ console.debug('STARTING SEARCH', { term, type, providers })
dispatch(setWindowTitle(i18n('search.title_window', { term: decodeURIComponent(term) })));
- dispatch(startSearch({ term, type }));
+ dispatch(startSearch({ term, type, providers }));
}
- }, [term, type])
+ }, [term, type, providersString])
- const onSubmit = (nextTerm) => {
- const encodedTerm = encodeURIComponent(nextTerm);
- navigate(`/search/${type}/${encodedTerm}`);
+ const onSubmit = (term) => {
+ updateSearchQuery(term, providers);
+ }
+
+ const updateSearchQuery = (term, providers) => {
+ const encodedTerm = encodeURIComponent(term);
+ navigate(`/search/${type}/${providers.join(',')}/${encodedTerm || ''}`);
}
const onReset = () => navigate('/search');
- const onSortChange = (field) => {
+ const onProvidersChange = (providers) => {
+ console.debug(providers)
+ // ON BLUR then trigger search event
+ updateSearchQuery(term, providers)
+ dispatch(hideContextMenu());
+ }
+
+ const onSortChange = (value) => {
let reverse = false;
- if (field !== null && sortField === field) {
+ if (value !== null && sortField === value) {
reverse = !sortReverse;
}
- dispatch(setSort(SORT_KEY, field, reverse));
+ dispatch(setSort(SORT_KEY, value, reverse));
dispatch(hideContextMenu());
}
@@ -62,16 +83,27 @@ const Search = () => {
{ value: 'duration', label: i18n('common.duration') },
];
+ const providerOptions = allProviders.map((value) => ({ value, label: value}))
+
const options = (
-
+ <>
+
+
+ >
);
return (
diff --git a/src/scss/views/_search.scss b/src/scss/views/_search.scss
index 00e0e4ef2..06a266cda 100755
--- a/src/scss/views/_search.scss
+++ b/src/scss/views/_search.scss
@@ -5,7 +5,7 @@
position: absolute;
top: 30px;
left: 90px;
- right: 150px;
+ right: 250px;
input {
@include feature_font();