2017-08-25 03:46:55 +02:00
|
|
|
import React, { Component } from 'react';
|
2017-08-25 02:01:15 +02:00
|
|
|
import { connect } from 'react-redux';
|
2017-08-25 04:32:42 +02:00
|
|
|
import FontAwesome from 'react-fontawesome';
|
2017-08-25 03:46:55 +02:00
|
|
|
import {
|
|
|
|
ButtonGroup,
|
|
|
|
ButtonDropdown,
|
|
|
|
DropdownToggle,
|
|
|
|
DropdownMenu,
|
2017-08-25 04:32:42 +02:00
|
|
|
DropdownItem,
|
|
|
|
Collapse,
|
|
|
|
Card,
|
|
|
|
CardBlock,
|
|
|
|
Progress
|
2017-08-25 03:46:55 +02:00
|
|
|
} from 'reactstrap';
|
2017-08-25 04:32:42 +02:00
|
|
|
import ws_send from '../socket';
|
2017-08-26 01:59:53 +02:00
|
|
|
import selectTorrent, { EXCLUSIVE, UNION, NONE } from '../actions/selection';
|
2017-08-24 14:30:22 +02:00
|
|
|
|
2017-08-25 04:32:42 +02:00
|
|
|
function File({ file }) {
|
|
|
|
// TODO: show progress bar
|
|
|
|
// TODO: edit priority
|
2017-08-24 14:30:22 +02:00
|
|
|
return (
|
2017-08-25 04:32:42 +02:00
|
|
|
<tr>
|
|
|
|
<td>{file.path}</td>
|
|
|
|
<td>{file.priority}</td>
|
|
|
|
<td>{file.availability}</td>
|
|
|
|
</tr>
|
2017-08-24 14:30:22 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-08-25 04:32:42 +02:00
|
|
|
// TODO: move to separate component
|
|
|
|
function CollapseToggle({ text, onToggle, open }) {
|
|
|
|
return (
|
|
|
|
<button
|
|
|
|
className="btn btn-sm btn-default"
|
|
|
|
onClick={onToggle}
|
|
|
|
>
|
|
|
|
{text}
|
|
|
|
<FontAwesome
|
|
|
|
name={`chevron-${open ? "up" : "down"}`}
|
|
|
|
style={{marginLeft: "0.25rem"}}
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
class Torrent extends Component {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.state = {
|
|
|
|
infoShown: false,
|
|
|
|
filesShown: false,
|
|
|
|
trackersShown: false,
|
|
|
|
peersShown: false
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleTorrentState(torrent) {
|
|
|
|
if (torrent.status === "paused") {
|
|
|
|
ws_send("RESUME_TORRENT", { id: torrent.id });
|
|
|
|
} else {
|
|
|
|
ws_send("PAUSE_TORRENT", { id: torrent.id });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { torrent, files } = this.props;
|
|
|
|
const status = s => s[0].toUpperCase() + s.slice(1);
|
|
|
|
|
2017-08-25 14:57:02 +02:00
|
|
|
if (!torrent || !files) {
|
|
|
|
return (
|
|
|
|
<p>Loading...</p>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-08-25 04:32:42 +02:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>{torrent.name}</h3>
|
|
|
|
<Progress value={torrent.progress * 100} style={{marginBottom: "0.5rem"}}>
|
|
|
|
{(torrent.progress * 100).toFixed(0)}%
|
|
|
|
</Progress>
|
|
|
|
<p>
|
|
|
|
<strong>State:</strong>
|
|
|
|
<span style={{marginLeft: "1rem"}}>
|
|
|
|
{status(torrent.status)}
|
|
|
|
</span>
|
|
|
|
<button
|
|
|
|
className="btn btn-sm btn-very-sm btn-info"
|
|
|
|
style={{marginLeft: "1rem"}}
|
|
|
|
onClick={() => this.toggleTorrentState(torrent)}
|
|
|
|
>
|
|
|
|
{torrent.status === "paused" ? "Resume" : "Pause"}
|
|
|
|
</button>
|
2017-08-25 04:36:01 +02:00
|
|
|
<button
|
|
|
|
className="btn btn-sm btn-very-sm btn-danger"
|
|
|
|
style={{marginLeft: "1rem"}}
|
|
|
|
>Remove</button>
|
2017-08-25 04:32:42 +02:00
|
|
|
</p>
|
|
|
|
<ButtonGroup>
|
|
|
|
<CollapseToggle
|
|
|
|
text="Info"
|
|
|
|
onToggle={() => this.setState({ infoShown: !this.state.infoShown })}
|
|
|
|
open={this.state.infoShown}
|
|
|
|
/>
|
|
|
|
<CollapseToggle
|
|
|
|
text="Files"
|
|
|
|
onToggle={() => this.setState({ filesShown: !this.state.filesShown })}
|
|
|
|
open={this.state.filesShown}
|
|
|
|
/>
|
|
|
|
<CollapseToggle
|
|
|
|
text="Trackers"
|
|
|
|
onToggle={() => this.setState({ trackersShown: !this.state.trackersShown })}
|
|
|
|
open={this.state.trackersShown}
|
|
|
|
/>
|
|
|
|
<CollapseToggle
|
|
|
|
text="Peers"
|
|
|
|
onToggle={() => this.setState({ peersShown: !this.state.peersShown })}
|
|
|
|
open={this.state.peersShown}
|
|
|
|
/>
|
|
|
|
</ButtonGroup>
|
|
|
|
<Collapse isOpen={this.state.infoShown}>
|
|
|
|
<dl>
|
|
|
|
<dt>Downloading to</dt>
|
|
|
|
<dd>{torrent.path}</dd>
|
|
|
|
<dt>Created</dt>
|
|
|
|
<dd>{torrent.created}</dd>
|
|
|
|
</dl>
|
|
|
|
</Collapse>
|
|
|
|
<Collapse isOpen={this.state.filesShown}>
|
|
|
|
<Card style={{marginBottom: "1rem"}}>
|
|
|
|
<CardBlock>
|
|
|
|
<table className="table table-striped" style={{marginBottom: "0"}}>
|
|
|
|
<tbody>
|
|
|
|
{files.map(file => <File file={file} />)}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</CardBlock>
|
|
|
|
</Card>
|
|
|
|
</Collapse>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-25 03:46:55 +02:00
|
|
|
class TorrentDetails extends Component {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.state = {
|
|
|
|
removeDropdown: false
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-08-26 01:34:04 +02:00
|
|
|
componentDidMount() {
|
|
|
|
const { dispatch } = this.props;
|
|
|
|
const { ids } = this.props.match.params;
|
|
|
|
const _ids = ids.split(",");
|
2017-08-26 01:59:53 +02:00
|
|
|
dispatch(selectTorrent(_ids, UNION));
|
2017-08-26 01:34:04 +02:00
|
|
|
}
|
|
|
|
|
2017-08-26 01:37:28 +02:00
|
|
|
componentWillUnmount() {
|
|
|
|
const { dispatch } = this.props;
|
2017-08-26 02:01:31 +02:00
|
|
|
dispatch(selectTorrent([], EXCLUSIVE, false));
|
2017-08-26 01:37:28 +02:00
|
|
|
}
|
|
|
|
|
2017-08-25 15:43:54 +02:00
|
|
|
renderHeader(selection) {
|
2017-08-25 03:46:55 +02:00
|
|
|
return (
|
|
|
|
<div>
|
2017-08-25 15:43:54 +02:00
|
|
|
<h3>{selection.length} torrents</h3>
|
2017-08-25 03:46:55 +02:00
|
|
|
<ButtonGroup>
|
2017-08-26 01:41:34 +02:00
|
|
|
<button
|
|
|
|
className="btn btn-default btn-sm"
|
|
|
|
onClick={() => {
|
|
|
|
selection.forEach(id => ws_send("PAUSE_TORRENT", { id }));
|
|
|
|
}}
|
|
|
|
>Pause all</button>
|
|
|
|
<button
|
|
|
|
className="btn btn-default btn-sm"
|
|
|
|
onClick={() => {
|
|
|
|
selection.forEach(id => ws_send("RESUME_TORRENT", { id }));
|
|
|
|
}}
|
|
|
|
>Resume all</button>
|
2017-08-25 03:46:55 +02:00
|
|
|
<ButtonDropdown
|
|
|
|
isOpen={this.state.removeDropdown}
|
|
|
|
toggle={() => this.setState({ removeDropdown: !this.state.removeDropdown })}
|
|
|
|
>
|
2017-08-25 04:32:42 +02:00
|
|
|
<DropdownToggle color="default" caret>
|
2017-08-25 03:46:55 +02:00
|
|
|
Remove
|
|
|
|
</DropdownToggle>
|
|
|
|
<DropdownMenu>
|
2017-08-25 04:36:01 +02:00
|
|
|
<DropdownItem>Remove all selected torrents</DropdownItem>
|
|
|
|
<DropdownItem>Remove and delete files</DropdownItem>
|
2017-08-25 03:46:55 +02:00
|
|
|
</DropdownMenu>
|
|
|
|
</ButtonDropdown>
|
|
|
|
</ButtonGroup>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2017-08-25 15:43:54 +02:00
|
|
|
const { torrents, files, selection } = this.props;
|
2017-08-25 04:32:42 +02:00
|
|
|
const _files = Object.values(files).reduce((s, f) => ({
|
|
|
|
...s, [f.torrent_id]: [...(s[f.torrent_id] || []), f]
|
|
|
|
}), {});
|
2017-08-25 03:46:55 +02:00
|
|
|
return (
|
|
|
|
<div>
|
2017-08-25 15:43:54 +02:00
|
|
|
{selection.length > 1 ? this.renderHeader(selection) : null}
|
2017-08-26 01:47:22 +02:00
|
|
|
{selection.slice(0, 3).map(id => <Torrent
|
2017-08-25 04:32:42 +02:00
|
|
|
torrent={torrents[id]}
|
|
|
|
files={_files[id] || []}
|
|
|
|
/>)}
|
2017-08-26 01:47:22 +02:00
|
|
|
{selection.length > 3 ?
|
|
|
|
<p class="text-center text-muted">
|
|
|
|
<strong>
|
|
|
|
...{selection.length - 3} more hidden...
|
|
|
|
</strong>
|
|
|
|
</p>
|
|
|
|
: null}
|
2017-08-25 03:46:55 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default connect(state => ({
|
|
|
|
router: state.router,
|
2017-08-25 04:32:42 +02:00
|
|
|
torrents: state.torrents,
|
2017-08-25 15:43:54 +02:00
|
|
|
files: state.files,
|
2017-08-26 01:34:04 +02:00
|
|
|
selection: state.selection,
|
|
|
|
server: state.server
|
2017-08-25 03:46:55 +02:00
|
|
|
}))(TorrentDetails);
|