diff --git a/apps/explorer_web/assets/__tests__/pages/transaction.js b/apps/explorer_web/assets/__tests__/pages/transaction.js index 55aa68f4b4..c7c2364cfd 100644 --- a/apps/explorer_web/assets/__tests__/pages/transaction.js +++ b/apps/explorer_web/assets/__tests__/pages/transaction.js @@ -12,3 +12,119 @@ test('RECEIVED_NEW_BLOCK', () => { expect(output.confirmations).toBe(4) }) + +describe('RECEIVED_NEW_TRANSACTION_BATCH', () => { + test('single transaction', () => { + const state = initialState + const action = { + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: [{ + transactionHtml: 'test' + }] + } + const output = reducer(state, action) + + expect(output.newTransactions).toEqual(['test']) + expect(output.batchCountAccumulator).toEqual(0) + }) + test('large batch of transactions', () => { + const state = initialState + const action = { + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: [{ + transactionHtml: 'test 1' + },{ + transactionHtml: 'test 2' + },{ + transactionHtml: 'test 3' + },{ + transactionHtml: 'test 4' + },{ + transactionHtml: 'test 5' + },{ + transactionHtml: 'test 6' + },{ + transactionHtml: 'test 7' + },{ + transactionHtml: 'test 8' + },{ + transactionHtml: 'test 9' + },{ + transactionHtml: 'test 10' + },{ + transactionHtml: 'test 11' + }] + } + const output = reducer(state, action) + + expect(output.newTransactions).toEqual([]) + expect(output.batchCountAccumulator).toEqual(11) + }) + test('single transaction after single transaction', () => { + const state = Object.assign({}, initialState, { + newTransactions: ['test 1'] + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: [{ + transactionHtml: 'test 2' + }] + } + const output = reducer(state, action) + + expect(output.newTransactions).toEqual(['test 1', 'test 2']) + expect(output.batchCountAccumulator).toEqual(0) + }) + test('single transaction after large batch of transactions', () => { + const state = Object.assign({}, initialState, { + newTransactions: [], + batchCountAccumulator: 11 + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: [{ + transactionHtml: 'test 12' + }] + } + const output = reducer(state, action) + + expect(output.newTransactions).toEqual([]) + expect(output.batchCountAccumulator).toEqual(12) + }) + test('large batch of transactions after large batch of transactions', () => { + const state = Object.assign({}, initialState, { + newTransactions: [], + batchCountAccumulator: 11 + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: [{ + transactionHtml: 'test 12' + },{ + transactionHtml: 'test 13' + },{ + transactionHtml: 'test 14' + },{ + transactionHtml: 'test 15' + },{ + transactionHtml: 'test 16' + },{ + transactionHtml: 'test 17' + },{ + transactionHtml: 'test 18' + },{ + transactionHtml: 'test 19' + },{ + transactionHtml: 'test 20' + },{ + transactionHtml: 'test 21' + },{ + transactionHtml: 'test 22' + }] + } + const output = reducer(state, action) + + expect(output.newTransactions).toEqual([]) + expect(output.batchCountAccumulator).toEqual(22) + }) +}) diff --git a/apps/explorer_web/assets/js/pages/chain.js b/apps/explorer_web/assets/js/pages/chain.js index 31d0297b3a..dbe1873731 100644 --- a/apps/explorer_web/assets/js/pages/chain.js +++ b/apps/explorer_web/assets/js/pages/chain.js @@ -11,7 +11,6 @@ const BATCH_THRESHOLD = 10 export const initialState = { batchCountAccumulator: 0, - channelDisconnected: false, newBlock: null, newTransactions: [] } diff --git a/apps/explorer_web/assets/js/pages/transaction.js b/apps/explorer_web/assets/js/pages/transaction.js index 53a4b88f76..7a9410c190 100644 --- a/apps/explorer_web/assets/js/pages/transaction.js +++ b/apps/explorer_web/assets/js/pages/transaction.js @@ -4,11 +4,16 @@ import numeral from 'numeral' import 'numeral/locales' import socket from '../socket' import router from '../router' -import { initRedux } from '../utils' +import { updateAllAges } from '../lib/from_now' +import { batchChannel, initRedux } from '../utils' + +const BATCH_THRESHOLD = 10 export const initialState = { + batchCountAccumulator: 0, blockNumber: null, - confirmations: null + confirmations: null, + newTransactions: [] } export function reducer (state = initialState, action) { @@ -25,6 +30,21 @@ export function reducer (state = initialState, action) { }) } else return state } + case 'RECEIVED_NEW_TRANSACTION_BATCH': { + debugger + if (!state.batchCountAccumulator && action.msgs.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + newTransactions: [ + ...state.newTransactions, + ...action.msgs.map(({transactionHtml}) => transactionHtml) + ] + }) + } else { + return Object.assign({}, state, { + batchCountAccumulator: state.batchCountAccumulator + action.msgs.length + }) + } + } default: return state } @@ -41,8 +61,41 @@ router.when('/transactions/:transactionHash').then(({ locale }) => initRedux(red }, render (state, oldState) { const $blockConfirmations = $('[data-selector="block-confirmations"]') + if (oldState.confirmations !== state.confirmations) { $blockConfirmations.empty().append(numeral(state.confirmations).format()) } } })) + +router.when('/transactions', { exactPathMatch: true }).then(({ locale }) => initRedux(reducer, { + main (store) { + const transactionsChannel = socket.channel(`transactions:new_transaction`) + transactionsChannel.join() + transactionsChannel.on('new_transaction', batchChannel((msgs) => + store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs: humps.camelizeKeys(msgs) })) + ) + }, + render (state, oldState) { + const $channelBatching = $('[data-selector="channel-batching-message"]') + const $channelBatchingCount = $('[data-selector="channel-batching-count"]') + const $transactionsList = $('[data-selector="transactions-list"]') + + if (state.batchCountAccumulator) { + $channelBatching.show() + $channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format() + } else { + $channelBatching.hide() + } + if (oldState.newTransactions !== state.newTransactions) { + const newTransactionsToInsert = state.newTransactions.slice(oldState.newTransactions.length) + $transactionsList + .children() + .slice($transactionsList.children().length - newTransactionsToInsert.length, $transactionsList.children().length) + .remove() + $transactionsList.prepend(newTransactionsToInsert.reverse().join('')) + + updateAllAges() + } + } +})) diff --git a/apps/explorer_web/lib/explorer_web/channels/transaction_channel.ex b/apps/explorer_web/lib/explorer_web/channels/transaction_channel.ex index 1bc8883898..516170a185 100644 --- a/apps/explorer_web/lib/explorer_web/channels/transaction_channel.ex +++ b/apps/explorer_web/lib/explorer_web/channels/transaction_channel.ex @@ -4,7 +4,7 @@ defmodule ExplorerWeb.TransactionChannel do """ use ExplorerWeb, :channel - alias ExplorerWeb.ChainView + alias ExplorerWeb.{ChainView, TransactionView} alias Phoenix.View intercept(["new_transaction"]) @@ -24,8 +24,17 @@ defmodule ExplorerWeb.TransactionChannel do transaction: transaction ) + rendered_transaction = + View.render_to_string( + TransactionView, + "_transaction.html", + locale: socket.assigns.locale, + transaction: transaction + ) + push(socket, "new_transaction", %{ - homepage_transaction_html: rendered_homepage_transaction + homepage_transaction_html: rendered_homepage_transaction, + transaction_html: rendered_transaction }) {:noreply, socket} diff --git a/apps/explorer_web/lib/explorer_web/templates/transaction/_transaction.html.eex b/apps/explorer_web/lib/explorer_web/templates/transaction/_transaction.html.eex new file mode 100644 index 0000000000..a358702df2 --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/templates/transaction/_transaction.html.eex @@ -0,0 +1,33 @@ +
<%= gettext("Showing %{count} Validated Transactions", count: Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###")) %>
+ + <%= for transaction <- @transactions do %> + <%= render ExplorerWeb.TransactionView, "_transaction.html", locale: @locale, transaction: transaction %> + <% end %> + - <%= for transaction <- @transactions do %> -