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/blocks.js

134 lines
3.9 KiB

import $ from 'jquery'
import omit from 'lodash.omit'
import last from 'lodash.last'
import min from 'lodash.min'
import max from 'lodash.max'
import keys from 'lodash.keys'
import rangeRight from 'lodash.rangeright'
import humps from 'humps'
import socket from '../socket'
import { connectElements } from '../lib/redux_helpers.js'
import { createAsyncLoadStore } from '../lib/async_listing_load'
import '../app'
export const initialState = {
channelDisconnected: false
}
export const blockReducer = withMissingBlocks(baseReducer)
function baseReducer (state = initialState, action) {
switch (action.type) {
case 'ELEMENTS_LOAD': {
return Object.assign({}, state, omit(action, 'type'))
}
case 'CHANNEL_DISCONNECTED': {
return Object.assign({}, state, {
channelDisconnected: true
})
}
case 'RECEIVED_NEW_BLOCK': {
if (state.channelDisconnected || state.beyondPageOne || state.blockType !== 'block') return state
const blockNumber = getBlockNumber(action.msg.blockHtml)
const minBlock = getBlockNumber(last(state.items))
if (state.items.length && blockNumber < minBlock) return state
return Object.assign({}, state, {
items: [action.msg.blockHtml, ...state.items]
})
}
default:
return state
}
}
const elements = {
'[data-selector="channel-disconnected-message"]': {
render ($el, state) {
// @ts-ignore
if (state.channelDisconnected && !window.loading) $el.show()
}
}
}
function getBlockNumber (blockHtml) {
return $(blockHtml).data('blockNumber')
}
function withMissingBlocks (reducer) {
return (...args) => {
const result = reducer(...args)
if (result.items.length < 2) return result
const blockNumbersToItems = result.items.reduce((acc, item) => {
const blockNumber = getBlockNumber(item)
acc[blockNumber] = acc[blockNumber] || item
return acc
}, {})
const blockNumbers = keys(blockNumbersToItems).map(x => parseInt(x, 10))
const minBlock = min(blockNumbers) || 0
const maxBlock = max(blockNumbers) || 0
if (maxBlock - minBlock > 100) return result
return Object.assign({}, result, {
items: rangeRight(minBlock, maxBlock + 1)
.map((blockNumber) => blockNumbersToItems[blockNumber] || placeHolderBlock(blockNumber))
})
}
}
const $blockListPage = $('[data-page="block-list"]')
const $uncleListPage = $('[data-page="uncle-list"]')
const $reorgListPage = $('[data-page="reorg-list"]')
if ($blockListPage.length || $uncleListPage.length || $reorgListPage.length) {
window.onbeforeunload = () => {
// @ts-ignore
window.loading = true
}
const blockType = $blockListPage.length ? 'block' : $uncleListPage.length ? 'uncle' : 'reorg'
const store = createAsyncLoadStore(
$blockListPage.length ? blockReducer : baseReducer,
Object.assign({}, initialState, { blockType }),
'dataset.blockNumber'
)
connectElements({ store, elements })
const blocksChannel = socket.channel('blocks:new_block', {})
blocksChannel.join()
blocksChannel.onError(() => store.dispatch({
type: 'CHANNEL_DISCONNECTED'
}))
blocksChannel.on('new_block', (msg) => store.dispatch({
type: 'RECEIVED_NEW_BLOCK',
msg: humps.camelizeKeys(msg)
}))
}
export function placeHolderBlock (blockNumber) {
return `
<div class="my-3" data-selector="place-holder" data-block-number="${blockNumber}">
<div
class="tile tile-type-block d-flex align-items-center fade-up"
style="min-height: 90px;"
>
<span class="loading-spinner-small ml-1 mr-4">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<div>
<span class="tile-title pr-0 pl-0">${blockNumber}</span>
<div class="tile-transactions">${
// @ts-ignore
window.localized['Block Processing']
}</div>
</div>
</div>
</div>
`
}