Blockchain explorer for Ethereum based network and a tool for inspecting and analyzing EVM based blockchains.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
blockscout/apps/block_scout_web/assets/js/pages/address/transactions.js

185 lines
5.6 KiB

import $ from 'jquery'
import omit from 'lodash.omit'
import URI from 'urijs'
import humps from 'humps'
import numeral from 'numeral'
import { subscribeChannel } from '../../socket'
import { connectElements } from '../../lib/redux_helpers.js'
import { createAsyncLoadStore } from '../../lib/async_listing_load'
import { batchChannel } from '../../lib/utils'
import '../address'
import { isFiltered } from './utils'
const BATCH_THRESHOLD = 6
export const initialState = {
addressHash: null,
channelDisconnected: false,
filter: null,
transactionsBatch: []
}
export function reducer (state, action) {
switch (action.type) {
case 'PAGE_LOAD':
case 'ELEMENTS_LOAD': {
return Object.assign({}, state, omit(action, 'type'))
}
case 'CHANNEL_DISCONNECTED': {
if (state.beyondPageOne) return state
return Object.assign({}, state, { channelDisconnected: true })
}
case 'RECEIVED_NEW_TRANSACTION': {
if (state.channelDisconnected) return state
if (state.beyondPageOne ||
(state.filter === 'to' && action.msg.toAddressHash !== state.addressHash) ||
(state.filter === 'from' && action.msg.fromAddressHash !== state.addressHash)) {
return state
}
return Object.assign({}, state, { items: [action.msg.transactionHtml, ...state.items] })
}
case 'RECEIVED_NEW_TRANSACTION_BATCH': {
if (state.channelDisconnected || state.beyondPageOne) return state
const transactionCount = state.transactionCount + action.msgs.length
if (!state.transactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) {
return Object.assign({}, state, {
items: [
...action.msgs.map(msg => msg.transactionHtml).reverse(),
...state.items
],
transactionCount
})
} else {
return Object.assign({}, state, {
transactionsBatch: [
...action.msgs.reverse(),
...state.transactionsBatch
],
transactionCount
})
}
}
case 'RECEIVED_NEW_REWARD': {
if (state.channelDisconnected) return state
return Object.assign({}, state, { items: [action.msg.rewardHtml, ...state.items] })
}
case 'TRANSACTION_BATCH_EXPANDED': {
return Object.assign({}, state, {
transactionsBatch: []
})
}
case 'TRANSACTIONS_FETCHED':
return Object.assign({}, state, { items: [...action.msg.items] })
case 'TRANSACTIONS_FETCH_ERROR': {
const $channelBatching = $('[data-selector="channel-batching-message"]')
$channelBatching.show()
return state
}
default:
return state
}
}
const elements = {
'[data-selector="channel-disconnected-message"]': {
render ($el, state) {
// @ts-ignore
if (state.channelDisconnected && !window.loading) $el.show()
}
},
'[data-test="filter_dropdown"]': {
render ($el, state) {
if (state.emptyResponse && !state.isSearch) {
if (isFiltered(state.filter)) {
$el.addClass('no-rm')
} else {
return $el.hide()
}
} else {
$el.removeClass('no-rm')
}
return $el.show()
}
},
'[data-selector="channel-batching-count"]': {
render ($el, state, _oldState) {
const $channelBatching = $('[data-selector="channel-batching-message"]')
if (!state.transactionsBatch.length) return $channelBatching.hide()
$channelBatching.show()
$el[0].innerHTML = numeral(state.transactionsBatch.length).format()
}
}
}
if ($('[data-page="address-transactions"]').length) {
window.onbeforeunload = () => {
// @ts-ignore
window.loading = true
}
const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash')
const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash
// @ts-ignore
const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true))
connectElements({ store, elements })
store.dispatch({
type: 'PAGE_LOAD',
addressHash,
filter,
beyondPageOne: !!blockNumber
})
const addressChannel = subscribeChannel(`addresses:${addressHash}`)
addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
addressChannel.on('transaction', batchChannel((msgs) =>
store.dispatch({
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: humps.camelizeKeys(msgs)
})
))
addressChannel.on('pending_transaction', batchChannel((msgs) =>
store.dispatch({
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: humps.camelizeKeys(msgs)
})
))
const rewardsChannel = subscribeChannel(`rewards:${addressHash}`)
rewardsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
rewardsChannel.on('new_reward', (msg) => {
store.dispatch({
type: 'RECEIVED_NEW_REWARD',
msg: humps.camelizeKeys(msg)
})
})
const $txReloadButton = $('[data-selector="reload-transactions-button"]')
const $channelBatching = $('[data-selector="channel-batching-message"]')
$txReloadButton.on('click', (event) => {
event.preventDefault()
loadTransactions(store)
$channelBatching.hide()
store.dispatch({
type: 'TRANSACTION_BATCH_EXPANDED'
})
})
}
function loadTransactions (store) {
const path = $('[class="card-body"]')[0].dataset.asyncListing
store.dispatch({ type: 'START_TRANSACTIONS_FETCH' })
// @ts-ignore
$.getJSON(path, { type: 'JSON' })
.done(response => store.dispatch({ type: 'TRANSACTIONS_FETCHED', msg: humps.camelizeKeys(response) }))
.fail(() => store.dispatch({ type: 'TRANSACTIONS_FETCH_ERROR' }))
.always(() => store.dispatch({ type: 'FINISH_TRANSACTIONS_FETCH' }))
}