Merge pull request #1167 from poanetwork/wsa-async-load-blocks-validated

Blocks Validated page async load
pull/1170/head v1.2.1-beta
Andrew Cravenho 6 years ago committed by GitHub
commit 4236b39a20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 40
      apps/block_scout_web/assets/__tests__/pages/address.js
  2. 46
      apps/block_scout_web/assets/__tests__/pages/address/validations.js
  3. 1
      apps/block_scout_web/assets/js/app.js
  4. 27
      apps/block_scout_web/assets/js/pages/address.js
  5. 64
      apps/block_scout_web/assets/js/pages/address/validations.js
  6. 53
      apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex
  7. 45
      apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex
  8. 10
      apps/block_scout_web/priv/gettext/default.pot
  9. 10
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po

@ -1,56 +1,28 @@
import { reducer, initialState } from '../../js/pages/address' import { reducer, initialState } from '../../js/pages/address'
describe('RECEIVED_NEW_BLOCK', () => { describe('RECEIVED_NEW_BLOCK', () => {
test('with new block', () => { test('increases validation count', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, { validationCount: 30 })
validationCount: 30,
validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }]
})
const action = { const action = {
type: 'RECEIVED_NEW_BLOCK', type: 'RECEIVED_NEW_BLOCK',
msg: { blockNumber: 2, blockHtml: 'test 2' } blockHtml: 'test 2'
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.validationCount).toEqual(31) expect(output.validationCount).toEqual(31)
expect(output.validatedBlocks).toEqual([
{ blockNumber: 2, blockHtml: 'test 2' },
{ blockNumber: 1, blockHtml: 'test 1' }
])
}) })
test('when channel has been disconnected', () => { test('when channel has been disconnected does not increase validation count', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
channelDisconnected: true, channelDisconnected: true,
validationCount: 30, validationCount: 30
validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }]
}) })
const action = { const action = {
type: 'RECEIVED_NEW_BLOCK', type: 'RECEIVED_NEW_BLOCK',
msg: { blockNumber: 2, blockHtml: 'test 2' } blockHtml: 'test 2'
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.validationCount).toEqual(30) expect(output.validationCount).toEqual(30)
expect(output.validatedBlocks).toEqual([
{ blockNumber: 1, blockHtml: 'test 1' }
])
})
test('beyond page one', () => {
const state = Object.assign({}, initialState, {
beyondPageOne: true,
validationCount: 30,
validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }]
})
const action = {
type: 'RECEIVED_NEW_BLOCK',
msg: { blockNumber: 2, blockHtml: 'test 2' }
}
const output = reducer(state, action)
expect(output.validationCount).toEqual(31)
expect(output.validatedBlocks).toEqual([
{ blockNumber: 1, blockHtml: 'test 1' }
])
}) })
}) })

@ -0,0 +1,46 @@
import { reducer, initialState } from '../../../js/pages/address/validations'
describe('RECEIVED_NEW_BLOCK', () => {
test('adds new block to the top of the list', () => {
const state = Object.assign({}, initialState, {
items: ['test 1']
})
const action = {
type: 'RECEIVED_NEW_BLOCK',
blockHtml: 'test 2'
}
const output = reducer(state, action)
expect(output.items).toEqual(['test 2', 'test 1'])
})
test('does nothing beyond page one', () => {
const state = Object.assign({}, initialState, {
beyondPageOne: true,
channelDisconnected: false,
items: ['test 1']
})
const action = {
type: 'RECEIVED_NEW_BLOCK',
blockHtml: 'test 2'
}
const output = reducer(state, action)
expect(output.items).toEqual(['test 1'])
})
test('does nothing when channel has been disconnected', () => {
const state = Object.assign({}, initialState, {
channelDisconnected: true,
items: ['test 1']
})
const action = {
type: 'RECEIVED_NEW_BLOCK',
blockHtml: 'test 2'
}
const output = reducer(state, action)
expect(output.items).toEqual(['test 1'])
})
})

@ -21,6 +21,7 @@ import 'bootstrap'
import './locale' import './locale'
import './pages/address' import './pages/address'
import './pages/address/validations'
import './pages/blocks' import './pages/blocks'
import './pages/chain' import './pages/chain'
import './pages/pending_transactions' import './pages/pending_transactions'

