diff --git a/client/source/sass/components/_directory-tree.scss b/client/source/sass/components/_directory-tree.scss index a4d3c0af..3c3b8ef1 100644 --- a/client/source/sass/components/_directory-tree.scss +++ b/client/source/sass/components/_directory-tree.scss @@ -2,6 +2,7 @@ .icon { display: inline-block; + fill: $directory-tree--icon--folder; height: 14px; margin-right: 6px; width: 14px; diff --git a/client/source/sass/components/_icons.scss b/client/source/sass/components/_icons.scss index bc5302be..011470dc 100644 --- a/client/source/sass/components/_icons.scss +++ b/client/source/sass/components/_icons.scss @@ -5,7 +5,7 @@ .icon { &__ring { - opacity: 0.5; + fill-opacity: 1; } } } diff --git a/client/source/sass/components/_modals.scss b/client/source/sass/components/_modals.scss index e990be10..dc2242b5 100644 --- a/client/source/sass/components/_modals.scss +++ b/client/source/sass/components/_modals.scss @@ -23,10 +23,15 @@ top: 10%; transform: translate(-50%, 0); width: 500px; - } -} -.modal { + &--align-center { + text-align: center; + } + } + + &__footer { + margin-top: $spacing-unit; + } &__button-group { text-align: right; @@ -38,9 +43,6 @@ } } } -} - -.modal { &__header { color: $modal--header--foreground; @@ -48,9 +50,6 @@ font-weight: 400; margin-bottom: 20px; } -} - -.modal { &__animation-enter { opacity: 0; diff --git a/client/source/sass/components/_torrent-details-panel.scss b/client/source/sass/components/_torrent-details-panel.scss index b4061e04..85860120 100644 --- a/client/source/sass/components/_torrent-details-panel.scss +++ b/client/source/sass/components/_torrent-details-panel.scss @@ -128,6 +128,7 @@ &__navigation { box-shadow: 1px 0 0 $torrent-details--navigation--border; background: $torrent-details--navigation--background; + min-width: 125px; padding-left: 0; padding-right: 0; diff --git a/client/source/sass/style.scss b/client/source/sass/style.scss index 1ab30f1d..eaf2f684 100644 --- a/client/source/sass/style.scss +++ b/client/source/sass/style.scss @@ -16,14 +16,15 @@ @import "base/typography"; @import "components/action-bar"; -@import "components/client-stats"; @import "components/application-content"; +@import "components/client-stats"; @import "components/directory-tree"; @import "components/dropdown"; @import "components/floating-action"; -@import "components/sidebar"; +@import "components/icons"; @import "components/modals"; @import "components/progress-bar"; +@import "components/sidebar"; @import "components/status-filter"; @import "components/textbox-repeater"; @import "components/torrent-details-panel"; diff --git a/client/source/scripts/actions/TorrentActions.js b/client/source/scripts/actions/TorrentActions.js index 8849d99c..d934c522 100644 --- a/client/source/scripts/actions/TorrentActions.js +++ b/client/source/scripts/actions/TorrentActions.js @@ -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 () { return axios.get('/ui/torrent-location') .then((json = {}) => { @@ -56,7 +75,7 @@ const TorrentActions = { }, fetchTorrents: function () { - return axios.get('/client/list') + return axios.get('/client/torrents') .then((json = {}) => { return json.data; }) diff --git a/client/source/scripts/components/filesystem/DirectoryFileList.js b/client/source/scripts/components/filesystem/DirectoryFileList.js index 7c1d9339..3f0fc0b4 100644 --- a/client/source/scripts/components/filesystem/DirectoryFileList.js +++ b/client/source/scripts/components/filesystem/DirectoryFileList.js @@ -12,7 +12,6 @@ export default class DirectoryFiles extends React.Component { }); let files = branch.map((file, fileIndex) => { - // console.log(file); let fileSize = format.data(file.sizeBytes, '', 1); return ( diff --git a/client/source/scripts/components/icons/ETA.js b/client/source/scripts/components/icons/ETA.js index 8b4026f1..3fe47f05 100644 --- a/client/source/scripts/components/icons/ETA.js +++ b/client/source/scripts/components/icons/ETA.js @@ -7,8 +7,8 @@ export default class ETA extends BaseIcon { return ( - - + + ); } diff --git a/client/source/scripts/components/icons/Remove.js b/client/source/scripts/components/icons/Remove.js new file mode 100644 index 00000000..9d99be60 --- /dev/null +++ b/client/source/scripts/components/icons/Remove.js @@ -0,0 +1,14 @@ +import React from 'react'; + +import BaseIcon from './BaseIcon'; + +export default class AddMini extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/source/scripts/components/modals/AddTorrents.js b/client/source/scripts/components/modals/AddTorrents.js index 09c48ed3..88c7f432 100644 --- a/client/source/scripts/components/modals/AddTorrents.js +++ b/client/source/scripts/components/modals/AddTorrents.js @@ -3,8 +3,10 @@ import classnames from 'classnames'; import React from 'react'; import EventTypes from '../../constants/EventTypes'; +import Modal from './Modal'; import TextboxRepeater from '../forms/TextboxRepeater'; import TorrentActions from '../../actions/TorrentActions'; +import UIActions from '../../actions/UIActions'; import UIStore from '../../stores/UIStore'; const METHODS_TO_BIND = [ @@ -45,35 +47,47 @@ export default class AddTorrents extends React.Component { UIStore.fetchLatestTorrentLocation(); } + dismissModal() { + UIActions.dismissModal(); + } + onLatestTorrentLocationChange() { 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() { return ( -
-
Add Torrents
-
-
- -
-
- -
-
- - -
+
+
+ +
+
+
); @@ -115,6 +129,11 @@ export default class AddTorrents extends React.Component { } render() { - return this.getContent(); + return ( + + ); } } diff --git a/client/source/scripts/components/modals/Modal.js b/client/source/scripts/components/modals/Modal.js new file mode 100644 index 00000000..7f2d913a --- /dev/null +++ b/client/source/scripts/components/modals/Modal.js @@ -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 ( + + ); + }); + + return ( +
+ {buttons} +
+ ); + } + + 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 ( +
+
{this.props.heading}
+
+ {this.props.content} +
+
+ {this.getModalButtons(this.props.actions)} +
+
+ ); + } +} + +Modal.defaultProps = { + alignment: 'left' +}; diff --git a/client/source/scripts/components/modals/Modals.js b/client/source/scripts/components/modals/Modals.js index fbf42e76..82dc3c0a 100644 --- a/client/source/scripts/components/modals/Modals.js +++ b/client/source/scripts/components/modals/Modals.js @@ -3,6 +3,7 @@ import React from 'react'; import AddTorrents from './AddTorrents'; import EventTypes from '../../constants/EventTypes'; +import Modal from './Modal'; import UIActions from '../../actions/UIActions'; import UIStore from '../../stores/UIStore'; @@ -11,7 +12,7 @@ const METHODS_TO_BIND = [ 'onModalChange' ]; -export default class Modal extends React.Component { +export default class Modals extends React.Component { constructor() { super(); @@ -32,12 +33,16 @@ export default class Modal extends React.Component { UIStore.unlisten(EventTypes.UI_MODAL_CHANGE, this.onModalChange); } + dismissModal() { + UIActions.dismissModal(); + } + handleModalClick(event) { event.stopPropagation(); } handleOverlayClick() { - UIActions.dismissModal(); + this.dismissModal(); } onModalChange() { @@ -47,7 +52,24 @@ export default class Modal extends React.Component { render() { 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 = ( + + ); + break; case 'add-torrents': modal = ( diff --git a/client/source/scripts/components/torrent-list/ActionBar.js b/client/source/scripts/components/torrent-list/ActionBar.js index 79f91077..eeed5591 100644 --- a/client/source/scripts/components/torrent-list/ActionBar.js +++ b/client/source/scripts/components/torrent-list/ActionBar.js @@ -4,9 +4,11 @@ import Action from './Action'; import Add from '../icons/Add'; import EventTypes from '../../constants/EventTypes'; import Pause from '../icons/Pause'; +import Remove from '../icons/Remove'; import SortDropdown from './SortDropdown'; import Start from '../icons/Start'; import Stop from '../icons/Stop'; +import stringUtil from '../../../../../util/stringUtil'; import TorrentActions from '../../actions/TorrentActions'; import TorrentFilterStore from '../../stores/TorrentFilterStore'; import TorrentStore from '../../stores/TorrentStore'; @@ -14,6 +16,7 @@ import UIActions from '../../actions/UIActions'; const METHODS_TO_BIND = [ 'handleAddTorrents', + 'handleRemoveTorrents', 'handleSortChange', 'handleStart', 'handleStop', @@ -46,6 +49,55 @@ export default class ActionBar extends React.Component { 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) { UIActions.setTorrentsSort(sortBy); } @@ -83,6 +135,8 @@ export default class ActionBar extends React.Component {
} clickHandler={this.handleAddTorrents} /> + } + clickHandler={this.handleRemoveTorrents} />
diff --git a/client/source/scripts/components/torrent-list/Torrent.js b/client/source/scripts/components/torrent-list/Torrent.js index f6e698d9..037287f1 100644 --- a/client/source/scripts/components/torrent-list/Torrent.js +++ b/client/source/scripts/components/torrent-list/Torrent.js @@ -57,7 +57,7 @@ export default class Torrent extends React.Component { }); return ( -