import React, { Component } from 'react';
import { connect } from 'react-redux';
import FontAwesome from 'react-fontawesome';
import {
ButtonGroup,
ButtonDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
Collapse,
Card,
CardBlock,
Progress,
Input,
} from 'reactstrap';
import moment from 'moment';
import TorrentOptions from './torrent_options';
import TorrentProgress from './torrent_progress';
import ws_send from '../socket';
import store from '../store';
import DateDisplay from './date';
import selectTorrent, {
EXCLUSIVE,
UNION,
SUBTRACT,
NONE
} from '../actions/selection';
import { updateResource } from '../actions/resources';
import { formatBitrate } from '../bitrate';
const dlURI = (uri, token, id) => `${uri.replace('ws', 'http')}/dl/${id}?token=${encodeURIComponent(token)}`;
function basename(path) {
const parts = path.split("/");
return parts[parts.length - 1];
}
class File extends Component {
shouldComponentUpdate(nextProps, _) {
return nextProps.file !== this.props.file;
}
render() {
const { dispatch, file } = this.props;
const { uri } = store.getState().socket;
const { download_token } = store.getState().server;
return (
);
}
}
class Peer extends Component {
shouldComponentUpdate(nextProps, _) {
return nextProps.peer !== this.props.peer;
}
render() {
const { peer } = this.props;
return (
{peer.ip}
{formatBitrate(peer.rate_up)} up
{formatBitrate(peer.rate_down)} down
has {`${(peer.availability * 100).toFixed(0)}%`}
);
}
}
// TODO: move to separate component
function CollapseToggle({ text, onToggle, open }) {
return (
);
}
class Torrent extends Component {
constructor() {
super();
this.state = {
infoShown: false,
filesShown: false,
trackersShown: false,
peersShown: false,
removeDropdown: false
};
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.torrent !== this.props.torrent
|| nextState.removeDropdown !== this.state.removeDropdown
|| nextState.peersShown !== this.state.peersShown
|| nextState.trackersShown !== this.state.trackersShown
|| nextState.filesShown !== this.state.filesShown
|| nextState.infoShown !== this.state.infoShown;
}
toggleTorrentState(torrent) {
if (torrent.status === "paused") {
ws_send("RESUME_TORRENT", { id: torrent.id });
} else {
ws_send("PAUSE_TORRENT", { id: torrent.id });
}
}
render() {
const { dispatch, torrent, files, trackers, peers } = this.props;
const status = s => s[0].toUpperCase() + s.slice(1);
if (!torrent || !files) {
return (
Loading...
);
}
return (
{torrent.name}
{
e.preventDefault();
this.toggleTorrentState(torrent);
}}
>
{status(torrent.status)}
this.setState({ removeDropdown: !this.state.removeDropdown })}
>
Remove
{
dispatch(selectTorrent([torrent.id], SUBTRACT));
ws_send("REMOVE_RESOURCE", { id: torrent.id });
}}
>Remove
{
dispatch(selectTorrent([torrent.id], SUBTRACT));
ws_send("REMOVE_RESOURCE", { id: torrent.id, artifacts: true });
}}
>Remove and delete files
this.setState({ infoShown: !this.state.infoShown })}
open={this.state.infoShown}
/>
this.setState({ filesShown: !this.state.filesShown })}
open={this.state.filesShown}
/>
this.setState({ trackersShown: !this.state.trackersShown })}
open={this.state.trackersShown}
/>
this.setState({ peersShown: !this.state.peersShown })}
open={this.state.peersShown}
/>
- Type
- {torrent.private ? "Private" : "Public"}
- Downloading to
- {torrent.path}
- Created
- Comment
- {torrent.comment || "None"}
- Creator
- {torrent.creator || "None"}
dispatch(updateResource({ id: torrent.id, priority }))}
downloadThrottle={torrent.throttle_down}
downloadThrottleChanged={throttle_down =>
dispatch(updateResource({ id: torrent.id, throttle_down }))}
uploadThrottle={torrent.throttle_up}
uploadThrottleChanged={throttle_up =>
dispatch(updateResource({ id: torrent.id, throttle_up }))}
/>
{this.state.filesShown
? files.slice()
.sort((a, b) => a.path.localeCompare(b.path))
.map(file => )
: null}
{trackers.map(tracker =>
{(() => {
const a = document.createElement("a");
a.href = tracker.url;
return a.hostname;
})()}
- URL
- {tracker.url}
- Last report
{tracker.error && - Error
}
{tracker.error &&
- {tracker.error}
}
)}
{this.state.peersShown ? peers.map(peer =>
) : null}
{peers.length === 0 &&
}
);
}
}
class TorrentDetails extends Component {
constructor() {
super();
this.state = {
removeDropdown: false
};
}
componentDidMount() {
const { dispatch } = this.props;
const { ids } = this.props.match.params;
const _ids = ids.split(",");
dispatch(selectTorrent(_ids, UNION));
}
componentWillUnmount() {
const { dispatch } = this.props;
dispatch(selectTorrent([], EXCLUSIVE, false));
}
renderHeader() {
const { dispatch, selection } = this.props;
return (
{selection.length} torrents
);
}
render() {
const {
torrents,
files,
trackers,
peers,
selection,
dispatch
} = this.props;
const index_by_tid = (res) => {
let _indexed = {};
Object.values(res).map(r => {
if (!(r.torrent_id in _indexed)) {
_indexed[r.torrent_id] = [];
}
_indexed[r.torrent_id].push(r);
});
return _indexed;
};
const _files = index_by_tid(files);
const _trackers = index_by_tid(trackers);
const _peers = index_by_tid(peers);
return (
{selection.length > 1 ? this.renderHeader.bind(this)() : null}
{selection.slice(0, 3).map(id =>
)}
{selection.length > 3 ?
...{selection.length - 3} more hidden...
: null}
);
}
}
export default connect(state => ({
router: state.router,
torrents: state.torrents,
files: state.files,
trackers: state.trackers,
peers: state.peers,
selection: state.selection,
server: state.server
}))(TorrentDetails);