@ -26,7 +26,6 @@ export const initialState = {
transactions: [], transactions: [],
internalTransactions: [], internalTransactions: [],
internalTransactionsBatch: [], internalTransactionsBatch: [],
validatedBlocks: [],
beyondPageOne: null, beyondPageOne: null,
@ -53,15 +52,7 @@ function baseReducer (state = initialState, action) {
if (state.channelDisconnected) return state if (state.channelDisconnected) return state
const validationCount = state.validationCount + 1 const validationCount = state.validationCount + 1
return Object.assign({}, state, { validationCount })
if (state.beyondPageOne) return Object.assign({}, state, { validationCount })
return Object.assign({}, state, {
validatedBlocks: [
action.msg,
...state.validatedBlocks
],
validationCount
})
} }
case 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH': { case 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH': {
if (state.channelDisconnected || state.beyondPageOne) return state if (state.channelDisconnected || state.beyondPageOne) return state
@ -209,22 +200,6 @@ const elements = {
$channelBatching.show() $channelBatching.show()
$el[0].innerHTML = numeral(state.internalTransactionsBatch.length).format() $el[0].innerHTML = numeral(state.internalTransactionsBatch.length).format()
} }
},
'[data-selector="validations-list"]': {
load ($el) {
return {
validatedBlocks: $el.children().map((index, el) => ({
blockNumber: parseInt(el.dataset.blockNumber),
blockHtml: el.outerHTML
})).toArray()
}
},
render ($el, state, oldState) {
if (oldState.validatedBlocks === state.validatedBlocks) return
const container = $el[0]
const newElements = _.map(state.validatedBlocks, ({ blockHtml }) => $(blockHtml)[0])
listMorph(container, newElements, { key: 'dataset.blockNumber' })
}
} }
} }

@ -0,0 +1,64 @@
import $ from 'jquery'
import _ from 'lodash'
import humps from 'humps'
import socket from '../../socket'
import { connectElements } from '../../lib/redux_helpers.js'
import { createAsyncLoadStore } from '../../lib/async_listing_load.js'
export const initialState = {
addressHash: null,
channelDisconnected: false
}
export function reducer (state = initialState, action) {
switch (action.type) {
case 'PAGE_LOAD':
case 'ELEMENTS_LOAD': {
return Object.assign({}, state, _.omit(action, 'type'))
}
case 'CHANNEL_DISCONNECTED': {
return Object.assign({}, state, { channelDisconnected: true })
}
case 'RECEIVED_NEW_BLOCK': {
if (state.channelDisconnected) return state
if (state.beyondPageOne) return state
return Object.assign({}, state, {
items: [
action.blockHtml,
...state.items
]
})
}
default:
return state
}
}
const elements = {
'[data-selector="channel-disconnected-message"]': {
render ($el, state) {
if (state.channelDisconnected) $el.show()
}
}
}
if ($('[data-page="blocks-validated"]').length) {
const store = createAsyncLoadStore(reducer, initialState, 'dataset.blockNumber')
connectElements({ store, elements })
const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash
store.dispatch({
type: 'PAGE_LOAD',
addressHash
})
const blocksChannel = socket.channel(`blocks:${addressHash}`, {})
blocksChannel.join()
blocksChannel.onError(() => store.dispatch({
type: 'CHANNEL_DISCONNECTED'
}))
blocksChannel.on('new_block', (msg) => store.dispatch({
type: 'RECEIVED_NEW_BLOCK',
blockHtml: humps.camelizeKeys(msg).blockHtml
}))
}

