From 5e5052808e41ecf2413a1eeeb7412b5baf751447 Mon Sep 17 00:00:00 2001
From: lukaw3d
Date: Fri, 24 Jun 2022 04:49:13 +0200
Subject: [PATCH] Make account, transactions, and validators list error more
readable
---
src/app/components/ErrorFormatter/index.tsx | 1 +
.../Features/TransactionHistory/index.tsx | 8 +++----
src/app/pages/AccountPage/index.tsx | 13 +++++-----
.../Features/ValidatorList/index.tsx | 5 +++-
src/app/state/account/index.ts | 13 +++++-----
src/app/state/account/saga.ts | 24 ++++++++++++++-----
src/app/state/account/types.ts | 5 ++--
src/app/state/staking/index.ts | 6 ++---
src/app/state/staking/saga.test.ts | 8 +++++--
src/app/state/staking/saga.ts | 13 ++++++----
src/app/state/staking/types.ts | 2 +-
src/locales/en/translation.json | 5 ++--
src/types/errors.ts | 1 +
13 files changed, 65 insertions(+), 39 deletions(-)
diff --git a/src/app/components/ErrorFormatter/index.tsx b/src/app/components/ErrorFormatter/index.tsx
index 21bd0052cf..8f64941594 100644
--- a/src/app/components/ErrorFormatter/index.tsx
+++ b/src/app/components/ErrorFormatter/index.tsx
@@ -81,6 +81,7 @@ export function ErrorFormatter(props: Props) {
indexerName: backendToLabel[backend()],
},
),
+ [WalletErrors.DisconnectedError]: t('errors.disconnectedError', 'Lost connection.'),
}
const error = errorMap[props.code]
diff --git a/src/app/pages/AccountPage/Features/TransactionHistory/index.tsx b/src/app/pages/AccountPage/Features/TransactionHistory/index.tsx
index 23026b0822..ab89955f1f 100644
--- a/src/app/pages/AccountPage/Features/TransactionHistory/index.tsx
+++ b/src/app/pages/AccountPage/Features/TransactionHistory/index.tsx
@@ -15,6 +15,7 @@ import {
selectTransactionsError,
} from '../../../../state/account/selectors'
import { selectSelectedNetwork } from 'app/state/network/selectors'
+import { ErrorFormatter } from 'app/components/ErrorFormatter'
interface Props {}
@@ -35,11 +36,8 @@ export function TransactionHistory(props: Props) {
{transactionsError && (
- {t(
- 'account.transaction.loadingError',
- "Couldn't load transactions. List may be empty or out of date.",
- )}{' '}
- {transactionsError}
+ {t('account.transaction.loadingError', "Couldn't load transactions.")}{' '}
+
)}
{allTransactions.length ? (
diff --git a/src/app/pages/AccountPage/index.tsx b/src/app/pages/AccountPage/index.tsx
index e15ecbaeee..87f9983082 100644
--- a/src/app/pages/AccountPage/index.tsx
+++ b/src/app/pages/AccountPage/index.tsx
@@ -3,6 +3,7 @@
* AccountPage
*
*/
+import { AlertBox } from 'app/components/AlertBox'
import { ErrorFormatter } from 'app/components/ErrorFormatter'
import { TransactionModal } from 'app/components/TransactionModal'
import { TransitionRoute } from 'app/components/TransitionRoute'
@@ -116,19 +117,19 @@ export function AccountPage(props: Props) {
)}
{account.accountError && (
-
- {t('account.loadingError', "Couldn't load account. Information may be missing or out of date.")}{' '}
- {account.accountError}
-
+
+ {t('account.loadingError', "Couldn't load account.")}{' '}
+
+
)}
{stake.updateDelegationsError && (
-
+
{t('delegations.loadingError', "Couldn't load delegations.")}{' '}
-
+
)}
{address && address !== '' && (
<>
diff --git a/src/app/pages/StakingPage/Features/ValidatorList/index.tsx b/src/app/pages/StakingPage/Features/ValidatorList/index.tsx
index f900a9b648..551cb015b7 100644
--- a/src/app/pages/StakingPage/Features/ValidatorList/index.tsx
+++ b/src/app/pages/StakingPage/Features/ValidatorList/index.tsx
@@ -4,6 +4,7 @@
*
*/
import { AmountFormatter } from 'app/components/AmountFormatter'
+import { ErrorFormatter } from 'app/components/ErrorFormatter'
import { ShortAddress } from 'app/components/ShortAddress'
import { ValidatorStatus } from 'app/pages/StakingPage/Features/ValidatorList/ValidatorStatus'
import { stakingActions } from 'app/state/staking'
@@ -112,7 +113,9 @@ export const ValidatorList = memo((props: Props) => {
staleTimestamp: new Date(validatorsTimestamp!).toLocaleString(),
})}
- {updateValidatorsError}
+ {validators.length <= 0 && (
+
+ )}
)}
) {},
accountLoaded(state, action: PayloadAction) {
- state.accountError = null
+ state.accountError = undefined
Object.assign(state, action.payload)
},
- accountError(state, action: PayloadAction) {
+ accountError(state, action: PayloadAction) {
state.accountError = action.payload
},
transactionsLoaded(state, action: PayloadAction) {
- state.transactionsError = null
+ state.transactionsError = undefined
state.transactions = action.payload
},
- transactionsError(state, action: PayloadAction) {
+ transactionsError(state, action: PayloadAction) {
state.transactionsError = action.payload
},
setLoading(state, action: PayloadAction) {
diff --git a/src/app/state/account/saga.ts b/src/app/state/account/saga.ts
index d9b7f06b93..d4116c4bc2 100644
--- a/src/app/state/account/saga.ts
+++ b/src/app/state/account/saga.ts
@@ -1,6 +1,7 @@
import { PayloadAction } from '@reduxjs/toolkit'
import { addressToPublicKey, parseRpcBalance } from 'app/lib/helpers'
import { all, call, fork, join, put, select, take, takeLatest } from 'typed-redux-saga'
+import { WalletError, WalletErrors } from 'types/errors'
import { accountActions as actions } from '.'
import { getExplorerAPIs, getOasisNic } from '../network/saga'
@@ -27,7 +28,7 @@ export function* loadAccount(action: PayloadAction) {
try {
const account = yield* call(getAccount, address)
yield put(actions.accountLoaded(account))
- } catch (apiError) {
+ } catch (apiError: any) {
console.error('get account failed, continuing to RPC fallback.', apiError)
try {
const account = yield* call([nic, nic.stakingAccount], { owner: publicKey, height: 0 })
@@ -35,8 +36,16 @@ export function* loadAccount(action: PayloadAction) {
yield put(actions.accountLoaded({ address, liquid_balance: parseFloat(balance.available) }))
} catch (rpcError) {
console.error('get account with RPC failed, continuing without updated account.', rpcError)
- yield put(actions.accountError('' + apiError))
- return
+ if (apiError instanceof WalletError) {
+ yield* put(actions.accountError({ code: apiError.type, message: apiError.message }))
+ } else {
+ yield* put(
+ actions.accountError({
+ code: WalletErrors.UnknownError,
+ message: apiError.message,
+ }),
+ )
+ }
}
}
}),
@@ -49,10 +58,13 @@ export function* loadAccount(action: PayloadAction) {
limit: 20,
})
yield put(actions.transactionsLoaded(transactions))
- } catch (e) {
+ } catch (e: any) {
console.error('get transactions list failed, continuing without updated list.', e)
- yield put(actions.transactionsError('' + e))
- return
+ if (e instanceof WalletError) {
+ yield* put(actions.transactionsError({ code: e.type, message: e.message }))
+ } else {
+ yield* put(actions.transactionsError({ code: WalletErrors.UnknownError, message: e.message }))
+ }
}
}),
),
diff --git a/src/app/state/account/types.ts b/src/app/state/account/types.ts
index bc578f19a3..793e81a91a 100644
--- a/src/app/state/account/types.ts
+++ b/src/app/state/account/types.ts
@@ -1,4 +1,5 @@
import { Transaction } from 'app/state/transaction/types'
+import { ErrorPayload } from 'types/errors'
export interface BalanceDetails {
total: number
@@ -15,7 +16,7 @@ export interface Account {
/* --- STATE --- */
export interface AccountState extends Account {
loading: boolean
- accountError: string | null
+ accountError?: ErrorPayload
transactions: Transaction[]
- transactionsError: string | null
+ transactionsError?: ErrorPayload
}
diff --git a/src/app/state/staking/index.ts b/src/app/state/staking/index.ts
index ad0f28dd2a..8ff4fdc5e8 100644
--- a/src/app/state/staking/index.ts
+++ b/src/app/state/staking/index.ts
@@ -9,7 +9,7 @@ export const initialState: StakingState = {
delegations: [],
updateDelegationsError: undefined,
validators: null,
- updateValidatorsError: null,
+ updateValidatorsError: undefined,
selectedValidatorDetails: null,
selectedValidator: null,
loading: false,
@@ -24,10 +24,10 @@ const slice = createSlice({
},
fetchAccount(state, action: PayloadAction) {},
updateValidators(state, action: PayloadAction) {
- state.updateValidatorsError = null
+ state.updateValidatorsError = undefined
state.validators = action.payload
},
- updateValidatorsError(state, action: PayloadAction<{ error: string; validators: Validators }>) {
+ updateValidatorsError(state, action: PayloadAction<{ error: ErrorPayload; validators: Validators }>) {
state.updateValidatorsError = action.payload.error
state.validators = action.payload.validators
},
diff --git a/src/app/state/staking/saga.test.ts b/src/app/state/staking/saga.test.ts
index 08046fa6e9..c8a866dc82 100644
--- a/src/app/state/staking/saga.test.ts
+++ b/src/app/state/staking/saga.test.ts
@@ -4,6 +4,7 @@ import * as matchers from 'redux-saga-test-plan/matchers'
import { EffectProviders, StaticProvider } from 'redux-saga-test-plan/providers'
import { select } from 'redux-saga/effects'
import { RootState } from 'types'
+import { WalletError, WalletErrors } from 'types/errors'
import { initialState, stakingActions, stakingReducer } from '.'
import { getExplorerAPIs, getOasisNic } from '../network/saga'
@@ -144,7 +145,7 @@ describe('Staking Sagas', () => {
})
it('should use fallback on mainnet', () => {
- getAllValidators.mockRejectedValue('apiFailed')
+ getAllValidators.mockRejectedValue(new WalletError(WalletErrors.IndexerAPIError, 'Request failed'))
const getMainnetDumpValidatorsMock = {
dump_timestamp: 1647996761337,
dump_timestamp_iso: '2022-03-23T00:52:41.337Z',
@@ -185,7 +186,10 @@ describe('Staking Sagas', () => {
.provide([...providers, [matchers.call.fn(getMainnetDumpValidators), getMainnetDumpValidatorsMock]])
.put(
stakingActions.updateValidatorsError({
- error: 'apiFailed',
+ error: {
+ code: WalletErrors.IndexerAPIError,
+ message: 'Request failed',
+ },
validators: {
timestamp: getMainnetDumpValidatorsMock.dump_timestamp,
network: 'mainnet',
diff --git a/src/app/state/staking/saga.ts b/src/app/state/staking/saga.ts
index 55d1a5ecbb..11995b982e 100644
--- a/src/app/state/staking/saga.ts
+++ b/src/app/state/staking/saga.ts
@@ -62,20 +62,23 @@ export function* refreshValidators() {
list: validators,
}),
)
- } catch (errorApi) {
+ } catch (errorApi: any) {
console.error('get validators list failed', errorApi)
- const fallback = yield* call(getFallbackValidators, network, '' + errorApi)
+ const fallback = yield* call(getFallbackValidators, network, errorApi)
yield* put(
stakingActions.updateValidatorsError({
- error: fallback.error,
+ error: {
+ code: fallback.error instanceof WalletError ? fallback.error.type : WalletErrors.UnknownError,
+ message: fallback.error.message,
+ },
validators: fallback.validators,
}),
)
}
}
-function* getFallbackValidators(network: NetworkType, errorApi: string) {
+function* getFallbackValidators(network: NetworkType, errorApi: Error) {
let fallbackValidators: Validators = {
timestamp: yield* call(now),
network: network,
@@ -104,7 +107,7 @@ function* getFallbackValidators(network: NetworkType, errorApi: string) {
} catch (errorDumpValidators) {
// If fetching dump_validators fails, fall back to empty list
return {
- error: 'Lost connection',
+ error: new WalletError(WalletErrors.DisconnectedError, 'Lost connection'),
validators: fallbackValidators,
}
}
diff --git a/src/app/state/staking/types.ts b/src/app/state/staking/types.ts
index cea487c27e..512e26f49a 100644
--- a/src/app/state/staking/types.ts
+++ b/src/app/state/staking/types.ts
@@ -60,7 +60,7 @@ export interface StakingState {
validators: Validators | null
/** Error from last attempt to update our list of validators */
- updateValidatorsError: string | null
+ updateValidatorsError?: ErrorPayload
/** List of active delegations for the selected account */
delegations: Delegation[]
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 0317c7b658..a23c6e5c81 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -12,7 +12,7 @@
"delegate": "Delegate"
},
"loading": "Loading account",
- "loadingError": "Couldn't load account. Information may be missing or out of date.",
+ "loadingError": "Couldn't load account.",
"otherTransaction": {
"designation": "Other address",
"header": "Unrecognized transaction, method '{{method}}'"
@@ -54,7 +54,7 @@
"received": "Received <0>0> delegation in escrow",
"sent": "Delegated <0>0> to validator"
},
- "loadingError": "Couldn't load transactions. List may be empty or out of date.",
+ "loadingError": "Couldn't load transactions.",
"reclaimEscrow": {
"received": "<0>0> reclaimed by delegator",
"sent": "Reclaimed <0>0> from validator"
@@ -106,6 +106,7 @@
"errors": {
"LedgerOasisAppIsNotOpen": "Oasis App on Ledger is closed.",
"cannotSendToSelf": "Cannot send to yourself",
+ "disconnectedError": "Lost connection.",
"duplicateTransaction": "Duplicate transaction",
"indexerAPIError": "{{indexerName}} appears to be down, some information may be missing or out of date. Please, come back later.",
"insufficientBalance": "Insufficient balance",
diff --git a/src/types/errors.ts b/src/types/errors.ts
index c39f29f0fc..5e51ed8df4 100644
--- a/src/types/errors.ts
+++ b/src/types/errors.ts
@@ -23,6 +23,7 @@ export enum WalletErrors {
LedgerTransactionRejected = 'transaction_rejected',
LedgerAppVersionNotSupported = 'ledger_version_not_supported',
IndexerAPIError = 'indexer_api_error',
+ DisconnectedError = 'disconnected_error',
}
export interface ErrorPayload {