Abstract modal

Add remove torrents modal
Add string util
Add icons for torrent details view
This commit is contained in:
John Furrow
2016-01-31 21:46:41 -08:00
parent 65a3024b7a
commit 5ae13e4d5d
24 changed files with 517 additions and 269 deletions

View File

@@ -2,6 +2,7 @@
.icon { .icon {
display: inline-block; display: inline-block;
fill: $directory-tree--icon--folder;
height: 14px; height: 14px;
margin-right: 6px; margin-right: 6px;
width: 14px; width: 14px;

View File

@@ -5,7 +5,7 @@
.icon { .icon {
&__ring { &__ring {
opacity: 0.5; fill-opacity: 1;
} }
} }
} }

View File

@@ -23,10 +23,15 @@
top: 10%; top: 10%;
transform: translate(-50%, 0); transform: translate(-50%, 0);
width: 500px; width: 500px;
}
}
.modal { &--align-center {
text-align: center;
}
}
&__footer {
margin-top: $spacing-unit;
}
&__button-group { &__button-group {
text-align: right; text-align: right;
@@ -38,9 +43,6 @@
} }
} }
} }
}
.modal {
&__header { &__header {
color: $modal--header--foreground; color: $modal--header--foreground;
@@ -48,9 +50,6 @@
font-weight: 400; font-weight: 400;
margin-bottom: 20px; margin-bottom: 20px;
} }
}
.modal {
&__animation-enter { &__animation-enter {
opacity: 0; opacity: 0;

View File

@@ -128,6 +128,7 @@
&__navigation { &__navigation {
box-shadow: 1px 0 0 $torrent-details--navigation--border; box-shadow: 1px 0 0 $torrent-details--navigation--border;
background: $torrent-details--navigation--background; background: $torrent-details--navigation--background;
min-width: 125px;
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;

View File

@@ -16,14 +16,15 @@
@import "base/typography"; @import "base/typography";
@import "components/action-bar"; @import "components/action-bar";
@import "components/client-stats";
@import "components/application-content"; @import "components/application-content";
@import "components/client-stats";
@import "components/directory-tree"; @import "components/directory-tree";
@import "components/dropdown"; @import "components/dropdown";
@import "components/floating-action"; @import "components/floating-action";
@import "components/sidebar"; @import "components/icons";
@import "components/modals"; @import "components/modals";
@import "components/progress-bar"; @import "components/progress-bar";
@import "components/sidebar";
@import "components/status-filter"; @import "components/status-filter";
@import "components/textbox-repeater"; @import "components/textbox-repeater";
@import "components/torrent-details-panel"; @import "components/torrent-details-panel";

View File

@@ -36,6 +36,25 @@ const TorrentActions = {
}); });
}, },
deleteTorrents: function(hash) {
return axios.post('/client/torrents/delete', {hash})
.then((json = {}) => {
return json.data;
})
.then((data) => {
AppDispatcher.dispatchServerAction({
type: ActionTypes.CLIENT_REMOVE_TORRENT_SUCCESS,
data
});
})
.catch((error) => {
AppDispatcher.dispatchServerAction({
type: ActionTypes.CLIENT_REMOVE_TORRENT_ERROR,
error
});
});
},
fetchLatestTorrentLocation: function () { fetchLatestTorrentLocation: function () {
return axios.get('/ui/torrent-location') return axios.get('/ui/torrent-location')
.then((json = {}) => { .then((json = {}) => {
@@ -56,7 +75,7 @@ const TorrentActions = {
}, },
fetchTorrents: function () { fetchTorrents: function () {
return axios.get('/client/list') return axios.get('/client/torrents')
.then((json = {}) => { .then((json = {}) => {
return json.data; return json.data;
}) })

View File

@@ -12,7 +12,6 @@ export default class DirectoryFiles extends React.Component {
}); });
let files = branch.map((file, fileIndex) => { let files = branch.map((file, fileIndex) => {
// console.log(file);
let fileSize = format.data(file.sizeBytes, '', 1); let fileSize = format.data(file.sizeBytes, '', 1);
return ( return (

View File

@@ -7,8 +7,8 @@ export default class ETA extends BaseIcon {
return ( return (
<svg className={`icon icon--eta ${this.props.className}`} <svg className={`icon icon--eta ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}> xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path class="icon__ring" d="M44.28,54.77a28.56,28.56,0,1,1,10.45-39A28.56,28.56,0,0,1,44.28,54.77Zm6-36.41a23.36,23.36,0,1,0-8.55,31.92A23.36,23.36,0,0,0,50.23,18.36Z"/> <path className="icon__ring" d="M44.28,54.77a28.56,28.56,0,1,1,10.45-39A28.56,28.56,0,0,1,44.28,54.77Zm6-36.41a23.36,23.36,0,1,0-8.55,31.92A23.36,23.36,0,0,0,50.23,18.36Z"/>
<polygon class="icon__hands" points="30 17.06 35.19 17.06 35.19 32.64 35.19 37.83 30 37.83 19.62 37.83 19.62 32.64 30 32.64 30 17.06"/> <polygon className="icon__hands" points="30 17.06 35.19 17.06 35.19 32.64 35.19 37.83 30 37.83 19.62 37.83 19.62 32.64 30 32.64 30 17.06"/>
</svg> </svg>
); );
} }

View File

@@ -0,0 +1,14 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class AddMini extends BaseIcon {
render() {
return (
<svg className={`icon icon--remove ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path d="M53.7,25.3H6.3v9.4h47.4"/>
</svg>
);
}
}

View File

@@ -3,8 +3,10 @@ import classnames from 'classnames';
import React from 'react'; import React from 'react';
import EventTypes from '../../constants/EventTypes'; import EventTypes from '../../constants/EventTypes';
import Modal from './Modal';
import TextboxRepeater from '../forms/TextboxRepeater'; import TextboxRepeater from '../forms/TextboxRepeater';
import TorrentActions from '../../actions/TorrentActions'; import TorrentActions from '../../actions/TorrentActions';
import UIActions from '../../actions/UIActions';
import UIStore from '../../stores/UIStore'; import UIStore from '../../stores/UIStore';
const METHODS_TO_BIND = [ const METHODS_TO_BIND = [
@@ -45,15 +47,34 @@ export default class AddTorrents extends React.Component {
UIStore.fetchLatestTorrentLocation(); UIStore.fetchLatestTorrentLocation();
} }
dismissModal() {
UIActions.dismissModal();
}
onLatestTorrentLocationChange() { onLatestTorrentLocationChange() {
this.setState({destination: UIStore.getLatestTorrentLocation()}); this.setState({destination: UIStore.getLatestTorrentLocation()});
} }
getActions() {
return [
{
clickHandler: null,
content: 'Cancel',
triggerDismiss: true,
type: 'secondary'
},
{
clickHandler: this.handleAddTorrents,
content: 'Add Torrent',
triggerDismiss: true,
type: 'primary'
}
];
}
getContent() { getContent() {
return ( return (
<div className="modal__content" onClick={this.handleMenuWrapperClick}> <div className="form">
<div className="modal__header">Add Torrents</div>
<div className="modal__content__container">
<div className="form__row"> <div className="form__row">
<TextboxRepeater placeholder="Torrent URL" <TextboxRepeater placeholder="Torrent URL"
handleTextboxAdd={this.handleUrlAdd} handleTextboxAdd={this.handleUrlAdd}
@@ -68,13 +89,6 @@ export default class AddTorrents extends React.Component {
value={this.state.destination} value={this.state.destination}
type="text" /> type="text" />
</div> </div>
<div className="modal__button-group form__row">
<button className="button button--deemphasize"
onClick={this.props.dismissModal}>Cancel</button>
<button className="button button--primary"
onClick={this.handleAddTorrents}>Add Torrent</button>
</div>
</div>
</div> </div>
); );
} }
@@ -115,6 +129,11 @@ export default class AddTorrents extends React.Component {
} }
render() { render() {
return this.getContent(); return (
<Modal heading="Add Torrents"
content={this.getContent()}
actions={this.getActions()}
dismiss={this.dismissModal} />
);
} }
} }

View File

@@ -0,0 +1,63 @@
import _ from 'lodash';
import classnames from 'classnames';
import React from 'react';
export default class Modal extends React.Component {
getModalButtons(actions) {
let buttons = actions.map((action, index) => {
let classes = classnames('button', {
'button--deemphasize': action.type === 'secondary',
'button--primary': action.type === 'primary'
});
return (
<button className={classes} onClick={this.getClickHandler(action)} key={index}>
{action.content}
</button>
);
});
return (
<div className="modal__button-group form__row">
{buttons}
</div>
);
}
getClickHandler(action) {
return () => {
if (action.triggerDismiss) {
this.props.dismiss();
}
if (action.clickHandler) {
action.clickHandler();
}
}
}
handleMenuWrapperClick(event) {
event.stopPropagation();
}
render() {
let contentClasses = classnames('modal__content',
`modal__content--align-${this.props.alignment}`);
return (
<div className={contentClasses} onClick={this.handleMenuWrapperClick}>
<div className="modal__header">{this.props.heading}</div>
<div className="modal__content__container">
{this.props.content}
</div>
<div className="modal__footer">
{this.getModalButtons(this.props.actions)}
</div>
</div>
);
}
}
Modal.defaultProps = {
alignment: 'left'
};

View File

@@ -3,6 +3,7 @@ import React from 'react';
import AddTorrents from './AddTorrents'; import AddTorrents from './AddTorrents';
import EventTypes from '../../constants/EventTypes'; import EventTypes from '../../constants/EventTypes';
import Modal from './Modal';
import UIActions from '../../actions/UIActions'; import UIActions from '../../actions/UIActions';
import UIStore from '../../stores/UIStore'; import UIStore from '../../stores/UIStore';
@@ -11,7 +12,7 @@ const METHODS_TO_BIND = [
'onModalChange' 'onModalChange'
]; ];
export default class Modal extends React.Component { export default class Modals extends React.Component {
constructor() { constructor() {
super(); super();
@@ -32,12 +33,16 @@ export default class Modal extends React.Component {
UIStore.unlisten(EventTypes.UI_MODAL_CHANGE, this.onModalChange); UIStore.unlisten(EventTypes.UI_MODAL_CHANGE, this.onModalChange);
} }
dismissModal() {
UIActions.dismissModal();
}
handleModalClick(event) { handleModalClick(event) {
event.stopPropagation(); event.stopPropagation();
} }
handleOverlayClick() { handleOverlayClick() {
UIActions.dismissModal(); this.dismissModal();
} }
onModalChange() { onModalChange() {
@@ -47,7 +52,24 @@ export default class Modal extends React.Component {
render() { render() {
let modal = null; let modal = null;
switch (this.state.activeModal) { let modalOptions = null;
let modalType = this.state.activeModal;
if (modalType && modalType.type) {
modalOptions = modalType;
modalType = modalType.type;
}
switch (modalType) {
case 'confirm':
modal = (
<Modal actions={modalOptions.actions}
alignment="center"
content={modalOptions.content}
dismiss={this.dismissModal}
heading={modalOptions.heading} />
);
break;
case 'add-torrents': case 'add-torrents':
modal = ( modal = (
<AddTorrents dismissModal={this.handleOverlayClick} /> <AddTorrents dismissModal={this.handleOverlayClick} />

View File

@@ -4,9 +4,11 @@ import Action from './Action';
import Add from '../icons/Add'; import Add from '../icons/Add';
import EventTypes from '../../constants/EventTypes'; import EventTypes from '../../constants/EventTypes';
import Pause from '../icons/Pause'; import Pause from '../icons/Pause';
import Remove from '../icons/Remove';
import SortDropdown from './SortDropdown'; import SortDropdown from './SortDropdown';
import Start from '../icons/Start'; import Start from '../icons/Start';
import Stop from '../icons/Stop'; import Stop from '../icons/Stop';
import stringUtil from '../../../../../util/stringUtil';
import TorrentActions from '../../actions/TorrentActions'; import TorrentActions from '../../actions/TorrentActions';
import TorrentFilterStore from '../../stores/TorrentFilterStore'; import TorrentFilterStore from '../../stores/TorrentFilterStore';
import TorrentStore from '../../stores/TorrentStore'; import TorrentStore from '../../stores/TorrentStore';
@@ -14,6 +16,7 @@ import UIActions from '../../actions/UIActions';
const METHODS_TO_BIND = [ const METHODS_TO_BIND = [
'handleAddTorrents', 'handleAddTorrents',
'handleRemoveTorrents',
'handleSortChange', 'handleSortChange',
'handleStart', 'handleStart',
'handleStop', 'handleStop',
@@ -46,6 +49,55 @@ export default class ActionBar extends React.Component {
UIActions.displayModal('add-torrents'); UIActions.displayModal('add-torrents');
} }
handleRemoveTorrentConfirm(torrents) {
TorrentActions.deleteTorrents(torrents);
}
handleRemoveTorrentDecline() {
console.log('decline');
}
handleRemoveTorrents() {
let selectedTorrents = TorrentStore.getSelectedTorrents() || [];
let selectedTorrentCount = selectedTorrents.length;
let actions = [
{
clickHandler: this.handleRemoveTorrentDecline,
content: 'No',
triggerDismiss: true,
type: 'secondary'
},
{
clickHandler: this.handleRemoveTorrentConfirm.bind(this, selectedTorrents),
content: 'Yes',
triggerDismiss: true,
type: 'primary'
}
];
let torrentText = stringUtil.pluralize('torrent', selectedTorrentCount);
let content = `You are about to remove ${selectedTorrentCount} ${torrentText}.`;
if (selectedTorrentCount === 0) {
actions = [
{
clickHandler: null,
content: 'OK',
triggerDismiss: true,
type: 'primary'
}
];
content = 'You haven\'t selected any torrents.';
}
UIActions.displayModal({
actions,
content,
heading: 'Are you sure?',
type: 'confirm'
});
}
handleSortChange(sortBy) { handleSortChange(sortBy) {
UIActions.setTorrentsSort(sortBy); UIActions.setTorrentsSort(sortBy);
} }
@@ -83,6 +135,8 @@ export default class ActionBar extends React.Component {
<div className="action-bar__group action-bar__group--has-divider"> <div className="action-bar__group action-bar__group--has-divider">
<Action label="Add Torrent" slug="add-torrent" icon={<Add />} <Action label="Add Torrent" slug="add-torrent" icon={<Add />}
clickHandler={this.handleAddTorrents} /> clickHandler={this.handleAddTorrents} />
<Action label="Remove Torrent" slug="remove-torrent" icon={<Remove />}
clickHandler={this.handleRemoveTorrents} />
</div> </div>
</div> </div>
</nav> </nav>

View File

@@ -57,7 +57,7 @@ export default class Torrent extends React.Component {
}); });
return ( return (
<li className={classes} onMouseDown={this.handleClick} <li className={classes} onClick={this.handleClick}
onContextMenu={this.handleRightClick}> onContextMenu={this.handleRightClick}>
<ul className="torrent__details"> <ul className="torrent__details">
<li className="torrent__details--primary text-overflow"> <li className="torrent__details--primary text-overflow">

View File

@@ -9,6 +9,8 @@ const ActionTypes = {
CLIENT_FETCH_TRANSFER_DATA_SUCCESS: 'CLIENT_FETCH_TRANSFER_DATA_SUCCESS', CLIENT_FETCH_TRANSFER_DATA_SUCCESS: 'CLIENT_FETCH_TRANSFER_DATA_SUCCESS',
CLIENT_FETCH_TRANSFER_HISTORY_ERROR: 'CLIENT_FETCH_TRANSFER_HISTORY_ERROR', CLIENT_FETCH_TRANSFER_HISTORY_ERROR: 'CLIENT_FETCH_TRANSFER_HISTORY_ERROR',
CLIENT_FETCH_TRANSFER_HISTORY_SUCCESS: 'CLIENT_FETCH_TRANSFER_HISTORY_SUCCESS', CLIENT_FETCH_TRANSFER_HISTORY_SUCCESS: 'CLIENT_FETCH_TRANSFER_HISTORY_SUCCESS',
CLIENT_REMOVE_TORRENT_ERROR: 'CLIENT_REMOVE_TORRENT_ERROR',
CLIENT_REMOVE_TORRENT_SUCCESS: 'CLIENT_REMOVE_TORRENT_SUCCESS',
CLIENT_SET_THROTTLE_ERROR: 'CLIENT_SET_THROTTLE_ERROR', CLIENT_SET_THROTTLE_ERROR: 'CLIENT_SET_THROTTLE_ERROR',
CLIENT_SET_THROTTLE_SUCCESS: 'CLIENT_SET_THROTTLE_SUCCESS', CLIENT_SET_THROTTLE_SUCCESS: 'CLIENT_SET_THROTTLE_SUCCESS',
CLIENT_START_TORRENT_ERROR: 'CLIENT_START_TORRENT_ERROR', CLIENT_START_TORRENT_ERROR: 'CLIENT_START_TORRENT_ERROR',

View File

@@ -61,7 +61,7 @@ class UIStoreClass extends BaseStore {
return this.torrentDetailsOpen; return this.torrentDetailsOpen;
} }
setActiveModal(modal) { setActiveModal(modal = {}) {
this.activeModal = modal; this.activeModal = modal;
this.emit(EventTypes.UI_MODAL_CHANGE); this.emit(EventTypes.UI_MODAL_CHANGE);
} }

File diff suppressed because one or more lines are too long

View File

@@ -749,6 +749,9 @@ body {
transform: translateX(-50%) translateY(-50%); transform: translateX(-50%) translateY(-50%);
width: 30px; } width: 30px; }
.application__content {
background: #e9eef2; }
.client-stat { .client-stat {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@@ -874,11 +877,9 @@ body {
.client-stat--upload .graph__line--rate { .client-stat--upload .graph__line--rate {
stroke: rgba(35, 135, 217, 0.4); } stroke: rgba(35, 135, 217, 0.4); }
.application__content {
background: #e9eef2; }
.directory-tree .icon { .directory-tree .icon {
display: inline-block; display: inline-block;
fill: rgba(82, 120, 147, 0.4);
height: 14px; height: 14px;
margin-right: 6px; margin-right: 6px;
width: 14px; } width: 14px; }
@@ -1085,6 +1086,99 @@ body {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 8px; } width: 8px; }
.icon--eta .icon__ring {
fill-opacity: 1; }
.modal {
background: rgba(26, 47, 61, 0.5);
height: 100%;
left: 0;
position: fixed;
top: 0;
-webkit-transition: opacity 0.5s;
transition: opacity 0.5s;
width: 100%;
z-index: 100; }
.modal__content {
background: #fff;
border-radius: 5px;
box-shadow: 0 0 0 1px rgba(26, 47, 61, 0.1), 0 0 35px rgba(26, 47, 61, 0.3);
left: 50%;
max-height: 80%;
max-width: 80%;
overflow: auto;
padding: 30px;
position: absolute;
top: 10%;
-webkit-transform: translate(-50%, 0);
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
width: 500px; }
.modal__content--align-center {
text-align: center; }
.modal__footer {
margin-top: 25px; }
.modal__button-group {
text-align: right; }
.modal__button-group .button + .button {
margin-left: 20px; }
.modal__header {
color: #313436;
font-size: 1.1em;
font-weight: 400;
margin-bottom: 20px; }
.modal__animation-enter {
opacity: 0; }
.modal__animation-enter-active {
opacity: 1; }
.modal__animation-leave {
opacity: 1;
pointer-events: none; }
.modal__animation-leave-active {
opacity: 0; }
.progress-bar {
height: 3px;
position: relative;
-webkit-transition: opacity 0.25s;
transition: opacity 0.25s;
z-index: 1; }
.is-selected.is-stopped .progress-bar {
opacity: 0.5; }
.progress-bar:after {
background: #e3e5e5;
content: '';
height: 1px;
left: 0;
position: absolute;
z-index: 0;
top: 1px;
-webkit-transition: background 0.25s;
transition: background 0.25s;
width: 100%; }
.is-selected .progress-bar:after {
background: rgba(255, 255, 255, 0.5); }
.is-selected.is-stopped .progress-bar:after {
background: rgba(255, 255, 255, 0.5); }
.progress-bar__fill {
background: #39ce83;
bottom: 0;
height: 100%;
left: 0;
position: absolute;
top: 0;
-webkit-transition: background 0.25s, width 0.25s;
transition: background 0.25s, width 0.25s;
z-index: 1; }
.is-completed .progress-bar__fill {
background: #258de5; }
.is-seeding .progress-bar__fill {
background: #258de5; }
.is-stopped .progress-bar__fill {
background: #e3e5e5; }
.is-selected .progress-bar__fill {
background: #fff; }
.application__sidebar { .application__sidebar {
box-shadow: 1px 0 rgba(6, 9, 11, 0.3); box-shadow: 1px 0 rgba(6, 9, 11, 0.3);
color: #53718a; color: #53718a;
@@ -1175,98 +1269,6 @@ body {
.sidebar__item--speed-limit .dropdown__items { .sidebar__item--speed-limit .dropdown__items {
font-size: 0.9em; } font-size: 0.9em; }
.modal {
background: rgba(26, 47, 61, 0.5);
height: 100%;
left: 0;
position: fixed;
top: 0;
-webkit-transition: opacity 0.5s;
transition: opacity 0.5s;
width: 100%;
z-index: 100; }
.modal__content {
background: #fff;
border-radius: 5px;
box-shadow: 0 0 0 1px rgba(26, 47, 61, 0.1), 0 0 35px rgba(26, 47, 61, 0.3);
left: 50%;
max-height: 80%;
max-width: 80%;
overflow: auto;
padding: 30px;
position: absolute;
top: 10%;
-webkit-transform: translate(-50%, 0);
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
width: 500px; }
.modal__button-group {
text-align: right; }
.modal__button-group .button + .button {
margin-left: 20px; }
.modal__header {
color: #313436;
font-size: 1.1em;
font-weight: 400;
margin-bottom: 20px; }
.modal__animation-enter {
opacity: 0; }
.modal__animation-enter-active {
opacity: 1; }
.modal__animation-leave {
opacity: 1;
pointer-events: none; }
.modal__animation-leave-active {
opacity: 0; }
.progress-bar {
height: 3px;
position: relative;
-webkit-transition: opacity 0.25s;
transition: opacity 0.25s;
z-index: 1; }
.is-selected.is-stopped .progress-bar {
opacity: 0.5; }
.progress-bar:after {
background: #e3e5e5;
content: '';
height: 1px;
left: 0;
position: absolute;
z-index: 0;
top: 1px;
-webkit-transition: background 0.25s;
transition: background 0.25s;
width: 100%; }
.is-selected .progress-bar:after {
background: rgba(255, 255, 255, 0.5); }
.is-selected.is-stopped .progress-bar:after {
background: rgba(255, 255, 255, 0.5); }
.progress-bar__fill {
background: #39ce83;
bottom: 0;
height: 100%;
left: 0;
position: absolute;
top: 0;
-webkit-transition: background 0.25s, width 0.25s;
transition: background 0.25s, width 0.25s;
z-index: 1; }
.is-completed .progress-bar__fill {
background: #258de5; }
.is-seeding .progress-bar__fill {
background: #258de5; }
.is-stopped .progress-bar__fill {
background: #e3e5e5; }
.is-selected .progress-bar__fill {
background: #fff; }
.status-filter { .status-filter {
font-size: 0.85em; font-size: 0.85em;
padding: 30px 0; } padding: 30px 0; }
@@ -1407,6 +1409,7 @@ body {
.torrent-details__navigation { .torrent-details__navigation {
box-shadow: 1px 0 0 rgba(4, 13, 19, 0.35); box-shadow: 1px 0 0 rgba(4, 13, 19, 0.35);
background: rgba(12, 27, 38, 0.5); background: rgba(12, 27, 38, 0.5);
min-width: 125px;
padding-left: 0; padding-left: 0;
padding-right: 0; } padding-right: 0; }
.torrent-details__navigation .navigation__item { .torrent-details__navigation .navigation__item {

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
let Datastore = require('nedb'); let Datastore = require('nedb');
let config = require('../../config'); let config = require('../../config');
let stringUtil = require('./util/stringUtil'); let stringUtil = require('../../util/stringUtil');
const MAX_CLEANUP_INTERVAL = 1000 * 60 * 60; // 1 hour const MAX_CLEANUP_INTERVAL = 1000 * 60 * 60; // 1 hour
const MAX_NEXT_ERA_UPDATE_INTERVAL = 1000 * 60 * 60 * 12; // 12 hours const MAX_NEXT_ERA_UPDATE_INTERVAL = 1000 * 60 * 60 * 12; // 12 hours

View File

@@ -49,6 +49,22 @@ var client = {
}); });
}, },
deleteTorrents: function(hash, callback) {
if (!util.isArray(hash)) {
hash = [hash];
} else {
hash = String(hash).split(',');
}
for (i = 0, len = hash.length; i < len; i++) {
rTorrent.get('d.erase', [hash[i]]).then(function(data) {
callback(null, data);
}, function(error) {
callback(error, null);
});
}
},
getTorrentDetails: function(hash, callback) { getTorrentDetails: function(hash, callback) {
var peerParams = [hash, ''].concat(clientUtil.defaults.peerPropertyMethods); var peerParams = [hash, ''].concat(clientUtil.defaults.peerPropertyMethods);
var fileParams = [hash, ''].concat(clientUtil.defaults.filePropertyMethods); var fileParams = [hash, ''].concat(clientUtil.defaults.filePropertyMethods);

View File

@@ -1,9 +0,0 @@
'use strict';
let stringUtil = {
capitalize: function (string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
module.exports = stringUtil;

View File

@@ -16,10 +16,6 @@ router.get('/history', function(req, res, next) {
history.get(req.query, ajaxUtil.getResponseFn(res)); history.get(req.query, ajaxUtil.getResponseFn(res));
}); });
router.get('/list', function(req, res, next) {
client.getTorrentList(ajaxUtil.getResponseFn(res));
});
router.put('/settings/speed-limits', function(req, res, next) { router.put('/settings/speed-limits', function(req, res, next) {
client.setSpeedLimits(req.body, ajaxUtil.getResponseFn(res)); client.setSpeedLimits(req.body, ajaxUtil.getResponseFn(res));
}); });
@@ -43,6 +39,15 @@ router.post('/torrent-details', function(req, res, next) {
client.getTorrentDetails(hash, ajaxUtil.getResponseFn(res)); client.getTorrentDetails(hash, ajaxUtil.getResponseFn(res));
}); });
router.get('/torrents', function(req, res, next) {
client.getTorrentList(ajaxUtil.getResponseFn(res));
});
router.post('/torrents/delete', function(req, res, next) {
var hash = req.body.hash;
client.deleteTorrents(hash, ajaxUtil.getResponseFn(res));
});
router.get('/methods.json', function(req, res, next) { router.get('/methods.json', function(req, res, next) {
var type = req.query.type; var type = req.query.type;
var args = req.query.args; var args = req.query.args;

21
util/stringUtil.js Normal file
View File

@@ -0,0 +1,21 @@
'use strict';
let stringUtil = {
capitalize: function (string) {
return string.charAt(0).toUpperCase() + string.slice(1);
},
pluralize: function (string, count) {
if (count !== 1) {
if (string.charAt(string.length - 1) === 'y') {
return `${string.substring(0, string.length - 1)}ies`;
} else {
return `${string}s`;
}
}
return string;
}
}
module.exports = stringUtil;