348 lines
9.3 KiB
JavaScript
348 lines
9.3 KiB
JavaScript
import React, { Component } from 'react';
|
|
|
|
import AdvancedSearch from './search-advanced-controls'
|
|
import TorrentsStatistic from './torrent-statistic'
|
|
|
|
import TextField from 'material-ui/TextField';
|
|
import RaisedButton from 'material-ui/RaisedButton';
|
|
import RefreshIndicator from 'material-ui/RefreshIndicator';
|
|
|
|
import Checkbox from 'material-ui/Checkbox';
|
|
import Visibility from 'material-ui/svg-icons/action/visibility';
|
|
import VisibilityOff from 'material-ui/svg-icons/action/visibility-off';
|
|
import AddIcon from 'material-ui/svg-icons/content/add';
|
|
import RemoveIcon from 'material-ui/svg-icons/content/remove';
|
|
|
|
import _ from 'lodash'
|
|
import singleton from './singleton';
|
|
|
|
class Search extends Component {
|
|
constructor(props)
|
|
{
|
|
super(props)
|
|
this.onSearchUpdate = () => {}
|
|
|
|
this.state = {
|
|
searchingIndicator: false,
|
|
safeSearchText: __('safe search enabled'),
|
|
safeSearchColor: 'rgb(0, 188, 212)',
|
|
moreTorrentsIndicator: false,
|
|
moreFilesIndicator: false,
|
|
orderBy: null,
|
|
orderDesc: false,
|
|
advancedSearch: false,
|
|
}
|
|
this.searchLimit = 10
|
|
this.advanced = {}
|
|
this.searchError = undefined;
|
|
}
|
|
|
|
search(oldSearch) {
|
|
window.router('/')
|
|
this.setState({
|
|
searchingIndicator: true
|
|
});
|
|
this.onSearchUpdate('indicator')
|
|
|
|
this.searchTorrents = [];
|
|
this.moreSearchTorrents = true;
|
|
this.searchFiles = [];
|
|
this.moreSearchFiles = true;
|
|
this.currentSearch = this.searchValue;
|
|
let queries = 2;
|
|
let searchTorrentsParams = {
|
|
limit: this.searchLimit,
|
|
safeSearch: !this.notSafeSearch,
|
|
orderBy: this.state.orderBy,
|
|
orderDesc: this.state.orderDesc,
|
|
};
|
|
if(this.state.advancedSearch && this.advanced)
|
|
searchTorrentsParams = Object.assign(searchTorrentsParams, this.advanced);
|
|
|
|
window.torrentSocket.emit('searchTorrent', oldSearch ? this.currentSearch : this.searchValue, searchTorrentsParams, window.customLoader((torrents) => {
|
|
if(torrents) {
|
|
this.searchTorrents = torrents;
|
|
if(torrents.length != this.searchLimit)
|
|
this.moreSearchTorrents = false;
|
|
}
|
|
else
|
|
{
|
|
this.moreSearchTorrents = false;
|
|
}
|
|
if(--queries == 0) {
|
|
this.setState({
|
|
searchingIndicator: false
|
|
});
|
|
}
|
|
this.onSearchUpdate('torrents')
|
|
}));
|
|
let searchFilesParams = {
|
|
limit: this.searchLimit,
|
|
safeSearch: !this.notSafeSearch,
|
|
orderBy: this.state.orderBy,
|
|
orderDesc: this.state.orderDesc,
|
|
};
|
|
if(this.state.advancedSearch && this.advanced)
|
|
searchFilesParams = Object.assign(searchFilesParams, this.advanced);
|
|
|
|
window.torrentSocket.emit('searchFiles', oldSearch ? this.currentSearch : this.searchValue, searchFilesParams, window.customLoader((torrents) => {
|
|
if(torrents) {
|
|
this.searchFiles = torrents;
|
|
let files = 0;
|
|
torrents.forEach((torrent) => {
|
|
if(torrent.path && torrent.path.length > 0)
|
|
files += torrent.path.length
|
|
});
|
|
if(files != this.searchLimit)
|
|
this.moreSearchFiles = false;
|
|
}
|
|
else
|
|
{
|
|
this.moreSearchFiles = false;
|
|
}
|
|
if(--queries == 0) {
|
|
this.setState({
|
|
searchingIndicator: false
|
|
});
|
|
}
|
|
this.onSearchUpdate('files')
|
|
}));
|
|
}
|
|
moreTorrents() {
|
|
this.setState({moreTorrentsIndicator: true});
|
|
this.onSearchUpdate('indicator')
|
|
|
|
window.torrentSocket.emit('searchTorrent', this.currentSearch, {
|
|
index: this.searchTorrents.length,
|
|
limit: this.searchLimit,
|
|
safeSearch: !this.notSafeSearch,
|
|
orderBy: this.state.orderBy,
|
|
orderDesc: this.state.orderDesc,
|
|
}, window.customLoader((torrents) => {
|
|
if(torrents) {
|
|
this.searchTorrents = this.searchTorrents.concat(torrents);
|
|
if(torrents.length != this.searchLimit)
|
|
this.moreSearchTorrents = false;
|
|
|
|
this.setState({moreTorrentsIndicator: false});
|
|
this.onSearchUpdate('more torrents')
|
|
}
|
|
}));
|
|
}
|
|
calcTorrentsFiles(torrents)
|
|
{
|
|
let files = 0;
|
|
torrents.forEach((torrent) => {
|
|
if(torrent.path && torrent.path.length > 0)
|
|
files += torrent.path.length
|
|
});
|
|
return files
|
|
}
|
|
moreFiles() {
|
|
let index = 0;
|
|
this.searchFiles.forEach((torrent) => {
|
|
if(torrent.path && torrent.path.length > 0)
|
|
index += torrent.path.length;
|
|
});
|
|
|
|
this.setState({moreFilesIndicator: true});
|
|
this.onSearchUpdate('indicator')
|
|
|
|
window.torrentSocket.emit('searchFiles', this.currentSearch, {
|
|
index: index,
|
|
limit: this.searchLimit,
|
|
safeSearch: !this.notSafeSearch,
|
|
orderBy: this.state.orderBy,
|
|
orderDesc: this.state.orderDesc,
|
|
}, window.customLoader((torrents) => {
|
|
if(torrents) {
|
|
this.searchFiles = this.searchFiles.concat(torrents);
|
|
|
|
if(this.calcTorrentsFiles(torrents) != this.searchLimit)
|
|
this.moreSearchFiles = false;
|
|
|
|
this.mergeFiles()
|
|
|
|
this.setState({moreFilesIndicator: false});
|
|
this.onSearchUpdate('more files')
|
|
}
|
|
}));
|
|
}
|
|
mergeFiles()
|
|
{
|
|
for(let i = 0; i < this.searchFiles.length; i++)
|
|
{
|
|
for(let j = i + 1; j < this.searchFiles.length; j++)
|
|
{
|
|
if(this.searchFiles[i].hash != this.searchFiles[j].hash)
|
|
continue
|
|
|
|
if(!this.searchFiles[i].remove)
|
|
{
|
|
this.searchFiles[i].path = this.searchFiles[i].path.concat(this.searchFiles[j].path)
|
|
}
|
|
|
|
this.searchFiles[j].remove = true
|
|
}
|
|
}
|
|
|
|
this.searchFiles = this.searchFiles.filter(torrent => !torrent.remove)
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.newStatisticFunc = (statistic) => {
|
|
if(statistic) {
|
|
this.stats = statistic;
|
|
this.forceUpdate();
|
|
}
|
|
};
|
|
window.torrentSocket.emit('statistic', window.customLoader(this.newStatisticFunc));
|
|
window.torrentSocket.on('newStatistic', this.newStatisticFunc);
|
|
|
|
this.remoteSearchTorrent = (torrents) => {
|
|
if(!torrents)
|
|
return
|
|
|
|
if(torrents.length === this.searchLimit)
|
|
this.moreSearchTorrents = true;
|
|
|
|
this.searchTorrents = _.unionBy(this.searchTorrents, torrents, 'hash')
|
|
this.onSearchUpdate('remote torrents')
|
|
}
|
|
window.torrentSocket.on('remoteSearchTorrent', this.remoteSearchTorrent);
|
|
|
|
this.remoteSearchFiles = (torrents) => {
|
|
if(!torrents)
|
|
return
|
|
|
|
if(torrents.length > 0 && this.calcTorrentsFiles(torrents) === this.searchLimit)
|
|
this.moreSearchFiles = true;
|
|
|
|
this.searchFiles = _.unionBy(this.searchFiles, torrents, 'hash')
|
|
this.mergeFiles()
|
|
this.onSearchUpdate('remote files')
|
|
}
|
|
window.torrentSocket.on('remoteSearchFiles', this.remoteSearchFiles);
|
|
}
|
|
componentWillUnmount()
|
|
{
|
|
if(this.newStatisticFunc)
|
|
window.torrentSocket.off('newStatistic', this.newStatisticFunc);
|
|
|
|
if(this.remoteSearchTorrent)
|
|
window.torrentSocket.off('remoteSearchTorrent', this.remoteSearchTorrent);
|
|
|
|
if(this.remoteSearchFiles)
|
|
window.torrentSocket.off('remoteSearchFiles', this.remoteSearchFiles);
|
|
}
|
|
setSafeSearch(ch) {
|
|
this.notSafeSearch = ch;
|
|
if(ch)
|
|
{
|
|
return {safeSearchText: __('safe search disabled'), safeSearchColor: '#EC407A'}
|
|
}
|
|
else
|
|
{
|
|
return {safeSearchText: __('safe search enabled'), safeSearchColor: 'rgb(0, 188, 212)'}
|
|
}
|
|
}
|
|
render() {
|
|
const style = {
|
|
refresh: {
|
|
display: 'inline-block',
|
|
position: 'relative',
|
|
},
|
|
};
|
|
|
|
return (
|
|
<div className="column w100p center">
|
|
<div className='row inline w100p pad0-75 search-row' style={{minWidth: '35em', backgroundColor: 'white', paddingTop: 0, paddingBottom: this.searchError ? 17 : 0, margin: 5, borderRadius: 3}}>
|
|
<TextField
|
|
style={{marginTop: -12}}
|
|
hintText={__('Search torrent or file')}
|
|
floatingLabelText={__('What to search?')}
|
|
fullWidth={true}
|
|
ref='searchInput'
|
|
defaultValue={this.searchValue}
|
|
errorText={this.searchError}
|
|
onKeyPress={(e) => {
|
|
if (e.key === 'Enter') {
|
|
this.search();
|
|
}
|
|
}}
|
|
onChange={e => {
|
|
this.searchValue = e.target.value
|
|
if(this.searchValue.length < 3 && this.searchValue.length > 0)
|
|
this.searchError = __('too short string for search');
|
|
else
|
|
this.searchError = undefined;
|
|
this.forceUpdate()
|
|
}}
|
|
/>
|
|
|
|
<div style={{width: 25, height: 25, margin: 2}}>
|
|
<Checkbox
|
|
ref='safeSearch'
|
|
checked={this.notSafeSearch ? true : false}
|
|
checkedIcon={<Visibility />}
|
|
uncheckedIcon={<VisibilityOff />}
|
|
iconStyle={{fill: this.state.safeSearchColor}}
|
|
onCheck={(ev, ch) => {
|
|
this.setState(this.setSafeSearch(ch));
|
|
}}
|
|
style={{paddingBottom: '0.8em'}}
|
|
/>
|
|
</div>
|
|
<div style={{width: 25, height: 25, margin: 2}}>
|
|
<Checkbox
|
|
ref='advancedSearch'
|
|
checked={this.state.advancedSearch}
|
|
checkedIcon={<RemoveIcon />}
|
|
uncheckedIcon={<AddIcon />}
|
|
iconStyle={{fill: 'black'}}
|
|
onCheck={(ev, ch) => {
|
|
this.setState({advancedSearch: ch});
|
|
}}
|
|
style={{paddingBottom: '0.8em'}}
|
|
/>
|
|
</div>
|
|
|
|
<RaisedButton style={{marginLeft: '10px'}} label={__('Search')} primary={true} onClick={() =>{
|
|
this.search()
|
|
}} />
|
|
</div>
|
|
{
|
|
this.state.advancedSearch
|
|
&&
|
|
<AdvancedSearch onChange={(state) => {
|
|
this.advanced = state;
|
|
}} state={this.advanced} />
|
|
|
|
}
|
|
{
|
|
this.stats
|
|
&&
|
|
<TorrentsStatistic stats={this.stats} />
|
|
}
|
|
{
|
|
this.state.searchingIndicator
|
|
?
|
|
<div className='pad1'>
|
|
<RefreshIndicator
|
|
size={50}
|
|
left={0}
|
|
top={0}
|
|
loadingColor="#FF9800"
|
|
status="loading"
|
|
style={style.refresh}
|
|
/>
|
|
</div>
|
|
:
|
|
null
|
|
}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default singleton(Search) |