Live reload transactions on homepage #35
pull/508/head
Jimmy Lauzau 6 years ago committed by GitHub
commit 4ce9115f9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      apps/explorer/lib/explorer/chain.ex
  2. 8
      apps/explorer/test/explorer/chain_test.exs
  3. 2
      apps/explorer_web/assets/__tests__/pages/address.js
  4. 126
      apps/explorer_web/assets/__tests__/pages/chain.js
  5. 152
      apps/explorer_web/assets/__tests__/pages/transaction.js
  6. 29
      apps/explorer_web/assets/css/components/_animations.scss
  7. 1
      apps/explorer_web/assets/js/app.js
  8. 2
      apps/explorer_web/assets/js/pages/address.js
  9. 60
      apps/explorer_web/assets/js/pages/block.js
  10. 69
      apps/explorer_web/assets/js/pages/chain.js
  11. 100
      apps/explorer_web/assets/js/pages/transaction.js
  12. 14
      apps/explorer_web/lib/explorer_web/channels/block_channel.ex
  13. 27
      apps/explorer_web/lib/explorer_web/channels/transaction_channel.ex
  14. 7
      apps/explorer_web/lib/explorer_web/notifier.ex
  15. 2
      apps/explorer_web/lib/explorer_web/templates/address/overview.html.eex
  16. 2
      apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex
  17. 48
      apps/explorer_web/lib/explorer_web/templates/block/_tile.html.eex
  18. 56
      apps/explorer_web/lib/explorer_web/templates/block/index.html.eex
  19. 2
      apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex
  20. 2
      apps/explorer_web/lib/explorer_web/templates/chain/_block.html.eex
  21. 42
      apps/explorer_web/lib/explorer_web/templates/chain/_transactions.html.eex
  22. 24
      apps/explorer_web/lib/explorer_web/templates/chain/show.html.eex
  23. 19
      apps/explorer_web/lib/explorer_web/templates/transaction/_tile.html.eex
  24. 44
      apps/explorer_web/lib/explorer_web/templates/transaction/index.html.eex
  25. 53
      apps/explorer_web/priv/gettext/default.pot
  26. 53
      apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po
  27. 4
      apps/explorer_web/test/explorer_web/features/pages/address_page.ex
  28. 4
      apps/explorer_web/test/explorer_web/features/pages/home_page.ex
  29. 4
      apps/explorer_web/test/explorer_web/features/pages/transaction_list_page.ex
  30. 18
      apps/explorer_web/test/explorer_web/features/viewing_addresses_test.exs
  31. 9
      apps/explorer_web/test/explorer_web/features/viewing_blocks_test.exs
  32. 104
      apps/explorer_web/test/explorer_web/features/viewing_transactions_test.exs

@ -587,9 +587,9 @@ defmodule Explorer.Chain do
iex> [%Transaction{hash: hash1}, %Transaction{hash: hash2}] = insert_list(2, :transaction) iex> [%Transaction{hash: hash1}, %Transaction{hash: hash2}] = insert_list(2, :transaction)
iex> [%Explorer.Chain.Transaction{hash: found_hash1}, %Explorer.Chain.Transaction{hash: found_hash2}] = iex> [%Explorer.Chain.Transaction{hash: found_hash1}, %Explorer.Chain.Transaction{hash: found_hash2}] =
...> Explorer.Chain.hashes_to_transactions([hash1, hash2]) ...> Explorer.Chain.hashes_to_transactions([hash1, hash2])
iex> found_hash1 == hash1 iex> found_hash1 in [hash1, hash2]
true true
iex> found_hash2 == hash2 iex> found_hash2 in [hash1, hash2]
true true
Returns `[]` if not found Returns `[]` if not found

@ -529,11 +529,9 @@ defmodule Explorer.ChainTest do
necessity_by_association: %{block: :required} necessity_by_association: %{block: :required}
) )
assert [%Transaction{hash: ^hash_without_index1}, %Transaction{hash: ^hash_without_index2}] = assert [hash_without_index1, hash_without_index2]
Chain.hashes_to_transactions( |> Chain.hashes_to_transactions(necessity_by_association: %{block: :optional})
[hash_without_index1, hash_without_index2], |> Enum.all?(&(&1.hash in [hash_without_index1, hash_without_index2]))
necessity_by_association: %{block: :optional}
)
end end
end end

