208 lines
5.5 KiB
JavaScript

import React, { useState, useEffect } from "react"
import PropTypes from "prop-types"
import { connect } from "react-redux"
import { addTorrent, searchTorrents } from "../../actions/torrents"
import { Map, List } from "immutable"
import Loader from "../loader/loader"
import { OverlayTrigger, Tooltip } from "react-bootstrap"
const mapStateToProps = (state) => ({
searching: state.torrentStore.get("searching"),
results: state.torrentStore.get("searchResults"),
});
const mapDispatchToProps = { addTorrent, searchTorrents };
const TorrentSearch = (props) => {
const [search, setSearch] = useState(props.match.params.search || "");
const [type, setType] = useState(props.match.params.type || "");
const [url, setUrl] = useState("");
const getUrl = () =>
`/torrents/search/${type}/${encodeURI(search)}`;
useEffect(() => {
if (search === "") { return }
if (type === "") { return }
const url = getUrl();
props.searchTorrents(url)
props.history.push(url);
}, [url]);
return (
<div className="row">
<div className="col-12 d-flex flex-row flex-wrap">
<input
type="text"
className="form-control mb-1 w-100 form-control-lg"
placeholder="Search torrents"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<div className="mb-3 w-100 d-flex">
<SearchButton
text="Search movies"
type="movies"
typeFromURL={type}
handleClick={() => {
setType("movies");
setUrl(getUrl());
}}/>
<SearchButton
text="Search shows"
type="shows"
typeFromURL={type}
handleClick={() => {
setType("shows");
setUrl(getUrl());
}}/>
</div>
</div>
<div className="col-12">
<TorrentList
searching={props.searching}
results={props.results}
addTorrent={props.addTorrent}
searchFromURL={search}
/>
</div>
</div>
);
}
TorrentSearch.propTypes = {
searching: PropTypes.bool.isRequired,
results: PropTypes.instanceOf(List),
searchFromURL: PropTypes.string,
match: PropTypes.object,
history: PropTypes.object,
addTorrent: PropTypes.func.isRequired,
searchTorrents: PropTypes.func.isRequired,
};
const SearchButton = (props) => {
const variant = (props.type === props.typeFromURL) ? "primary" : "secondary";
return (
<button
type="button"
className={`w-50 btn m-1 btn-lg btn-${variant}`}
onClick={props.handleClick}
>
<i className="fa fa-search" aria-hidden="true"></i> {props.text}
</button>
);
}
SearchButton.propTypes = {
type: PropTypes.string,
typeFromURL: PropTypes.string,
text: PropTypes.string,
handleClick: PropTypes.func.isRequired,
};
const TorrentList = (props) => {
if (props.searching) {
return (<Loader />);
}
if (props.searchFromURL === "") {
return null;
}
if (props.results.size === 0) {
return (
<div className="jumbotron">
<h2>No results</h2>
</div>
);
}
return (
<React.Fragment>
{props.results.map(function(el, index) {
return (
<Torrent
key={index}
data={el}
addTorrent={props.addTorrent}
/>);
})}
</React.Fragment>
);
}
TorrentList.propTypes = {
searching: PropTypes.bool.isRequired,
results: PropTypes.instanceOf(List),
searchFromURL: PropTypes.string,
addTorrent: PropTypes.func.isRequired,
};
const Torrent = (props) => (
<div className="alert d-flex border-bottom border-secondary align-items-center">
<TorrentHealth
url={props.data.get("url")}
seeders={props.data.get("seeders")}
leechers={props.data.get("leechers")}
/>
<span className="mx-3 text text-start text-break flex-fill">{props.data.get("name")}</span>
<div>
<span className="mx-1 badge badge-pill badge-warning">{props.data.get("quality")}</span>
<span className="mx-1 badge badge-pill badge-success">{props.data.get("source")}</span>
<span className="mx-1 badge badge-pill badge-info">{props.data.get("upload_user")}</span>
</div>
<div className="align-self-end ml-3">
<i className="fa fa-cloud-download clickable" onClick={() => props.addTorrent(props.data.get("url"))}></i>
</div>
</div>
);
Torrent.propTypes = {
data: PropTypes.instanceOf(Map),
addTorrent: PropTypes.func.isRequired,
};
const 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 = `align-self-start text text-center 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>
);
}
TorrentHealth.propTypes = {
url: PropTypes.string,
seeders: PropTypes.number,
leechers: PropTypes.number,
};
export default connect(mapStateToProps, mapDispatchToProps)(TorrentSearch);