Wallet Loading States & Spinner Update (#334)
* Add disclaimer modal to footer * Remove duplicate code & unnecessary styles * Fix formatting noise * remove un-used css style * Fix tslint error & add media query for modals * Nest Media Query * Replace '???' with Spinner & update spinner * Add loading states for wallet balances * Update wallet test * Remove excess data passed to wallet balance reducer & Fix wallet balance types * Merge 'develop' into 'loading-indicator' * Add 'light' prop to Spinner * Only show spinners when fetching data * Remove format diff * Apply naming conventions * Remove network name when offline
This commit is contained in:
parent
7e7c070abe
commit
0d5d0cea9a
|
@ -47,14 +47,28 @@ export function setWallet(value: IWallet): types.SetWalletAction {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TSetBalance = typeof setBalance;
|
export function setBalancePending(): types.SetBalancePendingAction {
|
||||||
export function setBalance(value: Wei): types.SetBalanceAction {
|
|
||||||
return {
|
return {
|
||||||
type: TypeKeys.WALLET_SET_BALANCE,
|
type: TypeKeys.WALLET_SET_BALANCE_PENDING
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSetBalance = typeof setBalanceFullfilled;
|
||||||
|
export function setBalanceFullfilled(
|
||||||
|
value: Wei
|
||||||
|
): types.SetBalanceFullfilledAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.WALLET_SET_BALANCE_FULFILLED,
|
||||||
payload: value
|
payload: value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setBalanceRejected(): types.SetBalanceRejectedAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.WALLET_SET_BALANCE_REJECTED
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type TSetTokenBalances = typeof setTokenBalances;
|
export type TSetTokenBalances = typeof setTokenBalances;
|
||||||
export function setTokenBalances(payload: {
|
export function setTokenBalances(payload: {
|
||||||
[key: string]: TokenValue;
|
[key: string]: TokenValue;
|
||||||
|
|
|
@ -33,10 +33,16 @@ export interface ResetWalletAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Set Balance ***/
|
/*** Set Balance ***/
|
||||||
export interface SetBalanceAction {
|
export interface SetBalancePendingAction {
|
||||||
type: TypeKeys.WALLET_SET_BALANCE;
|
type: TypeKeys.WALLET_SET_BALANCE_PENDING;
|
||||||
|
}
|
||||||
|
export interface SetBalanceFullfilledAction {
|
||||||
|
type: TypeKeys.WALLET_SET_BALANCE_FULFILLED;
|
||||||
payload: Wei;
|
payload: Wei;
|
||||||
}
|
}
|
||||||
|
export interface SetBalanceRejectedAction {
|
||||||
|
type: TypeKeys.WALLET_SET_BALANCE_REJECTED;
|
||||||
|
}
|
||||||
|
|
||||||
/*** Set Token Balance ***/
|
/*** Set Token Balance ***/
|
||||||
export interface SetTokenBalancesAction {
|
export interface SetTokenBalancesAction {
|
||||||
|
@ -94,7 +100,9 @@ export type WalletAction =
|
||||||
| UnlockPrivateKeyAction
|
| UnlockPrivateKeyAction
|
||||||
| SetWalletAction
|
| SetWalletAction
|
||||||
| ResetWalletAction
|
| ResetWalletAction
|
||||||
| SetBalanceAction
|
| SetBalancePendingAction
|
||||||
|
| SetBalanceFullfilledAction
|
||||||
|
| SetBalanceRejectedAction
|
||||||
| SetTokenBalancesAction
|
| SetTokenBalancesAction
|
||||||
| BroadcastTxRequestedAction
|
| BroadcastTxRequestedAction
|
||||||
| BroadcastTxFailedAction
|
| BroadcastTxFailedAction
|
||||||
|
|
|
@ -4,7 +4,9 @@ export enum TypeKeys {
|
||||||
WALLET_UNLOCK_MNEMONIC = 'WALLET_UNLOCK_MNEMONIC',
|
WALLET_UNLOCK_MNEMONIC = 'WALLET_UNLOCK_MNEMONIC',
|
||||||
WALLET_UNLOCK_WEB3 = 'WALLET_UNLOCK_WEB3',
|
WALLET_UNLOCK_WEB3 = 'WALLET_UNLOCK_WEB3',
|
||||||
WALLET_SET = 'WALLET_SET',
|
WALLET_SET = 'WALLET_SET',
|
||||||
WALLET_SET_BALANCE = 'WALLET_SET_BALANCE',
|
WALLET_SET_BALANCE_PENDING = 'WALLET_SET_BALANCE_PENDING',
|
||||||
|
WALLET_SET_BALANCE_FULFILLED = 'WALLET_SET_BALANCE_FULFILLED',
|
||||||
|
WALLET_SET_BALANCE_REJECTED = 'WALLET_SET_BALANCE_REJECTED',
|
||||||
WALLET_SET_TOKEN_BALANCES = 'WALLET_SET_TOKEN_BALANCES',
|
WALLET_SET_TOKEN_BALANCES = 'WALLET_SET_TOKEN_BALANCES',
|
||||||
WALLET_BROADCAST_TX_REQUESTED = 'WALLET_BROADCAST_TX_REQUESTED',
|
WALLET_BROADCAST_TX_REQUESTED = 'WALLET_BROADCAST_TX_REQUESTED',
|
||||||
WALLET_BROADCAST_TX_FAILED = 'WALLET_BROADCAST_TX_FAILED',
|
WALLET_BROADCAST_TX_FAILED = 'WALLET_BROADCAST_TX_FAILED',
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { TFetchCCRates } from 'actions/rates';
|
import { TFetchCCRates } from 'actions/rates';
|
||||||
import { Identicon, UnitDisplay } from 'components/ui';
|
import { Identicon, UnitDisplay } from 'components/ui';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config/data';
|
||||||
import { IWallet } from 'libs/wallet';
|
import { IWallet, Balance } from 'libs/wallet';
|
||||||
import { Wei } from 'libs/units';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import './AccountInfo.scss';
|
import './AccountInfo.scss';
|
||||||
|
import Spinner from 'components/ui/Spinner';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
balance: Wei;
|
balance: Balance;
|
||||||
wallet: IWallet;
|
wallet: IWallet;
|
||||||
network: NetworkConfig;
|
network: NetworkConfig;
|
||||||
fetchCCRates: TFetchCCRates;
|
fetchCCRates: TFetchCCRates;
|
||||||
|
@ -79,13 +79,17 @@ export default class AccountInfo extends React.Component<Props, State> {
|
||||||
className="AccountInfo-list-item-clickable mono wrap"
|
className="AccountInfo-list-item-clickable mono wrap"
|
||||||
onClick={this.toggleShowLongBalance}
|
onClick={this.toggleShowLongBalance}
|
||||||
>
|
>
|
||||||
<UnitDisplay
|
{balance.isPending ? (
|
||||||
value={balance}
|
<Spinner />
|
||||||
unit={'ether'}
|
) : (
|
||||||
displayShortBalance={!showLongBalance}
|
<UnitDisplay
|
||||||
/>
|
value={balance.wei}
|
||||||
|
unit={'ether'}
|
||||||
|
displayShortBalance={!showLongBalance}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
{` ${network.name}`}
|
{balance ? `${network.name}` : null}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@import "common/sass/variables";
|
@import 'common/sass/variables';
|
||||||
@import "common/sass/mixins";
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
.EquivalentValues {
|
.EquivalentValues {
|
||||||
&-title {
|
&-title {
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&-label {
|
&-label {
|
||||||
|
white-space: pre-wrap;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 36px;
|
min-width: 36px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Wei } from 'libs/units';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import './EquivalentValues.scss';
|
import './EquivalentValues.scss';
|
||||||
import { State } from 'reducers/rates';
|
import { State } from 'reducers/rates';
|
||||||
import { symbols } from 'actions/rates';
|
import { symbols } from 'actions/rates';
|
||||||
import { UnitDisplay } from 'components/ui';
|
import { UnitDisplay } from 'components/ui';
|
||||||
|
import { Balance } from 'libs/wallet';
|
||||||
|
import Spinner from 'components/ui/Spinner';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
balance?: Wei;
|
balance: Balance;
|
||||||
rates?: State['rates'];
|
rates?: State['rates'];
|
||||||
ratesError?: State['ratesError'];
|
ratesError?: State['ratesError'];
|
||||||
}
|
}
|
||||||
|
@ -29,18 +30,19 @@ export default class EquivalentValues extends React.Component<Props, {}> {
|
||||||
return (
|
return (
|
||||||
<li className="EquivalentValues-values-currency" key={key}>
|
<li className="EquivalentValues-values-currency" key={key}>
|
||||||
<span className="EquivalentValues-values-currency-label">
|
<span className="EquivalentValues-values-currency-label">
|
||||||
{key}:
|
{key + ': '}
|
||||||
</span>
|
</span>
|
||||||
<span className="EquivalentValues-values-currency-value">
|
<span className="EquivalentValues-values-currency-value">
|
||||||
{' '}
|
{balance.isPending ? (
|
||||||
{balance ? (
|
<Spinner />
|
||||||
|
) : (
|
||||||
<UnitDisplay
|
<UnitDisplay
|
||||||
unit={'ether'}
|
unit={'ether'}
|
||||||
value={balance.muln(rates[key])}
|
value={
|
||||||
|
balance.wei ? balance.wei.muln(rates[key]) : null
|
||||||
|
}
|
||||||
displayShortBalance={2}
|
displayShortBalance={2}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
'???'
|
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -7,8 +7,7 @@ import {
|
||||||
import { showNotification, TShowNotification } from 'actions/notifications';
|
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||||
import { fetchCCRates as dFetchCCRates, TFetchCCRates } from 'actions/rates';
|
import { fetchCCRates as dFetchCCRates, TFetchCCRates } from 'actions/rates';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config/data';
|
||||||
import { Wei } from 'libs/units';
|
import { IWallet, Balance } from 'libs/wallet';
|
||||||
import { IWallet } from 'libs/wallet/IWallet';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
@ -27,7 +26,7 @@ import OfflineToggle from './OfflineToggle';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
wallet: IWallet;
|
wallet: IWallet;
|
||||||
balance: Wei;
|
balance: Balance;
|
||||||
network: NetworkConfig;
|
network: NetworkConfig;
|
||||||
tokenBalances: TokenBalance[];
|
tokenBalances: TokenBalance[];
|
||||||
rates: State['rates'];
|
rates: State['rates'];
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
.Spinner {
|
||||||
|
animation: rotate 2s linear infinite;
|
||||||
|
|
||||||
|
&-x1 {
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-x2 {
|
||||||
|
height: 2em;
|
||||||
|
width: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-x3 {
|
||||||
|
height: 3em;
|
||||||
|
width: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-x4 {
|
||||||
|
height: 4em;
|
||||||
|
width: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-x5 {
|
||||||
|
height: 5em;
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .path {
|
||||||
|
stroke-linecap: round;
|
||||||
|
animation: dash 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-light {
|
||||||
|
& .path {
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-dark {
|
||||||
|
& .path {
|
||||||
|
stroke: #163151;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dash {
|
||||||
|
0% {
|
||||||
|
stroke-dasharray: 1, 150;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
stroke-dasharray: 90, 150;
|
||||||
|
stroke-dashoffset: -35;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dasharray: 90, 150;
|
||||||
|
stroke-dashoffset: -124;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,27 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import './Spinner.scss';
|
||||||
|
|
||||||
type Size = 'lg' | '2x' | '3x' | '4x' | '5x';
|
type Size = 'x1' | 'x2' | 'x3' | 'x4' | 'x5';
|
||||||
|
|
||||||
interface SpinnerProps {
|
interface SpinnerProps {
|
||||||
size?: Size;
|
size?: Size;
|
||||||
|
light?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Spinner = ({ size = 'fa-' }: SpinnerProps) => {
|
const Spinner = ({ size = 'x1', light = false }: SpinnerProps) => {
|
||||||
return <i className={`fa fa-spinner fa-spin fa-${size ? size : 'fw'}`} />;
|
const color = light ? 'Spinner-light' : 'Spinner-dark';
|
||||||
|
return (
|
||||||
|
<svg className={`Spinner Spinner-${size} ${color}`} viewBox="0 0 50 50">
|
||||||
|
<circle
|
||||||
|
className="path"
|
||||||
|
cx="25"
|
||||||
|
cy="25"
|
||||||
|
r="20"
|
||||||
|
fill="none"
|
||||||
|
strokeWidth="5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Spinner;
|
export default Spinner;
|
||||||
|
|
|
@ -14,7 +14,7 @@ interface Props {
|
||||||
* @type {TokenValue | Wei}
|
* @type {TokenValue | Wei}
|
||||||
* @memberof Props
|
* @memberof Props
|
||||||
*/
|
*/
|
||||||
value?: TokenValue | Wei;
|
value?: TokenValue | Wei | null;
|
||||||
/**
|
/**
|
||||||
* @description Symbol to display to the right of the value, such as 'ETH'
|
* @description Symbol to display to the right of the value, such as 'ETH'
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -43,7 +43,7 @@ const UnitDisplay: React.SFC<EthProps | TokenProps> = params => {
|
||||||
const { value, symbol, displayShortBalance } = params;
|
const { value, symbol, displayShortBalance } = params;
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return <span>???</span>;
|
return <span>Balance isn't available offline</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertedValue = isEthereumUnit(params)
|
const convertedValue = isEthereumUnit(params)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Wei } from 'libs/units';
|
import { Wei } from 'libs/units';
|
||||||
import { IWallet } from 'libs/wallet/IWallet';
|
import { IWallet, Balance } from 'libs/wallet';
|
||||||
import { RPCNode } from 'libs/nodes';
|
import { RPCNode } from 'libs/nodes';
|
||||||
import { NodeConfig, NetworkConfig } from 'config/data';
|
import { NodeConfig, NetworkConfig } from 'config/data';
|
||||||
import { TBroadcastTx } from 'actions/wallet';
|
import { TBroadcastTx } from 'actions/wallet';
|
||||||
|
@ -7,7 +7,7 @@ import { TShowNotification } from 'actions/notifications';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
wallet: IWallet;
|
wallet: IWallet;
|
||||||
balance: Wei;
|
balance: Balance;
|
||||||
node: NodeConfig;
|
node: NodeConfig;
|
||||||
nodeLib: RPCNode;
|
nodeLib: RPCNode;
|
||||||
chainId: NetworkConfig['chainId'];
|
chainId: NetworkConfig['chainId'];
|
||||||
|
|
|
@ -4,13 +4,13 @@ import { toWei, Wei, getDecimal } from 'libs/units';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { showNotification, TShowNotification } from 'actions/notifications';
|
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||||
import { broadcastTx, TBroadcastTx } from 'actions/wallet';
|
import { broadcastTx, TBroadcastTx } from 'actions/wallet';
|
||||||
import { IWallet } from 'libs/wallet/IWallet';
|
import { IWallet, Balance } from 'libs/wallet';
|
||||||
import { RPCNode } from 'libs/nodes';
|
import { RPCNode } from 'libs/nodes';
|
||||||
import { NodeConfig, NetworkConfig } from 'config/data';
|
import { NodeConfig, NetworkConfig } from 'config/data';
|
||||||
|
|
||||||
export interface IWithTx {
|
export interface IWithTx {
|
||||||
wallet: IWallet;
|
wallet: IWallet;
|
||||||
balance: Wei;
|
balance: Balance;
|
||||||
node: NodeConfig;
|
node: NodeConfig;
|
||||||
nodeLib: RPCNode;
|
nodeLib: RPCNode;
|
||||||
chainId: NetworkConfig['chainId'];
|
chainId: NetworkConfig['chainId'];
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import UnitDropdown from './UnitDropdown';
|
import UnitDropdown from './UnitDropdown';
|
||||||
import { Wei } from 'libs/units';
|
import { Balance } from 'libs/wallet';
|
||||||
import { UnitConverter } from 'components/renderCbs';
|
import { UnitConverter } from 'components/renderCbs';
|
||||||
interface Props {
|
interface Props {
|
||||||
decimal: number;
|
decimal: number;
|
||||||
unit: string;
|
unit: string;
|
||||||
tokens: string[];
|
tokens: string[];
|
||||||
balance: number | null | Wei;
|
balance: number | null | Balance;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
onAmountChange(value: string, unit: string): void;
|
onAmountChange(value: string, unit: string): void;
|
||||||
onUnitChange(unit: string): void;
|
onUnitChange(unit: string): void;
|
||||||
|
@ -30,10 +30,11 @@ export default class AmountField extends React.Component {
|
||||||
<UnitConverter decimal={decimal} onChange={this.callWithBaseUnit}>
|
<UnitConverter decimal={decimal} onChange={this.callWithBaseUnit}>
|
||||||
{({ onUserInput, convertedUnit }) => (
|
{({ onUserInput, convertedUnit }) => (
|
||||||
<input
|
<input
|
||||||
className={`form-control ${isFinite(Number(convertedUnit)) &&
|
className={`form-control ${
|
||||||
Number(convertedUnit) > 0
|
isFinite(Number(convertedUnit)) && Number(convertedUnit) > 0
|
||||||
? 'is-valid'
|
? 'is-valid'
|
||||||
: 'is-invalid'}`}
|
: 'is-invalid'
|
||||||
|
}`}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={translateRaw('SEND_amount_short')}
|
placeholder={translateRaw('SEND_amount_short')}
|
||||||
value={convertedUnit}
|
value={convertedUnit}
|
||||||
|
|
|
@ -115,7 +115,7 @@ class ConfirmationModal extends React.Component<Props, State> {
|
||||||
<div className="ConfModal">
|
<div className="ConfModal">
|
||||||
{isBroadcasting ? (
|
{isBroadcasting ? (
|
||||||
<div className="ConfModal-loading">
|
<div className="ConfModal-loading">
|
||||||
<Spinner size="5x" />
|
<Spinner size="x5" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -32,7 +32,7 @@ import {
|
||||||
import { UnitKey, Wei, getDecimal, toWei } from 'libs/units';
|
import { UnitKey, Wei, getDecimal, toWei } from 'libs/units';
|
||||||
import { isValidETHAddress } from 'libs/validators';
|
import { isValidETHAddress } from 'libs/validators';
|
||||||
// LIBS
|
// LIBS
|
||||||
import { IWallet, Web3Wallet } from 'libs/wallet';
|
import { IWallet, Balance, Web3Wallet } from 'libs/wallet';
|
||||||
import pickBy from 'lodash/pickBy';
|
import pickBy from 'lodash/pickBy';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
// REDUX
|
// REDUX
|
||||||
|
@ -92,7 +92,7 @@ interface State {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
wallet: IWallet;
|
wallet: IWallet;
|
||||||
balance: Wei;
|
balance: Balance;
|
||||||
nodeLib: RPCNode;
|
nodeLib: RPCNode;
|
||||||
network: NetworkConfig;
|
network: NetworkConfig;
|
||||||
tokens: MergedToken[];
|
tokens: MergedToken[];
|
||||||
|
@ -353,7 +353,7 @@ export class SendTransaction extends React.Component<Props, State> {
|
||||||
{generateTxProcessing && (
|
{generateTxProcessing && (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row form-group text-center">
|
<div className="row form-group text-center">
|
||||||
<Spinner size="5x" />
|
<Spinner size="x5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -574,7 +574,7 @@ export class SendTransaction extends React.Component<Props, State> {
|
||||||
value = getBalanceMinusGasCosts(
|
value = getBalanceMinusGasCosts(
|
||||||
bigGasLimit,
|
bigGasLimit,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
balance
|
balance.wei
|
||||||
).toString();
|
).toString();
|
||||||
} else {
|
} else {
|
||||||
const tokenBalance = this.props.tokenBalances.find(
|
const tokenBalance = this.props.tokenBalances.find(
|
||||||
|
|
|
@ -44,14 +44,13 @@ export default class CurrentRates extends Component<Pairs, State> {
|
||||||
name={pair + 'Amount'}
|
name={pair + 'Amount'}
|
||||||
/>
|
/>
|
||||||
<span className="SwapRates-panel-rate-amount">
|
<span className="SwapRates-panel-rate-amount">
|
||||||
{` ${origin} = ${toFixedIfLarger(
|
{` ${origin} = ${toFixedIfLarger(statePair * propsPair, 6)} ${
|
||||||
statePair * propsPair,
|
destination
|
||||||
6
|
}`}
|
||||||
)} ${destination}`}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Spinner />
|
<Spinner size="x1" light={true} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -183,11 +183,9 @@ EncodedCall:${data}`);
|
||||||
//TODO: parse args based on type
|
//TODO: parse args based on type
|
||||||
if (!suppliedArgs[name]) {
|
if (!suppliedArgs[name]) {
|
||||||
throw Error(
|
throw Error(
|
||||||
`Expected argument "${name}" of type "${type}" missing, suppliedArgs: ${JSON.stringify(
|
`Expected argument "${name}" of type "${
|
||||||
suppliedArgs,
|
type
|
||||||
null,
|
}" missing, suppliedArgs: ${JSON.stringify(suppliedArgs, null, 2)}`
|
||||||
2
|
|
||||||
)}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const value = suppliedArgs[name];
|
const value = suppliedArgs[name];
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Wei } from 'libs/units';
|
||||||
|
|
||||||
|
export interface Balance {
|
||||||
|
wei: Wei;
|
||||||
|
isPending: boolean;
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
export { IWallet } from './IWallet';
|
export { IWallet } from './IWallet';
|
||||||
|
export { Balance } from './balance';
|
||||||
export * from './deterministic';
|
export * from './deterministic';
|
||||||
export * from './non-deterministic';
|
export * from './non-deterministic';
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
|
import { SetBalanceFullfilledAction } from 'actions/wallet/actionTypes';
|
||||||
import {
|
import {
|
||||||
SetBalanceAction,
|
|
||||||
SetTokenBalancesAction,
|
SetTokenBalancesAction,
|
||||||
SetWalletAction,
|
SetWalletAction,
|
||||||
WalletAction,
|
WalletAction,
|
||||||
TypeKeys
|
TypeKeys
|
||||||
} from 'actions/wallet';
|
} from 'actions/wallet';
|
||||||
import { Wei, TokenValue } from 'libs/units';
|
import { TokenValue } from 'libs/units';
|
||||||
import { BroadcastTransactionStatus } from 'libs/transaction';
|
import { BroadcastTransactionStatus } from 'libs/transaction';
|
||||||
import { IWallet } from 'libs/wallet';
|
import { IWallet, Balance } from 'libs/wallet';
|
||||||
import { getTxFromBroadcastTransactionStatus } from 'selectors/wallet';
|
import { getTxFromBroadcastTransactionStatus } from 'selectors/wallet';
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
inst?: IWallet | null;
|
inst?: IWallet | null;
|
||||||
// in ETH
|
// in ETH
|
||||||
balance?: Wei | null;
|
balance: Balance | { wei: null };
|
||||||
tokens: {
|
tokens: {
|
||||||
[key: string]: TokenValue;
|
[key: string]: TokenValue;
|
||||||
};
|
};
|
||||||
|
@ -22,18 +22,36 @@ export interface State {
|
||||||
|
|
||||||
export const INITIAL_STATE: State = {
|
export const INITIAL_STATE: State = {
|
||||||
inst: null,
|
inst: null,
|
||||||
balance: null,
|
balance: { isPending: false, wei: null },
|
||||||
tokens: {},
|
tokens: {},
|
||||||
transactions: []
|
transactions: []
|
||||||
};
|
};
|
||||||
|
|
||||||
function setWallet(state: State, action: SetWalletAction): State {
|
function setWallet(state: State, action: SetWalletAction): State {
|
||||||
return { ...state, inst: action.payload, balance: null, tokens: {} };
|
return {
|
||||||
|
...state,
|
||||||
|
inst: action.payload,
|
||||||
|
balance: INITIAL_STATE.balance,
|
||||||
|
tokens: INITIAL_STATE.tokens
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBalance(state: State, action: SetBalanceAction): State {
|
function setBalancePending(state: State): State {
|
||||||
const weiBalance = action.payload;
|
return { ...state, balance: { ...state.balance, isPending: true } };
|
||||||
return { ...state, balance: weiBalance };
|
}
|
||||||
|
|
||||||
|
function setBalanceFullfilled(
|
||||||
|
state: State,
|
||||||
|
action: SetBalanceFullfilledAction
|
||||||
|
): State {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
balance: { wei: action.payload, isPending: false }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBalanceRejected(state: State): State {
|
||||||
|
return { ...state, balance: { ...state.balance, isPending: false } };
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTokenBalances(state: State, action: SetTokenBalancesAction): State {
|
function setTokenBalances(state: State, action: SetTokenBalancesAction): State {
|
||||||
|
@ -111,8 +129,12 @@ export function wallet(
|
||||||
return setWallet(state, action);
|
return setWallet(state, action);
|
||||||
case TypeKeys.WALLET_RESET:
|
case TypeKeys.WALLET_RESET:
|
||||||
return INITIAL_STATE;
|
return INITIAL_STATE;
|
||||||
case TypeKeys.WALLET_SET_BALANCE:
|
case TypeKeys.WALLET_SET_BALANCE_PENDING:
|
||||||
return setBalance(state, action);
|
return setBalancePending(state);
|
||||||
|
case TypeKeys.WALLET_SET_BALANCE_FULFILLED:
|
||||||
|
return setBalanceFullfilled(state, action);
|
||||||
|
case TypeKeys.WALLET_SET_BALANCE_REJECTED:
|
||||||
|
return setBalanceRejected(state);
|
||||||
case TypeKeys.WALLET_SET_TOKEN_BALANCES:
|
case TypeKeys.WALLET_SET_TOKEN_BALANCES:
|
||||||
return setTokenBalances(state, action);
|
return setTokenBalances(state, action);
|
||||||
case TypeKeys.WALLET_BROADCAST_TX_REQUESTED:
|
case TypeKeys.WALLET_BROADCAST_TX_REQUESTED:
|
||||||
|
|
|
@ -3,7 +3,9 @@ import {
|
||||||
broadCastTxFailed,
|
broadCastTxFailed,
|
||||||
BroadcastTxRequestedAction,
|
BroadcastTxRequestedAction,
|
||||||
broadcastTxSucceded,
|
broadcastTxSucceded,
|
||||||
setBalance,
|
setBalanceFullfilled,
|
||||||
|
setBalancePending,
|
||||||
|
setBalanceRejected,
|
||||||
setTokenBalances,
|
setTokenBalances,
|
||||||
setWallet,
|
setWallet,
|
||||||
UnlockKeystoreAction,
|
UnlockKeystoreAction,
|
||||||
|
@ -39,6 +41,7 @@ import translate from 'translations';
|
||||||
|
|
||||||
function* updateAccountBalance(): SagaIterator {
|
function* updateAccountBalance(): SagaIterator {
|
||||||
try {
|
try {
|
||||||
|
yield put(setBalancePending());
|
||||||
const wallet: null | IWallet = yield select(getWalletInst);
|
const wallet: null | IWallet = yield select(getWalletInst);
|
||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
return;
|
return;
|
||||||
|
@ -47,9 +50,9 @@ function* updateAccountBalance(): SagaIterator {
|
||||||
const address = yield apply(wallet, wallet.getAddressString);
|
const address = yield apply(wallet, wallet.getAddressString);
|
||||||
// network request
|
// network request
|
||||||
const balance: Wei = yield apply(node, node.getBalance, [address]);
|
const balance: Wei = yield apply(node, node.getBalance, [address]);
|
||||||
yield put(setBalance(balance));
|
yield put(setBalanceFullfilled(balance));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put({ type: 'updateAccountBalance_error', error });
|
yield put(setBalanceRejected());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,8 +194,6 @@ function* broadcastTx(action: BroadcastTxRequestedAction): SagaIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function* walletSaga(): SagaIterator {
|
export default function* walletSaga(): SagaIterator {
|
||||||
// useful for development
|
|
||||||
yield call(updateBalances);
|
|
||||||
yield [
|
yield [
|
||||||
takeEvery('WALLET_UNLOCK_PRIVATE_KEY', unlockPrivateKey),
|
takeEvery('WALLET_UNLOCK_PRIVATE_KEY', unlockPrivateKey),
|
||||||
takeEvery('WALLET_UNLOCK_KEYSTORE', unlockKeystore),
|
takeEvery('WALLET_UNLOCK_KEYSTORE', unlockKeystore),
|
||||||
|
|
|
@ -369,7 +369,7 @@ declare module 'bn.js' {
|
||||||
* @description reduct
|
* @description reduct
|
||||||
*/
|
*/
|
||||||
|
|
||||||
modn(b: number): number; //API consistency https://github.com/indutny/bn.js/pull/130
|
modn(b: number): BN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description rounded division
|
* @description rounded division
|
||||||
|
|
|
@ -16,9 +16,7 @@ describe('wallet reducer', () => {
|
||||||
|
|
||||||
expect(wallet(undefined, walletActions.setWallet(walletInstance))).toEqual({
|
expect(wallet(undefined, walletActions.setWallet(walletInstance))).toEqual({
|
||||||
...INITIAL_STATE,
|
...INITIAL_STATE,
|
||||||
inst: walletInstance,
|
inst: walletInstance
|
||||||
balance: null,
|
|
||||||
tokens: {}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue