diff --git a/index.js b/index.js
index e4a9fa9..dbcf304 100644
--- a/index.js
+++ b/index.js
@@ -295,17 +295,41 @@ io.on('connection', function(socket)
let args = [text, index, limit];
const orderBy = navigation.orderBy;
let order = '';
+ let where = '';
if(orderBy && orderBy.length > 0)
{
const orderDesc = navigation.orderDesc ? 'DESC' : 'ASC';
args.splice(1, 0, orderBy);
order = 'ORDER BY ?? ' + orderDesc;
}
+ if(safeSearch)
+ {
+ where += " and contentcategory != 'xxx' ";
+ }
+ if(navigation.type && navigation.type.length > 0)
+ {
+ where += ' and contenttype = ' + mysqlPool.escape(navigation.type) + ' ';
+ }
+ if(navigation.size)
+ {
+ if(navigation.size.max > 0)
+ where += ' and size < ' + mysqlPool.escape(navigation.size.max) + ' ';
+ if(navigation.size.min > 0)
+ where += ' and size > ' + mysqlPool.escape(navigation.size.min) + ' ';
+ }
+ if(navigation.files)
+ {
+ if(navigation.files.max > 0)
+ where += ' and files < ' + mysqlPool.escape(navigation.files.max) + ' ';
+ if(navigation.files.min > 0)
+ where += ' and files > ' + mysqlPool.escape(navigation.files.min) + ' ';
+ }
+ console.log(where)
let searchList = [];
//args.splice(orderBy && orderBy.length > 0 ? 1 : 0, 1);
- //mysqlPool.query('SELECT * FROM `torrents` WHERE `name` like \'%' + text + '%\' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
- sphinx.query('SELECT * FROM `torrents_index`,`torrents_index_delta` WHERE MATCH(?) ' + (safeSearch ? "and contentcategory != 'xxx'" : '') + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
+ //mysqlPool.query('SELECT * FROM `torrents` WHERE `name` like \'%' + text + '%\' ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
+ sphinx.query('SELECT * FROM `torrents_index`,`torrents_index_delta` WHERE MATCH(?) ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
if(!rows) {
console.log(error)
callback(undefined)
@@ -335,18 +359,42 @@ io.on('connection', function(socket)
let args = [text, index, limit];
const orderBy = navigation.orderBy;
let order = '';
+ let where = '';
+
if(orderBy && orderBy.length > 0)
{
const orderDesc = navigation.orderDesc ? 'DESC' : 'ASC';
args.splice(1, 0, orderBy);
order = 'ORDER BY ?? ' + orderDesc;
}
+ if(safeSearch)
+ {
+ where += " and contentcategory != 'xxx' ";
+ }
+ if(navigation.type && navigation.type.length > 0)
+ {
+ where += ' and contenttype = ' + mysqlPool.escape(navigation.type) + ' ';
+ }
+ if(navigation.size)
+ {
+ if(navigation.size.max > 0)
+ where += ' and size < ' + mysqlPool.escape(navigation.size.max) + ' ';
+ if(navigation.size.min > 0)
+ where += ' and size > ' + mysqlPool.escape(navigation.size.min) + ' ';
+ }
+ if(navigation.files)
+ {
+ if(navigation.files.max > 0)
+ where += ' and files < ' + mysqlPool.escape(navigation.files.max) + ' ';
+ if(navigation.files.min > 0)
+ where += ' and files > ' + mysqlPool.escape(navigation.files.min) + ' ';
+ }
let search = {};
let searchList = [];
//args.splice(orderBy && orderBy.length > 0 ? 1 : 0, 1);
- //mysqlPool.query('SELECT * FROM `files` inner join torrents on(torrents.hash = files.hash) WHERE files.path like \'%' + text + '%\' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
- sphinx.query('SELECT * FROM `files_index`,`files_index_delta` WHERE MATCH(?) ' + (safeSearch ? "and contentcategory != 'xxx'" : '') + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
+ //mysqlPool.query('SELECT * FROM `files` inner join torrents on(torrents.hash = files.hash) WHERE files.path like \'%' + text + '%\' ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
+ sphinx.query('SELECT * FROM `files_index`,`files_index_delta` WHERE MATCH(?) ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
if(!rows) {
console.log(error)
callback(undefined)
diff --git a/package.json b/package.json
index 230fc60..936503a 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
"phantomjs-prebuilt": "^2.1.15",
"react": "^15.6.2",
"react-dom": "^15.6.2",
+ "react-input-range": "^1.2.1",
"react-tap-event-plugin": "^2.0.1",
"sitemap": "^1.13.0",
"socket.io": "^2.0.4"
diff --git a/src/css/izi/components.css b/src/css/izi/components.css
index e452495..77b69a8 100644
--- a/src/css/izi/components.css
+++ b/src/css/izi/components.css
@@ -59,3 +59,28 @@
width: 100%
}
}
+
+.filter-control-border {
+ top: -10px;
+ padding-left: 40px;
+}
+
+@media only screen and (max-width: 530px)
+{
+ .filter-control-row {
+ flex-wrap: wrap;
+ padding-top: 28px;
+ }
+
+ .filter-row {
+ padding-top: 30px;
+ align-items: inherit !important;
+ flex-direction: column;
+ }
+
+ .filter-control-border {
+ padding-top: 5px;
+ top: 6px;
+ padding-left: 0px;
+ }
+}
\ No newline at end of file
diff --git a/src/input-files-filter.js b/src/input-files-filter.js
new file mode 100644
index 0000000..c1080b1
--- /dev/null
+++ b/src/input-files-filter.js
@@ -0,0 +1,72 @@
+import React, { Component } from 'react';
+import InputRange from 'react-input-range';
+import 'react-input-range/lib/css/index.css';
+import formatBytes from './format-bytes'
+
+import SelectField from 'material-ui/SelectField';
+import MenuItem from 'material-ui/MenuItem';
+import Checkbox from 'material-ui/Checkbox';
+
+export default class InputFilesFilter extends Component {
+ constructor(props)
+ {
+ super(props)
+ this.state = {
+ files: this.props.value || { min: 0, max: 50 },
+ enabled: false || this.props.enabled,
+ filesMax: this.props.filesMax || 100 // 1mb
+ }
+ }
+ setState(val)
+ {
+ if(val.filesMax && this.state.files.max > val.filesMax)
+ val.files = {min: this.state.files.min, max: val.filesMax};
+
+ if(val.filesMax && this.state.files.min > val.filesMax)
+ val.files = {min: 0, max: val.files ? val.files.max || this.state.files.max : this.state.files.max };
+
+ super.setState(val, () => {
+ if(this.props.onChange)
+ this.props.onChange({
+ enabled: this.state.enabled,
+ filesMax: this.state.filesMax,
+ files: !this.state.enabled ? {min: 0, max: 0} : this.state.files
+ })
+ })
+ }
+ render() {
+ return (
+
+
this.setState({enabled: !this.state.enabled})}
+ />
+
+ this.setState({ files })}
+ />
+ this.setState({filesMax: value})}
+ className='filter-control-border'
+ >
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/input-size.js b/src/input-size.js
new file mode 100644
index 0000000..2aecc9a
--- /dev/null
+++ b/src/input-size.js
@@ -0,0 +1,73 @@
+import React, { Component } from 'react';
+import InputRange from 'react-input-range';
+import 'react-input-range/lib/css/index.css';
+import formatBytes from './format-bytes'
+
+import SelectField from 'material-ui/SelectField';
+import MenuItem from 'material-ui/MenuItem';
+import Checkbox from 'material-ui/Checkbox';
+
+export default class InputSize extends Component {
+ constructor(props)
+ {
+ super(props)
+ this.state = {
+ size: this.props.value || { min: 0, max: 500 * 1024 },
+ enabled: false || this.props.enabled,
+ maxSize: this.props.maxSize || 1024 * 1024 // 1mb
+ }
+ }
+ setState(val)
+ {
+ if(val.maxSize && this.state.size.max > val.maxSize)
+ val.size = {min: this.state.size.min, max: val.maxSize};
+
+ if(val.maxSize && this.state.size.min > val.maxSize)
+ val.size = {min: 0, max: val.size ? val.size.max || this.state.size.max : this.state.size.max };
+
+ super.setState(val, () => {
+ if(this.props.onChange)
+ this.props.onChange({
+ enabled: this.state.enabled,
+ maxSize: this.state.maxSize,
+ size: !this.state.enabled ? {min: 0, max: 0} : this.state.size
+ })
+ })
+ }
+ render() {
+ return (
+
+
this.setState({enabled: !this.state.enabled})}
+ />
+
+ formatBytes(size)}
+ style={this.props.style}
+ className={this.props.className}
+ onChange={size => this.setState({ size })}
+ />
+ this.setState({maxSize: value})}
+ className='filter-control-border'
+ >
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/search-advanced-controls.js b/src/search-advanced-controls.js
new file mode 100644
index 0000000..101185f
--- /dev/null
+++ b/src/search-advanced-controls.js
@@ -0,0 +1,56 @@
+import React, { Component } from 'react';
+import SelectField from 'material-ui/SelectField';
+import MenuItem from 'material-ui/MenuItem';
+import InputSize from './input-size';
+import FilesFilterInput from './input-files-filter';
+
+export default class AdvancedSearchControl extends Component {
+ constructor(props)
+ {
+ super(props)
+ this.state = {
+ type: undefined,
+ size: {min: 0, max: 500 * 1024},
+ maxSize: 1024 * 1024,
+ sizeEnabled: false,
+ filesEnabled: false,
+ files: {min: 0, max: 5},
+ filesMax: 100,
+ }
+ if(this.props.state)
+ this.state = Object.assign(this.state, this.props.state)
+ }
+ setState(val)
+ {
+ super.setState(val, (v) => {
+ if(this.props.onChange)
+ this.props.onChange(this.state)
+ })
+ }
+ render() {
+ return (
+
+
this.setState({type: value})}
+ >
+
+
+
+
+
+
+
+
+
+
+ this.setState({size, maxSize, sizeEnabled: enabled})} />
+
+
+ this.setState({files, filesMax, filesEnabled: enabled})} />
+
+
+ );
+ }
+}
diff --git a/src/search.js b/src/search.js
index 193056b..aa37f79 100644
--- a/src/search.js
+++ b/src/search.js
@@ -1,6 +1,7 @@
import React, { Component } from 'react';
import SearchResults from './search-results'
+import AdvancedSearch from './search-advanced-controls'
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import RefreshIndicator from 'material-ui/RefreshIndicator';
@@ -8,6 +9,8 @@ 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 SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
@@ -28,8 +31,11 @@ export default class Search extends Component {
moreFilesIndicator: false,
orderBy: null,
orderDesc: false,
+ advancedSearch: false,
}
this.searchLimit = 10
+ this.advanced = {}
+ this.searchError = undefined;
if(session)
{
@@ -42,6 +48,9 @@ export default class Search extends Component {
Object.assign(this.state, this.setSafeSearch(session.notSafeSearch))
this.state.orderBy = session.orderBy;
this.state.orderDesc = session.orderDesc;
+ this.state.advancedSearch = session.advancedSearch;
+ this.advanced = session.advanced;
+ this.searchError = session.searchError;
}
}
@@ -55,12 +64,16 @@ export default class Search extends Component {
this.moreSearchFiles = true;
this.currentSearch = this.searchValue;
let queries = 2;
- window.torrentSocket.emit('searchTorrent', oldSearch ? this.currentSearch : this.searchValue, {
+ let searchTorrentsParams = {
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
- }, window.customLoader((torrents) => {
+ };
+ 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)
@@ -78,12 +91,16 @@ export default class Search extends Component {
this.forceUpdate();
}
}));
- window.torrentSocket.emit('searchFiles', oldSearch ? this.currentSearch : this.searchValue, {
+ let searchFilesParams = {
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
- }, window.customLoader((torrents) => {
+ };
+ 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;
@@ -181,6 +198,9 @@ export default class Search extends Component {
notSafeSearch: this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
+ advancedSearch: this.state.advancedSearch,
+ advanced: this.advanced,
+ searchError: this.searchError,
}
}
setSafeSearch(ch) {
@@ -221,31 +241,62 @@ export default class Search extends Component {
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}}
+ 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()
+ }}
/>
{
this.search()
}} />
-
-
}
- uncheckedIcon={
}
- label={
{this.state.safeSearchText}}
- iconStyle={{fill: this.state.safeSearchColor}}
- onCheck={(ev, ch) => {
- this.setState(this.setSafeSearch(ch));
- }}
- style={{paddingBottom: '0.8em'}}
- />
+
+
+ }
+ uncheckedIcon={}
+ label={{this.state.safeSearchText}}
+ iconStyle={{fill: this.state.safeSearchColor}}
+ onCheck={(ev, ch) => {
+ this.setState(this.setSafeSearch(ch));
+ }}
+ style={{paddingBottom: '0.8em'}}
+ />
+
+
+
}
+ uncheckedIcon={
}
+ label={
advanced search}
+ iconStyle={{fill: 'black'}}
+ onCheck={(ev, ch) => {
+ this.setState({advancedSearch: ch});
+ }}
+ style={{paddingBottom: '0.8em'}}
+ />
+
+ {
+ this.state.advancedSearch
+ &&
+
{
+ this.advanced = state;
+ }} state={this.advanced} />
+ }
{
this.stats
?