scratch-www/src/views/search/search.jsx

242 lines
8.3 KiB
React
Raw Normal View History

const bindAll = require('lodash.bindall');
const connect = require('react-redux').connect;
const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types');
const React = require('react');
2016-04-18 16:51:45 -04:00
const api = require('../../lib/api');
const Button = require('../../components/forms/button.jsx');
2018-07-27 12:54:59 -04:00
const Form = require('../../components/forms/form.jsx');
const Grid = require('../../components/grid/grid.jsx');
const navigationActions = require('../../redux/navigation.js');
2018-07-27 13:09:48 -04:00
const Select = require('../../components/forms/select.jsx');
const TitleBanner = require('../../components/title-banner/title-banner.jsx');
const Tabs = require('../../components/tabs/tabs.jsx');
2016-04-18 16:51:45 -04:00
const Page = require('../../components/page/www/page.jsx');
const render = require('../../lib/render.jsx');
2016-04-18 16:51:45 -04:00
2018-08-22 08:05:20 -04:00
const ACCEPTABLE_MODES = ['trending', 'popular', ''];
2016-04-18 16:51:45 -04:00
require('./search.scss');
class Search extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'getSearchState',
2018-07-27 12:54:59 -04:00
'handleChangeSortMode',
'handleGetSearchMore',
'getTab'
]);
this.state = this.getSearchState();
this.state.loaded = [];
this.state.loadNumber = 16;
2018-07-27 12:54:59 -04:00
this.state.mode = '';
this.state.offset = 0;
this.state.loadMore = false;
2018-08-22 14:17:26 -04:00
let mode = '';
const m = query.lastIndexOf('mode=');
if (m !== -1) {
mode = query.substring(m + 5, query.length).toLowerCase();
}
while (mode.indexOf('/') > -1) {
mode = mode.substring(0, term.indexOf('/'));
}
while (term.indexOf('&') > -1) {
mode = mode.substring(0, term.indexOf('&'));
}
mode = decodeURIComponent(mode.split('+').join(' '));
this.state.mode = mode;
}
componentDidMount () {
const query = window.location.search;
const q = query.lastIndexOf('q=');
let term = '';
if (q !== -1) {
term = query.substring(q + 2, query.length).toLowerCase();
}
while (term.indexOf('/') > -1) {
term = term.substring(0, term.indexOf('/'));
}
while (term.indexOf('&') > -1) {
term = term.substring(0, term.indexOf('&'));
}
term = decodeURIComponent(term.split('+').join(' '));
this.props.dispatch(navigationActions.setSearchTerm(term));
}
componentDidUpdate (prevProps) {
if (this.props.searchTerm !== prevProps.searchTerm) {
this.handleGetSearchMore();
}
}
getSearchState () {
let pathname = window.location.pathname.toLowerCase();
if (pathname[pathname.length - 1] === '/') {
pathname = pathname.substring(0, pathname.length - 1);
}
const start = pathname.lastIndexOf('/');
const type = pathname.substring(start + 1, pathname.length);
return {
tab: type,
loadNumber: 16
};
}
2018-07-27 12:54:59 -04:00
handleChangeSortMode (name, value) {
2018-08-22 08:05:20 -04:00
if (ACCEPTABLE_MODES.indexOf(value) !== -1) {
2018-07-27 12:54:59 -04:00
const term = this.props.searchTerm.split(' ').join('+');
window.location =
2018-07-31 17:26:03 -04:00
`${window.location.origin}/search/${this.state.tab}?q=${term}&mode=${value}`;
2018-07-27 12:54:59 -04:00
}
}
handleGetSearchMore () {
let termText = '';
2016-06-09 07:32:25 -04:00
if (this.props.searchTerm !== '') {
termText = `&q=${encodeURIComponent(this.props.searchTerm.split(' ').join('+'))}`;
}
const locale = this.props.intl.locale;
const loadNumber = this.state.loadNumber;
const offset = this.state.offset;
2018-07-27 12:54:59 -04:00
const mode = this.state.mode;
const queryString = `limit=${loadNumber}&offset=${offset}&language=${locale}&mode=${mode}${termText}`;
api({
uri: `/search/${this.state.tab}?${queryString}`
}, (err, body) => {
const loadedSoFar = this.state.loaded;
2016-06-09 07:32:25 -04:00
Array.prototype.push.apply(loadedSoFar, body);
const currentOffset = this.state.offset + this.state.loadNumber;
2018-02-12 09:26:39 -05:00
const willLoadMore = body.length === this.state.loadNumber;
this.setState({
loaded: loadedSoFar,
offset: currentOffset,
loadMore: willLoadMore
});
});
}
getTab (type) {
const term = this.props.searchTerm.split(' ').join('+');
let allTab = (
<a href={`/search/${type}?q=${term}/`}>
<li>
<img
className={`tab-icon ${type}`}
src={`/svgs/tabs/${type}-inactive.svg`}
/>
<FormattedMessage id={`general.${type}`} />
</li>
</a>
);
2018-01-31 13:00:27 -05:00
if (this.state.tab === type) {
allTab = (
<a href={`/search/${type}?q=${term}/`}>
<li className="active">
<img
className={`tab-icon ${type}`}
src={`/svgs/tabs/${type}-active.svg`}
/>
<FormattedMessage id={`general.${type}`} />
</li>
</a>
);
2016-04-18 16:51:45 -04:00
}
return allTab;
}
getProjectBox () {
const results = (
<Grid
cards
showAvatar
2018-02-12 09:26:39 -05:00
itemType={this.state.tab}
items={this.state.loaded}
showFavorites={false}
showLoves={false}
showViews={false}
/>
);
let searchAction = null;
if (this.state.loaded.length === 0 && this.state.offset !== 0) {
searchAction = <h2 className="search-prompt"><FormattedMessage id="general.searchEmpty" /></h2>;
} else if (this.state.loadMore) {
searchAction = (
2018-02-12 09:26:39 -05:00
<Button
onClick={this.handleGetSearchMore}
>
<FormattedMessage id="general.loadMore" />
</Button>
);
}
return (
<div
id="projectBox"
key="projectBox"
>
{results}
{searchAction}
</div>
);
}
render () {
2016-04-18 16:51:45 -04:00
return (
<div>
<div className="outer">
<TitleBanner className="masthead">
<div className="inner">
<h1 className="title-banner-h1">
<FormattedMessage id="general.search" />
</h1>
2018-01-30 09:53:25 -05:00
</div>
</TitleBanner>
<Tabs>
{this.getTab('projects')}
{this.getTab('studios')}
</Tabs>
2018-07-27 12:54:59 -04:00
<div className="sort-controls">
<Form className="sort-mode">
<Select
name="sort"
options={[
{
value: 'trending',
2018-08-01 09:16:41 -04:00
label: this.props.intl.formatMessage({id: 'search.trending'})
2018-07-27 12:54:59 -04:00
},
{
value: 'popular',
2018-08-01 09:16:41 -04:00
label: this.props.intl.formatMessage({id: 'search.popular'})
2018-07-27 12:54:59 -04:00
}
]}
value={this.state.mode}
onChange={this.handleChangeSortMode}
/>
</Form>
</div>
2018-02-12 09:26:39 -05:00
{this.getProjectBox()}
2016-04-18 16:51:45 -04:00
</div>
</div>
);
}
}
2016-04-18 16:51:45 -04:00
Search.propTypes = {
dispatch: PropTypes.func,
intl: intlShape,
2018-01-31 13:00:27 -05:00
searchTerm: PropTypes.string
2016-10-31 10:05:08 -04:00
};
const mapStateToProps = state => ({
searchTerm: state.navigation
});
const WrappedSearch = injectIntl(Search);
const ConnectedSearch = connect(mapStateToProps)(WrappedSearch);
2016-10-31 10:05:08 -04:00
render(
<Page><ConnectedSearch /></Page>,
document.getElementById('app'),
{navigation: navigationActions.navigationReducer}
);