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> [%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

@ -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

@ -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, {

@ -1,26 +1,134 @@
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 action = {
type: 'CHANNEL_DISCONNECTED'
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{
transactionHtml: 'test'
}]
}
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, {
newBlock: 'last new block'
newTransactions: ['test 1']
})
const action = {
type: 'RECEIVED_NEW_BLOCK',
msg: {
homepageBlockHtml: 'new block'
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.newBlock).toEqual('new block')
expect(output.newTransactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(22)
})
})

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

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

@ -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'

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

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

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

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

@ -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

@ -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

@ -18,7 +18,7 @@
<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>
<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 %>
<span class="mr-4" data-test="address_contract_creator">

@ -81,7 +81,7 @@
<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" 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 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">
<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 %>
<div class="tile">
<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 %>
<%= render ExplorerWeb.BlockView, "_tile.html", locale: @locale, block: block %>
<% 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>
<% end %>
<%= if @next_page_params do %>
<%= link(

@ -36,7 +36,7 @@
<h2 class="card-title"><%= gettext "Transactions" %></h2>
<span data-selector="transactions-list">
<%= for transaction <- @transactions do %>
<%= render "_transaction.html", locale: @locale, transaction: transaction %>
<%= render ExplorerWeb.TransactionView, "_tile.html", locale: @locale, transaction: transaction %>
<% end %>
</span>
<% 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">
<%= link(@block, to: block_path(ExplorerWeb.Endpoint, :show, @locale, @block), class: "tile-title") %>
<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">
<%= gettext "Total transactions" %>
</span>
<span class="dashboard-banner-network-stats-value">
<%= @transaction_estimated_count |> Cldr.Number.to_string!(format: "#,###") %>
<span class="dashboard-banner-network-stats-value" data-selector="transaction-count">
<%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %>
</span>
</div>
<div class="dashboard-banner-network-stats-item">
@ -54,7 +54,7 @@
<section class="container">
<div class="card">
<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>
<div class="row" data-selector="chain-block-list">
<%= for block <- @chain.blocks do %>
@ -63,5 +63,21 @@
</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>

@ -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="row">
<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" 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">
<span class="tile-label">
<%= ExplorerWeb.TransactionView.transaction_display_type(@transaction) %>
</span>
<div class="tile-status-label" data-test="transaction_status">
<%= ExplorerWeb.TransactionView.formatted_status(@transaction) %>
</div>
<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 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 data-from-now="<%= @transaction.block.timestamp %>"></span>
@ -28,11 +21,11 @@
</span>
</span>
</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">
<%= ExplorerWeb.TransactionView.value(@transaction, include_label: false) %> <%= gettext "Ether" %>
</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>

@ -43,42 +43,24 @@
</div>
<div class="card-body">
<h2 class="card-title mb-0"><%= gettext "Transactions" %></h2>
<p><%= gettext("Showing %{count} Validated Transactions", count: Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###")) %></p>
<%= 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 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 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 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>
<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 %>
</span>
<%= if @next_page_params do %>
<%= link(
gettext("Older"),

@ -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 ""

@ -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 ""

@ -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

@ -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

@ -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

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

@ -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

@ -14,7 +14,8 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
gas_used: 123_987
})
4
[oldest_transaction | _] =
3
|> insert_list(:transaction)
|> with_block()
@ -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
@ -77,6 +79,43 @@ defmodule ExplorerWeb.ViewingTransactionsTest do
|> 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
transaction =
:transaction
@ -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)

Loading…
Cancel
Save