-
Notifications
You must be signed in to change notification settings - Fork 43
/
saga.ts
145 lines (131 loc) · 5.34 KB
/
saga.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { PayloadAction } from '@reduxjs/toolkit'
import { all, call, delay, fork, join, put, select, take, takeLatest } from 'typed-redux-saga'
import { WalletError, WalletErrors } from 'types/errors'
import { accountActions } from '.'
import { getExplorerAPIs } from '../network/saga'
import { takeLatestCancelable } from '../takeLatestCancelable'
import { stakingActions } from '../staking'
import { fetchAccount as stakingFetchAccount } from '../staking/saga'
import { refreshAccount as walletRefreshAccount } from '../wallet/saga'
import { transactionActions } from '../transaction'
import { selectAddress } from '../wallet/selectors'
import { selectAccountAddress, selectAccountAvailableBalance } from './selectors'
import { getAccountBalanceWithFallback } from '../../lib/getAccountBalanceWithFallback'
import { walletActions } from '../wallet'
const ACCOUNT_REFETCHING_INTERVAL = process.env.REACT_APP_E2E_TEST ? 5 * 1000 : 30 * 1000
const TRANSACTIONS_LIMIT = 20
export function* fetchAccount(action: PayloadAction<string>) {
const address = action.payload
yield* put(accountActions.setLoading(true))
const { getTransactionsList } = yield* call(getExplorerAPIs)
yield* all([
join(
yield* fork(function* () {
try {
const account = yield* call(getAccountBalanceWithFallback, address)
yield* put(accountActions.accountLoaded(account))
yield* put(walletActions.updateBalance({ address: account.address, balance: account }))
} catch (apiError: any) {
if (apiError instanceof WalletError) {
yield* put(accountActions.accountError({ code: apiError.type, message: apiError.message }))
} else {
yield* put(
accountActions.accountError({
code: WalletErrors.UnknownError,
message: apiError.message,
}),
)
}
}
}),
),
join(
yield* fork(function* () {
try {
const transactions = yield* call(getTransactionsList, {
accountId: address,
limit: TRANSACTIONS_LIMIT,
})
yield* put(accountActions.transactionsLoaded(transactions))
} catch (e: any) {
console.error('get transactions list failed, continuing without updated list.', e)
if (e instanceof WalletError) {
yield* put(accountActions.transactionsError({ code: e.type, message: e.message }))
} else {
yield* put(
accountActions.transactionsError({ code: WalletErrors.UnknownError, message: e.message }),
)
}
}
}),
),
])
yield* put(accountActions.setLoading(false))
}
/**
* When a transaction is done, and it is related to the account we currently have in state
* refresh the data.
*/
export function* refreshAccountOnTransaction() {
while (true) {
const { payload } = yield* take(transactionActions.transactionSent)
const otherAddress = payload.type === 'transfer' ? payload.to : payload.validator
yield* call(refreshAccount, otherAddress)
}
}
export function* refreshAccountOnParaTimeTransaction() {
while (true) {
const { payload } = yield* take(transactionActions.paraTimeTransactionSent)
yield* call(refreshAccount, payload)
}
}
function* refreshAccount(address: string) {
const from = yield* select(selectAddress)
const currentAccount = yield* select(selectAccountAddress)
if (currentAccount === from || currentAccount === address) {
yield* put(accountActions.fetchAccount(currentAccount))
yield* put(stakingActions.fetchAccount(currentAccount))
}
}
export function* fetchingOnAccountPage() {
yield* takeLatestCancelable({
startOnAction: accountActions.openAccountPage,
cancelOnAction: accountActions.closeAccountPage,
task: function* (startAction) {
const address = startAction.payload
try {
// Note: tasks triggered by `put` can't get canceled by closeAccountPage.
yield* put(accountActions.fetchAccount(address))
yield* put(stakingActions.fetchAccount(address))
yield* take(accountActions.accountLoaded)
// Start refetching balance. If balance changes then fetch transactions and staking too.
while (true) {
yield* delay(ACCOUNT_REFETCHING_INTERVAL)
if (document.hidden) continue
let refreshedAccount
try {
const { getAccount } = yield* call(getExplorerAPIs)
refreshedAccount = yield* call(getAccount, address)
} catch (apiError: any) {
console.error('refetching account failed, continuing without updated account.', apiError)
continue // Ignore error when refetching, and don't fallback to more expensive gRPC
}
const staleAvailableBalance = yield* select(selectAccountAvailableBalance)
if (staleAvailableBalance !== refreshedAccount.available) {
yield* call(fetchAccount, startAction)
yield* call(stakingFetchAccount, startAction)
yield* call(walletRefreshAccount, address)
}
}
} finally {
yield* put(accountActions.clearAccount())
}
},
})
}
export function* accountSaga() {
yield* fork(fetchingOnAccountPage)
yield* fork(refreshAccountOnTransaction)
yield* fork(refreshAccountOnParaTimeTransaction)
yield* takeLatest(accountActions.fetchAccount, fetchAccount)
}