@ -100,6 +100,7 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual(['test']) expect(output.newTransactions).toEqual(['test'])
expect(output.batchCountAccumulator).toEqual(0) expect(output.batchCountAccumulator).toEqual(0)
expect(output.transactionCount).toEqual(1)
}) })
test('large batch of transactions', () => { test('large batch of transactions', () => {
const state = initialState const state = initialState
@ -133,6 +134,7 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual([]) expect(output.newTransactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(11) expect(output.batchCountAccumulator).toEqual(11)
expect(output.transactionCount).toEqual(11)
}) })
test('single transaction after single transaction', () => { test('single transaction after single transaction', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {

@ -1,26 +1,134 @@
import { reducer, initialState } from '../../js/pages/chain' import { reducer, initialState } from '../../js/pages/chain'
test('CHANNEL_DISCONNECTED', () => { test('RECEIVED_NEW_BLOCK', () => {
const state = Object.assign({}, initialState, {
newBlock: 'last new block'
})
const action = {
type: 'RECEIVED_NEW_BLOCK',
msg: {
homepageBlockHtml: 'new block'
}
}
const output = reducer(state, action)
expect(output.newBlock).toEqual('new block')
})
describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
test('single transaction', () => {
const state = initialState const state = initialState
const action = { const action = {
type: 'CHANNEL_DISCONNECTED' type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{
transactionHtml: 'test'
}]
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.channelDisconnected).toBe(true) 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)
test('RECEIVED_NEW_BLOCK', () => { 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, { const state = Object.assign({}, initialState, {
newBlock: 'last new block' newTransactions: ['test 1']
}) })
const action = { const action = {
type: 'RECEIVED_NEW_BLOCK', type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msg: { msgs: [{
homepageBlockHtml: 'new block' 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) const output = reducer(state, action)
expect(output.newBlock).toEqual('new block') expect(output.newTransactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(22)
})
}) })

@ -1,9 +1,9 @@
import { reducer, initialState } from '../../js/pages/transaction' import { reducer, initialState } from '../../js/pages/transaction'
test('RECEIVED_UPDATED_CONFIRMATIONS', () => { test('RECEIVED_NEW_BLOCK', () => {
const state = { ...initialState, blockNumber: 1 } const state = { ...initialState, blockNumber: 1 }
const action = { const action = {
type: 'RECEIVED_UPDATED_CONFIRMATIONS', type: 'RECEIVED_NEW_BLOCK',
msg: { msg: {
blockNumber: 5 blockNumber: 5
} }
@ -12,3 +12,151 @@ test('RECEIVED_UPDATED_CONFIRMATIONS', () => {
expect(output.confirmations).toBe(4) 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)
})
})

@ -3,7 +3,7 @@
100% {opacity: 1;} 100% {opacity: 1;}
} }
@keyframes fade-up { @keyframes fade-up-blocks-homepage {
0% { 0% {
flex-basis: 0%; flex-basis: 0%;
width: 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 { .fade-in {
animation: fade-in 1s ease-out forwards; 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 { .fade-up {
will-change: transform, opacity, width; will-change: transform, opacity, height;
max-height: 98px; 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);
} }

@ -26,5 +26,6 @@ import './lib/tooltip'
import './lib/smart_contract/read_function' import './lib/smart_contract/read_function'
import './pages/address' import './pages/address'
import './pages/block'
import './pages/chain' import './pages/chain'
import './pages/transaction' import './pages/transaction'

@ -5,6 +5,7 @@ import 'numeral/locales'
import socket from '../socket' import socket from '../socket'
import router from '../router' import router from '../router'
import { batchChannel, initRedux } from '../utils' import { batchChannel, initRedux } from '../utils'
import { updateAllAges } from '../lib/from_now'
const BATCH_THRESHOLD = 10 const BATCH_THRESHOLD = 10
@ -108,6 +109,7 @@ router.when('/addresses/:addressHash').then((params) => initRedux(reducer, {
} }
if (oldState.newTransactions !== state.newTransactions && $transactionsList.length) { if (oldState.newTransactions !== state.newTransactions && $transactionsList.length) {
$transactionsList.prepend(state.newTransactions.slice(oldState.newTransactions.length).reverse().join('')) $transactionsList.prepend(state.newTransactions.slice(oldState.newTransactions.length).reverse().join(''))
updateAllAges()
} }
} }
})) }))

@ -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()
}
}
}))

@ -1,46 +1,99 @@
import $ from 'jquery' import $ from 'jquery'
import humps from 'humps' import humps from 'humps'
import numeral from 'numeral'
import 'numeral/locales'
import router from '../router' import router from '../router'
import socket from '../socket' import socket from '../socket'
import { updateAllAges } from '../lib/from_now' import { updateAllAges } from '../lib/from_now'
import { initRedux } from '../utils' import { batchChannel, initRedux } from '../utils'
const BATCH_THRESHOLD = 10
export const initialState = { export const initialState = {
batchCountAccumulator: 0,
newBlock: null, newBlock: null,
channelDisconnected: false newTransactions: [],
transactionCount: null
} }
export function reducer (state = initialState, action) { export function reducer (state = initialState, action) {
switch (action.type) { switch (action.type) {
case 'CHANNEL_DISCONNECTED': { case 'PAGE_LOAD': {
return Object.assign({}, state, { return Object.assign({}, state, {
channelDisconnected: true transactionCount: numeral(action.transactionCount).value()
}) })
} }
case 'RECEIVED_NEW_BLOCK': { case 'RECEIVED_NEW_BLOCK': {
return Object.assign({}, state, { 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: default:
return state return state
} }
} }
router.when('', { exactPathMatch: true }).then(() => initRedux(reducer, { router.when('', { exactPathMatch: true }).then(({ locale }) => initRedux(reducer, {
main (store) { main (store) {
const blocksChannel = socket.channel(`blocks:new_block`) const blocksChannel = socket.channel(`blocks:new_block`)
numeral.locale(locale)
store.dispatch({
type: 'PAGE_LOAD',
transactionCount: $('[data-selector="transaction-count"]').text()
})
blocksChannel.join() blocksChannel.join()
blocksChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) blocksChannel.on('new_block', msg => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }))
blocksChannel.on('new_block', msg => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', 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) { render (state, oldState) {
const $blockList = $('[data-selector="chain-block-list"]') 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) { if (oldState.newBlock !== state.newBlock) {
$blockList.children().last().remove() $blockList.children().last().remove()
$blockList.prepend(state.newBlock) $blockList.prepend(state.newBlock)
updateAllAges() 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()
}
} }
})) }))

@ -4,27 +4,63 @@ import numeral from 'numeral'
import 'numeral/locales' import 'numeral/locales'
import socket from '../socket' import socket from '../socket'
import router from '../router' 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 = { export const initialState = {
batchCountAccumulator: 0,
beyondPageOne: null,
blockNumber: null, blockNumber: null,
confirmations: null channelDisconnected: false,
confirmations: null,
newTransactions: [],
transactionCount: null
} }
export function reducer (state = initialState, action) { export function reducer (state = initialState, action) {
switch (action.type) { switch (action.type) {
case 'PAGE_LOAD': { case 'PAGE_LOAD': {
return Object.assign({}, state, { return Object.assign({}, state, {
blockNumber: parseInt(action.blockNumber, 10) beyondPageOne: !!action.index,
blockNumber: parseInt(action.blockNumber, 10),
transactionCount: numeral(action.transactionCount).value()
}) })
} }
case 'RECEIVED_UPDATED_CONFIRMATIONS': { case 'CHANNEL_DISCONNECTED': {
if (state.beyondPageOne) return state
return Object.assign({}, state, {
channelDisconnected: true,
batchCountAccumulator: 0
})
}
case 'RECEIVED_NEW_BLOCK': {
if ((action.msg.blockNumber - state.blockNumber) > state.confirmations) { if ((action.msg.blockNumber - state.blockNumber) > state.confirmations) {
return Object.assign({}, state, { return Object.assign({}, state, {
confirmations: action.msg.blockNumber - state.blockNumber confirmations: action.msg.blockNumber - state.blockNumber
}) })
} else return state } 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: default:
return state return state
} }
@ -32,17 +68,65 @@ export function reducer (state = initialState, action) {
router.when('/transactions/:transactionHash').then(({ locale }) => initRedux(reducer, { router.when('/transactions/:transactionHash').then(({ locale }) => initRedux(reducer, {
main (store) { main (store) {
const channel = socket.channel(`transactions:confirmations`, {}) const blocksChannel = socket.channel(`blocks:new_block`, {})
const $transactionBlockNumber = $('[data-selector="block-number"]') const $transactionBlockNumber = $('[data-selector="block-number"]')
numeral.locale(locale) numeral.locale(locale)
store.dispatch({ type: 'PAGE_LOAD', blockNumber: $transactionBlockNumber.text() }) store.dispatch({
channel.join() type: 'PAGE_LOAD',
channel.on('update', (msg) => store.dispatch({ type: 'RECEIVED_UPDATED_CONFIRMATIONS', msg: humps.camelizeKeys(msg) })) blockNumber: $transactionBlockNumber.text()
})
blocksChannel.join()
blocksChannel.on('new_block', (msg) => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }))
}, },
render (state, oldState) { render (state, oldState) {
const $blockConfirmations = $('[data-selector="block-confirmations"]') const $blockConfirmations = $('[data-selector="block-confirmations"]')
if (oldState.confirmations !== state.confirmations) { if (oldState.confirmations !== state.confirmations) {
$blockConfirmations.empty().append(numeral(state.confirmations).format()) $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()
}
}
}))

@ -4,7 +4,7 @@ defmodule ExplorerWeb.BlockChannel do
""" """
use ExplorerWeb, :channel use ExplorerWeb, :channel
alias ExplorerWeb.ChainView alias ExplorerWeb.{BlockView, ChainView}
alias Phoenix.View alias Phoenix.View
intercept(["new_block"]) intercept(["new_block"])
@ -16,6 +16,14 @@ defmodule ExplorerWeb.BlockChannel do
def handle_out("new_block", %{block: block}, socket) do def handle_out("new_block", %{block: block}, socket) do
Gettext.put_locale(ExplorerWeb.Gettext, socket.assigns.locale) 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 = rendered_homepage_block =
View.render_to_string( View.render_to_string(
ChainView, ChainView,
@ -25,7 +33,9 @@ defmodule ExplorerWeb.BlockChannel do
) )
push(socket, "new_block", %{ 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} {:noreply, socket}

@ -1,10 +1,33 @@
defmodule ExplorerWeb.TransactionChannel do defmodule ExplorerWeb.TransactionChannel do
@moduledoc """ @moduledoc """
Establishes pub/sub channel for transaction page live updates. Establishes pub/sub channel for live updates of transaction events.
""" """
use ExplorerWeb, :channel 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} {:ok, %{}, socket}
end 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 end

@ -15,9 +15,6 @@ defmodule ExplorerWeb.Notifier do
end end
def handle_event({:chain_event, :blocks, blocks}) do 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) Enum.each(blocks, &broadcast_block/1)
end end
@ -47,6 +44,10 @@ defmodule ExplorerWeb.Notifier do
end end
defp broadcast_transaction(transaction) do defp broadcast_transaction(transaction) do
Endpoint.broadcast("transactions:new_transaction", "new_transaction", %{
transaction: transaction
})
Endpoint.broadcast("addresses:#{transaction.from_address_hash}", "transaction", %{ Endpoint.broadcast("addresses:#{transaction.from_address_hash}", "transaction", %{
address: transaction.from_address, address: transaction.from_address,
transaction: transaction transaction: transaction

@ -18,7 +18,7 @@
<h1 class="card-title"><%= address_title(@address) %> Details </h1> <h1 class="card-title"><%= address_title(@address) %> Details </h1>
<h3 class="<%= if ExplorerWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address %></h3> <h3 class="<%= if ExplorerWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address %></h3>
<div class="d-flex flex-row justify-content-start text-muted"> <div class="d-flex flex-row justify-content-start text-muted">
<span class="mr-4"><span data-selector="transaction-count"><%= Cldr.Number.to_string!(@transaction_count) %></span> <%= gettext "Transactions" %></span> <span class="mr-4"><span data-selector="transaction-count"><%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %></span> <%= gettext "Transactions" %></span>
<%= if contract?(@address) do %> <%= if contract?(@address) do %>
<span class="mr-4" data-test="address_contract_creator"> <span class="mr-4" data-test="address_contract_creator">

@ -81,7 +81,7 @@
<div class="card-body"> <div class="card-body">
<div data-selector="channel-batching-message" style="display:none;"> <div data-selector="channel-batching-message" style="display:none;">
<div data-selector="reload-button" class="alert alert-info"> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link" data-selector="channel-batching-count"><%= gettext "More messages have come in" %></a> <a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a>
</div> </div>
</div> </div>
<div data-selector="channel-disconnected-message" style="display:none;"> <div data-selector="channel-disconnected-message" style="display:none;">

@ -0,0 +1,48 @@
<div class="tile fade-up">
<div class="row">
<div class="col-md-6">
<!-- block height -->
<%= link(
@block,
class: "tile-title",
to: block_path(ExplorerWeb.Endpoint, :show, @locale, @block),
"data-test": "block_number",
"data-block-number": to_string(@block.number)
) %>
<div>
<!-- transactions -->
<span class="mr-2">
<%= ngettext("%{count} transaction", "%{count} transactions", Enum.count(@block.transactions)) %>
</span>
<!-- size -->
<span class="mr-2"> <%= Cldr.Unit.new(:byte, @block.size) |> Cldr.Unit.to_string! %> </span>
<!-- age -->
<span data-from-now="<%= @block.timestamp %>"></span>
</div>
<div class="">
<!-- validator -->
<%= gettext "Miner" %>
<span class="ml-2">
<%= link to: address_path(ExplorerWeb.Endpoint, :show, @locale, @block.miner_hash) do %>
<%= @block.miner_hash %>
<% end %>
</span>
</div>
</div>
<div class="col-md-6 text-right d-flex flex-column align-items-end justify-content-end">
<!-- Gas Used -->
<div class="">
<%= formatted_gas(@block.gas_used) %>
(<%= formatted_gas(@block.gas_used / @block.gas_limit, format: "#.#%") %>)
<%= gettext "Gas Used" %>
</div>
<div class="progress w-25">
<div class="progress-bar" role="progressbar" style="width: <%= formatted_gas(@block.gas_used / @block.gas_limit, format: "#.#%") %>;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
<!-- Gas Limit -->
<span> <%= formatted_gas(@block.gas_limit) %> <%= gettext "Gas Limit" %> </span>
</div>
</div>
</div>

@ -3,64 +3,12 @@
<div class="card-body"> <div class="card-body">
<h1>Blocks</h1> <h1>Blocks</h1>
<p>
<%= gettext(
"Showing #%{start_block} to #%{end_block}",
start_block: List.first(@blocks).number,
end_block: List.last(@blocks).number
) %>
</p>
<span data-selector="blocks-list">
<%= for block <- @blocks do %> <%= for block <- @blocks do %>
<div class="tile"> <%= render ExplorerWeb.BlockView, "_tile.html", locale: @locale, block: block %>
<div class="row">
<div class="col-md-6">
<!-- block height -->
<%= 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)
) %>
<div>
<!-- transactions -->
<span class="mr-2">
<%= ngettext("%{count} transaction", "%{count} transactions", Enum.count(block.transactions)) %>
</span>
<!-- size -->
<span class="mr-2"> <%= Cldr.Unit.new(:byte, block.size) |> Cldr.Unit.to_string! %> </span>
<!-- age -->
<span data-from-now="<%= block.timestamp %>"></span>
</div>
<div class="">
<!-- validator -->
<%= gettext "Miner" %>
<span class="ml-2">
<%= link to: address_path(ExplorerWeb.Endpoint, :show, @locale, block.miner_hash) do %>
<%= block.miner_hash %>
<% end %> <% end %>
</span> </span>
</div>
</div>
<div class="col-md-6 text-right d-flex flex-column align-items-end justify-content-end">
<!-- Gas Used -->
<div class="">
<%= formatted_gas(block.gas_used) %>
(<%= formatted_gas(block.gas_used / block.gas_limit, format: "#.#%") %>)
<%= gettext "Gas Used" %>
</div>
<div class="progress w-25">
<div class="progress-bar" role="progressbar" style="width: <%= formatted_gas(block.gas_used / block.gas_limit, format: "#.#%") %>;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
<!-- Gas Limit -->
<span> <%= formatted_gas(block.gas_limit) %> <%= gettext "Gas Limit" %> </span>
</div>
</div>
</div>
<% end %>
<%= if @next_page_params do %> <%= if @next_page_params do %>
<%= link( <%= link(

@ -36,7 +36,7 @@
<h2 class="card-title"><%= gettext "Transactions" %></h2> <h2 class="card-title"><%= gettext "Transactions" %></h2>
<span data-selector="transactions-list"> <span data-selector="transactions-list">
<%= for transaction <- @transactions do %> <%= for transaction <- @transactions do %>
<%= render "_transaction.html", locale: @locale, transaction: transaction %> <%= render ExplorerWeb.TransactionView, "_tile.html", locale: @locale, transaction: transaction %>
<% end %> <% end %>
</span> </span>
<% else %> <% else %>

@ -1,4 +1,4 @@
<div class="col-sm-3 fade-up" data-selector="chain-block" data-block-number="<%= @block.number %>"> <div class="col-sm-3 fade-up-blocks-homepage" data-selector="chain-block" data-block-number="<%= @block.number %>">
<div class="tile d-flex flex-column"> <div class="tile d-flex flex-column">
<%= link(@block, to: block_path(ExplorerWeb.Endpoint, :show, @locale, @block), class: "tile-title") %> <%= link(@block, to: block_path(ExplorerWeb.Endpoint, :show, @locale, @block), class: "tile-title") %>
<div> <div>

@ -1,42 +0,0 @@
<div class="card">
<div class="card-body">
<%= link(gettext("View All Transactions →"), to: transaction_path(@conn, :index, Gettext.get_locale), class: "button button--secondary button--xsmall float-right") %>
<h2 class="card-title"><%= gettext "Transactions" %></h2>
<%= for transaction <- @chain.transactions do %>
<div class="tile tile-type-<%= ExplorerWeb.TransactionView.type_suffix(transaction) %> tile-status--<%= ExplorerWeb.TransactionView.status(transaction) %>" data-test="<%= ExplorerWeb.TransactionView.type_suffix(transaction) %>" data-transaction-hash="<%= transaction.hash %>">
<div class="row" data-test="chain_transaction">
<div class="col-md-2 d-flex flex-column align-items-left justify-content-start justify-content-lg-center">
<div class="ml-4">
<span class="tile-label" data-test="transaction_type"> <%= ExplorerWeb.TransactionView.transaction_display_type(transaction) %></span>
<div class="tile-status-label" data-test="transaction_status"><%= ExplorerWeb.TransactionView.formatted_status(transaction) %></div>
</div>
</div>
<div class="col-md-7 col-lg-8 d-flex flex-column">
<%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: transaction.hash %>
<span class="text-nowrap">
<%= render ExplorerWeb.AddressView, "_link.html", address_hash: transaction.from_address_hash, contract: ExplorerWeb.AddressView.contract?(transaction.from_address), locale: @locale %>
&rarr;
<%= render ExplorerWeb.AddressView, "_link.html", address_hash: ExplorerWeb.TransactionView.to_address_hash(transaction), contract: ExplorerWeb.AddressView.contract?(transaction.to_address), locale: @locale %>
</span>
<span>
<span class="ml-1" data-from-now="<%= transaction.block.timestamp %>"></span>
<span class="ml-1">
<%= link(
gettext("Block #%{number}", number: to_string(transaction.block.number)),
to: block_path(@conn, :show, @conn.assigns.locale, transaction.block)
) %>
</span>
</span>
</div>
<div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column justify-content-start text-md-right">
<span class="tile-title">
<%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %> <%= gettext "Ether" %>
</span>
<span><%= ExplorerWeb.TransactionView.formatted_fee(transaction, denomination: :ether) %> <%= gettext "Fee" %></span>
</div>
</div>
</div>
<% end %>
</div>
</div>

@ -35,8 +35,8 @@
<span class="dashboard-banner-network-stats-label"> <span class="dashboard-banner-network-stats-label">
<%= gettext "Total transactions" %> <%= gettext "Total transactions" %>
</span> </span>
<span class="dashboard-banner-network-stats-value"> <span class="dashboard-banner-network-stats-value" data-selector="transaction-count">
<%= @transaction_estimated_count |> Cldr.Number.to_string!(format: "#,###") %> <%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %>
</span> </span>
</div> </div>
<div class="dashboard-banner-network-stats-item"> <div class="dashboard-banner-network-stats-item">
@ -54,7 +54,7 @@
<section class="container"> <section class="container">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<%= 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") %>
<h2 class="card-title"><%= gettext "Blocks" %></h2> <h2 class="card-title"><%= gettext "Blocks" %></h2>
<div class="row" data-selector="chain-block-list"> <div class="row" data-selector="chain-block-list">
<%= for block <- @chain.blocks do %> <%= for block <- @chain.blocks do %>
@ -63,5 +63,21 @@
</div> </div>
</div> </div>
</div> </div>
<%= render ExplorerWeb.ChainView, "_transactions.html", assigns %> <!-- We hardcoded the height on this element to keep the page from bouncing during the intro animation. -->
<div class="card" style="height: 694px;">
<div class="card-body">
<div data-selector="channel-batching-message" style="display:none;">
<div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a>
</div>
</div>
<%= link(gettext("View All Transactions →"), to: transaction_path(ExplorerWeb.Endpoint, :index, Gettext.get_locale), class: "button button--secondary button--xsmall float-right") %>
<h2 class="card-title"><%= gettext "Transactions" %></h2>
<span data-selector="transactions-list">
<%= for transaction <- @chain.transactions do %>
<%= render ExplorerWeb.TransactionView, "_tile.html", locale: @locale, transaction: transaction %>
<% end %>
</span>
</div>
</div>
</section> </section>

@ -1,22 +1,15 @@
<div class="tile tile-type-<%= ExplorerWeb.TransactionView.type_suffix(@transaction) %> fade-in tile-status--<%= ExplorerWeb.TransactionView.status(@transaction) %>" data-transaction-hash="<%= @transaction.hash %>"> <div class="tile tile-type-<%= ExplorerWeb.TransactionView.type_suffix(@transaction) %> tile-status--<%= ExplorerWeb.TransactionView.status(@transaction) %> fade-up" data-test="<%= ExplorerWeb.TransactionView.type_suffix(@transaction) %>" data-transaction-hash="<%= @transaction.hash %>">
<div class="row"> <div class="row" data-test="chain_transaction">
<div class="pl-5 col-md-2 d-flex flex-column align-items-left justify-content-start justify-content-lg-center"> <div class="pl-5 col-md-2 d-flex flex-column align-items-left justify-content-start justify-content-lg-center">
<span class="tile-label"> <span class="tile-label" data-test="transaction_type"> <%= ExplorerWeb.TransactionView.transaction_display_type(@transaction) %></span>
<%= ExplorerWeb.TransactionView.transaction_display_type(@transaction) %> <div class="tile-status-label" data-test="transaction_status"><%= ExplorerWeb.TransactionView.formatted_status(@transaction) %></div>
</span>
<div class="tile-status-label" data-test="transaction_status">
<%= ExplorerWeb.TransactionView.formatted_status(@transaction) %>
</div>
</div> </div>
<div class="col-md-7 col-lg-8 d-flex flex-column"> <div class="col-md-7 col-lg-8 d-flex flex-column">
<%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: @transaction.hash %> <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: @transaction.hash %>
<span class="text-nowrap"> <span class="text-nowrap">
<%= 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: @transaction.from_address_hash, contract: ExplorerWeb.AddressView.contract?(@transaction.from_address), locale: @locale %>
&rarr; &rarr;
<%= render ExplorerWeb.AddressView, "_link.html", address_hash: ExplorerWeb.TransactionView.to_address_hash(@transaction), contract: ExplorerWeb.AddressView.contract?(@transaction.to_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 %>
</span> </span>
<span> <span>
<span data-from-now="<%= @transaction.block.timestamp %>"></span> <span data-from-now="<%= @transaction.block.timestamp %>"></span>
@ -28,11 +21,11 @@
</span> </span>
</span> </span>
</div> </div>
<div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column justify-content-start align-items-end text-md-right"> <div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column justify-content-start text-md-right">
<span class="tile-title"> <span class="tile-title">
<%= ExplorerWeb.TransactionView.value(@transaction, include_label: false) %> <%= gettext "Ether" %> <%= ExplorerWeb.TransactionView.value(@transaction, include_label: false) %> <%= gettext "Ether" %>
</span> </span>
<span class="mr-2 mr-sm-0 text-muted"> <%= ExplorerWeb.TransactionView.formatted_fee(@transaction, denomination: :ether) %> <%= gettext "Fee" %></span> <span><%= ExplorerWeb.TransactionView.formatted_fee(@transaction, denomination: :ether) %> <%= gettext "Fee" %></span>
</div> </div>
</div> </div>
</div> </div>

@ -43,42 +43,24 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-0"><%= gettext "Transactions" %></h2> <div data-selector="channel-batching-message" style="display:none;">
<p><%= gettext("Showing %{count} Validated Transactions", count: Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###")) %></p> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a>
<%= for transaction <- @transactions do %>
<div class="tile tile-type-<%= ExplorerWeb.TransactionView.type_suffix(transaction) %> tile-status--<%= ExplorerWeb.TransactionView.status(transaction) %>" data-test="<%= ExplorerWeb.TransactionView.type_suffix(transaction) %>" data-transaction-hash="<%= transaction.hash %>">
<div class="row justify-content-end" data-test="chain_transaction">
<div class="col-md-3 col-lg-2 d-flex align-items-lg-center">
<div class="d-flex flex-md-column ml-md-4">
<span class="tile-label mr-1 mr-md-0" data-test="transaction_type"> <%= ExplorerWeb.TransactionView.transaction_display_type(transaction) %></span>
<div class="tile-status-label" data-test="transaction_status"><%= ExplorerWeb.TransactionView.formatted_status(transaction) %></div>
</div>
</div>
<div class="col-md-9 col-lg-7 d-flex flex-column">
<%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: transaction.hash %>
<span>
<%= render ExplorerWeb.AddressView, "_link.html", address_hash: transaction.from_address_hash, contract: ExplorerWeb.AddressView.contract?(transaction.from_address), locale: @locale %>
&rarr;
<%= render ExplorerWeb.AddressView, "_link.html", address_hash: ExplorerWeb.TransactionView.to_address_hash(transaction), contract: ExplorerWeb.AddressView.contract?(transaction.to_address), locale: @locale %>
</span>
<span>
<span data-from-now="<%= transaction.block.timestamp %>"></span>
<span class="ml-1">
<%= link(
gettext("Block #%{number}", number: to_string(transaction.block.number)),
to: block_path(@conn, :show, @conn.assigns.locale, transaction.block)
) %>
</span>
</span>
</div> </div>
<div class="col-md-9 col-lg-3 d-flex flex-column flex-md-row flex-lg-column justify-content-start text-lg-right mt-3 mt-lg-0">
<span class="tile-title mr-1 mr-lg-0"><%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %> <%= gettext "Ether" %></span>
<span> <%= ExplorerWeb.TransactionView.formatted_fee(transaction, denomination: :ether) %> <%= gettext "Fee" %></span>
</div> </div>
<div data-selector="channel-disconnected-message" style="display:none;">
<div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer transactions" %></a>
</div> </div>
</div> </div>
<h2 class="card-title mb-0"><%= gettext "Transactions" %></h2>
<p><%= gettext("Showing") %> <span data-selector="transaction-count"><%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %></span> <%= gettext("Validated Transactions") %></p>
<span data-selector="transactions-list">
<%= for transaction <- @transactions do %>
<%= render ExplorerWeb.TransactionView, "_tile.html", locale: @locale, transaction: transaction %>
<% end %> <% end %>
</span>
<%= if @next_page_params do %> <%= if @next_page_params do %>
<%= link( <%= link(
gettext("Older"), gettext("Older"),

@ -15,7 +15,7 @@ msgstr ""
msgid "Copyright %{year} POA" msgid "Copyright %{year} POA"
msgstr "" 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 #: lib/explorer_web/templates/block/overview.html.eex:84
msgid "Gas Used" msgid "Gas Used"
msgstr "" 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:13
#: lib/explorer_web/templates/block_transaction/index.html.eex:26 #: lib/explorer_web/templates/block_transaction/index.html.eex:26
#: lib/explorer_web/templates/block_transaction/index.html.eex:36 #: 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/layout/_topnav.html.eex:18
#: lib/explorer_web/templates/pending_transaction/index.html.eex:46 #: 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" msgid "Transactions"
msgstr "" msgstr ""
@ -62,12 +62,12 @@ msgstr ""
msgid "Difficulty" msgid "Difficulty"
msgstr "" 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 #: lib/explorer_web/templates/block/overview.html.eex:92
msgid "Gas Limit" msgid "Gas Limit"
msgstr "" 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/block/overview.html.eex:70
#: lib/explorer_web/templates/chain/_block.html.eex:9 #: lib/explorer_web/templates/chain/_block.html.eex:9
msgid "Miner" msgid "Miner"
@ -285,10 +285,8 @@ msgstr ""
#: #:
#: lib/explorer_web/templates/address_internal_transaction/_internal_transaction.html.eex:29 #: 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/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/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/overview.html.eex:81
#: lib/explorer_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16 #: lib/explorer_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16
#: lib/explorer_web/views/wei_helpers.ex:71 #: lib/explorer_web/views/wei_helpers.ex:71
@ -355,10 +353,8 @@ msgid "All"
msgstr "" msgstr ""
#: lib/explorer_web/templates/address_transaction/_transaction.html.eex:62 #: 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/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" msgid "Fee"
msgstr "" msgstr ""
@ -469,10 +465,10 @@ msgstr ""
#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:133 #: 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/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/block_transaction/index.html.eex:51
#: lib/explorer_web/templates/pending_transaction/index.html.eex:79 #: 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" msgid "Older"
msgstr "" msgstr ""
@ -547,7 +543,7 @@ msgid "View All Blocks →"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/chain/_transactions.html.eex:3 #: lib/explorer_web/templates/chain/show.html.eex:74
msgid "View All Transactions →" msgid "View All Transactions →"
msgstr "" msgstr ""
@ -568,6 +564,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/address_transaction/index.html.eex:89 #: 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" msgid "Connection Lost, click to load newer transactions"
msgstr "" msgstr ""
@ -583,11 +580,6 @@ msgstr ""
msgid "Internal Transaction" msgid "Internal Transaction"
msgstr "" msgstr ""
#, elixir-format
#: lib/explorer_web/templates/address_transaction/index.html.eex:84
msgid "More messages have come in"
msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/address/overview.html.eex:13 #: lib/explorer_web/templates/address/overview.html.eex:13
#: lib/explorer_web/templates/address/overview.html.eex:66 #: lib/explorer_web/templates/address/overview.html.eex:66
@ -629,9 +621,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/address_transaction/_transaction.html.eex:49 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:49
#: lib/explorer_web/templates/address_transaction/_transaction.html.eex:111 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:111
#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:25 #: lib/explorer_web/templates/transaction/_tile.html.eex:18
#: lib/explorer_web/templates/chain/_transactions.html.eex:25
#: lib/explorer_web/templates/transaction/index.html.eex:69
msgid "Block #%{number}" msgid "Block #%{number}"
msgstr "" msgstr ""
@ -725,8 +715,25 @@ msgid "Transfers"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/block/index.html.eex:29 #: lib/explorer_web/templates/block/_tile.html.eex:15
msgid "%{count} transaction" msgid "%{count} transaction"
msgid_plural "%{count} transactions" msgid_plural "%{count} transactions"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" 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 ""

@ -27,7 +27,7 @@ msgstr "Blocks"
msgid "Copyright %{year} POA" msgid "Copyright %{year} POA"
msgstr "%{year} POA Network Ltd. All rights reserved" 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 #: lib/explorer_web/templates/block/overview.html.eex:84
msgid "Gas Used" msgid "Gas Used"
msgstr "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:13
#: lib/explorer_web/templates/block_transaction/index.html.eex:26 #: lib/explorer_web/templates/block_transaction/index.html.eex:26
#: lib/explorer_web/templates/block_transaction/index.html.eex:36 #: 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/layout/_topnav.html.eex:18
#: lib/explorer_web/templates/pending_transaction/index.html.eex:46 #: 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" msgid "Transactions"
msgstr "Transactions" msgstr "Transactions"
@ -74,12 +74,12 @@ msgstr "Block #%{number} Details"
msgid "Difficulty" msgid "Difficulty"
msgstr "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 #: lib/explorer_web/templates/block/overview.html.eex:92
msgid "Gas Limit" msgid "Gas Limit"
msgstr "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/block/overview.html.eex:70
#: lib/explorer_web/templates/chain/_block.html.eex:9 #: lib/explorer_web/templates/chain/_block.html.eex:9
msgid "Miner" msgid "Miner"
@ -297,10 +297,8 @@ msgstr ""
#: #:
#: lib/explorer_web/templates/address_internal_transaction/_internal_transaction.html.eex:29 #: 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/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/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/overview.html.eex:81
#: lib/explorer_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16 #: lib/explorer_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16
#: lib/explorer_web/views/wei_helpers.ex:71 #: lib/explorer_web/views/wei_helpers.ex:71
@ -367,10 +365,8 @@ msgid "All"
msgstr "" msgstr ""
#: lib/explorer_web/templates/address_transaction/_transaction.html.eex:62 #: 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/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" msgid "Fee"
msgstr "" msgstr ""
@ -481,10 +477,10 @@ msgstr ""
#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:133 #: 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/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/block_transaction/index.html.eex:51
#: lib/explorer_web/templates/pending_transaction/index.html.eex:79 #: 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" msgid "Older"
msgstr "" msgstr ""
@ -559,7 +555,7 @@ msgid "View All Blocks →"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/chain/_transactions.html.eex:3 #: lib/explorer_web/templates/chain/show.html.eex:74
msgid "View All Transactions →" msgid "View All Transactions →"
msgstr "" msgstr ""
@ -580,6 +576,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/address_transaction/index.html.eex:89 #: 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" msgid "Connection Lost, click to load newer transactions"
msgstr "" msgstr ""
@ -595,11 +592,6 @@ msgstr ""
msgid "Internal Transaction" msgid "Internal Transaction"
msgstr "" msgstr ""
#, elixir-format
#: lib/explorer_web/templates/address_transaction/index.html.eex:84
msgid "More messages have come in"
msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/address/overview.html.eex:13 #: lib/explorer_web/templates/address/overview.html.eex:13
#: lib/explorer_web/templates/address/overview.html.eex:66 #: lib/explorer_web/templates/address/overview.html.eex:66
@ -641,9 +633,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/address_transaction/_transaction.html.eex:49 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:49
#: lib/explorer_web/templates/address_transaction/_transaction.html.eex:111 #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:111
#: lib/explorer_web/templates/block_transaction/_transaction.html.eex:25 #: lib/explorer_web/templates/transaction/_tile.html.eex:18
#: lib/explorer_web/templates/chain/_transactions.html.eex:25
#: lib/explorer_web/templates/transaction/index.html.eex:69
msgid "Block #%{number}" msgid "Block #%{number}"
msgstr "" msgstr ""
@ -737,8 +727,25 @@ msgid "Transfers"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/explorer_web/templates/block/index.html.eex:29 #: lib/explorer_web/templates/block/_tile.html.eex:15
msgid "%{count} transaction" msgid "%{count} transaction"
msgid_plural "%{count} transactions" msgid_plural "%{count} transactions"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" 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 ""

@ -43,6 +43,10 @@ defmodule ExplorerWeb.AddressPage do
css("[data-internal-transaction-id='#{id}'] [data-address-hash='#{address_hash}'][data-test='address_hash_link']") css("[data-internal-transaction-id='#{id}'] [data-address-hash='#{address_hash}'][data-test='address_hash_link']")
end 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(%Transaction{hash: transaction_hash}), do: transaction(transaction_hash)
def transaction(%Hash{} = hash) do def transaction(%Hash{} = hash) do

@ -29,6 +29,10 @@ defmodule ExplorerWeb.HomePage do
css("[data-test='chain_transaction']", count: count) css("[data-test='chain_transaction']", count: count)
end end
def transaction(%Transaction{hash: transaction_hash}) do
css("[data-transaction-hash='#{transaction_hash}']")
end
def transaction_status(%Transaction{hash: transaction_hash}) do def transaction_status(%Transaction{hash: transaction_hash}) do
css("[data-transaction-hash='#{transaction_hash}'] [data-test='transaction_status']") css("[data-transaction-hash='#{transaction_hash}'] [data-test='transaction_status']")
end end

@ -19,6 +19,10 @@ defmodule ExplorerWeb.TransactionListPage do
css("[data-transaction-hash='#{hash}'] [data-test='transaction_type']", text: "Contract Creation") css("[data-transaction-hash='#{hash}'] [data-test='transaction_type']", text: "Contract Creation")
end end
def non_loaded_transaction_count(count) do
css("[data-selector='channel-batching-count']", text: count)
end
def transaction(%Transaction{hash: transaction_hash}) do def transaction(%Transaction{hash: transaction_hash}) do
css("[data-transaction-hash='#{transaction_hash}']") css("[data-transaction-hash='#{transaction_hash}']")
end end

@ -250,6 +250,24 @@ defmodule ExplorerWeb.ViewingAddressesTest do
|> assert_has(AddressPage.transaction(transaction2)) |> assert_has(AddressPage.transaction(transaction2))
end 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 test "transaction count live updates", %{addresses: addresses, session: session} do
session session
|> AddressPage.visit_page(addresses.lincoln) |> AddressPage.visit_page(addresses.lincoln)

@ -93,4 +93,13 @@ defmodule ExplorerWeb.ViewingBlocksTest do
|> BlockListPage.visit_page() |> BlockListPage.visit_page()
|> assert_has(BlockListPage.block(block)) |> assert_has(BlockListPage.block(block))
end 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 end

@ -14,7 +14,8 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
gas_used: 123_987 gas_used: 123_987
}) })
4 [oldest_transaction | _] =
3
|> insert_list(:transaction) |> insert_list(:transaction)
|> with_block() |> with_block()
@ -24,7 +25,13 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
lincoln = insert(:address) lincoln = insert(:address)
taft = 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 :transaction
|> insert( |> insert(
value: Wei.from(Decimal.new(5656), :ether), 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) |> 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. internal = insert(:internal_transaction, index: 0, transaction: newest_transaction)
txn_from_lincoln =
:transaction
|> insert(from_address: lincoln, to_address: taft)
|> with_block(block)
internal = insert(:internal_transaction, index: 0, transaction: transaction)
{:ok, {:ok,
%{ %{
@ -56,7 +57,8 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
internal: internal, internal: internal,
lincoln: lincoln, lincoln: lincoln,
taft: taft, taft: taft,
transaction: transaction, first_shown_transaction: newest_transaction,
last_shown_transaction: oldest_transaction,
txn_from_lincoln: txn_from_lincoln txn_from_lincoln: txn_from_lincoln
}} }}
end end
@ -77,6 +79,43 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
|> assert_has(HomePage.transactions(count: 5)) |> assert_has(HomePage.transactions(count: 5))
end 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 test "contract creation is shown for to_address on home page", %{session: session} do
transaction = transaction =
:transaction :transaction
@ -90,7 +129,11 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
|> assert_has(HomePage.contract_creation(internal_transaction)) |> assert_has(HomePage.contract_creation(internal_transaction))
end 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 session
|> TransactionListPage.visit_page() |> TransactionListPage.visit_page()
|> assert_has(TransactionListPage.transaction(transaction)) |> assert_has(TransactionListPage.transaction(transaction))
@ -119,23 +162,50 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
|> TransactionListPage.visit_page() |> TransactionListPage.visit_page()
|> assert_has(TransactionListPage.contract_creation(transaction)) |> assert_has(TransactionListPage.contract_creation(transaction))
end 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 end
describe "viewing a transaction page" do 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 session
|> TransactionListPage.visit_page() |> TransactionListPage.visit_page()
|> TransactionListPage.click_transaction(transaction) |> TransactionListPage.click_transaction(transaction)
|> assert_has(TransactionPage.detail_hash(transaction)) |> assert_has(TransactionPage.detail_hash(transaction))
end 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 session
|> TransactionPage.visit_page(transaction) |> TransactionPage.visit_page(transaction)
|> assert_has(TransactionPage.detail_hash(transaction)) |> assert_has(TransactionPage.detail_hash(transaction))
end 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 session
|> TransactionPage.visit_page(transaction) |> TransactionPage.visit_page(transaction)
|> TransactionPage.click_logs() |> TransactionPage.click_logs()
@ -145,7 +215,7 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
test "can visit an address from the transaction logs page", %{ test "can visit an address from the transaction logs page", %{
lincoln: lincoln, lincoln: lincoln,
session: session, session: session,
transaction: transaction first_shown_transaction: transaction
} do } do
session session
|> TransactionLogsPage.visit_page(transaction) |> TransactionLogsPage.visit_page(transaction)
@ -153,7 +223,7 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
|> assert_has(AddressPage.detail_hash(lincoln)) |> assert_has(AddressPage.detail_hash(lincoln))
end 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)] blocks = [insert(:block, number: transaction.block_number + 10)]
TransactionPage.visit_page(session, transaction) TransactionPage.visit_page(session, transaction)

Loading…
Cancel
Save