Merge pull request #1279 from poanetwork/wsa-async-load-home-page-transactions

Async load home page transactions
pull/1281/head
William Sanches 6 years ago committed by GitHub
commit d81c5dd765
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      apps/block_scout_web/assets/__tests__/pages/chain.js
  2. 42
      apps/block_scout_web/assets/js/pages/chain.js
  3. 13
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  4. 35
      apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex
  5. 2
      apps/block_scout_web/lib/block_scout_web/router.ex
  6. 17
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  7. 6
      apps/block_scout_web/priv/gettext/default.pot
  8. 6
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  9. 35
      apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs
  10. 54
      apps/block_scout_web/test/block_scout_web/controllers/recent_transactions_controller_test.exs

@ -214,8 +214,8 @@ test('RECEIVED_NEW_EXCHANGE_RATE', () => {
}) })
describe('RECEIVED_NEW_TRANSACTION_BATCH', () => { describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
test('single transaction', () => { test('single transaction with no loading or errors', () => {
const state = initialState const state = Object.assign(initialState, { transactionsLoading: false, transactionError: false } )
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH', type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{ msgs: [{
@ -228,6 +228,34 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.transactionsBatch.length).toEqual(0) expect(output.transactionsBatch.length).toEqual(0)
expect(output.transactionCount).toEqual(1) expect(output.transactionCount).toEqual(1)
}) })
test('single transaction with error loading first transactions', () => {
const state = Object.assign({}, initialState, { transactionsError: true } )
const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{
transactionHtml: 'test'
}]
}
const output = reducer(state, action)
expect(output.transactions).toEqual([])
expect(output.transactionsBatch.length).toEqual(0)
expect(output.transactionCount).toEqual(1)
})
test('single transaction while loading', () => {
const state = Object.assign({}, initialState, { transactionsLoading: true } )
const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{
transactionHtml: 'test'
}]
}
const output = reducer(state, action)
expect(output.transactions).toEqual([])
expect(output.transactionsBatch.length).toEqual(0)
expect(output.transactionCount).toEqual(1)
})
test('large batch of transactions', () => { test('large batch of transactions', () => {
const state = initialState const state = initialState
const action = { const action = {

@ -19,6 +19,8 @@ export const initialState = {
blocks: [], blocks: [],
transactions: [], transactions: [],
transactionsBatch: [], transactionsBatch: [],
transactionsError: false,
transactionsLoading: true,
transactionCount: null, transactionCount: null,
usdMarketCap: null usdMarketCap: null
} }
@ -62,6 +64,10 @@ function baseReducer (state = initialState, action) {
const transactionCount = state.transactionCount + action.msgs.length const transactionCount = state.transactionCount + action.msgs.length
if (state.transactionsLoading || state.transactionsError) {
return Object.assign({}, state, { transactionCount })
}
if (!state.transactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) { if (!state.transactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) {
return Object.assign({}, state, { return Object.assign({}, state, {
transactions: [ transactions: [
@ -80,6 +86,14 @@ function baseReducer (state = initialState, action) {
}) })
} }
} }
case 'START_TRANSACTIONS_FETCH':
return Object.assign({}, state, { transactionsError: false, transactionsLoading: true })
case 'TRANSACTIONS_FETCHED':
return Object.assign({}, state, { transactions: [...action.msg.transactions] })
case 'TRANSACTIONS_FETCH_ERROR':
return Object.assign({}, state, { transactionsError: true })
case 'FINISH_TRANSACTIONS_FETCH':
return Object.assign({}, state, { transactionsLoading: false })
default: default:
return state return state
} }
@ -158,14 +172,19 @@ const elements = {
listMorph(container, newElements, { key: 'dataset.blockNumber', horizontal: true }) listMorph(container, newElements, { key: 'dataset.blockNumber', horizontal: true })
} }
}, },
'[data-selector="transactions-list"] [data-selector="error-message"]': {
render ($el, state, oldState) {
$el.toggle(state.transactionsError)
}
},
'[data-selector="transactions-list"] [data-selector="loading-message"]': {
render ($el, state, oldState) {
$el.toggle(state.transactionsLoading)
}
},
'[data-selector="transactions-list"]': { '[data-selector="transactions-list"]': {
load ($el) { load ($el) {
return { return { transactionsPath: $el[0].dataset.transactionsPath }
transactions: $el.children().map((index, el) => ({
transactionHash: el.dataset.identifierHash,
transactionHtml: el.outerHTML
})).toArray()
}
}, },
render ($el, state, oldState) { render ($el, state, oldState) {
if (oldState.transactions === state.transactions) return if (oldState.transactions === state.transactions) return
@ -188,6 +207,8 @@ const $chainDetailsPage = $('[data-page="chain-details"]')
if ($chainDetailsPage.length) { if ($chainDetailsPage.length) {
const store = createStore(reducer) const store = createStore(reducer)
connectElements({ store, elements }) connectElements({ store, elements })
loadTransactions(store)
$('[data-selector="transactions-list"] [data-selector="error-message"]').on('click', _event => loadTransactions(store))
exchangeRateChannel.on('new_rate', (msg) => store.dispatch({ exchangeRateChannel.on('new_rate', (msg) => store.dispatch({
type: 'RECEIVED_NEW_EXCHANGE_RATE', type: 'RECEIVED_NEW_EXCHANGE_RATE',
@ -216,6 +237,15 @@ if ($chainDetailsPage.length) {
}))) })))
} }
function loadTransactions (store) {
const path = store.getState().transactionsPath
store.dispatch({type: 'START_TRANSACTIONS_FETCH'})
$.getJSON(path)
.done(response => store.dispatch({type: 'TRANSACTIONS_FETCHED', msg: humps.camelizeKeys(response)}))
.fail(() => store.dispatch({type: 'TRANSACTIONS_FETCH_ERROR'}))
.always(() => store.dispatch({type: 'FINISH_TRANSACTIONS_FETCH'}))
}
export function placeHolderBlock (blockNumber) { export function placeHolderBlock (blockNumber) {
return ` return `
<div <div

@ -14,17 +14,6 @@ defmodule BlockScoutWeb.ChainController do
transaction_estimated_count = Chain.transaction_estimated_count() transaction_estimated_count = Chain.transaction_estimated_count()
transactions =
Chain.recent_collated_transactions(
necessity_by_association: %{
:block => :required,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional
},
paging_options: %PagingOptions{page_size: 5}
)
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
market_history_data = market_history_data =
@ -43,7 +32,7 @@ defmodule BlockScoutWeb.ChainController do
available_supply: available_supply(Chain.supply_for_days(30), exchange_rate), available_supply: available_supply(Chain.supply_for_days(30), exchange_rate),
market_history_data: market_history_data, market_history_data: market_history_data,
transaction_estimated_count: transaction_estimated_count, transaction_estimated_count: transaction_estimated_count,
transactions: transactions transactions_path: recent_transactions_path(conn, :index)
) )
end end

@ -0,0 +1,35 @@
defmodule BlockScoutWeb.RecentTransactionsController do
use BlockScoutWeb, :controller
alias Explorer.{Chain, PagingOptions}
alias Explorer.Chain.Hash
alias Phoenix.View
def index(conn, _params) do
with true <- ajax?(conn) do
recent_transactions =
Chain.recent_collated_transactions(
necessity_by_association: %{
:block => :required,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional
},
paging_options: %PagingOptions{page_size: 5}
)
transactions =
Enum.map(recent_transactions, fn transaction ->
%{
transaction_hash: Hash.to_string(transaction.hash),
transaction_html:
View.render_to_string(BlockScoutWeb.TransactionView, "_tile.html", transaction: transaction)
}
end)
json(conn, %{transactions: transactions})
else
_ -> unprocessable_entity(conn)
end
end
end

@ -76,6 +76,8 @@ defmodule BlockScoutWeb.Router do
resources("/pending_transactions", PendingTransactionController, only: [:index]) resources("/pending_transactions", PendingTransactionController, only: [:index])
resources("/recent_transactions", RecentTransactionsController, only: [:index])
get("/txs", TransactionController, :index) get("/txs", TransactionController, :index)
resources "/tx", TransactionController, only: [:show] do resources "/tx", TransactionController, only: [:show] do

@ -72,10 +72,19 @@
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions 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>
<span data-selector="transactions-list"> <span data-selector="transactions-list" data-transactions-path="<%= @transactions_path %>">
<%= for transaction <- @transactions do %> <button data-selector="error-message" class="alert alert-danger col-12 text-left" style="display: none;">
<%= render BlockScoutWeb.TransactionView, "_tile.html", transaction: transaction %> <span class="alert-link">
<% end %> <%= gettext "Something went wrong, click to retry." %>
</span>
</button>
<div data-selector="loading-message" class="tile tile-muted text-center mt-3">
<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>
</span> </span>
</div> </div>
</div> </div>

@ -1189,6 +1189,7 @@ msgstr ""
#: 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:63 #: lib/block_scout_web/templates/address_validation/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:82 #: lib/block_scout_web/templates/address_validation/index.html.eex:82
#: lib/block_scout_web/templates/chain/show.html.eex:86
#: 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 ""
@ -1532,3 +1533,8 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:5 #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:5
msgid "Emission Contract" msgid "Emission Contract"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:78
msgid "Something went wrong, click to retry."
msgstr ""

@ -1189,6 +1189,7 @@ msgstr ""
#: 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:63 #: lib/block_scout_web/templates/address_validation/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:82 #: lib/block_scout_web/templates/address_validation/index.html.eex:82
#: lib/block_scout_web/templates/chain/show.html.eex:86
#: 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 ""
@ -1532,3 +1533,8 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:5 #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:5
msgid "Emission Contract" msgid "Emission Contract"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:78
msgid "Something went wrong, click to retry."
msgstr ""

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.ChainControllerTest do
import BlockScoutWeb.Router.Helpers, only: [chain_path: 2, block_path: 3, transaction_path: 3, address_path: 3] import BlockScoutWeb.Router.Helpers, only: [chain_path: 2, block_path: 3, transaction_path: 3, address_path: 3]
alias Explorer.Chain.Block alias Explorer.Chain.{Block, Hash}
alias Explorer.Counters.AddressesWithBalanceCounter alias Explorer.Counters.AddressesWithBalanceCounter
describe "GET index/2" do describe "GET index/2" do
@ -41,39 +41,6 @@ defmodule BlockScoutWeb.ChainControllerTest do
refute(Enum.member?(conn.assigns.blocks, old_block)) refute(Enum.member?(conn.assigns.blocks, old_block))
end end
test "only returns transactions with an associated block", %{conn: conn} do
associated =
:transaction
|> insert()
|> with_block()
unassociated = insert(:transaction)
start_supervised!(AddressesWithBalanceCounter)
AddressesWithBalanceCounter.consolidate()
conn = get(conn, "/")
transaction_hashes = Enum.map(conn.assigns.transactions, fn transaction -> transaction.hash end)
assert(Enum.member?(transaction_hashes, associated.hash))
refute(Enum.member?(transaction_hashes, unassociated.hash))
end
test "returns a transaction", %{conn: conn} do
transaction =
:transaction
|> insert()
|> with_block()
start_supervised!(AddressesWithBalanceCounter)
AddressesWithBalanceCounter.consolidate()
conn = get(conn, "/")
assert(List.first(conn.assigns.transactions).hash == transaction.hash)
end
test "returns market history data", %{conn: conn} do test "returns market history data", %{conn: conn} do
today = Date.utc_today() today = Date.utc_today()
for day <- -40..0, do: insert(:market_history, date: Date.add(today, day)) for day <- -40..0, do: insert(:market_history, date: Date.add(today, day))

@ -0,0 +1,54 @@
defmodule BlockScoutWeb.RecentTransactionsControllerTest do
use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [recent_transactions_path: 2]
alias Explorer.Chain.Hash
describe "GET index/2" do
test "returns a transaction", %{conn: conn} do
transaction =
:transaction
|> insert()
|> with_block()
conn =
conn
|> put_req_header("x-requested-with", "xmlhttprequest")
|> get(recent_transactions_path(conn, :index))
assert response = json_response(conn, 200)["transactions"]
response_hashes = Enum.map(response, & &1["transaction_hash"])
assert Enum.member?(response_hashes, Hash.to_string(transaction.hash))
end
test "only returns transactions with an associated block", %{conn: conn} do
associated =
:transaction
|> insert()
|> with_block()
unassociated = insert(:transaction)
conn =
conn
|> put_req_header("x-requested-with", "xmlhttprequest")
|> get(recent_transactions_path(conn, :index))
assert response = json_response(conn, 200)["transactions"]
response_hashes = Enum.map(response, & &1["transaction_hash"])
assert Enum.member?(response_hashes, Hash.to_string(associated.hash))
refute Enum.member?(response_hashes, Hash.to_string(unassociated.hash))
end
test "only responds to ajax requests", %{conn: conn} do
conn = get(conn, recent_transactions_path(conn, :index))
assert conn.status == 422
end
end
end
Loading…
Cancel
Save