Files
Iris/src/js/views/Search.js

351 lines
10 KiB
JavaScript
Raw Normal View History

2016-11-03 08:57:16 +13:00
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Switch, Route } from 'react-router-dom';
2019-09-05 15:36:11 +12:00
import Header from '../components/Header';
import Icon from '../components/Icon';
import DropdownField from '../components/Fields/DropdownField';
import TrackList from '../components/TrackList';
import ArtistGrid from '../components/ArtistGrid';
import AlbumGrid from '../components/AlbumGrid';
import PlaylistGrid from '../components/PlaylistGrid';
import LazyLoadListener from '../components/LazyLoadListener';
import SearchForm from '../components/Fields/SearchForm';
import URILink from '../components/URILink';
2020-06-09 08:17:19 +12:00
import SearchResults from '../components/SearchResults';
2019-09-05 15:36:11 +12:00
import * as coreActions from '../services/core/actions';
import * as uiActions from '../services/ui/actions';
import * as mopidyActions from '../services/mopidy/actions';
import * as spotifyActions from '../services/spotify/actions';
import {
titleCase,
getIndexedRecords,
} from '../util/helpers';
import { sortItems } from '../util/arrays';
2019-09-05 15:36:11 +12:00
class Search extends React.Component {
2019-09-05 15:36:11 +12:00
constructor(props) {
super(props);
this.state = {
2020-06-09 21:09:29 +12:00
type: props.type || 'all',
term: props.term || '',
2019-09-05 15:36:11 +12:00
};
}
componentDidMount = () => {
const {
uiActions: {
setWindowTitle,
},
} = this.props;
setWindowTitle('Search');
2019-09-05 15:36:11 +12:00
// Auto-focus on the input field
$(document).find('.search-form input').focus();
}
componentDidUpdate = ({
type: prevType,
term: prevTerm,
mopidy_connected: prev_mopidy_connected,
}) => {
const {
type: typeProp,
term: termProp,
mopidy_connected,
uri_schemes_search_enabled,
} = this.props;
const { type, term } = this.state;
// Already connected, but search properties changed
if (prev_mopidy_connected && mopidy_connected) {
if (prevType !== typeProp || prevTerm !== termProp) {
this.digestUri();
2019-09-05 15:36:11 +12:00
}
// Connected
} else if (!prev_mopidy_connected && mopidy_connected && uri_schemes_search_enabled) {
this.search(type, term);
2019-09-05 15:36:11 +12:00
}
}
onSubmit = (term) => {
2020-06-09 08:17:19 +12:00
const { type } = this.state;
const { history } = this.props;
const encodedTerm = encodeURIComponent(term);
2019-09-05 15:36:11 +12:00
this.setState(
{ term },
() => {
history.push(`/search/${type}/${encodedTerm}`);
2019-09-05 15:36:11 +12:00
},
);
}
2020-06-09 21:09:29 +12:00
onReset = () => {
const { history } = this.props;
history.push('/search');
}
onSortChange = (value) => {
const { uiActions: { hideContextMenu } } = this.props;
this.setSort(value);
hideContextMenu();
}
2019-09-05 15:36:11 +12:00
onSourceChange = (value) => {
const {
uiActions: {
set,
hideContextMenu,
},
} = this.props;
set({ uri_schemes_search_enabled: value });
hideContextMenu();
}
onSourceClose = () => {
spotifyActions.clearSearchResults();
mopidyActions.clearSearchResults();
this.search();
};
digestUri = () => {
const {
type,
term,
} = this.props;
if (type && term) {
this.setState({ type, term }, () => {
this.search();
});
2020-06-09 21:09:29 +12:00
} else if (!term || term === '') {
this.clearSearch();
2019-09-05 15:36:11 +12:00
}
}
clearSearch = () => {
const {
spotifyActions,
mopidyActions,
uiActions: {
setWindowTitle,
},
} = this.props;
2019-09-05 15:36:11 +12:00
spotifyActions.clearSearchResults();
mopidyActions.clearSearchResults();
setWindowTitle('Search');
this.setState({ term: '' });
}
search = () => {
const {
uiActions: {
setWindowTitle,
},
mopidy_connected,
uri_schemes_search_enabled,
mopidyActions,
spotifyActions,
mopidy_search_results: {
query: {
term: mopidyTerm,
type: mopidyType,
} = {},
},
spotify_search_results: {
query: {
term: spotifyTerm,
type: spotifyType,
} = {},
},
} = this.props;
const {
type,
term,
} = this.state;
console.info(`Searching for ${type} matching "${term}"`);
setWindowTitle(`Search: ${decodeURIComponent(term)}`);
if (type && term && mopidy_connected && uri_schemes_search_enabled) {
if (mopidyTerm !== term || mopidyType !== type) {
mopidyActions.clearSearchResults();
mopidyActions.getSearchResults(type, term);
2019-09-05 15:36:11 +12:00
}
if ((spotifyTerm !== term || spotifyType !== type) && uri_schemes_search_enabled.includes('spotify:')) {
spotifyActions.clearSearchResults();
spotifyActions.getSearchResults(type, term);
2019-09-05 15:36:11 +12:00
}
}
}
loadMore = (type) => {
2019-09-05 15:36:11 +12:00
alert(`load more: ${type}`);
// this.props.spotifyActions.getURL(this.props['spotify_'+type+'_more'], 'SPOTIFY_SEARCH_RESULTS_LOADED_MORE_'+type.toUpperCase());
}
setSort = (value) => {
const {
sort,
sort_reverse,
uiActions: {
set,
},
} = this.props;
2019-09-05 15:36:11 +12:00
let reverse = false;
if (sort === value) reverse = !sort_reverse;
2019-09-05 15:36:11 +12:00
const data = {
search_results_sort_reverse: reverse,
search_results_sort: value,
};
set(data);
}
2020-06-09 08:17:19 +12:00
render = () => {
const {
term,
type,
} = this.state;
const {
uri_schemes,
sort,
2020-06-09 16:44:17 +12:00
sort_reverse,
history,
uri_schemes_search_enabled,
uiActions,
2020-06-09 08:17:19 +12:00
} = this.props;
2019-09-05 15:36:11 +12:00
const sort_options = [
{ value: 'followers', label: 'Popularity' },
{ value: 'name', label: 'Name' },
{ value: 'artists.name', label: 'Artist' },
{ value: 'duration', label: 'Duration' },
{ value: 'uri', label: 'Source' },
2019-09-05 15:36:11 +12:00
];
const provider_options = [];
2020-06-09 08:17:19 +12:00
for (let i = 0; i < uri_schemes.length; i++) {
2019-09-05 15:36:11 +12:00
provider_options.push({
2020-06-09 08:17:19 +12:00
value: uri_schemes[i],
label: titleCase(uri_schemes[i].replace(':', '').replace('+', ' ')),
2019-09-05 15:36:11 +12:00
});
}
const options = (
<span>
<DropdownField
icon="swap_vert"
2019-09-05 15:36:11 +12:00
name="Sort"
2020-06-09 08:17:19 +12:00
value={sort}
valueAsLabel
2019-09-05 15:36:11 +12:00
options={sort_options}
2020-06-09 16:44:17 +12:00
selected_icon={sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down'}
handleChange={this.onSortChange}
2019-09-05 15:36:11 +12:00
/>
<DropdownField
icon="cloud"
name="Sources"
2020-06-09 16:44:17 +12:00
value={uri_schemes_search_enabled}
2019-09-05 15:36:11 +12:00
options={provider_options}
handleChange={this.onSourceChange}
onClose={this.onSourceClose}
2019-09-05 15:36:11 +12:00
/>
</span>
);
return (
<div className="view search-view">
2020-06-09 16:44:17 +12:00
<Header options={options} uiActions={uiActions}>
2019-09-05 15:36:11 +12:00
<Icon name="search" type="material" />
</Header>
<SearchForm
2020-06-09 21:09:29 +12:00
key={`search_form_${type}_${term}`}
2020-06-09 16:44:17 +12:00
history={history}
term={term}
onSubmit={this.onSubmit}
2020-06-09 21:09:29 +12:00
onReset={this.onReset}
2019-09-05 15:36:11 +12:00
/>
<div className="content-wrapper">
<Switch>
2020-06-09 08:17:19 +12:00
<Route path="/search/artists/:term">
<SearchResults type="artists" query={{ term, type }} />
2019-09-05 15:36:11 +12:00
</Route>
2020-06-09 08:17:19 +12:00
<Route path="/search/albums/:term">
<SearchResults type="albums" query={{ term, type }} />
2019-09-05 15:36:11 +12:00
</Route>
2020-06-09 08:17:19 +12:00
<Route path="/search/playlists/:term">
<SearchResults type="playlists" query={{ term, type }} />
2019-09-05 15:36:11 +12:00
</Route>
2020-06-09 08:17:19 +12:00
<Route path="/search/tracks/:term">
<SearchResults type="tracks" query={{ term, type }} />
2019-09-05 15:36:11 +12:00
</Route>
<Route path="/search">
2020-06-09 08:17:19 +12:00
<div className="search-result-sections cf">
2020-06-09 16:44:17 +12:00
<section className="search-result-sections__item">
<div className="inner">
<SearchResults type="artists" query={{ term, type }} all />
</div>
</section>
<section className="search-result-sections__item">
<div className="inner">
<SearchResults type="albums" query={{ term, type }} all />
</div>
</section>
<section className="search-result-sections__item">
<div className="inner">
<SearchResults type="playlists" query={{ term, type }} all />
</div>
</section>
2020-06-09 08:17:19 +12:00
</div>
<SearchResults type="tracks" query={{ term, type }} all />
2019-09-05 15:36:11 +12:00
</Route>
</Switch>
</div>
</div>
);
}
2016-11-03 08:57:16 +13:00
}
2019-09-05 15:36:11 +12:00
const mapStateToProps = (state, ownProps) => ({
type: ownProps.match.params.type,
term: ownProps.match.params.term,
mopidy_connected: state.mopidy.connected,
albums: (state.core.albums ? state.core.albums : []),
artists: (state.core.artists ? state.core.artists : []),
playlists: (state.core.playlists ? state.core.playlists : []),
tracks: (state.core.tracks ? state.core.tracks : []),
uri_schemes_search_enabled: (state.ui.uri_schemes_search_enabled ? state.ui.uri_schemes_search_enabled : []),
uri_schemes_priority: (state.ui.uri_schemes_priority ? state.ui.uri_schemes_priority : []),
uri_schemes: (state.mopidy.uri_schemes ? state.mopidy.uri_schemes : []),
mopidy_search_results: (state.mopidy.search_results ? state.mopidy.search_results : {}),
spotify_search_results: (state.spotify.search_results ? state.spotify.search_results : {}),
sort: (state.ui.search_results_sort ? state.ui.search_results_sort : 'followers.total'),
sort_reverse: (!!state.ui.search_results_sort_reverse),
});
const mapDispatchToProps = (dispatch) => ({
coreActions: bindActionCreators(coreActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Search);