@ -5,12 +5,16 @@ defmodule BlockScoutWeb.AddressValidationController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias Explorer.{Chain, Market} import BlockScoutWeb.Chain,
only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.BlockView
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
alias Explorer.{Chain, Market}
alias Phoenix.View
def index(conn, %{"address_id" => address_hash_string} = params) do def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash) do {:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash) do
full_options = full_options =
@ -28,15 +32,52 @@ defmodule BlockScoutWeb.AddressValidationController do
blocks_plus_one = Chain.get_blocks_validated_by_address(full_options, address) blocks_plus_one = Chain.get_blocks_validated_by_address(full_options, address)
{blocks, next_page} = split_list_by_page(blocks_plus_one) {blocks, next_page} = split_list_by_page(blocks_plus_one)
next_page_path =
case next_page_params(next_page, blocks, params) do
nil ->
nil
next_page_params ->
address_validation_path(
conn,
:index,
address_hash_string,
Map.delete(next_page_params, "type")
)
end
items =
Enum.map(blocks, fn block ->
View.render_to_string(
BlockView,
"_tile.html",
conn: conn,
block: block,
block_type: BlockView.block_type(block)
)
end)
json(conn, %{items: items, next_page_path: next_page_path})
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash) do
render( render(
conn, conn,
"index.html", "index.html",
address: address, address: address,
blocks: blocks, current_path: current_path(conn),
transaction_count: transaction_count(address), transaction_count: transaction_count(address),
validation_count: validation_count(address), validation_count: validation_count(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null()
next_page_params: next_page_params(next_page, blocks, params)
) )
else else
:error -> :error ->

@ -1,7 +1,7 @@
<section class="container"> <section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %> <%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<section> <section data-page="blocks-validated">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<!-- DESKTOP TAB NAV --> <!-- DESKTOP TAB NAV -->
@ -98,7 +98,7 @@
</ul> </ul>
</div> </div>
<div class="card-body"> <div data-async-listing="<%= @current_path %>" class="card-body">
<div data-selector="channel-disconnected-message" style="display: none;"> <div data-selector="channel-disconnected-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-danger"> <div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer validations" %></a> <a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer validations" %></a>
@ -106,24 +106,31 @@
</div> </div>
<h2 class="card-title"><%=gettext("Blocks Validated")%></h2> <h2 class="card-title"><%=gettext("Blocks Validated")%></h2>
<span data-selector="validations-list"> <div data-loading-message class="tile tile-muted text-center mt-3">
<%= for block <- @blocks do %> <span class="loading-spinner-small mr-2">
<%= render BlockScoutWeb.BlockView, "_tile.html", block: block, block_type: BlockScoutWeb.BlockView.block_type(block)%> <span class="loading-spinner-block-1"></span>
<% end %> <span class="loading-spinner-block-2"></span>
</span> </span>
<div> <%= gettext("Loading...") %>
<%= if @next_page_params do %> </div>
<%= link( <div data-empty-response-message class="tile tile-muted text-center" style="display: none;">
gettext("Older"), <span><%= gettext "There are no blocks validated by this address." %></span>
class: "button button-secondary button-sm float-right mt-3", </div>
to: address_validation_path( <button data-error-message class="alert alert-danger col-12 text-left" style="display: none;">
@conn, <span class="alert-link">
:index, <%= gettext "Something went wrong, click to reload." %>
@address, </span>
@next_page_params </button>
) <div data-items data-selector="validations-list"></div>
) %> <a data-next-page-button href="#" class="button button-secondary button-small float-right mt-4" style="display: none;">
<% end %> <%= gettext("Older") %>
</a>
<div data-loading-button class="button button-secondary button-small float-right mt-4" style="display: none;">
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading...") %>
</div> </div>
</div> <!-- Card Body --> </div> <!-- Card Body -->
</div> <!-- Card --> </div> <!-- Card -->

@ -639,7 +639,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31
#: lib/block_scout_web/templates/address_validation/index.html.eex:117 #: lib/block_scout_web/templates/address_validation/index.html.eex:126
#: lib/block_scout_web/templates/block/index.html.eex:30 #: lib/block_scout_web/templates/block/index.html.eex:30
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50 #: lib/block_scout_web/templates/block_transaction/index.html.eex:50
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:41 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:41
@ -1188,6 +1188,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_validation/index.html.eex:114
#: lib/block_scout_web/templates/address_validation/index.html.eex:133
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -1417,7 +1419,13 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/address_validation/index.html.eex:121
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:23 #: lib/block_scout_web/templates/tokens/holder/index.html.eex:23
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
msgid "Something went wrong, click to reload." msgid "Something went wrong, click to reload."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_validation/index.html.eex:117
msgid "There are no blocks validated by this address."
msgstr ""

@ -639,7 +639,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31
#: lib/block_scout_web/templates/address_validation/index.html.eex:117 #: lib/block_scout_web/templates/address_validation/index.html.eex:126
#: lib/block_scout_web/templates/block/index.html.eex:30 #: lib/block_scout_web/templates/block/index.html.eex:30
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50 #: lib/block_scout_web/templates/block_transaction/index.html.eex:50
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:41 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:41
@ -1188,6 +1188,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_validation/index.html.eex:114
#: lib/block_scout_web/templates/address_validation/index.html.eex:133
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -1417,7 +1419,13 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/address_validation/index.html.eex:121
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:23 #: lib/block_scout_web/templates/tokens/holder/index.html.eex:23
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
msgid "Something went wrong, click to reload." msgid "Something went wrong, click to reload."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_validation/index.html.eex:117
msgid "There are no blocks validated by this address."
msgstr ""

Loading…
Cancel
Save