Token Balance retry button & equivalent values fix (#1453)
* Add refresh button to token balances error. Refactor actions a bit. * Fix assertion error from bn on rates
This commit is contained in:
parent
c9676cac62
commit
c3d1e4e3af
|
@ -160,9 +160,16 @@ export function setWalletConfig(config: WalletConfig): types.SetWalletConfigActi
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TSetAccountBalance = typeof setAccountBalance;
|
export type TRefreshAccountBalance = typeof refreshAccountBalance;
|
||||||
export function setAccountBalance(): types.SetAccountBalanceAction {
|
export function refreshAccountBalance(): types.RefreshAccountBalanceAction {
|
||||||
return {
|
return {
|
||||||
type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE
|
type: TypeKeys.WALLET_REFRESH_ACCOUNT_BALANCE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TRefreshTokenBalances = typeof refreshTokenBalances;
|
||||||
|
export function refreshTokenBalances(): types.RefreshTokenBalancesAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.WALLET_REFRESH_TOKEN_BALANCES
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,8 +125,12 @@ export interface SetPasswordPendingAction {
|
||||||
type: TypeKeys.WALLET_SET_PASSWORD_PENDING;
|
type: TypeKeys.WALLET_SET_PASSWORD_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SetAccountBalanceAction {
|
export interface RefreshAccountBalanceAction {
|
||||||
type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE;
|
type: TypeKeys.WALLET_REFRESH_ACCOUNT_BALANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefreshTokenBalancesAction {
|
||||||
|
type: TypeKeys.WALLET_REFRESH_TOKEN_BALANCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Union Type ***/
|
/*** Union Type ***/
|
||||||
|
@ -148,4 +152,5 @@ export type WalletAction =
|
||||||
| SetWalletTokensAction
|
| SetWalletTokensAction
|
||||||
| SetWalletConfigAction
|
| SetWalletConfigAction
|
||||||
| SetPasswordPendingAction
|
| SetPasswordPendingAction
|
||||||
| SetAccountBalanceAction;
|
| RefreshAccountBalanceAction
|
||||||
|
| RefreshTokenBalancesAction;
|
||||||
|
|
|
@ -20,5 +20,6 @@ export enum TypeKeys {
|
||||||
WALLET_SET_CONFIG = 'WALLET_SET_CONFIG',
|
WALLET_SET_CONFIG = 'WALLET_SET_CONFIG',
|
||||||
WALLET_RESET = 'WALLET_RESET',
|
WALLET_RESET = 'WALLET_RESET',
|
||||||
WALLET_SET_PASSWORD_PENDING = 'WALLET_SET_PASSWORD_PENDING',
|
WALLET_SET_PASSWORD_PENDING = 'WALLET_SET_PASSWORD_PENDING',
|
||||||
WALLET_SET_ACCOUNT_BALANCE = 'WALLET_SET_ACCOUNT_BALANCE'
|
WALLET_REFRESH_ACCOUNT_BALANCE = 'WALLET_REFRESH_ACCOUNT_BALANCE',
|
||||||
|
WALLET_REFRESH_TOKEN_BALANCES = 'WALLET_REFRESH_TOKEN_BALANCES'
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,24 +58,39 @@ export interface CCResponse {
|
||||||
[symbol: string]: ISymbol;
|
[symbol: string]: ISymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRates extends ISymbol {
|
interface IRatesResponse {
|
||||||
Response?: 'Error';
|
[key: string]: number;
|
||||||
|
}
|
||||||
|
interface IRatesError {
|
||||||
|
Response: 'Error';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchRates = (symbols: string[] = []): Promise<CCResponse> =>
|
export const fetchRates = (symbols: string[] = []): Promise<CCResponse> =>
|
||||||
fetch(CCRates(symbols))
|
fetch(CCRates(symbols))
|
||||||
.then(response => handleJSONResponse(response, ERROR_MESSAGE))
|
.then(response => handleJSONResponse(response, ERROR_MESSAGE))
|
||||||
.then((rates: IRates) => {
|
.then((rates: IRatesResponse | IRatesError) => {
|
||||||
// API errors come as 200s, so check the json for error
|
// API errors come as 200s, so check the json for error
|
||||||
if (rates.Response && rates.Response === 'Error') {
|
if ((rates as IRatesError).Response === 'Error') {
|
||||||
throw new Error('Failed to fetch rates');
|
throw new Error('Failed to fetch rates');
|
||||||
}
|
}
|
||||||
|
return rates;
|
||||||
|
})
|
||||||
|
.then((rates: IRatesResponse) => {
|
||||||
|
// Sometimes the API erroneously gives tokens an extremely high value,
|
||||||
|
// like 10000000 ETH to 1 token. Filter those out. If that ever turns
|
||||||
|
// out to be true, we should all go home.
|
||||||
|
return Object.keys(rates).reduce((filteredRates: IRatesResponse, key) => {
|
||||||
|
if (rates[key] > 0.000001) {
|
||||||
|
filteredRates[key] = rates[key];
|
||||||
|
}
|
||||||
|
return filteredRates;
|
||||||
|
}, {});
|
||||||
|
})
|
||||||
|
.then((rates: IRatesResponse) => {
|
||||||
// All currencies are in ETH right now. We'll do token -> eth -> value to
|
// All currencies are in ETH right now. We'll do token -> eth -> value to
|
||||||
// do it all in one request
|
// do it all in one request to their respective rates via ETH.
|
||||||
// to their respective rates via ETH.
|
|
||||||
return symbols.reduce(
|
return symbols.reduce(
|
||||||
(eqRates, sym: keyof ISymbol) => {
|
(eqRates, sym) => {
|
||||||
if (rates[sym]) {
|
if (rates[sym]) {
|
||||||
eqRates[sym] = rateSymbols.symbols.all.reduce(
|
eqRates[sym] = rateSymbols.symbols.all.reduce(
|
||||||
(symRates, rateSym) => {
|
(symRates, rateSym) => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Spinner from 'components/ui/Spinner';
|
||||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { NetworkConfig } from 'types/network';
|
import { NetworkConfig } from 'types/network';
|
||||||
import { TSetAccountBalance, setAccountBalance } from 'actions/wallet';
|
import { TRefreshAccountBalance, refreshAccountBalance } from 'actions/wallet';
|
||||||
import './AccountInfo.scss';
|
import './AccountInfo.scss';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
|
@ -30,7 +30,7 @@ interface State {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
setAccountBalance: TSetAccountBalance;
|
refreshAccountBalance: TRefreshAccountBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = OwnProps & StateProps & DispatchProps;
|
type Props = OwnProps & StateProps & DispatchProps;
|
||||||
|
@ -170,7 +170,7 @@ class AccountInfo extends React.Component<Props, State> {
|
||||||
!isOffline && (
|
!isOffline && (
|
||||||
<button
|
<button
|
||||||
className="AccountInfo-section-refresh"
|
className="AccountInfo-section-refresh"
|
||||||
onClick={this.props.setAccountBalance}
|
onClick={this.props.refreshAccountBalance}
|
||||||
>
|
>
|
||||||
<i className="fa fa-refresh" />
|
<i className="fa fa-refresh" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -214,5 +214,5 @@ function mapStateToProps(state: AppState): StateProps {
|
||||||
isOffline: getOffline(state)
|
isOffline: getOffline(state)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const mapDispatchToProps: DispatchProps = { setAccountBalance };
|
const mapDispatchToProps: DispatchProps = { refreshAccountBalance };
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountInfo);
|
export default connect(mapStateToProps, mapDispatchToProps)(AccountInfo);
|
||||||
|
|
|
@ -33,6 +33,19 @@
|
||||||
margin-top: $space;
|
margin-top: $space;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-error {
|
||||||
|
color: $brand-danger;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&-message {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
margin-left: $space-md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-buttons {
|
&-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
@ -11,7 +11,9 @@ import {
|
||||||
scanWalletForTokens,
|
scanWalletForTokens,
|
||||||
TScanWalletForTokens,
|
TScanWalletForTokens,
|
||||||
setWalletTokens,
|
setWalletTokens,
|
||||||
TSetWalletTokens
|
TSetWalletTokens,
|
||||||
|
refreshTokenBalances,
|
||||||
|
TRefreshTokenBalances
|
||||||
} from 'actions/wallet';
|
} from 'actions/wallet';
|
||||||
import { getAllTokens, getOffline } from 'selectors/config';
|
import { getAllTokens, getOffline } from 'selectors/config';
|
||||||
import { getTokenBalances, getWalletInst, getWalletConfig, TokenBalance } from 'selectors/wallet';
|
import { getTokenBalances, getWalletInst, getWalletConfig, TokenBalance } from 'selectors/wallet';
|
||||||
|
@ -36,6 +38,7 @@ interface ActionProps {
|
||||||
removeCustomToken: TRemoveCustomToken;
|
removeCustomToken: TRemoveCustomToken;
|
||||||
scanWalletForTokens: TScanWalletForTokens;
|
scanWalletForTokens: TScanWalletForTokens;
|
||||||
setWalletTokens: TSetWalletTokens;
|
setWalletTokens: TSetWalletTokens;
|
||||||
|
refreshTokenBalances: TRefreshTokenBalances;
|
||||||
}
|
}
|
||||||
type Props = StateProps & ActionProps;
|
type Props = StateProps & ActionProps;
|
||||||
|
|
||||||
|
@ -56,12 +59,18 @@ class TokenBalances extends React.Component<Props> {
|
||||||
let content;
|
let content;
|
||||||
if (isOffline) {
|
if (isOffline) {
|
||||||
content = (
|
content = (
|
||||||
<div className="TokenBalances-offline well well-sm">
|
<div className="TokenBalances-offline well well-sm">{translate('SCAN_TOKENS_OFFLINE')}</div>
|
||||||
Token balances are unavailable offline
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
} else if (tokensError) {
|
} else if (tokensError) {
|
||||||
content = <h5>{tokensError}</h5>;
|
content = (
|
||||||
|
<div className="TokenBalances-error well well-md">
|
||||||
|
<h5 className="TokenBalances-error-message">{tokensError}</h5>
|
||||||
|
<button onClick={this.props.refreshTokenBalances} className="btn btn-default btn-sm">
|
||||||
|
{translate('X_TRY_AGAIN')}
|
||||||
|
<i className="fa fa-refresh" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
} else if (isTokensLoading) {
|
} else if (isTokensLoading) {
|
||||||
content = (
|
content = (
|
||||||
<div className="TokenBalances-loader">
|
<div className="TokenBalances-loader">
|
||||||
|
@ -126,5 +135,6 @@ export default connect(mapStateToProps, {
|
||||||
addCustomToken,
|
addCustomToken,
|
||||||
removeCustomToken,
|
removeCustomToken,
|
||||||
scanWalletForTokens,
|
scanWalletForTokens,
|
||||||
setWalletTokens
|
setWalletTokens,
|
||||||
|
refreshTokenBalances
|
||||||
})(TokenBalances);
|
})(TokenBalances);
|
||||||
|
|
|
@ -104,7 +104,7 @@ function setTokenBalanceRejected(state: State): State {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isTokensLoading: false,
|
isTokensLoading: false,
|
||||||
tokensError: 'Failed to fetch token value'
|
tokensError: translateRaw('SCAN_TOKENS_FAIL')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ export function* fetchRatesSaga(action: FetchCCRatesRequested): SagaIterator {
|
||||||
const rates: CCResponse = yield call(fetchRates, action.payload);
|
const rates: CCResponse = yield call(fetchRates, action.payload);
|
||||||
yield put(fetchCCRatesSucceeded(rates));
|
yield put(fetchCCRatesSucceeded(rates));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error('Failed to fetch rates:', e);
|
||||||
yield put(fetchCCRatesFailed());
|
yield put(fetchCCRatesFailed());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,7 +320,8 @@ export default function* walletSaga(): SagaIterator {
|
||||||
takeEvery(TypeKeys.WALLET_SCAN_WALLET_FOR_TOKENS, scanWalletForTokens),
|
takeEvery(TypeKeys.WALLET_SCAN_WALLET_FOR_TOKENS, scanWalletForTokens),
|
||||||
takeEvery(TypeKeys.WALLET_SET_WALLET_TOKENS, handleSetWalletTokens),
|
takeEvery(TypeKeys.WALLET_SET_WALLET_TOKENS, handleSetWalletTokens),
|
||||||
takeEvery(TypeKeys.WALLET_SET_TOKEN_BALANCE_PENDING, updateTokenBalance),
|
takeEvery(TypeKeys.WALLET_SET_TOKEN_BALANCE_PENDING, updateTokenBalance),
|
||||||
takeEvery(TypeKeys.WALLET_SET_ACCOUNT_BALANCE, updateAccountBalance),
|
takeEvery(TypeKeys.WALLET_REFRESH_ACCOUNT_BALANCE, updateAccountBalance),
|
||||||
|
takeEvery(TypeKeys.WALLET_REFRESH_TOKEN_BALANCES, updateTokenBalances),
|
||||||
// Foreign actions
|
// Foreign actions
|
||||||
takeEvery(ConfigTypeKeys.CONFIG_TOGGLE_OFFLINE, updateBalances),
|
takeEvery(ConfigTypeKeys.CONFIG_TOGGLE_OFFLINE, updateBalances),
|
||||||
takeEvery(CustomTokenTypeKeys.CUSTOM_TOKEN_ADD, handleCustomTokenAdd)
|
takeEvery(CustomTokenTypeKeys.CUSTOM_TOKEN_ADD, handleCustomTokenAdd)
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
"X_PRIVKEY2": "Private Key",
|
"X_PRIVKEY2": "Private Key",
|
||||||
"X_PRIVKEYDESC": "This is the unencrypted text version of your private key, meaning no password is necessary. If someone were to find your unencrypted private key, they could access your wallet without a password. For this reason, encrypted versions are typically recommended. ",
|
"X_PRIVKEYDESC": "This is the unencrypted text version of your private key, meaning no password is necessary. If someone were to find your unencrypted private key, they could access your wallet without a password. For this reason, encrypted versions are typically recommended. ",
|
||||||
"X_SAVE": "Save ",
|
"X_SAVE": "Save ",
|
||||||
|
"X_TRY_AGAIN": "Try again",
|
||||||
"CX_WARNING_1": "Make sure you have **external backups** of any wallets you store here. Many things could happen that would cause you to lose the data in this Chrome Extension, including uninstalling and reinstalling the extension. This extension is a way to easily access your wallets, **not** a way to back them up. ",
|
"CX_WARNING_1": "Make sure you have **external backups** of any wallets you store here. Many things could happen that would cause you to lose the data in this Chrome Extension, including uninstalling and reinstalling the extension. This extension is a way to easily access your wallets, **not** a way to back them up. ",
|
||||||
"SIDEBAR_ACCOUNTADDR": "Account Address ",
|
"SIDEBAR_ACCOUNTADDR": "Account Address ",
|
||||||
"SIDEBAR_ACCOUNTBAL": "Account Balance ",
|
"SIDEBAR_ACCOUNTBAL": "Account Balance ",
|
||||||
|
@ -103,6 +104,7 @@
|
||||||
"SCAN_TOKENS": "Scan For Tokens",
|
"SCAN_TOKENS": "Scan For Tokens",
|
||||||
"SCAN_TOKENS_FAIL": "Failed to fetch token values",
|
"SCAN_TOKENS_FAIL": "Failed to fetch token values",
|
||||||
"SCAN_TOKENS_FAIL_NO_TOKENS": "No tokens found",
|
"SCAN_TOKENS_FAIL_NO_TOKENS": "No tokens found",
|
||||||
|
"SCAN_TOKENS_OFFLINE": "Token balances are unavailable offline",
|
||||||
"SEND_GAS": "Gas ",
|
"SEND_GAS": "Gas ",
|
||||||
"SEND_TRANSFERTOTAL": "Send Entire Balance ",
|
"SEND_TRANSFERTOTAL": "Send Entire Balance ",
|
||||||
"SEND_GENERATE": "Generate Transaction ",
|
"SEND_GENERATE": "Generate Transaction ",
|
||||||
|
|
Loading…
Reference in New Issue