Stop flickering + added loader in AddressSelector (#4149)
* Stop UI flickering + added loader to AddressSelector #4103 * PR Grumbles
This commit is contained in:
parent
f0eab337d8
commit
57ce845e4c
@ -220,13 +220,27 @@ export default class TransferStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action _attachWalletOperation = (txhash) => {
|
@action _attachWalletOperation = (txhash) => {
|
||||||
|
if (!txhash || /^(0x)?0*$/.test(txhash)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let ethSubscriptionId = null;
|
let ethSubscriptionId = null;
|
||||||
|
|
||||||
|
// Number of blocks left to look-up (unsub after 15 blocks if nothing)
|
||||||
|
let nBlocksLeft = 15;
|
||||||
|
|
||||||
return this.api.subscribe('eth_blockNumber', () => {
|
return this.api.subscribe('eth_blockNumber', () => {
|
||||||
this.api.eth
|
this.api.eth
|
||||||
.getTransactionReceipt(txhash)
|
.getTransactionReceipt(txhash)
|
||||||
.then((tx) => {
|
.then((tx) => {
|
||||||
|
if (nBlocksLeft <= 0) {
|
||||||
|
this.api.unsubscribe(ethSubscriptionId);
|
||||||
|
ethSubscriptionId = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tx) {
|
if (!tx) {
|
||||||
|
nBlocksLeft--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +253,10 @@ export default class TransferStore {
|
|||||||
this.operation = operations[0];
|
this.operation = operations[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.api.unsubscribe(ethSubscriptionId);
|
||||||
|
ethSubscriptionId = null;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
this.api.unsubscribe(ethSubscriptionId);
|
this.api.unsubscribe(ethSubscriptionId);
|
||||||
ethSubscriptionId = null;
|
ethSubscriptionId = null;
|
||||||
});
|
});
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
transition: transform ease-out 0.1s;
|
transition: transform ease-out 0.1s;
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&.copied {
|
&.copied {
|
||||||
animation-duration: 0.25s;
|
animation-duration: 0.25s;
|
||||||
|
@ -15,6 +15,22 @@
|
|||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.outerInput {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1rem;
|
||||||
|
right: 9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
appearance: textfield;
|
appearance: textfield;
|
||||||
@ -58,13 +74,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
margin: 1rem 2.5rem 0.25em;
|
margin: 1rem 0.5rem 0.25em;
|
||||||
color: rgba(255, 255, 255, 0.498039);
|
color: rgba(255, 255, 255, 0.498039);
|
||||||
}
|
}
|
||||||
|
|
||||||
.underline {
|
.underline {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 9rem 0 2.5rem;
|
margin: 0 0.5rem 0 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
@ -78,7 +94,7 @@
|
|||||||
|
|
||||||
.input {
|
.input {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
padding: 0 9rem 0.5em 2.5rem;
|
padding: 0 9rem 0.5em 0.5rem;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
padding-right: 6rem;
|
padding-right: 6rem;
|
||||||
@ -92,7 +108,7 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
margin: 2rem 2rem 0;
|
margin: 2rem 0 0;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -107,8 +123,11 @@
|
|||||||
|
|
||||||
.title {
|
.title {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: 1.5em;
|
|
||||||
font-color: white;
|
font-color: white;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards {
|
.cards {
|
||||||
|
@ -25,6 +25,7 @@ import TextFieldUnderline from 'material-ui/TextField/TextFieldUnderline';
|
|||||||
|
|
||||||
import AccountCard from '~/ui/AccountCard';
|
import AccountCard from '~/ui/AccountCard';
|
||||||
import InputAddress from '~/ui/Form/InputAddress';
|
import InputAddress from '~/ui/Form/InputAddress';
|
||||||
|
import Loading from '~/ui/Loading';
|
||||||
import Portal from '~/ui/Portal';
|
import Portal from '~/ui/Portal';
|
||||||
import { nodeOrStringProptype } from '~/util/proptypes';
|
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||||
import { validateAddress } from '~/util/validation';
|
import { validateAddress } from '~/util/validation';
|
||||||
@ -130,7 +131,7 @@ class AddressSelect extends Component {
|
|||||||
const input = (
|
const input = (
|
||||||
<InputAddress
|
<InputAddress
|
||||||
accountsInfo={ accountsInfo }
|
accountsInfo={ accountsInfo }
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ (disabled || readOnly) && allowCopy ? allowCopy : false }
|
||||||
className={ className }
|
className={ className }
|
||||||
disabled={ disabled || readOnly }
|
disabled={ disabled || readOnly }
|
||||||
error={ error }
|
error={ error }
|
||||||
@ -182,17 +183,18 @@ class AddressSelect extends Component {
|
|||||||
<label className={ styles.label } htmlFor={ id }>
|
<label className={ styles.label } htmlFor={ id }>
|
||||||
{ label }
|
{ label }
|
||||||
</label>
|
</label>
|
||||||
|
<div className={ styles.outerInput }>
|
||||||
<input
|
<input
|
||||||
id={ id }
|
id={ id }
|
||||||
className={ styles.input }
|
className={ styles.input }
|
||||||
placeholder={ ilHint }
|
placeholder={ ilHint }
|
||||||
|
|
||||||
onBlur={ this.handleInputBlur }
|
onBlur={ this.handleInputBlur }
|
||||||
onFocus={ this.handleInputFocus }
|
onFocus={ this.handleInputFocus }
|
||||||
onChange={ this.handleChange }
|
onChange={ this.handleChange }
|
||||||
|
|
||||||
ref={ this.setInputRef }
|
ref={ this.setInputRef }
|
||||||
/>
|
/>
|
||||||
|
{ this.renderLoader() }
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={ styles.underline }>
|
<div className={ styles.underline }>
|
||||||
<TextFieldUnderline
|
<TextFieldUnderline
|
||||||
@ -210,6 +212,19 @@ class AddressSelect extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLoader () {
|
||||||
|
if (!this.store.loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Loading
|
||||||
|
className={ styles.loader }
|
||||||
|
size={ 0.5 }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderCurrentInput () {
|
renderCurrentInput () {
|
||||||
const { inputValue } = this.state;
|
const { inputValue } = this.state;
|
||||||
|
|
||||||
@ -304,7 +319,9 @@ class AddressSelect extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.category } key={ `${key}_${index}` }>
|
<div className={ styles.category } key={ `${key}_${index}` }>
|
||||||
<div className={ styles.title }>{ label }</div>
|
<div className={ styles.title }>
|
||||||
|
<h3>{ label }</h3>
|
||||||
|
</div>
|
||||||
{ content }
|
{ content }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observable, action } from 'mobx';
|
import { observable, action, transaction } from 'mobx';
|
||||||
import { flatMap, uniqBy } from 'lodash';
|
import { flatMap, uniqBy } from 'lodash';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
@ -26,6 +26,7 @@ const ZERO = /^(0x)?0*$/;
|
|||||||
|
|
||||||
export default class AddressSelectStore {
|
export default class AddressSelectStore {
|
||||||
|
|
||||||
|
@observable loading = false;
|
||||||
@observable values = [];
|
@observable values = [];
|
||||||
@observable registryValues = [];
|
@observable registryValues = [];
|
||||||
|
|
||||||
@ -224,21 +225,28 @@ export default class AddressSelectStore {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Registries Lookup
|
// Clear the previous results after 50ms
|
||||||
|
// if still fetching
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
transaction(() => {
|
||||||
this.registryValues = [];
|
this.registryValues = [];
|
||||||
|
this.loading = true;
|
||||||
|
});
|
||||||
|
}, 50);
|
||||||
|
|
||||||
const lookups = this.regLookups.map((regLookup) => regLookup(value));
|
const lookups = this.regLookups.map((regLookup) => regLookup(value));
|
||||||
|
|
||||||
Promise
|
// Registries Lookup
|
||||||
|
return Promise
|
||||||
.all(lookups)
|
.all(lookups)
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
return results
|
return results
|
||||||
.filter((result) => result && !ZERO.test(result.address));
|
.filter((result) => result && !ZERO.test(result.address));
|
||||||
})
|
})
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
results = uniqBy(results, (result) => result.address);
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
this.registryValues = results
|
const registryValues = uniqBy(results, (result) => result.address)
|
||||||
.map((result) => {
|
.map((result) => {
|
||||||
const lowercaseAddress = result.address.toLowerCase();
|
const lowercaseAddress = result.address.toLowerCase();
|
||||||
|
|
||||||
@ -253,6 +261,11 @@ export default class AddressSelectStore {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
transaction(() => {
|
||||||
|
this.loading = false;
|
||||||
|
this.registryValues = registryValues;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ class InputAddress extends Component {
|
|||||||
|
|
||||||
const props = {};
|
const props = {};
|
||||||
|
|
||||||
if (!readOnly && !disabled) {
|
if (!disabled) {
|
||||||
props.focused = focused;
|
props.focused = focused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,15 +21,22 @@ import styles from './loading.css';
|
|||||||
|
|
||||||
export default class Loading extends Component {
|
export default class Loading extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
size: PropTypes.number
|
size: PropTypes.number
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
className: '',
|
||||||
|
size: 2
|
||||||
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const size = (this.props.size || 2) * 60;
|
const { className, size } = this.props;
|
||||||
|
const computedSize = size * 60;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.loading }>
|
<div className={ [ styles.loading, className ].join(' ') }>
|
||||||
<CircularProgress size={ size } />
|
<CircularProgress size={ computedSize } />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,9 @@ $top: 20vh;
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
z-index: -10;
|
z-index: -10;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
* {
|
* {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
@ -83,6 +86,7 @@ $top: 20vh;
|
|||||||
top: 0.5rem;
|
top: 0.5rem;
|
||||||
right: 1rem;
|
right: 1rem;
|
||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
transition-property: opacity;
|
transition-property: opacity;
|
||||||
transition-duration: 0.25s;
|
transition-duration: 0.25s;
|
||||||
|
Loading…
Reference in New Issue
Block a user