diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index fc8a228664..d81dfc308c 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -587,9 +587,9 @@ defmodule Explorer.Chain do iex> [%Transaction{hash: hash1}, %Transaction{hash: hash2}] = insert_list(2, :transaction) iex> [%Explorer.Chain.Transaction{hash: found_hash1}, %Explorer.Chain.Transaction{hash: found_hash2}] = ...> Explorer.Chain.hashes_to_transactions([hash1, hash2]) - iex> found_hash1 == hash1 + iex> found_hash1 in [hash1, hash2] true - iex> found_hash2 == hash2 + iex> found_hash2 in [hash1, hash2] true Returns `[]` if not found diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 6dad44b353..a8af458790 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -529,11 +529,9 @@ defmodule Explorer.ChainTest do necessity_by_association: %{block: :required} ) - assert [%Transaction{hash: ^hash_without_index1}, %Transaction{hash: ^hash_without_index2}] = - Chain.hashes_to_transactions( - [hash_without_index1, hash_without_index2], - necessity_by_association: %{block: :optional} - ) + assert [hash_without_index1, hash_without_index2] + |> Chain.hashes_to_transactions(necessity_by_association: %{block: :optional}) + |> Enum.all?(&(&1.hash in [hash_without_index1, hash_without_index2])) end end diff --git a/apps/explorer_web/assets/__tests__/pages/address.js b/apps/explorer_web/assets/__tests__/pages/address.js index c4a55398df..78bc3b84dd 100644 --- a/apps/explorer_web/assets/__tests__/pages/address.js +++ b/apps/explorer_web/assets/__tests__/pages/address.js @@ -100,6 +100,7 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => { expect(output.newTransactions).toEqual(['test']) expect(output.batchCountAccumulator).toEqual(0) + expect(output.transactionCount).toEqual(1) }) test('large batch of transactions', () => { const state = initialState @@ -133,6 +134,7 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => { expect(output.newTransactions).toEqual([]) expect(output.batchCountAccumulator).toEqual(11) + expect(output.transactionCount).toEqual(11) }) test('single transaction after single transaction', () => { const state = Object.assign({}, initialState, { diff --git a/apps/explorer_web/assets/__tests__/pages/chain.js b/apps/explorer_web/assets/__tests__/pages/chain.js index c33e3bbc0d..86c84ad572 100644 --- a/apps/explorer_web/assets/__tests__/pages/chain.js +++ b/apps/explorer_web/assets/__tests__/pages/chain.js @@ -1,19 +1,9 @@ import { reducer, initialState } from '../../js/pages/chain' -test('CHANNEL_DISCONNECTED', () => { - const state = initialState - const action = { - type: 'CHANNEL_DISCONNECTED' - } - const output = reducer(state, action) - - expect(output.channelDisconnected).toBe(true) -}) - test('RECEIVED_NEW_BLOCK', () => { - const state = Object.assign({}, initialState, { - newBlock: 'last new block' - }) + const state = Object.assign({}, initialState, { + newBlock: 'last new block' + }) const action = { type: 'RECEIVED_NEW_BLOCK', msg: { @@ -24,3 +14,121 @@ test('RECEIVED_NEW_BLOCK', () => { expect(output.newBlock).toEqual('new block') }) + +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) + expect(output.transactionCount).toEqual(1) + }) + 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) + expect(output.transactionCount).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/__tests__/pages/transaction.js b/apps/explorer_web/assets/__tests__/pages/transaction.js index ec380fbe01..20da637dae 100644 --- a/apps/explorer_web/assets/__tests__/pages/transaction.js +++ b/apps/explorer_web/assets/__tests__/pages/transaction.js @@ -1,9 +1,9 @@ import { reducer, initialState } from '../../js/pages/transaction' -test('RECEIVED_UPDATED_CONFIRMATIONS', () => { +test('RECEIVED_NEW_BLOCK', () => { const state = { ...initialState, blockNumber: 1 } const action = { - type: 'RECEIVED_UPDATED_CONFIRMATIONS', + type: 'RECEIVED_NEW_BLOCK', msg: { blockNumber: 5 } @@ -12,3 +12,151 @@ test('RECEIVED_UPDATED_CONFIRMATIONS', () => { 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) + expect(output.transactionCount).toEqual(1) + }) + 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) + expect(output.transactionCount).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) + }) + test('after disconnection', () => { + const state = Object.assign({}, initialState, { + channelDisconnected: true + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: [{ + transactionHtml: 'test' + }] + } + const output = reducer(state, action) + + expect(output.newTransactions).toEqual([]) + expect(output.batchCountAccumulator).toEqual(0) + }) + test('on page 2+', () => { + const state = Object.assign({}, initialState, { + beyondPageOne: true + }) + const action = { + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: [{ + transactionHtml: 'test' + }] + } + const output = reducer(state, action) + + expect(output.newTransactions).toEqual([]) + expect(output.batchCountAccumulator).toEqual(0) + }) +}) diff --git a/apps/explorer_web/assets/css/components/_animations.scss b/apps/explorer_web/assets/css/components/_animations.scss index d01ac6579d..22cd004011 100644 --- a/apps/explorer_web/assets/css/components/_animations.scss +++ b/apps/explorer_web/assets/css/components/_animations.scss @@ -3,7 +3,7 @@ 100% {opacity: 1;} } -@keyframes fade-up { +@keyframes fade-up-blocks-homepage { 0% { flex-basis: 0%; width: 0%; @@ -23,13 +23,36 @@ } } +@keyframes fade-up { + 0% { + height: 0; + opacity: 0; + } + 25% { + opacity: 0; + transform: translateY(10px) scale(0.97); + } + 50% { + height: 98px; + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + } +} + .fade-in { animation: fade-in 1s ease-out forwards; } +.fade-up-blocks-homepage { + will-change: transform, opacity, width; + max-height: 98px; + animation: fade-up-blocks-homepage 0.6s cubic-bezier(0.455, 0.03, 0.515, 0.955); +} .fade-up { - will-change: transform, opacity, width; + will-change: transform, opacity, height; max-height: 98px; - animation: fade-up 0.55s cubic-bezier(0.455, 0.03, 0.515, 0.955); + animation: fade-up 0.6s cubic-bezier(0.455, 0.03, 0.515, 0.955); } diff --git a/apps/explorer_web/assets/js/app.js b/apps/explorer_web/assets/js/app.js index 0fbcfcbd28..7066c283c9 100644 --- a/apps/explorer_web/assets/js/app.js +++ b/apps/explorer_web/assets/js/app.js @@ -26,5 +26,6 @@ import './lib/tooltip' import './lib/smart_contract/read_function' import './pages/address' +import './pages/block' import './pages/chain' import './pages/transaction' diff --git a/apps/explorer_web/assets/js/pages/address.js b/apps/explorer_web/assets/js/pages/address.js index 3ee81ebd7f..37057903ae 100644 --- a/apps/explorer_web/assets/js/pages/address.js +++ b/apps/explorer_web/assets/js/pages/address.js @@ -5,6 +5,7 @@ import 'numeral/locales' import socket from '../socket' import router from '../router' import { batchChannel, initRedux } from '../utils' +import { updateAllAges } from '../lib/from_now' const BATCH_THRESHOLD = 10 @@ -108,6 +109,7 @@ router.when('/addresses/:addressHash').then((params) => initRedux(reducer, { } if (oldState.newTransactions !== state.newTransactions && $transactionsList.length) { $transactionsList.prepend(state.newTransactions.slice(oldState.newTransactions.length).reverse().join('')) + updateAllAges() } } })) diff --git a/apps/explorer_web/assets/js/pages/block.js b/apps/explorer_web/assets/js/pages/block.js new file mode 100644 index 0000000000..2c32d9ffae --- /dev/null +++ b/apps/explorer_web/assets/js/pages/block.js @@ -0,0 +1,60 @@ +import $ from 'jquery' +import humps from 'humps' +import socket from '../socket' +import router from '../router' +import { updateAllAges } from '../lib/from_now' +import { initRedux } from '../utils' + +export const initialState = { + beyondPageOne: null, + channelDisconnected: false, + newBlock: null +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'PAGE_LOAD': { + return Object.assign({}, state, { + beyondPageOne: !!action.blockNumber + }) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + channelDisconnected: true + }) + } + case 'RECEIVED_NEW_BLOCK': { + if (state.channelDisconnected || state.beyondPageOne) return state + + return Object.assign({}, state, { + newBlock: action.msg.blockHtml + }) + } + default: + return state + } +} + +router.when('/blocks', { exactPathMatch: true }).then(({ blockNumber }) => initRedux(reducer, { + main (store) { + const blocksChannel = socket.channel(`blocks:new_block`, {}) + store.dispatch({ type: 'PAGE_LOAD', blockNumber }) + blocksChannel.join() + blocksChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + blocksChannel.on('new_block', (msg) => + store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }) + ) + }, + render (state, oldState) { + const $channelDisconnected = $('[data-selector="channel-disconnected-message"]') + const $blocksList = $('[data-selector="blocks-list"]') + + if (state.channelDisconnected) $channelDisconnected.show() + if (oldState.newBlock !== state.newBlock) { + $blocksList.prepend(state.newBlock) + updateAllAges() + } + } +})) diff --git a/apps/explorer_web/assets/js/pages/chain.js b/apps/explorer_web/assets/js/pages/chain.js index 3a6d4f7fe1..20b9772fe8 100644 --- a/apps/explorer_web/assets/js/pages/chain.js +++ b/apps/explorer_web/assets/js/pages/chain.js @@ -1,46 +1,99 @@ import $ from 'jquery' import humps from 'humps' +import numeral from 'numeral' +import 'numeral/locales' import router from '../router' import socket from '../socket' import { updateAllAges } from '../lib/from_now' -import { initRedux } from '../utils' +import { batchChannel, initRedux } from '../utils' + +const BATCH_THRESHOLD = 10 export const initialState = { + batchCountAccumulator: 0, newBlock: null, - channelDisconnected: false + newTransactions: [], + transactionCount: null } export function reducer (state = initialState, action) { switch (action.type) { - case 'CHANNEL_DISCONNECTED': { + case 'PAGE_LOAD': { return Object.assign({}, state, { - channelDisconnected: true + transactionCount: numeral(action.transactionCount).value() }) } case 'RECEIVED_NEW_BLOCK': { return Object.assign({}, state, { - newBlock: humps.camelizeKeys(action.msg).homepageBlockHtml + newBlock: action.msg.homepageBlockHtml }) } + case 'RECEIVED_NEW_TRANSACTION_BATCH': { + if (!state.batchCountAccumulator && action.msgs.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + newTransactions: [ + ...state.newTransactions, + ...action.msgs.map(({transactionHtml}) => transactionHtml) + ], + transactionCount: state.transactionCount + action.msgs.length + }) + } else { + return Object.assign({}, state, { + batchCountAccumulator: state.batchCountAccumulator + action.msgs.length, + transactionCount: state.transactionCount + action.msgs.length + }) + } + } default: return state } } -router.when('', { exactPathMatch: true }).then(() => initRedux(reducer, { +router.when('', { exactPathMatch: true }).then(({ locale }) => initRedux(reducer, { main (store) { const blocksChannel = socket.channel(`blocks:new_block`) + numeral.locale(locale) + store.dispatch({ + type: 'PAGE_LOAD', + transactionCount: $('[data-selector="transaction-count"]').text() + }) blocksChannel.join() - blocksChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) - blocksChannel.on('new_block', msg => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg })) + blocksChannel.on('new_block', msg => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) })) + + 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 $blockList = $('[data-selector="chain-block-list"]') + const $channelBatching = $('[data-selector="channel-batching-message"]') + const $channelBatchingCount = $('[data-selector="channel-batching-count"]') + const $transactionsList = $('[data-selector="transactions-list"]') + const $transactionCount = $('[data-selector="transaction-count"]') if (oldState.newBlock !== state.newBlock) { $blockList.children().last().remove() $blockList.prepend(state.newBlock) updateAllAges() } + if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format()) + 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/assets/js/pages/transaction.js b/apps/explorer_web/assets/js/pages/transaction.js index 3cbe9ebb55..02a178d2ce 100644 --- a/apps/explorer_web/assets/js/pages/transaction.js +++ b/apps/explorer_web/assets/js/pages/transaction.js @@ -4,27 +4,63 @@ 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, + beyondPageOne: null, blockNumber: null, - confirmations: null + channelDisconnected: false, + confirmations: null, + newTransactions: [], + transactionCount: null } export function reducer (state = initialState, action) { switch (action.type) { case 'PAGE_LOAD': { return Object.assign({}, state, { - blockNumber: parseInt(action.blockNumber, 10) + beyondPageOne: !!action.index, + blockNumber: parseInt(action.blockNumber, 10), + transactionCount: numeral(action.transactionCount).value() + }) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + channelDisconnected: true, + batchCountAccumulator: 0 }) } - case 'RECEIVED_UPDATED_CONFIRMATIONS': { + case 'RECEIVED_NEW_BLOCK': { if ((action.msg.blockNumber - state.blockNumber) > state.confirmations) { return Object.assign({}, state, { confirmations: action.msg.blockNumber - state.blockNumber }) } else return state } + case 'RECEIVED_NEW_TRANSACTION_BATCH': { + if (state.channelDisconnected || state.beyondPageOne) return state + + if (!state.batchCountAccumulator && action.msgs.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + newTransactions: [ + ...state.newTransactions, + ...action.msgs.map(({transactionHtml}) => transactionHtml) + ], + transactionCount: state.transactionCount + action.msgs.length + }) + } else { + return Object.assign({}, state, { + batchCountAccumulator: state.batchCountAccumulator + action.msgs.length, + transactionCount: state.transactionCount + action.msgs.length + }) + } + } default: return state } @@ -32,17 +68,65 @@ export function reducer (state = initialState, action) { router.when('/transactions/:transactionHash').then(({ locale }) => initRedux(reducer, { main (store) { - const channel = socket.channel(`transactions:confirmations`, {}) + const blocksChannel = socket.channel(`blocks:new_block`, {}) const $transactionBlockNumber = $('[data-selector="block-number"]') numeral.locale(locale) - store.dispatch({ type: 'PAGE_LOAD', blockNumber: $transactionBlockNumber.text() }) - channel.join() - channel.on('update', (msg) => store.dispatch({ type: 'RECEIVED_UPDATED_CONFIRMATIONS', msg: humps.camelizeKeys(msg) })) + store.dispatch({ + type: 'PAGE_LOAD', + blockNumber: $transactionBlockNumber.text() + }) + blocksChannel.join() + blocksChannel.on('new_block', (msg) => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) })) }, 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((params) => initRedux(reducer, { + main (store) { + const { locale, index } = params + const transactionsChannel = socket.channel(`transactions:new_transaction`) + numeral.locale(locale) + store.dispatch({ + type: 'PAGE_LOAD', + transactionCount: $('[data-selector="transaction-count"]').text(), + index + }) + transactionsChannel.join() + transactionsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + 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 $channelDisconnected = $('[data-selector="channel-disconnected-message"]') + const $transactionsList = $('[data-selector="transactions-list"]') + const $transactionCount = $('[data-selector="transaction-count"]') + + if (state.channelDisconnected) $channelDisconnected.show() + if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format()) + 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/block_channel.ex b/apps/explorer_web/lib/explorer_web/channels/block_channel.ex index de32f4fba8..24ca952827 100644 --- a/apps/explorer_web/lib/explorer_web/channels/block_channel.ex +++ b/apps/explorer_web/lib/explorer_web/channels/block_channel.ex @@ -4,7 +4,7 @@ defmodule ExplorerWeb.BlockChannel do """ use ExplorerWeb, :channel - alias ExplorerWeb.ChainView + alias ExplorerWeb.{BlockView, ChainView} alias Phoenix.View intercept(["new_block"]) @@ -16,6 +16,14 @@ defmodule ExplorerWeb.BlockChannel do def handle_out("new_block", %{block: block}, socket) do Gettext.put_locale(ExplorerWeb.Gettext, socket.assigns.locale) + rendered_block = + View.render_to_string( + BlockView, + "_tile.html", + locale: socket.assigns.locale, + block: block + ) + rendered_homepage_block = View.render_to_string( ChainView, @@ -25,7 +33,9 @@ defmodule ExplorerWeb.BlockChannel do ) push(socket, "new_block", %{ - homepage_block_html: rendered_homepage_block + homepage_block_html: rendered_homepage_block, + block_html: rendered_block, + blockNumber: block.number }) {:noreply, socket} 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 75bd4f3f96..dfa8900302 100644 --- a/apps/explorer_web/lib/explorer_web/channels/transaction_channel.ex +++ b/apps/explorer_web/lib/explorer_web/channels/transaction_channel.ex @@ -1,10 +1,33 @@ defmodule ExplorerWeb.TransactionChannel do @moduledoc """ - Establishes pub/sub channel for transaction page live updates. + Establishes pub/sub channel for live updates of transaction events. """ use ExplorerWeb, :channel - def join("transactions:confirmations", _params, socket) do + alias ExplorerWeb.TransactionView + alias Phoenix.View + + intercept(["new_transaction"]) + + def join("transactions:new_transaction", _params, socket) do {:ok, %{}, socket} end + + def handle_out("new_transaction", %{transaction: transaction}, socket) do + Gettext.put_locale(ExplorerWeb.Gettext, socket.assigns.locale) + + rendered_transaction = + View.render_to_string( + TransactionView, + "_tile.html", + locale: socket.assigns.locale, + transaction: transaction + ) + + push(socket, "new_transaction", %{ + transaction_html: rendered_transaction + }) + + {:noreply, socket} + end end diff --git a/apps/explorer_web/lib/explorer_web/notifier.ex b/apps/explorer_web/lib/explorer_web/notifier.ex index 88617f2739..81ea019d59 100644 --- a/apps/explorer_web/lib/explorer_web/notifier.ex +++ b/apps/explorer_web/lib/explorer_web/notifier.ex @@ -15,9 +15,6 @@ defmodule ExplorerWeb.Notifier do end def handle_event({:chain_event, :blocks, blocks}) do - max_numbered_block = Enum.max_by(blocks, & &1.number).number - Endpoint.broadcast("transactions:confirmations", "update", %{block_number: max_numbered_block}) - Enum.each(blocks, &broadcast_block/1) end @@ -47,6 +44,10 @@ defmodule ExplorerWeb.Notifier do end defp broadcast_transaction(transaction) do + Endpoint.broadcast("transactions:new_transaction", "new_transaction", %{ + transaction: transaction + }) + Endpoint.broadcast("addresses:#{transaction.from_address_hash}", "transaction", %{ address: transaction.from_address, transaction: transaction diff --git a/apps/explorer_web/lib/explorer_web/templates/address/overview.html.eex b/apps/explorer_web/lib/explorer_web/templates/address/overview.html.eex index e681600ad3..57bb132f7c 100644 --- a/apps/explorer_web/lib/explorer_web/templates/address/overview.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/address/overview.html.eex @@ -18,7 +18,7 @@

<%= address_title(@address) %> Details

<%= @address %>

- <%= Cldr.Number.to_string!(@transaction_count) %> <%= gettext "Transactions" %> + <%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %> <%= gettext "Transactions" %> <%= if contract?(@address) do %> diff --git a/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex index 571dcf2b24..409f4500ca 100644 --- a/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex @@ -81,7 +81,7 @@
diff --git a/apps/explorer_web/lib/explorer_web/templates/block/_tile.html.eex b/apps/explorer_web/lib/explorer_web/templates/block/_tile.html.eex new file mode 100644 index 0000000000..e12e553188 --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/templates/block/_tile.html.eex @@ -0,0 +1,48 @@ +
+
+
+ + <%= link( + @block, + class: "tile-title", + to: block_path(ExplorerWeb.Endpoint, :show, @locale, @block), + "data-test": "block_number", + "data-block-number": to_string(@block.number) + ) %> +
+ + + <%= ngettext("%{count} transaction", "%{count} transactions", Enum.count(@block.transactions)) %> + + + <%= Cldr.Unit.new(:byte, @block.size) |> Cldr.Unit.to_string! %> + + +
+
+ + <%= gettext "Miner" %> + + <%= link to: address_path(ExplorerWeb.Endpoint, :show, @locale, @block.miner_hash) do %> + <%= @block.miner_hash %> + <% end %> + +
+
+
+ +
+ <%= formatted_gas(@block.gas_used) %> + (<%= formatted_gas(@block.gas_used / @block.gas_limit, format: "#.#%") %>) + <%= gettext "Gas Used" %> +
+
+
;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"> + +
+
+ + <%= formatted_gas(@block.gas_limit) %> <%= gettext "Gas Limit" %> +
+
+
diff --git a/apps/explorer_web/lib/explorer_web/templates/block/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/block/index.html.eex index 54b43bec24..d5edf5020a 100644 --- a/apps/explorer_web/lib/explorer_web/templates/block/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/block/index.html.eex @@ -3,64 +3,12 @@

Blocks

-

- <%= gettext( - "Showing #%{start_block} to #%{end_block}", - start_block: List.first(@blocks).number, - end_block: List.last(@blocks).number - ) %> -

- <%= for block <- @blocks do %> -
-
-
- - <%= link( - block, - class: "tile-title", - to: block_path(@conn, :show, @conn.assigns.locale, block), - "data-test": "block_number", - "data-block-number": to_string(block.number) - ) %> -
- - - <%= ngettext("%{count} transaction", "%{count} transactions", Enum.count(block.transactions)) %> - - - <%= Cldr.Unit.new(:byte, block.size) |> Cldr.Unit.to_string! %> - - -
-
- - <%= gettext "Miner" %> - - <%= link to: address_path(ExplorerWeb.Endpoint, :show, @locale, block.miner_hash) do %> - <%= block.miner_hash %> - <% end %> - -
-
-
- -
- <%= formatted_gas(block.gas_used) %> - (<%= formatted_gas(block.gas_used / block.gas_limit, format: "#.#%") %>) - <%= gettext "Gas Used" %> -
-
-
;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"> - -
-
- - <%= formatted_gas(block.gas_limit) %> <%= gettext "Gas Limit" %> -
-
-
- <% end %> + + <%= for block <- @blocks do %> + <%= render ExplorerWeb.BlockView, "_tile.html", locale: @locale, block: block %> + <% end %> + <%= if @next_page_params do %> <%= link( diff --git a/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex index 2079e5bdc0..ef4b70f9ca 100644 --- a/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex @@ -36,7 +36,7 @@

<%= gettext "Transactions" %>

<%= for transaction <- @transactions do %> - <%= render "_transaction.html", locale: @locale, transaction: transaction %> + <%= render ExplorerWeb.TransactionView, "_tile.html", locale: @locale, transaction: transaction %> <% end %> <% else %> diff --git a/apps/explorer_web/lib/explorer_web/templates/chain/_block.html.eex b/apps/explorer_web/lib/explorer_web/templates/chain/_block.html.eex index ee9ba26f85..88cbe9d301 100644 --- a/apps/explorer_web/lib/explorer_web/templates/chain/_block.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/chain/_block.html.eex @@ -1,4 +1,4 @@ -
+
<%= link(@block, to: block_path(ExplorerWeb.Endpoint, :show, @locale, @block), class: "tile-title") %>
diff --git a/apps/explorer_web/lib/explorer_web/templates/chain/_transactions.html.eex b/apps/explorer_web/lib/explorer_web/templates/chain/_transactions.html.eex deleted file mode 100644 index bceaeffa1f..0000000000 --- a/apps/explorer_web/lib/explorer_web/templates/chain/_transactions.html.eex +++ /dev/null @@ -1,42 +0,0 @@ -
-
- <%= link(gettext("View All Transactions →"), to: transaction_path(@conn, :index, Gettext.get_locale), class: "button button--secondary button--xsmall float-right") %> -

<%= gettext "Transactions" %>

- <%= for transaction <- @chain.transactions do %> -
-
-
-
- <%= ExplorerWeb.TransactionView.transaction_display_type(transaction) %> -
<%= ExplorerWeb.TransactionView.formatted_status(transaction) %>
-
-
-
- <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: transaction.hash %> - - <%= render ExplorerWeb.AddressView, "_link.html", address_hash: transaction.from_address_hash, contract: ExplorerWeb.AddressView.contract?(transaction.from_address), locale: @locale %> - → - <%= render ExplorerWeb.AddressView, "_link.html", address_hash: ExplorerWeb.TransactionView.to_address_hash(transaction), contract: ExplorerWeb.AddressView.contract?(transaction.to_address), locale: @locale %> - - - - - <%= link( - gettext("Block #%{number}", number: to_string(transaction.block.number)), - to: block_path(@conn, :show, @conn.assigns.locale, transaction.block) - ) %> - - -
-
- - <%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %> <%= gettext "Ether" %> - - <%= ExplorerWeb.TransactionView.formatted_fee(transaction, denomination: :ether) %> <%= gettext "Fee" %> -
-
-
- <% end %> - -
-
diff --git a/apps/explorer_web/lib/explorer_web/templates/chain/show.html.eex b/apps/explorer_web/lib/explorer_web/templates/chain/show.html.eex index 88cfca0ecf..15ee6be8ae 100644 --- a/apps/explorer_web/lib/explorer_web/templates/chain/show.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/chain/show.html.eex @@ -35,8 +35,8 @@ <%= gettext "Total transactions" %> - - <%= @transaction_estimated_count |> Cldr.Number.to_string!(format: "#,###") %> + + <%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %>
@@ -54,7 +54,7 @@
- <%= link(gettext("View All Blocks →"), to: block_path(@conn, :index, Gettext.get_locale), class: "button button--secondary button--xsmall float-right") %> + <%= link(gettext("View All Blocks →"), to: block_path(ExplorerWeb.Endpoint, :index, Gettext.get_locale), class: "button button--secondary button--xsmall float-right") %>

<%= gettext "Blocks" %>

<%= for block <- @chain.blocks do %> @@ -63,5 +63,21 @@
- <%= render ExplorerWeb.ChainView, "_transactions.html", assigns %> + +
+
+ + <%= link(gettext("View All Transactions →"), to: transaction_path(ExplorerWeb.Endpoint, :index, Gettext.get_locale), class: "button button--secondary button--xsmall float-right") %> +

<%= gettext "Transactions" %>

+ + <%= for transaction <- @chain.transactions do %> + <%= render ExplorerWeb.TransactionView, "_tile.html", locale: @locale, transaction: transaction %> + <% end %> + +
+
diff --git a/apps/explorer_web/lib/explorer_web/templates/block_transaction/_transaction.html.eex b/apps/explorer_web/lib/explorer_web/templates/transaction/_tile.html.eex similarity index 64% rename from apps/explorer_web/lib/explorer_web/templates/block_transaction/_transaction.html.eex rename to apps/explorer_web/lib/explorer_web/templates/transaction/_tile.html.eex index e376e109d1..c2b5dd41aa 100644 --- a/apps/explorer_web/lib/explorer_web/templates/block_transaction/_transaction.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/transaction/_tile.html.eex @@ -1,22 +1,15 @@ -
-
+
+
- - <%= ExplorerWeb.TransactionView.transaction_display_type(@transaction) %> - -
- <%= ExplorerWeb.TransactionView.formatted_status(@transaction) %> -
+ <%= ExplorerWeb.TransactionView.transaction_display_type(@transaction) %> +
<%= ExplorerWeb.TransactionView.formatted_status(@transaction) %>
- <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: @transaction.hash %> + <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: @transaction.hash %> <%= render ExplorerWeb.AddressView, "_link.html", address_hash: @transaction.from_address_hash, contract: ExplorerWeb.AddressView.contract?(@transaction.from_address), locale: @locale %> - → - <%= render ExplorerWeb.AddressView, "_link.html", address_hash: ExplorerWeb.TransactionView.to_address_hash(@transaction), contract: ExplorerWeb.AddressView.contract?(@transaction.to_address), locale: @locale %> - @@ -28,11 +21,11 @@
-
+
<%= ExplorerWeb.TransactionView.value(@transaction, include_label: false) %> <%= gettext "Ether" %> - <%= ExplorerWeb.TransactionView.formatted_fee(@transaction, denomination: :ether) %> <%= gettext "Fee" %> + <%= ExplorerWeb.TransactionView.formatted_fee(@transaction, denomination: :ether) %> <%= gettext "Fee" %>
diff --git a/apps/explorer_web/lib/explorer_web/templates/transaction/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/transaction/index.html.eex index eff3762f30..d406397c97 100644 --- a/apps/explorer_web/lib/explorer_web/templates/transaction/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/transaction/index.html.eex @@ -43,42 +43,24 @@
+ +

<%= gettext "Transactions" %>

-

<%= gettext("Showing %{count} Validated Transactions", count: Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###")) %>

+

<%= gettext("Showing") %> <%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %> <%= gettext("Validated Transactions") %>

+ + <%= for transaction <- @transactions do %> + <%= render ExplorerWeb.TransactionView, "_tile.html", locale: @locale, transaction: transaction %> + <% end %> + - <%= for transaction <- @transactions do %> -
-
-
-
- <%= ExplorerWeb.TransactionView.transaction_display_type(transaction) %> -
<%= ExplorerWeb.TransactionView.formatted_status(transaction) %>
-
-
-
- <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: transaction.hash %> - - <%= render ExplorerWeb.AddressView, "_link.html", address_hash: transaction.from_address_hash, contract: ExplorerWeb.AddressView.contract?(transaction.from_address), locale: @locale %> - → - <%= render ExplorerWeb.AddressView, "_link.html", address_hash: ExplorerWeb.TransactionView.to_address_hash(transaction), contract: ExplorerWeb.AddressView.contract?(transaction.to_address), locale: @locale %> - - - - - <%= link( - gettext("Block #%{number}", number: to_string(transaction.block.number)), - to: block_path(@conn, :show, @conn.assigns.locale, transaction.block) - ) %> - - -
-
- <%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %> <%= gettext "Ether" %> - <%= ExplorerWeb.TransactionView.formatted_fee(transaction, denomination: :ether) %> <%= gettext "Fee" %> -
-
-
- <% end %> <%= if @next_page_params do %> <%= link( gettext("Older"), diff --git a/apps/explorer_web/priv/gettext/default.pot b/apps/explorer_web/priv/gettext/default.pot index 3d0d13cfde..bc1573317f 100644 --- a/apps/explorer_web/priv/gettext/default.pot +++ b/apps/explorer_web/priv/gettext/default.pot @@ -15,7 +15,7 @@ msgstr "" msgid "Copyright %{year} POA" msgstr "" -#: lib/explorer_web/templates/block/index.html.eex:51 +#: lib/explorer_web/templates/block/_tile.html.eex:37 #: lib/explorer_web/templates/block/overview.html.eex:84 msgid "Gas Used" msgstr "" @@ -43,10 +43,10 @@ msgstr "" #: lib/explorer_web/templates/block_transaction/index.html.eex:13 #: lib/explorer_web/templates/block_transaction/index.html.eex:26 #: lib/explorer_web/templates/block_transaction/index.html.eex:36 -#: lib/explorer_web/templates/chain/_transactions.html.eex:4 +#: lib/explorer_web/templates/chain/show.html.eex:75 #: lib/explorer_web/templates/layout/_topnav.html.eex:18 #: lib/explorer_web/templates/pending_transaction/index.html.eex:46 -#: lib/explorer_web/templates/transaction/index.html.eex:46 +#: lib/explorer_web/templates/transaction/index.html.eex:56 msgid "Transactions" msgstr "" @@ -62,12 +62,12 @@ msgstr "" msgid "Difficulty" msgstr "" -#: lib/explorer_web/templates/block/index.html.eex:59 +#: lib/explorer_web/templates/block/_tile.html.eex:45 #: lib/explorer_web/templates/block/overview.html.eex:92 msgid "Gas Limit" msgstr "" -#: lib/explorer_web/templates/block/index.html.eex:38 +#: lib/explorer_web/templates/block/_tile.html.eex:24 #: lib/explorer_web/templates/block/overview.html.eex:70 #: lib/explorer_web/templates/chain/_block.html.eex:9 msgid "Miner" @@ -285,10 +285,8 @@ msgstr "" #: #: lib/explorer_web/templates/address_internal_transaction/_internal_transaction.html.eex:29 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:60 -#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:33 -#: lib/explorer_web/templates/chain/_transactions.html.eex:33 #: lib/explorer_web/templates/pending_transaction/index.html.eex:71 -#: lib/explorer_web/templates/transaction/index.html.eex:76 +#: lib/explorer_web/templates/transaction/_tile.html.eex:26 #: lib/explorer_web/templates/transaction/overview.html.eex:81 #: lib/explorer_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16 #: lib/explorer_web/views/wei_helpers.ex:71 @@ -355,10 +353,8 @@ msgid "All" msgstr "" #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:62 -#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:35 -#: lib/explorer_web/templates/chain/_transactions.html.eex:35 #: lib/explorer_web/templates/pending_transaction/index.html.eex:72 -#: lib/explorer_web/templates/transaction/index.html.eex:77 +#: lib/explorer_web/templates/transaction/_tile.html.eex:28 msgid "Fee" msgstr "" @@ -469,10 +465,10 @@ msgstr "" #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:133 #: lib/explorer_web/templates/address_transaction/index.html.eex:146 -#: lib/explorer_web/templates/block/index.html.eex:67 +#: lib/explorer_web/templates/block/index.html.eex:15 #: lib/explorer_web/templates/block_transaction/index.html.eex:51 #: lib/explorer_web/templates/pending_transaction/index.html.eex:79 -#: lib/explorer_web/templates/transaction/index.html.eex:84 +#: lib/explorer_web/templates/transaction/index.html.eex:66 msgid "Older" msgstr "" @@ -547,7 +543,7 @@ msgid "View All Blocks →" msgstr "" #, elixir-format -#: lib/explorer_web/templates/chain/_transactions.html.eex:3 +#: lib/explorer_web/templates/chain/show.html.eex:74 msgid "View All Transactions →" msgstr "" @@ -568,6 +564,7 @@ msgstr "" #, elixir-format #: lib/explorer_web/templates/address_transaction/index.html.eex:89 +#: lib/explorer_web/templates/transaction/index.html.eex:53 msgid "Connection Lost, click to load newer transactions" msgstr "" @@ -583,11 +580,6 @@ msgstr "" msgid "Internal Transaction" msgstr "" -#, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:84 -msgid "More messages have come in" -msgstr "" - #, elixir-format #: lib/explorer_web/templates/address/overview.html.eex:13 #: lib/explorer_web/templates/address/overview.html.eex:66 @@ -629,9 +621,7 @@ msgstr "" #, elixir-format #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:49 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:111 -#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:25 -#: lib/explorer_web/templates/chain/_transactions.html.eex:25 -#: lib/explorer_web/templates/transaction/index.html.eex:69 +#: lib/explorer_web/templates/transaction/_tile.html.eex:18 msgid "Block #%{number}" msgstr "" @@ -725,8 +715,25 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/explorer_web/templates/block/index.html.eex:29 +#: lib/explorer_web/templates/block/_tile.html.eex:15 msgid "%{count} transaction" msgid_plural "%{count} transactions" msgstr[0] "" msgstr[1] "" + +#, elixir-format +#: lib/explorer_web/templates/address_transaction/index.html.eex:84 +#: lib/explorer_web/templates/chain/show.html.eex:71 +#: lib/explorer_web/templates/transaction/index.html.eex:48 +msgid "More transactions have come in" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/transaction/index.html.eex:57 +msgid "Showing" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/transaction/index.html.eex:57 +msgid "Validated Transactions" +msgstr "" diff --git a/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po index f00a37e331..ae1e2d7481 100644 --- a/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po @@ -27,7 +27,7 @@ msgstr "Blocks" msgid "Copyright %{year} POA" msgstr "%{year} POA Network Ltd. All rights reserved" -#: lib/explorer_web/templates/block/index.html.eex:51 +#: lib/explorer_web/templates/block/_tile.html.eex:37 #: lib/explorer_web/templates/block/overview.html.eex:84 msgid "Gas Used" msgstr "Gas Used" @@ -55,10 +55,10 @@ msgstr "POA Network Explorer" #: lib/explorer_web/templates/block_transaction/index.html.eex:13 #: lib/explorer_web/templates/block_transaction/index.html.eex:26 #: lib/explorer_web/templates/block_transaction/index.html.eex:36 -#: lib/explorer_web/templates/chain/_transactions.html.eex:4 +#: lib/explorer_web/templates/chain/show.html.eex:75 #: lib/explorer_web/templates/layout/_topnav.html.eex:18 #: lib/explorer_web/templates/pending_transaction/index.html.eex:46 -#: lib/explorer_web/templates/transaction/index.html.eex:46 +#: lib/explorer_web/templates/transaction/index.html.eex:56 msgid "Transactions" msgstr "Transactions" @@ -74,12 +74,12 @@ msgstr "Block #%{number} Details" msgid "Difficulty" msgstr "Difficulty" -#: lib/explorer_web/templates/block/index.html.eex:59 +#: lib/explorer_web/templates/block/_tile.html.eex:45 #: lib/explorer_web/templates/block/overview.html.eex:92 msgid "Gas Limit" msgstr "Gas Limit" -#: lib/explorer_web/templates/block/index.html.eex:38 +#: lib/explorer_web/templates/block/_tile.html.eex:24 #: lib/explorer_web/templates/block/overview.html.eex:70 #: lib/explorer_web/templates/chain/_block.html.eex:9 msgid "Miner" @@ -297,10 +297,8 @@ msgstr "" #: #: lib/explorer_web/templates/address_internal_transaction/_internal_transaction.html.eex:29 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:60 -#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:33 -#: lib/explorer_web/templates/chain/_transactions.html.eex:33 #: lib/explorer_web/templates/pending_transaction/index.html.eex:71 -#: lib/explorer_web/templates/transaction/index.html.eex:76 +#: lib/explorer_web/templates/transaction/_tile.html.eex:26 #: lib/explorer_web/templates/transaction/overview.html.eex:81 #: lib/explorer_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16 #: lib/explorer_web/views/wei_helpers.ex:71 @@ -367,10 +365,8 @@ msgid "All" msgstr "" #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:62 -#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:35 -#: lib/explorer_web/templates/chain/_transactions.html.eex:35 #: lib/explorer_web/templates/pending_transaction/index.html.eex:72 -#: lib/explorer_web/templates/transaction/index.html.eex:77 +#: lib/explorer_web/templates/transaction/_tile.html.eex:28 msgid "Fee" msgstr "" @@ -481,10 +477,10 @@ msgstr "" #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:133 #: lib/explorer_web/templates/address_transaction/index.html.eex:146 -#: lib/explorer_web/templates/block/index.html.eex:67 +#: lib/explorer_web/templates/block/index.html.eex:15 #: lib/explorer_web/templates/block_transaction/index.html.eex:51 #: lib/explorer_web/templates/pending_transaction/index.html.eex:79 -#: lib/explorer_web/templates/transaction/index.html.eex:84 +#: lib/explorer_web/templates/transaction/index.html.eex:66 msgid "Older" msgstr "" @@ -559,7 +555,7 @@ msgid "View All Blocks →" msgstr "" #, elixir-format -#: lib/explorer_web/templates/chain/_transactions.html.eex:3 +#: lib/explorer_web/templates/chain/show.html.eex:74 msgid "View All Transactions →" msgstr "" @@ -580,6 +576,7 @@ msgstr "" #, elixir-format #: lib/explorer_web/templates/address_transaction/index.html.eex:89 +#: lib/explorer_web/templates/transaction/index.html.eex:53 msgid "Connection Lost, click to load newer transactions" msgstr "" @@ -595,11 +592,6 @@ msgstr "" msgid "Internal Transaction" msgstr "" -#, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:84 -msgid "More messages have come in" -msgstr "" - #, elixir-format #: lib/explorer_web/templates/address/overview.html.eex:13 #: lib/explorer_web/templates/address/overview.html.eex:66 @@ -641,9 +633,7 @@ msgstr "" #, elixir-format #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:49 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:111 -#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:25 -#: lib/explorer_web/templates/chain/_transactions.html.eex:25 -#: lib/explorer_web/templates/transaction/index.html.eex:69 +#: lib/explorer_web/templates/transaction/_tile.html.eex:18 msgid "Block #%{number}" msgstr "" @@ -737,8 +727,25 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/explorer_web/templates/block/index.html.eex:29 +#: lib/explorer_web/templates/block/_tile.html.eex:15 msgid "%{count} transaction" msgid_plural "%{count} transactions" msgstr[0] "" msgstr[1] "" + +#, elixir-format +#: lib/explorer_web/templates/address_transaction/index.html.eex:84 +#: lib/explorer_web/templates/chain/show.html.eex:71 +#: lib/explorer_web/templates/transaction/index.html.eex:48 +msgid "More transactions have come in" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/transaction/index.html.eex:57 +msgid "Showing" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/transaction/index.html.eex:57 +msgid "Validated Transactions" +msgstr "" diff --git a/apps/explorer_web/test/explorer_web/features/pages/address_page.ex b/apps/explorer_web/test/explorer_web/features/pages/address_page.ex index a8ad4d8e17..88edbadd6d 100644 --- a/apps/explorer_web/test/explorer_web/features/pages/address_page.ex +++ b/apps/explorer_web/test/explorer_web/features/pages/address_page.ex @@ -43,6 +43,10 @@ defmodule ExplorerWeb.AddressPage do css("[data-internal-transaction-id='#{id}'] [data-address-hash='#{address_hash}'][data-test='address_hash_link']") end + def non_loaded_transaction_count(count) do + css("[data-selector='channel-batching-count']", text: count) + end + def transaction(%Transaction{hash: transaction_hash}), do: transaction(transaction_hash) def transaction(%Hash{} = hash) do diff --git a/apps/explorer_web/test/explorer_web/features/pages/home_page.ex b/apps/explorer_web/test/explorer_web/features/pages/home_page.ex index c27d044273..f9e799788c 100644 --- a/apps/explorer_web/test/explorer_web/features/pages/home_page.ex +++ b/apps/explorer_web/test/explorer_web/features/pages/home_page.ex @@ -29,6 +29,10 @@ defmodule ExplorerWeb.HomePage do css("[data-test='chain_transaction']", count: count) end + def transaction(%Transaction{hash: transaction_hash}) do + css("[data-transaction-hash='#{transaction_hash}']") + end + def transaction_status(%Transaction{hash: transaction_hash}) do css("[data-transaction-hash='#{transaction_hash}'] [data-test='transaction_status']") end diff --git a/apps/explorer_web/test/explorer_web/features/pages/transaction_list_page.ex b/apps/explorer_web/test/explorer_web/features/pages/transaction_list_page.ex index 68963594e8..53927f15b5 100644 --- a/apps/explorer_web/test/explorer_web/features/pages/transaction_list_page.ex +++ b/apps/explorer_web/test/explorer_web/features/pages/transaction_list_page.ex @@ -19,6 +19,10 @@ defmodule ExplorerWeb.TransactionListPage do css("[data-transaction-hash='#{hash}'] [data-test='transaction_type']", text: "Contract Creation") end + def non_loaded_transaction_count(count) do + css("[data-selector='channel-batching-count']", text: count) + end + def transaction(%Transaction{hash: transaction_hash}) do css("[data-transaction-hash='#{transaction_hash}']") end diff --git a/apps/explorer_web/test/explorer_web/features/viewing_addresses_test.exs b/apps/explorer_web/test/explorer_web/features/viewing_addresses_test.exs index 834649b23f..29f210398d 100644 --- a/apps/explorer_web/test/explorer_web/features/viewing_addresses_test.exs +++ b/apps/explorer_web/test/explorer_web/features/viewing_addresses_test.exs @@ -250,6 +250,24 @@ defmodule ExplorerWeb.ViewingAddressesTest do |> assert_has(AddressPage.transaction(transaction2)) end + test "count of non-loaded transactions on live update when batch overflow", %{addresses: addresses, session: session} do + transaction_hashes = + 30 + |> insert_list(:transaction, from_address: addresses.lincoln) + |> with_block() + |> Repo.preload([:block, :from_address, :to_address]) + |> Enum.map(& &1.hash) + + session + |> AddressPage.visit_page(addresses.lincoln) + |> assert_has(AddressPage.balance()) + + Notifier.handle_event({:chain_event, :transactions, transaction_hashes}) + + session + |> assert_has(AddressPage.non_loaded_transaction_count("30")) + end + test "transaction count live updates", %{addresses: addresses, session: session} do session |> AddressPage.visit_page(addresses.lincoln) diff --git a/apps/explorer_web/test/explorer_web/features/viewing_blocks_test.exs b/apps/explorer_web/test/explorer_web/features/viewing_blocks_test.exs index 9b4b9a2a3e..b56b4fc63e 100644 --- a/apps/explorer_web/test/explorer_web/features/viewing_blocks_test.exs +++ b/apps/explorer_web/test/explorer_web/features/viewing_blocks_test.exs @@ -93,4 +93,13 @@ defmodule ExplorerWeb.ViewingBlocksTest do |> BlockListPage.visit_page() |> assert_has(BlockListPage.block(block)) end + + test "viewing new blocks via live update on list page", %{session: session} do + BlockListPage.visit_page(session) + + block = insert(:block, number: 42) + Notifier.handle_event({:chain_event, :blocks, [block]}) + + assert_has(session, BlockListPage.block(block)) + end end diff --git a/apps/explorer_web/test/explorer_web/features/viewing_transactions_test.exs b/apps/explorer_web/test/explorer_web/features/viewing_transactions_test.exs index 5e51ac69d4..e395a75081 100644 --- a/apps/explorer_web/test/explorer_web/features/viewing_transactions_test.exs +++ b/apps/explorer_web/test/explorer_web/features/viewing_transactions_test.exs @@ -14,9 +14,10 @@ defmodule ExplorerWeb.ViewingTransactionsTest do gas_used: 123_987 }) - 4 - |> insert_list(:transaction) - |> with_block() + [oldest_transaction | _] = + 3 + |> insert_list(:transaction) + |> with_block() pending = insert(:transaction, block_hash: nil, gas: 5891, index: nil) pending_contract = insert(:transaction, to_address: nil, block_hash: nil, gas: 5891, index: nil) @@ -24,7 +25,13 @@ defmodule ExplorerWeb.ViewingTransactionsTest do lincoln = insert(:address) taft = insert(:address) - transaction = + # From Lincoln to Taft. + txn_from_lincoln = + :transaction + |> insert(from_address: lincoln, to_address: taft) + |> with_block(block) + + newest_transaction = :transaction |> insert( value: Wei.from(Decimal.new(5656), :ether), @@ -39,15 +46,9 @@ defmodule ExplorerWeb.ViewingTransactionsTest do ) |> with_block(block, gas_used: Decimal.new(1_230_000_000_000_123_000), status: :ok) - insert(:log, address: lincoln, index: 0, transaction: transaction) + insert(:log, address: lincoln, index: 0, transaction: newest_transaction) - # From Lincoln to Taft. - txn_from_lincoln = - :transaction - |> insert(from_address: lincoln, to_address: taft) - |> with_block(block) - - internal = insert(:internal_transaction, index: 0, transaction: transaction) + internal = insert(:internal_transaction, index: 0, transaction: newest_transaction) {:ok, %{ @@ -56,7 +57,8 @@ defmodule ExplorerWeb.ViewingTransactionsTest do internal: internal, lincoln: lincoln, taft: taft, - transaction: transaction, + first_shown_transaction: newest_transaction, + last_shown_transaction: oldest_transaction, txn_from_lincoln: txn_from_lincoln }} end @@ -71,10 +73,47 @@ defmodule ExplorerWeb.ViewingTransactionsTest do end describe "viewing transaction lists" do - test "transactions on the home page", %{session: session} do + test "transactions on the homepage", %{session: session} do + session + |> HomePage.visit_page() + |> assert_has(HomePage.transactions(count: 5)) + end + + test "viewing new transactions via live update on the homepage", %{ + session: session, + last_shown_transaction: last_shown_transaction + } do + session + |> HomePage.visit_page() + |> assert_has(HomePage.transactions(count: 5)) + + transaction = + :transaction + |> insert() + |> with_block() + + Notifier.handle_event({:chain_event, :transactions, [transaction.hash]}) + + session + |> assert_has(HomePage.transactions(count: 5)) + |> assert_has(HomePage.transaction(transaction)) + |> refute_has(HomePage.transaction(last_shown_transaction)) + end + + test "count of non-loaded transactions on homepage live update when batch overflow", %{session: session} do + transaction_hashes = + 30 + |> insert_list(:transaction) + |> with_block() + |> Enum.map(& &1.hash) + session |> HomePage.visit_page() |> assert_has(HomePage.transactions(count: 5)) + + Notifier.handle_event({:chain_event, :transactions, transaction_hashes}) + + assert_has(session, AddressPage.non_loaded_transaction_count("30")) end test "contract creation is shown for to_address on home page", %{session: session} do @@ -90,7 +129,11 @@ defmodule ExplorerWeb.ViewingTransactionsTest do |> assert_has(HomePage.contract_creation(internal_transaction)) end - test "viewing the default transactions tab", %{session: session, transaction: transaction, pending: pending} do + test "viewing the default transactions tab", %{ + session: session, + first_shown_transaction: transaction, + pending: pending + } do session |> TransactionListPage.visit_page() |> assert_has(TransactionListPage.transaction(transaction)) @@ -119,23 +162,50 @@ defmodule ExplorerWeb.ViewingTransactionsTest do |> TransactionListPage.visit_page() |> assert_has(TransactionListPage.contract_creation(transaction)) end + + test "viewing new transactions via live update on list page", %{session: session} do + TransactionListPage.visit_page(session) + + transaction = + :transaction + |> insert() + |> with_block() + + Notifier.handle_event({:chain_event, :transactions, [transaction.hash]}) + + assert_has(session, TransactionListPage.transaction(transaction)) + end + + test "count of non-loaded transactions on list page live update when batch overflow", %{session: session} do + transaction_hashes = + 30 + |> insert_list(:transaction) + |> with_block() + |> Enum.map(& &1.hash) + + TransactionListPage.visit_page(session) + + Notifier.handle_event({:chain_event, :transactions, transaction_hashes}) + + assert_has(session, TransactionListPage.non_loaded_transaction_count("30")) + end end describe "viewing a transaction page" do - test "can navigate to transaction show from list page", %{session: session, transaction: transaction} do + test "can navigate to transaction show from list page", %{session: session, first_shown_transaction: transaction} do session |> TransactionListPage.visit_page() |> TransactionListPage.click_transaction(transaction) |> assert_has(TransactionPage.detail_hash(transaction)) end - test "can see a transaction's details", %{session: session, transaction: transaction} do + test "can see a transaction's details", %{session: session, first_shown_transaction: transaction} do session |> TransactionPage.visit_page(transaction) |> assert_has(TransactionPage.detail_hash(transaction)) end - test "can view a transaction's logs", %{session: session, transaction: transaction} do + test "can view a transaction's logs", %{session: session, first_shown_transaction: transaction} do session |> TransactionPage.visit_page(transaction) |> TransactionPage.click_logs() @@ -145,7 +215,7 @@ defmodule ExplorerWeb.ViewingTransactionsTest do test "can visit an address from the transaction logs page", %{ lincoln: lincoln, session: session, - transaction: transaction + first_shown_transaction: transaction } do session |> TransactionLogsPage.visit_page(transaction) @@ -153,7 +223,7 @@ defmodule ExplorerWeb.ViewingTransactionsTest do |> assert_has(AddressPage.detail_hash(lincoln)) end - test "block confirmations via live update", %{session: session, transaction: transaction} do + test "block confirmations via live update", %{session: session, first_shown_transaction: transaction} do blocks = [insert(:block, number: transaction.block_number + 10)] TransactionPage.visit_page(session, transaction)