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, password, id) => `${uri.replace('ws', 'http')}/dl/${id}?password=${encodeURIComponent(password)}`; function basename(path) { const parts = path.split("/"); return parts[parts.length - 1]; } function File({ dispatch, file }) { const { uri, password } = store.getState().socket; return (
{file.progress === 1.0 ? "done" : `${(file.progress * 100).toFixed(0)}%`}
{file.progress === 1.0 ? {basename(file.path)} : basename(file.path)}
dispatch(updateResource({ id: file.id, priority: parseInt(e.target.value) }))} >
); } function Peer({ peer }) { 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 }; } 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 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} />
Downloading to
{torrent.path}
Created
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 }))} />
{files.slice().sort((a, b) => a.path.localeCompare(b.path)).map(file => )}
{trackers.map(tracker =>
{(() => { const a = document.createElement("a"); a.href = tracker.url; return a.hostname; })()} {/* TODO: wire up this button: */}
URL
{tracker.url}
Last report
{tracker.error &&
Error
} {tracker.error &&
{tracker.error}
}
)}
{peers.map(peer => )}
{peers.length === 0 &&

No connected peers.

}
}
); } } 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
{ e.preventDefault(); selection.forEach(id => ws_send("RESUME_TORRENT", { id })); }} > { e.preventDefault(); selection.forEach(id => ws_send("PAUSE_TORRENT", { id })); }} > this.setState({ removeDropdown: !this.state.removeDropdown })} > Remove all { dispatch(selectTorrent(selection, SUBTRACT)); selection.forEach(id => ws_send("REMOVE_RESOURCE", { id })); }} >Remove selected torrents Remove selected torrents and delete files

); } render() { const { torrents, files, trackers, peers, selection, dispatch } = this.props; const _files = Object.values(files).reduce((s, f) => ({ ...s, [f.torrent_id]: [...(s[f.torrent_id] || []), f] }), {}); const _trackers = Object.values(trackers).reduce((s, t) => ({ ...s, [t.torrent_id]: [...(s[t.torrent_id] || []), t] }), {}); const _peers = Object.values(peers).reduce((s, p) => ({ ...s, [p.torrent_id]: [...(s[p.torrent_id] || []), p] }), {}); 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);