diff --git a/apps/block_scout_web/assets/__tests__/pages/address.js b/apps/block_scout_web/assets/__tests__/pages/address.js index 96981a6f5a..035e415074 100644 --- a/apps/block_scout_web/assets/__tests__/pages/address.js +++ b/apps/block_scout_web/assets/__tests__/pages/address.js @@ -145,61 +145,36 @@ describe('RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH', () => { }) describe('RECEIVED_NEW_TRANSACTION', () => { - test('with new transaction', () => { + test('increment the transactions count', () => { const state = Object.assign({}, initialState, { - transactions: [{ transactionHash: 1, transactionHtml: 'test 1' }] + addressHash: "0x001", + transactionCount: 1 }) - const action = { - type: 'RECEIVED_NEW_TRANSACTION', - msg: { transactionHash: 2, transactionHtml: 'test 2' } - } - const output = reducer(state, action) - expect(output.transactions).toEqual([ - { transactionHash: 2, transactionHtml: 'test 2' }, - { transactionHash: 1, transactionHtml: 'test 1' } - ]) - }) - test('when channel has been disconnected', () => { - const state = Object.assign({}, initialState, { - channelDisconnected: true, - transactions: [{ transactionHash: 1, transactionHtml: 'test 1' }] - }) const action = { type: 'RECEIVED_NEW_TRANSACTION', - msg: { transactionHash: 2, transactionHtml: 'test 2' } + msg: { fromAddressHash: "0x001", transactionHash: 2, transactionHtml: 'test 2' } } - const output = reducer(state, action) - expect(output.transactions).toEqual([ - { transactionHash: 1, transactionHtml: 'test 1' } - ]) - }) - test('beyond page one', () => { - const state = Object.assign({}, initialState, { - beyondPageOne: true, - transactions: [{ transactionHash: 1, transactionHtml: 'test 1' }] - }) - const action = { - type: 'RECEIVED_NEW_TRANSACTION', - msg: { transactionHash: 2, transactionHtml: 'test 2' } - } - const output = reducer(state, action) + const newState = reducer(state, action) - expect(output.transactions).toEqual([ - { transactionHash: 1, transactionHtml: 'test 1' } - ]) + expect(newState.transactionCount).toEqual(2) }) - test('with filtered out transaction', () => { + + test('does not increment the count if the channel is disconnected', () => { const state = Object.assign({}, initialState, { - filter: 'to' + addressHash: "0x001", + transactionCount: 1, + channelDisconnected: true }) + const action = { type: 'RECEIVED_NEW_TRANSACTION', - msg: { transactionHash: 2, transactionHtml: 'test 2' } + msg: { fromAddressHash: "0x001", transactionHash: 2, transactionHtml: 'test 2' } } - const output = reducer(state, action) - expect(output.transactions).toEqual([]) + const newState = reducer(state, action) + + expect(newState.transactionCount).toEqual(1) }) }) diff --git a/apps/block_scout_web/assets/__tests__/pages/address/transactions.js b/apps/block_scout_web/assets/__tests__/pages/address/transactions.js new file mode 100644 index 0000000000..c4676cd6a2 --- /dev/null +++ b/apps/block_scout_web/assets/__tests__/pages/address/transactions.js @@ -0,0 +1,124 @@ +import { reducer, initialState } from '../../../js/pages/address/transactions' + +describe('RECEIVED_NEW_TRANSACTION', () => { + test('with new transaction', () => { + const state = Object.assign({}, initialState, { + items: ['transaction html'] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION', + msg: { transactionHtml: 'another transaction html' } + } + const output = reducer(state, action) + + expect(output.items).toEqual([ 'another transaction html', 'transaction html' ]) + }) + + test('when channel has been disconnected', () => { + const state = Object.assign({}, initialState, { + channelDisconnected: true, + items: ['transaction html'] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION', + msg: { transactionHtml: 'another transaction html' } + } + const output = reducer(state, action) + + expect(output.items).toEqual(['transaction html']) + }) + + test('beyond page one', () => { + const state = Object.assign({}, initialState, { + beyondPageOne: true, + items: ['transaction html'] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION', + msg: { transactionHtml: 'another transaction html' } + } + const output = reducer(state, action) + + expect(output.items).toEqual([ 'transaction html' ]) + }) + + test('adds the new transaction to state even when it is filtered by to', () => { + const state = Object.assign({}, initialState, { + addressHash: '0x001', + filter: 'to', + items: [] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION', + msg: { + fromAddressHash: '0x002', + transactionHtml: 'transaction html', + toAddressHash: '0x001' + } + } + const output = reducer(state, action) + + expect(output.items).toEqual(['transaction html']) + }) + + test( + 'does nothing when it is filtered by to but the toAddressHash is different from addressHash', + () => { + const state = Object.assign({}, initialState, { + addressHash: '0x001', + filter: 'to', + items: [] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION', + msg: { + fromAddressHash: '0x003', + transactionHtml: 'transaction html', + toAddressHash: '0x002' + } + } + const output = reducer(state, action) + + expect(output.items).toEqual([]) + }) + + test('adds the new transaction to state even when it is filtered by from', () => { + const state = Object.assign({}, initialState, { + addressHash: '0x001', + filter: 'from', + items: [] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION', + msg: { + fromAddressHash: '0x001', + transactionHtml: 'transaction html', + toAddressHash: '0x002' + } + } + const output = reducer(state, action) + + expect(output.items).toEqual(['transaction html']) + }) + + test( + 'does nothing when it is filtered by from but the fromAddressHash is different from addressHash', + () => { + const state = Object.assign({}, initialState, { + addressHash: '0x001', + filter: 'to', + items: [] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION', + msg: { + addressHash: '0x001', + transactionHtml: 'transaction html', + fromAddressHash: '0x002' + } + } + const output = reducer(state, action) + + expect(output.items).toEqual([]) + }) +}) diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js index e1fcc24b45..30430547cb 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/transactions' import './pages/address/validations' import './pages/blocks' import './pages/chain' diff --git a/apps/block_scout_web/assets/js/pages/address.js b/apps/block_scout_web/assets/js/pages/address.js index 15cbd1ea21..615cefc394 100644 --- a/apps/block_scout_web/assets/js/pages/address.js +++ b/apps/block_scout_web/assets/js/pages/address.js @@ -22,11 +22,10 @@ export const initialState = { transactionCount: null, validationCount: null, - transactions: [], internalTransactions: [], internalTransactionsBatch: [], - - beyondPageOne: null + validatedBlocks: [], + beyondPageOne: false } export function reducer (state = initialState, action) { @@ -80,19 +79,7 @@ export function reducer (state = initialState, action) { const transactionCount = (action.msg.fromAddressHash === state.addressHash) ? state.transactionCount + 1 : state.transactionCount - if (state.beyondPageOne || - (state.filter === 'to' && action.msg.toAddressHash !== state.addressHash) || - (state.filter === 'from' && action.msg.fromAddressHash !== state.addressHash)) { - return Object.assign({}, state, { transactionCount }) - } - - return Object.assign({}, state, { - transactions: [ - action.msg, - ...state.transactions - ], - transactionCount: transactionCount - }) + return Object.assign({}, state, { transactionCount }) } case 'RECEIVED_UPDATED_BALANCE': { return Object.assign({}, state, { @@ -139,32 +126,6 @@ const elements = { $el.empty().append(numeral(state.validationCount).format()) } }, - '[data-selector="empty-transactions-list"]': { - render ($el, state) { - if (state.transactions.length || state.loadingNextPage || state.pagingError) { - $el.hide() - } else { - $el.show() - } - } - }, - '[data-selector="transactions-list"]': { - load ($el) { - return { - transactions: $el.children().map((index, el) => ({ - transactionHash: el.dataset.transactionHash, - transactionHtml: el.outerHTML - })).toArray() - } - }, - render ($el, state, oldState) { - if (oldState.transactions === state.transactions) return - - const container = $el[0] - const newElements = _.map(state.transactions, ({ transactionHtml }) => $(transactionHtml)[0]) - return listMorph(container, newElements, { key: 'dataset.transactionHash' }) - } - }, '[data-selector="internal-transactions-list"]': { load ($el) { return { diff --git a/apps/block_scout_web/assets/js/pages/address/transactions.js b/apps/block_scout_web/assets/js/pages/address/transactions.js new file mode 100644 index 0000000000..5876b37874 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/transactions.js @@ -0,0 +1,73 @@ +import $ from 'jquery' +import _ from 'lodash' +import URI from 'urijs' +import humps from 'humps' +import socket from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load' + +export const initialState = { + addressHash: null, + channelDisconnected: false, + filter: null +} + +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 ] }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected) $el.show() + } + } +} + +if ($('[data-page="address-transactions"]').length) { + const store = createAsyncLoadStore(reducer, initialState, 'dataset.transactionHash') + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true)) + + connectElements({ store, elements }) + + store.dispatch({ + type: 'PAGE_LOAD', + addressHash, + filter, + beyondPageOne: !!blockNumber + }) + + const addressChannel = socket.channel(`addresses:${addressHash}`, {}) + addressChannel.join() + addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + addressChannel.on('transaction', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_TRANSACTION', + msg: humps.camelizeKeys(msg) + }) + }) +} diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex index 3c2d1230ad..4122e3375e 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex @@ -2,7 +2,7 @@ <%= render BlockScoutWeb.AddressView, "overview.html", assigns %> -
+
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> @@ -69,7 +69,7 @@ <%= gettext("Loading") %>...
-
+