mirror of
https://github.com/zoriya/flood.git
synced 2025-12-21 22:55:14 +00:00
Abstract modal
Add remove torrents modal Add string util Add icons for torrent details view
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
.icon {
|
.icon {
|
||||||
|
|
||||||
&__ring {
|
&__ring {
|
||||||
opacity: 0.5;
|
fill-opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
14
client/source/scripts/components/icons/Remove.js
Normal file
14
client/source/scripts/components/icons/Remove.js
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
client/source/scripts/components/modals/Modal.js
Normal file
63
client/source/scripts/components/modals/Modal.js
Normal 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'
|
||||||
|
};
|
||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
let stringUtil = {
|
|
||||||
capitalize: function (string) {
|
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = stringUtil;
|
|
||||||
@@ -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
21
util/stringUtil.js
Normal 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;
|
||||||
Reference in New Issue
Block a user