Add loading indicator to add torrents button

This commit is contained in:
John Furrow
2016-02-08 22:31:00 -08:00
parent 3d9a3dbf6b
commit a30f46292d
10 changed files with 170 additions and 6 deletions

View File

@@ -45,6 +45,24 @@
padding: 8px 22px;
transition: background 0.25s;
&.has-icon {
.icon {
height: 16px;
margin: 0 $spacing-unit * 1/3;
vertical-align: middle;
width: 16px;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
}
&--deemphasize {
background: $button--deemphasized--background;
color: $button--deemphasized--foreground;
@@ -64,7 +82,6 @@
background: $button--primary--background--hover;
}
}
}
.form {

View File

@@ -1,3 +1,13 @@
@keyframes loading-indicator-dots-pulse {
0% {
fill-opacity: 0.2;
}
100% {
fill-opacity: 0.8;
}
}
.icon {
&--eta {
@@ -9,4 +19,26 @@
}
}
}
&--loading-indicator {
.loading-indicator {
&--dots {
&__dot {
animation: loading-indicator-dots-pulse 0.6s linear alternate infinite;
fill: $white;
&--center {
animation-delay: 0.2s;
}
&--right {
animation-delay: 0.4s;
}
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
$blue: #258de5;
$green: #39ce83;
$white: #fff;
$background: #1a2f3d;
$foreground: #53718a;

View File

@@ -0,0 +1,16 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class FolderClosedOutline extends BaseIcon {
render() {
return (
<svg className={`icon icon--loading-indicator loading-indicator--dots ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path className="loading-indicator--dots__dot loading-indicator--dots__dot--right" d="M25,11.74h4.73a1.89,1.89,0,0,1,1.89,1.89v4.73a1.89,1.89,0,0,1-1.89,1.89H25a1.89,1.89,0,0,1-1.89-1.89V13.63A1.89,1.89,0,0,1,25,11.74Z"/>
<path className="loading-indicator--dots__dot loading-indicator--dots__dot--center" d="M13.63,11.74h4.73a1.89,1.89,0,0,1,1.89,1.89v4.73a1.89,1.89,0,0,1-1.89,1.89H13.63a1.89,1.89,0,0,1-1.89-1.89V13.63A1.89,1.89,0,0,1,13.63,11.74Z"/>
<path className="loading-indicator--dots__dot loading-indicator--dots__dot--left" d="M2.27,11.74H7A1.89,1.89,0,0,1,8.9,13.63v4.73A1.89,1.89,0,0,1,7,20.26H2.27A1.89,1.89,0,0,1,.38,18.37V13.63A1.89,1.89,0,0,1,2.27,11.74Z"/>
</svg>
);
}
}

View File

@@ -3,9 +3,11 @@ import classnames from 'classnames';
import React from 'react';
import EventTypes from '../../constants/EventTypes';
import LoadingIndicatorDots from '../icons/LoadingIndicatorDots';
import Modal from './Modal';
import TextboxRepeater from '../forms/TextboxRepeater';
import TorrentActions from '../../actions/TorrentActions';
import TorrentStore from '../../stores/TorrentStore';
import UIActions from '../../actions/UIActions';
import UIStore from '../../stores/UIStore';
@@ -16,6 +18,7 @@ const METHODS_TO_BIND = [
'handleUrlChange',
'handleUrlRemove',
'handleAddTorrents',
'onAddTorrentSuccess',
'onLatestTorrentLocationChange'
];
@@ -24,8 +27,10 @@ export default class AddTorrents extends React.Component {
super();
this.state = {
addTorrentsError: null,
destination: null,
isExpanded: false,
isAddingTorrents: false,
urlTextboxes: [{value: null}]
};
@@ -39,10 +44,12 @@ export default class AddTorrents extends React.Component {
}
componentDidMount() {
TorrentStore.listen(EventTypes.CLIENT_ADD_TORRENT_SUCCESS, this.onAddTorrentSuccess);
UIStore.listen(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
}
componentWillUnmount() {
TorrentStore.unlisten(EventTypes.CLIENT_ADD_TORRENT_SUCCESS, this.onAddTorrentSuccess);
UIStore.unlisten(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
UIStore.fetchLatestTorrentLocation();
}
@@ -51,11 +58,30 @@ export default class AddTorrents extends React.Component {
UIActions.dismissModal();
}
onAddTorrentError() {
this.setState({
addTorrentsError: 'There was an error, but I have no idea what happened!',
isAddingTorrents: false
});
}
onAddTorrentSuccess() {
this.dismissModal();
}
onLatestTorrentLocationChange() {
this.setState({destination: UIStore.getLatestTorrentLocation()});
}
getActions() {
let icon = null;
let primaryButtonText = 'Add Torrent';
if (this.state.isAddingTorrents) {
icon = <LoadingIndicatorDots viewBox="0 0 32 32" />;
primaryButtonText = 'Adding...';
}
return [
{
clickHandler: null,
@@ -65,7 +91,13 @@ export default class AddTorrents extends React.Component {
},
{
clickHandler: this.handleAddTorrents,
content: 'Add Torrent',
content: (
<span>
{icon}
{primaryButtonText}
</span>
),
supplementalClassName: icon != null ? 'has-icon' : '',
triggerDismiss: true,
type: 'primary'
}
@@ -73,8 +105,19 @@ export default class AddTorrents extends React.Component {
}
getContent() {
let error = null;
if (this.state.addTorrentsError) {
error = (
<div className="form__row">
{this.state.addTorrentsError}
</div>
);
}
return (
<div className="form">
{error}
<div className="form__row">
<TextboxRepeater placeholder="Torrent URL"
handleTextboxAdd={this.handleUrlAdd}
@@ -94,6 +137,7 @@ export default class AddTorrents extends React.Component {
}
handleAddTorrents() {
this.setState({isAddingTorrents: true});
let torrentUrls = _.pluck(this.state.urlTextboxes, 'value');
TorrentActions.addTorrents(torrentUrls, this.state.destination);
}

View File

@@ -1,4 +1,6 @@
const EventTypes = {
CLIENT_ADD_TORRENT_ERROR: 'CLIENT_ADD_TORRENT_ERROR',
CLIENT_ADD_TORRENT_SUCCESS: 'CLIENT_ADD_TORRENT_SUCCESS',
CLIENT_SET_THROTTLE_ERROR: 'CLIENT_SET_THROTTLE_ERROR',
CLIENT_SET_THROTTLE_SUCCESS: 'CLIENT_SET_THROTTLE_SUCCESS',
CLIENT_TORRENTS_REQUEST_ERROR: 'CLIENT_TORRENTS_REQUEST_ERROR',

View File

@@ -54,6 +54,14 @@ class TorrentStoreClass extends BaseStore {
return this.selectedTorrents;
}
handleAddTorrentError(error) {
this.emit(EventTypes.CLIENT_ADD_TORRENT_ERROR);
}
handleAddTorrentSuccess(data) {
this.emit(EventTypes.CLIENT_ADD_TORRENT_SUCCESS);
}
setSelectedTorrents(event, hash) {
this.selectedTorrents = selectTorrents({
event,
@@ -156,6 +164,12 @@ AppDispatcher.register((payload) => {
case ActionTypes.CLIENT_FETCH_TORRENT_DETAILS_SUCCESS:
TorrentStore.setTorrentDetails(action.data.hash, action.data.torrentDetails);
break;
case ActionTypes.CLIENT_ADD_TORRENT_ERROR:
TorrentStore.handleAddTorrentError(action.error);
break;
case ActionTypes.CLIENT_ADD_TORRENT_SUCCESS:
TorrentStore.handleAddTorrentSuccess(action.data);
break;
case ActionTypes.CLIENT_FETCH_TORRENTS_SUCCESS:
TorrentStore.setTorrents(action.data.torrents);
break;

File diff suppressed because one or more lines are too long

View File

@@ -537,6 +537,15 @@ th {
padding: 8px 22px;
-webkit-transition: background 0.25s;
transition: background 0.25s; }
.button.has-icon .icon {
height: 16px;
margin: 0 8.33333px;
vertical-align: middle;
width: 16px; }
.button.has-icon .icon:first-child {
margin-left: 0; }
.button.has-icon .icon:last-child {
margin-right: 0; }
.button--deemphasize {
background: #e9eef2;
color: #66717a; }
@@ -1129,9 +1138,32 @@ body {
transform: translate(-50%, -50%);
width: 8px; }
@-webkit-keyframes loading-indicator-dots-pulse {
0% {
fill-opacity: 0.2; }
100% {
fill-opacity: 0.8; } }
@keyframes loading-indicator-dots-pulse {
0% {
fill-opacity: 0.2; }
100% {
fill-opacity: 0.8; } }
.icon--eta .icon__ring {
fill-opacity: 1; }
.icon--loading-indicator .loading-indicator--dots__dot {
-webkit-animation: loading-indicator-dots-pulse 0.6s linear alternate infinite;
animation: loading-indicator-dots-pulse 0.6s linear alternate infinite;
fill: #fff; }
.icon--loading-indicator .loading-indicator--dots__dot--center {
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s; }
.icon--loading-indicator .loading-indicator--dots__dot--right {
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s; }
@-webkit-keyframes loading-indicator-swipe {
0% {
-webkit-transform: translateX(-100%);

File diff suppressed because one or more lines are too long