165 lines
4.3 KiB
JavaScript

import React, { useState } from "react"
import PropTypes from "prop-types"
import { Map, List } from "immutable"
import { connect } from "react-redux"
import { fetchTorrents, addTorrent, removeTorrent } from "../../actions/torrents"
const mapStateToProps = (state) => ({
torrents: state.torrentStore.get("torrents")
});
const mapDispatchToProps = {
fetchTorrents, addTorrent, removeTorrent,
};
const TorrentList = (props) => (
<div>
<AddTorrent addTorrent={props.addTorrent} />
<Torrents
torrents={props.torrents}
removeTorrent={props.removeTorrent}
/>
</div>
);
TorrentList.propTypes = {
fetchTorrents: PropTypes.func.isRequired,
addTorrent: PropTypes.func.isRequired,
removeTorrent: PropTypes.func.isRequired,
torrents: PropTypes.instanceOf(List),
};
export default connect(mapStateToProps, mapDispatchToProps)(TorrentList);
const AddTorrent = (props) => {
const [url, setUrl] = useState("");
const handleSubmit = (ev) => {
if (ev) { ev.preventDefault(); }
if (url === "") { return; }
props.addTorrent(url);
setUrl("");
}
return (
<div className="row">
<div className="col-xs-12 col-md-12">
<form className="input-group" onSubmit={() => handleSubmit()}>
<input
className="form-control"
placeholder="Add torrent URL"
value={url}
onChange={(e) => setUrl(e.target.value)}
/>
<span className="input-group-btn">
<button
className="btn btn-primary"
type="button"
onClick={() => handleSubmit()}
>
Add
</button>
</span>
</form>
</div>
</div>
);
}
AddTorrent.propTypes = {
addTorrent: PropTypes.func.isRequired,
};
const Torrents = (props) => {
if (props.torrents.size === 0) {
return (
<div className="row">
<div className="col-xs-12 col-md-12">
<h3>Torrents</h3>
<div className="panel panel-default">
<div className="panel-heading">No torrents</div>
</div>
</div>
</div>
);
}
return (
<div className="row">
<div className="col-xs-12 col-md-12">
<h3>Torrents</h3>
{props.torrents.map((el, index) => (
<Torrent
key={index}
data={el}
removeTorrent={props.removeTorrent}
/>
))}
</div>
</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 = "progress-bar progress-bar-info active";
if (done) {
progressStyle = "progress-bar progress-bar-success";
}
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="panel panel-default">
<div className="panel-heading">
{props.data.get("name")}
<span className="fa fa-trash clickable pull-right" onClick={() => handleClick()}></span>
</div>
<div className="panel-body">
{started &&
<div className="progress progress-striped">
<div
className={progressStyle}
style={{width: percentDone}}>
</div>
</div>
}
{!started &&
<p>Download not yet started</p>
}
{started &&
<div>
<p>{downloadedSize} / {totalSize} - {percentDone} - {downloadRate}</p>
</div>
}
</div>
</div>
);
}
Torrent.propTypes = {
removeTorrent: PropTypes.func.isRequired,
data: PropTypes.instanceOf(Map),
};
const prettySize = (fileSizeInBytes) => {
var i = -1;
var byteUnits = [" kB", " MB", " GB", " TB", "PB", "EB", "ZB", "YB"];
do {
fileSizeInBytes = fileSizeInBytes / 1024;
i++;
} while (fileSizeInBytes > 1024);
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
};