diff --git a/apps/block_scout_web/assets/__tests__/pages/address.js b/apps/block_scout_web/assets/__tests__/pages/address.js index fe6506e22b..5e87b5b73d 100644 --- a/apps/block_scout_web/assets/__tests__/pages/address.js +++ b/apps/block_scout_web/assets/__tests__/pages/address.js @@ -1,56 +1,28 @@ import { reducer, initialState } from '../../js/pages/address' describe('RECEIVED_NEW_BLOCK', () => { - test('with new block', () => { - const state = Object.assign({}, initialState, { - validationCount: 30, - validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }] - }) + test('increases validation count', () => { + const state = Object.assign({}, initialState, { validationCount: 30 }) const action = { type: 'RECEIVED_NEW_BLOCK', - msg: { blockNumber: 2, blockHtml: 'test 2' } + blockHtml: 'test 2' } const output = reducer(state, action) expect(output.validationCount).toEqual(31) - expect(output.validatedBlocks).toEqual([ - { blockNumber: 2, blockHtml: 'test 2' }, - { blockNumber: 1, blockHtml: 'test 1' } - ]) }) - test('when channel has been disconnected', () => { + test('when channel has been disconnected does not increase validation count', () => { const state = Object.assign({}, initialState, { channelDisconnected: true, - validationCount: 30, - validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }] + validationCount: 30 }) const action = { type: 'RECEIVED_NEW_BLOCK', - msg: { blockNumber: 2, blockHtml: 'test 2' } + blockHtml: 'test 2' } const output = reducer(state, action) expect(output.validationCount).toEqual(30) - expect(output.validatedBlocks).toEqual([ - { blockNumber: 1, blockHtml: 'test 1' } - ]) - }) - test('beyond page one', () => { - const state = Object.assign({}, initialState, { - beyondPageOne: true, - validationCount: 30, - validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }] - }) - const action = { - type: 'RECEIVED_NEW_BLOCK', - msg: { blockNumber: 2, blockHtml: 'test 2' } - } - const output = reducer(state, action) - - expect(output.validationCount).toEqual(31) - expect(output.validatedBlocks).toEqual([ - { blockNumber: 1, blockHtml: 'test 1' } - ]) }) }) diff --git a/apps/block_scout_web/assets/__tests__/pages/address/validations.js b/apps/block_scout_web/assets/__tests__/pages/address/validations.js new file mode 100644 index 0000000000..f8bf284029 --- /dev/null +++ b/apps/block_scout_web/assets/__tests__/pages/address/validations.js @@ -0,0 +1,46 @@ +import { reducer, initialState } from '../../../js/pages/address/validations' + +describe('RECEIVED_NEW_BLOCK', () => { + test('adds new block to the top of the list', () => { + const state = Object.assign({}, initialState, { + items: ['test 1'] + }) + const action = { + type: 'RECEIVED_NEW_BLOCK', + blockHtml: 'test 2' + } + const output = reducer(state, action) + + expect(output.items).toEqual(['test 2', 'test 1']) + }) + + test('does nothing beyond page one', () => { + const state = Object.assign({}, initialState, { + beyondPageOne: true, + channelDisconnected: false, + items: ['test 1'] + }) + const action = { + type: 'RECEIVED_NEW_BLOCK', + blockHtml: 'test 2' + } + const output = reducer(state, action) + + expect(output.items).toEqual(['test 1']) + }) + + test('does nothing when channel has been disconnected', () => { + const state = Object.assign({}, initialState, { + channelDisconnected: true, + items: ['test 1'] + }) + const action = { + type: 'RECEIVED_NEW_BLOCK', + blockHtml: 'test 2' + } + const output = reducer(state, action) + + expect(output.items).toEqual(['test 1']) + }) +}) + diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js index 8f084b5521..e1fcc24b45 100644 --- a/apps/block_scout_web/assets/js/app.js +++ b/apps/block_scout_web/assets/js/app.js @@ -21,6 +21,7 @@ import 'bootstrap' import './locale' import './pages/address' +import './pages/address/validations' import './pages/blocks' import './pages/chain' import './pages/pending_transactions' diff --git a/apps/block_scout_web/assets/js/pages/address.js b/apps/block_scout_web/assets/js/pages/address.js index 683fc66a57..ce790e04b5 100644 --- a/apps/block_scout_web/assets/js/pages/address.js +++ b/apps/block_scout_web/assets/js/pages/address.js @@ -26,7 +26,6 @@ export const initialState = { transactions: [], internalTransactions: [], internalTransactionsBatch: [], - validatedBlocks: [], beyondPageOne: null, @@ -53,15 +52,7 @@ function baseReducer (state = initialState, action) { if (state.channelDisconnected) return state const validationCount = state.validationCount + 1 - - if (state.beyondPageOne) return Object.assign({}, state, { validationCount }) - return Object.assign({}, state, { - validatedBlocks: [ - action.msg, - ...state.validatedBlocks - ], - validationCount - }) + return Object.assign({}, state, { validationCount }) } case 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH': { if (state.channelDisconnected || state.beyondPageOne) return state @@ -209,22 +200,6 @@ const elements = { $channelBatching.show() $el[0].innerHTML = numeral(state.internalTransactionsBatch.length).format() } - }, - '[data-selector="validations-list"]': { - load ($el) { - return { - validatedBlocks: $el.children().map((index, el) => ({ - blockNumber: parseInt(el.dataset.blockNumber), - blockHtml: el.outerHTML - })).toArray() - } - }, - render ($el, state, oldState) { - if (oldState.validatedBlocks === state.validatedBlocks) return - const container = $el[0] - const newElements = _.map(state.validatedBlocks, ({ blockHtml }) => $(blockHtml)[0]) - listMorph(container, newElements, { key: 'dataset.blockNumber' }) - } } } diff --git a/apps/block_scout_web/assets/js/pages/address/validations.js b/apps/block_scout_web/assets/js/pages/address/validations.js new file mode 100644 index 0000000000..6aca07a98b --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/validations.js @@ -0,0 +1,64 @@ +import $ from 'jquery' +import _ from 'lodash' +import humps from 'humps' +import socket from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load.js' + +export const initialState = { + addressHash: null, + channelDisconnected: false +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'PAGE_LOAD': + 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) return state + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + items: [ + action.blockHtml, + ...state.items + ] + }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected) $el.show() + } + } +} + +if ($('[data-page="blocks-validated"]').length) { + const store = createAsyncLoadStore(reducer, initialState, 'dataset.blockNumber') + connectElements({ store, elements }) + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + store.dispatch({ + type: 'PAGE_LOAD', + addressHash + }) + + const blocksChannel = socket.channel(`blocks:${addressHash}`, {}) + blocksChannel.join() + blocksChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + blocksChannel.on('new_block', (msg) => store.dispatch({ + type: 'RECEIVED_NEW_BLOCK', + blockHtml: humps.camelizeKeys(msg).blockHtml + })) +}