213 lines
5.9 KiB
JavaScript
213 lines
5.9 KiB
JavaScript
import React from "react"
|
|
import { connect } from "react-redux"
|
|
import { bindActionCreators } from "redux"
|
|
import { addTorrent, searchTorrents } from "../../actions/torrents"
|
|
import Loader from "../loader/loader"
|
|
|
|
import { OverlayTrigger, Tooltip } from "react-bootstrap"
|
|
|
|
function mapStateToProps(state) {
|
|
return {
|
|
searching: state.torrentStore.get("searching"),
|
|
results: state.torrentStore.get("searchResults"),
|
|
};
|
|
}
|
|
const mapDispatchToProps = (dispatch) =>
|
|
bindActionCreators({ addTorrent, searchTorrents }, dispatch)
|
|
|
|
class TorrentSearch extends React.PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleSearchInput = this.handleSearchInput.bind(this);
|
|
this.state = { search: (this.props.router.params.search || "") };
|
|
}
|
|
handleSearchInput() {
|
|
this.setState({ search: this.refs.search.value });
|
|
}
|
|
handleClick(type) {
|
|
if (this.state.search === "") { return }
|
|
const url = `/torrents/search/${type}/${encodeURI(this.state.search)}`;
|
|
this.props.router.push(url);
|
|
}
|
|
render() {
|
|
const searchFromURL = this.props.router.params.search || "";
|
|
const typeFromURL = this.props.router.params.type || "";
|
|
return (
|
|
<div>
|
|
<div className="col-xs-12">
|
|
<form className="form-horizontal" onSubmit={(e) => e.preventDefault()}>
|
|
<div className="form-group">
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="Search torrents"
|
|
value={this.state.search}
|
|
onChange={this.handleSearchInput}
|
|
ref="search"
|
|
/>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div className="row">
|
|
<SearchButton
|
|
text="Search movies"
|
|
type="movies"
|
|
typeFromURL={typeFromURL}
|
|
handleClick={() => this.handleClick("movies")}
|
|
/>
|
|
<SearchButton
|
|
text="Search shows"
|
|
type="shows"
|
|
typeFromURL={typeFromURL}
|
|
handleClick={() => this.handleClick("shows")}
|
|
/>
|
|
</div>
|
|
<hr />
|
|
<div className="row">
|
|
<TorrentList
|
|
searching={this.props.searching}
|
|
results={this.props.results}
|
|
addTorrent={this.props.addTorrent}
|
|
searchFromURL={searchFromURL}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function SearchButton(props) {
|
|
const color = (props.type === props.typeFromURL) ? "primary" : "default";
|
|
return (
|
|
<div className="col-xs-6">
|
|
<button
|
|
className={`btn btn-${color} full-width`}
|
|
type="button"
|
|
onClick={props.handleClick}
|
|
>
|
|
<i className="fa fa-search" aria-hidden="true"></i> {props.text}
|
|
</button>
|
|
</div>
|
|
|
|
);
|
|
}
|
|
|
|
function TorrentList(props) {
|
|
if (props.searching) {
|
|
return (<Loader />);
|
|
}
|
|
|
|
if (props.searchFromURL === "") {
|
|
return null;
|
|
}
|
|
|
|
if (props.results.size === 0) {
|
|
return (
|
|
<div className="col-xs-12">
|
|
<div className="well well-lg">
|
|
<h2>No results</h2>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="col-xs-12">
|
|
{props.results.map(function(el, index) {
|
|
return (
|
|
<Torrent
|
|
key={index}
|
|
data={el}
|
|
addTorrent={props.addTorrent}
|
|
/>);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Torrent(props) {
|
|
return (
|
|
<div className="row">
|
|
<div className="col-xs-12">
|
|
<table className="table responsive table-align-middle torrent-search-result">
|
|
<tbody>
|
|
<tr>
|
|
<td rowSpan="2" className="col-xs-1 torrent-small-width">
|
|
<h4>
|
|
<TorrentHealth
|
|
url={props.data.get("url")}
|
|
seeders={props.data.get("seeders")}
|
|
leechers={props.data.get("leechers")}
|
|
/>
|
|
</h4>
|
|
</td>
|
|
<td colSpan="4" className="col-xs-9 title">
|
|
<span className="torrent-title">{props.data.get("name")}</span>
|
|
</td>
|
|
<td rowSpan="2" className="col-xs-1 torrent-small-width">
|
|
<h4 className="pull-right clickable" onClick={() => props.addTorrent(props.data.get("url"))}>
|
|
<i className="fa fa-cloud-download" aria-hidden="true"></i>
|
|
</h4>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="col-xs-1 torrent-label">
|
|
<span className="label label-warning">{props.data.get("quality")}</span>
|
|
</td>
|
|
<td className="col-xs-1 torrent-label">
|
|
<span className="label label-success">{props.data.get("source")}</span>
|
|
</td>
|
|
<td className="col-xs-1 torrent-label">
|
|
<span className="label label-info">{props.data.get("upload_user")}</span>
|
|
</td>
|
|
<td className="col-xs-7 torrent-label"></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function TorrentHealth(props) {
|
|
const seeders = props.seeders || 0;
|
|
const leechers = props.leechers || 1;
|
|
|
|
let color;
|
|
let health;
|
|
let ratio = seeders/leechers;
|
|
|
|
if (seeders > 20) {
|
|
health = "good";
|
|
color = "success";
|
|
} else {
|
|
if (ratio > 1) {
|
|
health = "medium";
|
|
color = "warning";
|
|
} else {
|
|
health = "bad";
|
|
color = "danger";
|
|
}
|
|
}
|
|
|
|
const className = `text text-${color}`;
|
|
const tooltip = (
|
|
<Tooltip id={`tooltip-health-${props.url}`}>
|
|
<p><span className={className}>Health: {health}</span></p>
|
|
<p>Seeders: {seeders}</p>
|
|
<p>Leechers: {props.leechers}</p>
|
|
</Tooltip>
|
|
);
|
|
|
|
return (
|
|
<OverlayTrigger placement="right" overlay={tooltip}>
|
|
<span className={className}>
|
|
<i className="fa fa-circle" aria-hidden="true"></i>
|
|
</span>
|
|
</OverlayTrigger>
|
|
);
|
|
}
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(TorrentSearch);
|