135 lines
3.5 KiB
JavaScript
135 lines
3.5 KiB
JavaScript
import React, { useState } from "react";
|
|
import PropTypes from "prop-types";
|
|
import { Map, List } from "immutable";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
|
|
import { prettySize } from "../../utils";
|
|
import { addTorrent, removeTorrent } from "../../actions/torrents";
|
|
|
|
const TorrentList = () => {
|
|
const torrents = useSelector((state) => state.torrentStore.get("torrents"));
|
|
const dispatch = useDispatch();
|
|
|
|
return (
|
|
<div className="row">
|
|
<div className="col-12">
|
|
<AddTorrent addTorrent={(url) => dispatch(addTorrent(url))} />
|
|
<Torrents
|
|
torrents={torrents}
|
|
removeTorrent={(id) => dispatch(removeTorrent(id))}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
export default TorrentList;
|
|
|
|
const AddTorrent = (props) => {
|
|
const [url, setUrl] = useState("");
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
if (url === "") {
|
|
return;
|
|
}
|
|
props.addTorrent(url);
|
|
setUrl("");
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={(e) => handleSubmit(e)}>
|
|
<input
|
|
type="text"
|
|
className="form-control mb-3 w-100"
|
|
placeholder="Add torrent URL"
|
|
onSubmit={handleSubmit}
|
|
value={url}
|
|
onChange={(e) => setUrl(e.target.value)}
|
|
/>
|
|
</form>
|
|
);
|
|
};
|
|
AddTorrent.propTypes = {
|
|
addTorrent: PropTypes.func.isRequired,
|
|
};
|
|
|
|
const Torrents = (props) => {
|
|
if (props.torrents.size === 0) {
|
|
return (
|
|
<div className="jumbotron">
|
|
<h2>No torrents</h2>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="d-flex flex-wrap">
|
|
{props.torrents.map((el, index) => (
|
|
<Torrent key={index} data={el} removeTorrent={props.removeTorrent} />
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
Torrents.propTypes = {
|
|
removeTorrent: PropTypes.func.isRequired,
|
|
torrents: PropTypes.instanceOf(List),
|
|
};
|
|
|
|
const Torrent = (props) => {
|
|
const handleClick = () => {
|
|
props.removeTorrent(props.data.get("id"));
|
|
};
|
|
|
|
const done = props.data.get("is_finished");
|
|
var progressStyle = done
|
|
? "success"
|
|
: "info progress-bar-striped progress-bar-animated";
|
|
const progressBarClass = "progress-bar bg-" + progressStyle;
|
|
|
|
var percentDone = props.data.get("percent_done");
|
|
const started = percentDone !== 0;
|
|
if (started) {
|
|
percentDone = Number(percentDone).toFixed(1) + "%";
|
|
}
|
|
|
|
// Pretty sizes
|
|
const downloadedSize = prettySize(props.data.get("downloaded_size"));
|
|
const totalSize = prettySize(props.data.get("total_size"));
|
|
const downloadRate = prettySize(props.data.get("download_rate")) + "/s";
|
|
return (
|
|
<div className="card w-100 mb-3">
|
|
<h5 className="card-header">
|
|
<span className="text text-break">{props.data.get("name")}</span>
|
|
<span
|
|
className="fa fa-trash clickable pull-right"
|
|
onClick={() => handleClick()}
|
|
></span>
|
|
</h5>
|
|
<div className="card-body pb-0">
|
|
{started && (
|
|
<React.Fragment>
|
|
<div className="progress bg-light">
|
|
<div
|
|
className={progressBarClass}
|
|
style={{ width: percentDone }}
|
|
role="progressbar"
|
|
aria-valuenow={percentDone}
|
|
aria-valuemin="0"
|
|
aria-valuemax="100"
|
|
></div>
|
|
</div>
|
|
<p>
|
|
{downloadedSize} / {totalSize} - {percentDone} - {downloadRate}
|
|
</p>
|
|
</React.Fragment>
|
|
)}
|
|
{!started && <p>Download not yet started</p>}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
Torrent.propTypes = {
|
|
removeTorrent: PropTypes.func.isRequired,
|
|
data: PropTypes.instanceOf(Map),
|
|
};
|