diff --git a/src/reducers/resource.js b/src/reducers/resource.js index d7f0125..f6db220 100644 --- a/src/reducers/resource.js +++ b/src/reducers/resource.js @@ -12,21 +12,17 @@ function hack(old, _new) { export default function resourceReducer(type) { return (state = {}, action) => { + let ns = {...state}; switch (action.type) { case UPDATE_RESOURCES: - return { - ...state, - ...action.resources - .filter(r => r.type === type) - .reduce((s, r) => ({ - ...s, - [r.id]: hack(state[r.id], { ...state[r.id], ...r }) - }), {}) - }; + action.resources + .filter(r => r.type ===type) + .map(r => ns[r.id] = hack(state[r.id], { ...state[r.id], ...r })); + return ns; case RESOURCES_REMOVED: - return Object.values(state) - .filter(r => action.ids.indexOf(r.id) === -1) - .reduce((s, r) => ({ ...s, [r.id]: r }), {}); + action.ids + .map(id => delete ns[id]); + return ns; case SOCKET_UPDATE: const _state = action.state; return _state === SOCKET_STATE.CONNECTING ? {} : state; diff --git a/src/reducers/subscribe.js b/src/reducers/subscribe.js index 97fc85f..f205a60 100644 --- a/src/reducers/subscribe.js +++ b/src/reducers/subscribe.js @@ -8,12 +8,12 @@ export default function subscribe(state = [], action) { return [ ...state, ...ids.map(id => ({ serial, id })) ]; } case UNSUBSCRIBE: { - const { ids } = action; - return state.filter(sub => ids.indexOf(sub.id) === -1); + const ids = new Set(action.ids); + return state.filter(sub => !ids.has(sub.id)); } case RESOURCES_REMOVED: { - const { ids } = action; - return state.filter(sub => ids.indexOf(sub.id) === -1); + const ids = new Set(action.ids); + return state.filter(sub => !ids.has(sub.id)); } } return state; diff --git a/src/ui/torrent_details.js b/src/ui/torrent_details.js index da2b2c4..f7da266 100644 --- a/src/ui/torrent_details.js +++ b/src/ui/torrent_details.js @@ -35,55 +35,69 @@ function basename(path) { return parts[parts.length - 1]; } -function File({ dispatch, file }) { - const { uri } = store.getState().socket; - const { download_token } = store.getState().server; - 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) - }))} +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 ( +
+ - - - - - - - + {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)}%`}
-
- ); +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 @@ -114,6 +128,14 @@ class Torrent extends Component { }; } + shouldComponentUpdate(nextProps, nextState) { + return nextProps.torrent !== this.props.torrent + || 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 }); @@ -222,9 +244,11 @@ class Torrent extends Component {
- {files.slice().sort((a, b) => - a.path.localeCompare(b.path)).map(file => - )} + {this.state.filesShown + ? files.slice() + .sort((a, b) => a.path.localeCompare(b.path)) + .map(file => ) + : null}
@@ -264,7 +288,7 @@ class Torrent extends Component {
- {peers.map(peer => )} + {this.state.peersShown ? peers.map(peer => ) : null}
{peers.length === 0 &&
@@ -361,15 +385,21 @@ class TorrentDetails extends Component { 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] - }), {}); + 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} diff --git a/src/ui/torrent_table.js b/src/ui/torrent_table.js index c80d5de..d68665c 100644 --- a/src/ui/torrent_table.js +++ b/src/ui/torrent_table.js @@ -12,55 +12,7 @@ const name_style = { whiteSpace: 'nowrap' }; -class TorrentTable extends Component { - render() { - const { selection, torrents, dispatch } = this.props; - return ( - - - - - - - - - - - - - {Object.values(torrents).slice().sort((a, b) => a.name.localeCompare(b.name)).map(t => - - )} - -
- { - if (selection.length > 0) { - dispatch(selectTorrent([], EXCLUSIVE)); - } else { - dispatch(selectTorrent(Object.keys(torrents), EXCLUSIVE)); - } - }} - /> - nameupdown - - ratio - - - - progress
- ); - } -} - -class Torrent extends Component { +class _Torrent extends Component { shouldComponentUpdate(nextProps, _) { const { selection, torrent } = this.props; const nt = nextProps.torrent; @@ -111,6 +63,60 @@ class Torrent extends Component { } } +const Torrent = connect((state, props) => { + return { + torrent: state.torrents[props.id], + selection: state.selection, + }; +})(_Torrent); + +class TorrentTable extends Component { + render() { + const { selection, torrents, dispatch } = this.props; + return ( + + + + + + + + + + + + + {Object.values(torrents).slice().sort((a, b) => a.name.localeCompare(b.name)).map(t => + + )} + +
+ { + if (selection.length > 0) { + dispatch(selectTorrent([], EXCLUSIVE)); + } else { + dispatch(selectTorrent(Object.keys(torrents), EXCLUSIVE)); + } + }} + /> + nameupdown + + ratio + + + + progress
+ ); + } +} + export default connect(state => ({ torrents: state.torrents, selection: state.selection,