Subscribe to peers/files/trackers/pieces

This commit is contained in:
Drew DeVault 2017-08-24 21:13:01 -04:00
parent 9d1fa85582
commit 409238ba58
8 changed files with 108 additions and 54 deletions

View File

@ -9,7 +9,7 @@
.torrent { .torrent {
&.paused { &.paused {
color: $gray-dark; color: $gray-dark;
background: $gray-lighter; background-color: $gray-lighter;
} }
&.idle { &.idle {
@ -25,12 +25,16 @@
} }
} }
&.pending { &.paused {
background-image: linear-gradient(to right, darken($gray-lighter, 10), $gray-lighter); background-image: linear-gradient(to right, darken($gray-lighter, 20), darken($gray-lighter, 40));
}
&.pending, &.idle {
background-image: linear-gradient(to right, lighten($brand-info, 20), $brand-info);
} }
&.seeding { &.seeding {
background-image: linear-gradient(to right, lighten($brand-info, 20), $brand-info); background-image: linear-gradient(to right, lighten($brand-primary, 20), $brand-primary);
} }
&.leeching { &.leeching {

View File

@ -5,14 +5,14 @@ export const FILTER_UNSUBSCRIBE = 'FILTER_UNSUBSCRIBE';
export function filter_subscribe(kind='torrent', criteria=[]) { export function filter_subscribe(kind='torrent', criteria=[]) {
return dispatch => { return dispatch => {
const serial = ws_send(FILTER_SUBSCRIBE, { criteria, kind }); const serial = ws_send(FILTER_SUBSCRIBE, { kind, criteria });
dispatch({ type: FILTER_SUBSCRIBE, serial }); dispatch({ type: FILTER_SUBSCRIBE, serial, kind, criteria });
}; };
} }
export function filter_unsubscribe(serial) { export function filter_unsubscribe(serial) {
return dispatch => { return dispatch => {
ws_send(FILTER_UNSUBSCRIBE, { serial }); ws_send(FILTER_UNSUBSCRIBE, { filter_serial: serial });
dispatch({ type: FILTER_UNSUBSCRIBE, serial }); dispatch({ type: FILTER_UNSUBSCRIBE, serial });
}; };
} }

View File

@ -6,12 +6,19 @@ import {
export default function filter_subscribe(state = [], action) { export default function filter_subscribe(state = [], action) {
switch (action.type) { switch (action.type) {
case FILTER_SUBSCRIBE: { case FILTER_SUBSCRIBE: {
const { serial } = action; const { serial, kind, criteria } = action;
return [...state, serial]; return [
...state,
{
serial,
kind,
criteria
}
];
} }
case FILTER_UNSUBSCRIBE: { case FILTER_UNSUBSCRIBE: {
const { serial } = action; const { _serial } = action;
return state.filter(s => s !== serial); return state.filter(({ serial }) => serial !== _serial);
} }
} }
return state; return state;

View File

@ -10,12 +10,13 @@ export default function ws_send(type, body, callback = null) {
if (callback) { if (callback) {
transactions[_serial] = callback; transactions[_serial] = callback;
} }
const msg = JSON.stringify({ const obj = {
type, type,
serial: _serial, serial: _serial,
...body ...body
}); };
console.log("->", msg); const msg = JSON.stringify(obj);
console.log("->", obj);
ws.send(msg); ws.send(msg);
return _serial; return _serial;
} }

View File

@ -19,5 +19,6 @@ const store = createStore(
); );
export const dispatch = action => store.dispatch(action); export const dispatch = action => store.dispatch(action);
export const getState = () => store.getState();
export default store; export default store;

72
src/torrent_state.js Normal file
View File

@ -0,0 +1,72 @@
import { filter_subscribe, filter_unsubscribe } from './actions/filter_subscribe';
import { push } from 'react-router-redux';
/* Listen pal, we're just gonna pretend this import isn't happening */
import { dispatch, getState } from './store';
export function activeTorrents() {
const { pathname } = getState().router.location;
if (pathname.indexOf("/torrents/") !== 0) {
return [];
} else {
return pathname.slice("/torrents/".length).split(",");
}
}
// TODO: invoke on page load for /torrents/:id
export function updateSubscriptions(added, removed) {
if (added.length > 0) {
added.forEach(t => {
const criteria = [
{ field: "torrent_id", op: "==", value: t }
];
dispatch(filter_subscribe("peer", criteria));
dispatch(filter_subscribe("file", criteria));
dispatch(filter_subscribe("piece", criteria));
dispatch(filter_subscribe("tracker", criteria));
});
}
if (removed.length > 0) {
const subscriptions = getState().filter_subscribe;
removed.forEach(t => {
const serials = subscriptions
.filter(sub => sub.criteria[0] && sub.criteria[0].value == t)
.map(sub => sub.serial);
serials.forEach(serial => dispatch(filter_unsubscribe(serial)));
});
}
}
export const selectop = {
EXCLUSIVE: 1,
UNION: 2,
SUBTRACT: 3
};
export function selectTorrent(t, action = UNION) {
let active = activeTorrents(getState().router);
let removed = [], added = [];
switch (action) {
case selectop.EXCLUSIVE:
removed = active.slice();
active = [t.id];
added = [t.id];
break;
case selectop.UNION:
if (active.indexOf(t.id) === -1) {
active = [...active, t.id];
added = [t.id];
}
break;
case selectop.SUBTRACT:
if (active.indexOf(t.id) !== -1) {
removed = [t.id];
active = active.filter(a => a != t.id);
}
break;
}
const url = active.length === 0 ? "/" : `/torrents/${active.join(',')}`;
if (url !== getState().router.location) {
dispatch(push(url));
}
updateSubscriptions(added, removed);
}

View File

@ -1,6 +1,9 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
// TODO: use component lifecycle functions here to invoke
// torrent_state.updateSubscriptions
function torrent_details(props) { function torrent_details(props) {
return ( return (
<div> <div>

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { push } from 'react-router-redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { activeTorrents, selectTorrent, selectop } from '../torrent_state';
function formatBitrate(bitrate) { function formatBitrate(bitrate) {
if (bitrate > 1000000000) { if (bitrate > 1000000000) {
@ -14,44 +14,10 @@ function formatBitrate(bitrate) {
} }
} }
function activeTorrents(router) {
const { pathname } = router.location;
if (pathname.indexOf("/torrents/") !== 0) {
return [];
} else {
return pathname.slice("/torrents/".length).split(",");
}
}
const EXCLUSIVE = 1, UNION = 2, SUBTRACT = 3;
function selectTorrent(dispatch, t, router, action = UNION) {
let active = activeTorrents(router);
switch (action) {
case EXCLUSIVE:
active = [t.id];
break;
case UNION:
if (active.indexOf(t.id) === -1) {
active = [...active, t.id];
}
break;
case SUBTRACT:
if (active.indexOf(t.id) !== -1) {
active = active.filter(a => a != t.id);
}
break;
}
const url = active.length === 0 ? "/" : `/torrents/${active.join(',')}`;
if (url !== router.location) {
dispatch(push(url));
}
}
class TorrentTable extends Component { class TorrentTable extends Component {
render() { render() {
const { torrents, router, dispatch } = this.props; const { torrents } = this.props;
const active = activeTorrents(router); const active = activeTorrents();
return ( return (
<table className="table"> <table className="table">
<thead> <thead>
@ -81,8 +47,8 @@ class TorrentTable extends Component {
type="checkbox" type="checkbox"
checked={active.indexOf(t.id) !== -1} checked={active.indexOf(t.id) !== -1}
onChange={e => onChange={e =>
selectTorrent(dispatch, selectTorrent(t,
t, router, e.target.checked ? UNION : SUBTRACT)} e.target.checked ? selectop.UNION : selectop.SUBTRACT)}
/> />
</td> </td>
<td> <td>
@ -90,7 +56,7 @@ class TorrentTable extends Component {
href="#" href="#"
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
selectTorrent(dispatch, t, router, EXCLUSIVE); selectTorrent(t, selectop.EXCLUSIVE);
}} }}
> >
{t.name} {t.name}