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:
William O'Beirne 2018-04-05 17:19:14 -04:00 committed by Daniel Ternyak
parent c9676cac62
commit c3d1e4e3af
11 changed files with 82 additions and 27 deletions

View File

@ -160,9 +160,16 @@ export function setWalletConfig(config: WalletConfig): types.SetWalletConfigActi
};
}
export type TSetAccountBalance = typeof setAccountBalance;
export function setAccountBalance(): types.SetAccountBalanceAction {
export type TRefreshAccountBalance = typeof refreshAccountBalance;
export function refreshAccountBalance(): types.RefreshAccountBalanceAction {
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
};
}

View File

@ -125,8 +125,12 @@ export interface SetPasswordPendingAction {
type: TypeKeys.WALLET_SET_PASSWORD_PENDING;
}
export interface SetAccountBalanceAction {
type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE;
export interface RefreshAccountBalanceAction {
type: TypeKeys.WALLET_REFRESH_ACCOUNT_BALANCE;
}
export interface RefreshTokenBalancesAction {
type: TypeKeys.WALLET_REFRESH_TOKEN_BALANCES;
}
/*** Union Type ***/
@ -148,4 +152,5 @@ export type WalletAction =
| SetWalletTokensAction
| SetWalletConfigAction
| SetPasswordPendingAction
| SetAccountBalanceAction;
| RefreshAccountBalanceAction
| RefreshTokenBalancesAction;

View File

@ -20,5 +20,6 @@ export enum TypeKeys {
WALLET_SET_CONFIG = 'WALLET_SET_CONFIG',
WALLET_RESET = 'WALLET_RESET',
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'
}

View File

@ -58,24 +58,39 @@ export interface CCResponse {
[symbol: string]: ISymbol;
}
interface IRates extends ISymbol {
Response?: 'Error';
interface IRatesResponse {
[key: string]: number;
}
interface IRatesError {
Response: 'Error';
}
export const fetchRates = (symbols: string[] = []): Promise<CCResponse> =>
fetch(CCRates(symbols))
.then(response => handleJSONResponse(response, ERROR_MESSAGE))
.then((rates: IRates) => {
.then((rates: IRatesResponse | IRatesError) => {
// 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');
}
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
// do it all in one request
// to their respective rates via ETH.
// do it all in one request to their respective rates via ETH.
return symbols.reduce(
(eqRates, sym: keyof ISymbol) => {
(eqRates, sym) => {
if (rates[sym]) {
eqRates[sym] = rateSymbols.symbols.all.reduce(
(symRates, rateSym) => {

View File

@ -9,7 +9,7 @@ import Spinner from 'components/ui/Spinner';
import { getNetworkConfig, getOffline } from 'selectors/config';
import { AppState } from 'reducers';
import { NetworkConfig } from 'types/network';
import { TSetAccountBalance, setAccountBalance } from 'actions/wallet';
import { TRefreshAccountBalance, refreshAccountBalance } from 'actions/wallet';
import './AccountInfo.scss';
interface OwnProps {
@ -30,7 +30,7 @@ interface State {
}
interface DispatchProps {
setAccountBalance: TSetAccountBalance;
refreshAccountBalance: TRefreshAccountBalance;
}
type Props = OwnProps & StateProps & DispatchProps;
@ -170,7 +170,7 @@ class AccountInfo extends React.Component<Props, State> {
!isOffline && (
<button
className="AccountInfo-section-refresh"
onClick={this.props.setAccountBalance}
onClick={this.props.refreshAccountBalance}
>
<i className="fa fa-refresh" />
</button>
@ -214,5 +214,5 @@ function mapStateToProps(state: AppState): StateProps {
isOffline: getOffline(state)
};
}
const mapDispatchToProps: DispatchProps = { setAccountBalance };
const mapDispatchToProps: DispatchProps = { refreshAccountBalance };
export default connect(mapStateToProps, mapDispatchToProps)(AccountInfo);

View File

@ -33,6 +33,19 @@
margin-top: $space;
}
&-error {
color: $brand-danger;
text-align: center;
&-message {
margin-top: 0;
}
.fa {
margin-left: $space-md;
}
}
&-buttons {
display: flex;
flex-wrap: wrap;

View File

@ -11,7 +11,9 @@ import {
scanWalletForTokens,
TScanWalletForTokens,
setWalletTokens,
TSetWalletTokens
TSetWalletTokens,
refreshTokenBalances,
TRefreshTokenBalances
} from 'actions/wallet';
import { getAllTokens, getOffline } from 'selectors/config';
import { getTokenBalances, getWalletInst, getWalletConfig, TokenBalance } from 'selectors/wallet';
@ -36,6 +38,7 @@ interface ActionProps {
removeCustomToken: TRemoveCustomToken;
scanWalletForTokens: TScanWalletForTokens;
setWalletTokens: TSetWalletTokens;
refreshTokenBalances: TRefreshTokenBalances;
}
type Props = StateProps & ActionProps;
@ -56,12 +59,18 @@ class TokenBalances extends React.Component<Props> {
let content;
if (isOffline) {
content = (
<div className="TokenBalances-offline well well-sm">
Token balances are unavailable offline
</div>
<div className="TokenBalances-offline well well-sm">{translate('SCAN_TOKENS_OFFLINE')}</div>
);
} 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) {
content = (
<div className="TokenBalances-loader">
@ -126,5 +135,6 @@ export default connect(mapStateToProps, {
addCustomToken,
removeCustomToken,
scanWalletForTokens,
setWalletTokens
setWalletTokens,
refreshTokenBalances
})(TokenBalances);

View File

@ -104,7 +104,7 @@ function setTokenBalanceRejected(state: State): State {
return {
...state,
isTokensLoading: false,
tokensError: 'Failed to fetch token value'
tokensError: translateRaw('SCAN_TOKENS_FAIL')
};
}

View File

@ -9,6 +9,7 @@ export function* fetchRatesSaga(action: FetchCCRatesRequested): SagaIterator {
const rates: CCResponse = yield call(fetchRates, action.payload);
yield put(fetchCCRatesSucceeded(rates));
} catch (e) {
console.error('Failed to fetch rates:', e);
yield put(fetchCCRatesFailed());
return;
}

View File

@ -320,7 +320,8 @@ export default function* walletSaga(): SagaIterator {
takeEvery(TypeKeys.WALLET_SCAN_WALLET_FOR_TOKENS, scanWalletForTokens),
takeEvery(TypeKeys.WALLET_SET_WALLET_TOKENS, handleSetWalletTokens),
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
takeEvery(ConfigTypeKeys.CONFIG_TOGGLE_OFFLINE, updateBalances),
takeEvery(CustomTokenTypeKeys.CUSTOM_TOKEN_ADD, handleCustomTokenAdd)

View File

@ -42,6 +42,7 @@
"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_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. ",
"SIDEBAR_ACCOUNTADDR": "Account Address ",
"SIDEBAR_ACCOUNTBAL": "Account Balance ",
@ -103,6 +104,7 @@
"SCAN_TOKENS": "Scan For Tokens",
"SCAN_TOKENS_FAIL": "Failed to fetch token values",
"SCAN_TOKENS_FAIL_NO_TOKENS": "No tokens found",
"SCAN_TOKENS_OFFLINE": "Token balances are unavailable offline",
"SEND_GAS": "Gas ",
"SEND_TRANSFERTOTAL": "Send Entire Balance ",
"SEND_GENERATE": "Generate Transaction ",