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;
|
||||
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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
$blue: #258de5;
|
||||
$green: #39ce83;
|
||||
$white: #fff;
|
||||
|
||||
$background: #1a2f3d;
|
||||
$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 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);
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user