mirror of
https://github.com/zoriya/flood.git
synced 2025-12-20 06:05:15 +00:00
Add loading indicator to add torrents button
This commit is contained in:
@@ -45,6 +45,24 @@
|
|||||||
padding: 8px 22px;
|
padding: 8px 22px;
|
||||||
transition: background 0.25s;
|
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 {
|
&--deemphasize {
|
||||||
background: $button--deemphasized--background;
|
background: $button--deemphasized--background;
|
||||||
color: $button--deemphasized--foreground;
|
color: $button--deemphasized--foreground;
|
||||||
@@ -64,7 +82,6 @@
|
|||||||
background: $button--primary--background--hover;
|
background: $button--primary--background--hover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
@keyframes loading-indicator-dots-pulse {
|
||||||
|
0% {
|
||||||
|
fill-opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
fill-opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
|
||||||
&--eta {
|
&--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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
$blue: #258de5;
|
$blue: #258de5;
|
||||||
$green: #39ce83;
|
$green: #39ce83;
|
||||||
|
$white: #fff;
|
||||||
|
|
||||||
$background: #1a2f3d;
|
$background: #1a2f3d;
|
||||||
$foreground: #53718a;
|
$foreground: #53718a;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,11 @@ import classnames from 'classnames';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import EventTypes from '../../constants/EventTypes';
|
import EventTypes from '../../constants/EventTypes';
|
||||||
|
import LoadingIndicatorDots from '../icons/LoadingIndicatorDots';
|
||||||
import Modal from './Modal';
|
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 TorrentStore from '../../stores/TorrentStore';
|
||||||
import UIActions from '../../actions/UIActions';
|
import UIActions from '../../actions/UIActions';
|
||||||
import UIStore from '../../stores/UIStore';
|
import UIStore from '../../stores/UIStore';
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ const METHODS_TO_BIND = [
|
|||||||
'handleUrlChange',
|
'handleUrlChange',
|
||||||
'handleUrlRemove',
|
'handleUrlRemove',
|
||||||
'handleAddTorrents',
|
'handleAddTorrents',
|
||||||
|
'onAddTorrentSuccess',
|
||||||
'onLatestTorrentLocationChange'
|
'onLatestTorrentLocationChange'
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -24,8 +27,10 @@ export default class AddTorrents extends React.Component {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
addTorrentsError: null,
|
||||||
destination: null,
|
destination: null,
|
||||||
isExpanded: false,
|
isExpanded: false,
|
||||||
|
isAddingTorrents: false,
|
||||||
urlTextboxes: [{value: null}]
|
urlTextboxes: [{value: null}]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,10 +44,12 @@ export default class AddTorrents extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
TorrentStore.listen(EventTypes.CLIENT_ADD_TORRENT_SUCCESS, this.onAddTorrentSuccess);
|
||||||
UIStore.listen(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
UIStore.listen(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
TorrentStore.unlisten(EventTypes.CLIENT_ADD_TORRENT_SUCCESS, this.onAddTorrentSuccess);
|
||||||
UIStore.unlisten(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
UIStore.unlisten(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
||||||
UIStore.fetchLatestTorrentLocation();
|
UIStore.fetchLatestTorrentLocation();
|
||||||
}
|
}
|
||||||
@@ -51,11 +58,30 @@ export default class AddTorrents extends React.Component {
|
|||||||
UIActions.dismissModal();
|
UIActions.dismissModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAddTorrentError() {
|
||||||
|
this.setState({
|
||||||
|
addTorrentsError: 'There was an error, but I have no idea what happened!',
|
||||||
|
isAddingTorrents: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddTorrentSuccess() {
|
||||||
|
this.dismissModal();
|
||||||
|
}
|
||||||
|
|
||||||
onLatestTorrentLocationChange() {
|
onLatestTorrentLocationChange() {
|
||||||
this.setState({destination: UIStore.getLatestTorrentLocation()});
|
this.setState({destination: UIStore.getLatestTorrentLocation()});
|
||||||
}
|
}
|
||||||
|
|
||||||
getActions() {
|
getActions() {
|
||||||
|
let icon = null;
|
||||||
|
let primaryButtonText = 'Add Torrent';
|
||||||
|
|
||||||
|
if (this.state.isAddingTorrents) {
|
||||||
|
icon = <LoadingIndicatorDots viewBox="0 0 32 32" />;
|
||||||
|
primaryButtonText = 'Adding...';
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
@@ -65,7 +91,13 @@ export default class AddTorrents extends React.Component {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
clickHandler: this.handleAddTorrents,
|
clickHandler: this.handleAddTorrents,
|
||||||
content: 'Add Torrent',
|
content: (
|
||||||
|
<span>
|
||||||
|
{icon}
|
||||||
|
{primaryButtonText}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
supplementalClassName: icon != null ? 'has-icon' : '',
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'primary'
|
type: 'primary'
|
||||||
}
|
}
|
||||||
@@ -73,8 +105,19 @@ export default class AddTorrents extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getContent() {
|
getContent() {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
if (this.state.addTorrentsError) {
|
||||||
|
error = (
|
||||||
|
<div className="form__row">
|
||||||
|
{this.state.addTorrentsError}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form">
|
<div className="form">
|
||||||
|
{error}
|
||||||
<div className="form__row">
|
<div className="form__row">
|
||||||
<TextboxRepeater placeholder="Torrent URL"
|
<TextboxRepeater placeholder="Torrent URL"
|
||||||
handleTextboxAdd={this.handleUrlAdd}
|
handleTextboxAdd={this.handleUrlAdd}
|
||||||
@@ -94,6 +137,7 @@ export default class AddTorrents extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleAddTorrents() {
|
handleAddTorrents() {
|
||||||
|
this.setState({isAddingTorrents: true});
|
||||||
let torrentUrls = _.pluck(this.state.urlTextboxes, 'value');
|
let torrentUrls = _.pluck(this.state.urlTextboxes, 'value');
|
||||||
TorrentActions.addTorrents(torrentUrls, this.state.destination);
|
TorrentActions.addTorrents(torrentUrls, this.state.destination);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
const EventTypes = {
|
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_ERROR: 'CLIENT_SET_THROTTLE_ERROR',
|
||||||
CLIENT_SET_THROTTLE_SUCCESS: 'CLIENT_SET_THROTTLE_SUCCESS',
|
CLIENT_SET_THROTTLE_SUCCESS: 'CLIENT_SET_THROTTLE_SUCCESS',
|
||||||
CLIENT_TORRENTS_REQUEST_ERROR: 'CLIENT_TORRENTS_REQUEST_ERROR',
|
CLIENT_TORRENTS_REQUEST_ERROR: 'CLIENT_TORRENTS_REQUEST_ERROR',
|
||||||
|
|||||||
@@ -54,6 +54,14 @@ class TorrentStoreClass extends BaseStore {
|
|||||||
return this.selectedTorrents;
|
return this.selectedTorrents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleAddTorrentError(error) {
|
||||||
|
this.emit(EventTypes.CLIENT_ADD_TORRENT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAddTorrentSuccess(data) {
|
||||||
|
this.emit(EventTypes.CLIENT_ADD_TORRENT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedTorrents(event, hash) {
|
setSelectedTorrents(event, hash) {
|
||||||
this.selectedTorrents = selectTorrents({
|
this.selectedTorrents = selectTorrents({
|
||||||
event,
|
event,
|
||||||
@@ -156,6 +164,12 @@ AppDispatcher.register((payload) => {
|
|||||||
case ActionTypes.CLIENT_FETCH_TORRENT_DETAILS_SUCCESS:
|
case ActionTypes.CLIENT_FETCH_TORRENT_DETAILS_SUCCESS:
|
||||||
TorrentStore.setTorrentDetails(action.data.hash, action.data.torrentDetails);
|
TorrentStore.setTorrentDetails(action.data.hash, action.data.torrentDetails);
|
||||||
break;
|
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:
|
case ActionTypes.CLIENT_FETCH_TORRENTS_SUCCESS:
|
||||||
TorrentStore.setTorrents(action.data.torrents);
|
TorrentStore.setTorrents(action.data.torrents);
|
||||||
break;
|
break;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -537,6 +537,15 @@ th {
|
|||||||
padding: 8px 22px;
|
padding: 8px 22px;
|
||||||
-webkit-transition: background 0.25s;
|
-webkit-transition: background 0.25s;
|
||||||
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 {
|
.button--deemphasize {
|
||||||
background: #e9eef2;
|
background: #e9eef2;
|
||||||
color: #66717a; }
|
color: #66717a; }
|
||||||
@@ -1129,9 +1138,32 @@ body {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 8px; }
|
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 {
|
.icon--eta .icon__ring {
|
||||||
fill-opacity: 1; }
|
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 {
|
@-webkit-keyframes loading-indicator-swipe {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: translateX(-100%);
|
-webkit-transform: translateX(-100%);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user