make internal transactions load async

pull/1133/head
Gustavo Santos Ferreira 6 years ago
parent 2895b6c61b
commit 833cc8aae8
  1. 39
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  2. 46
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  3. 77
      apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs

@ -8,10 +8,12 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.InternalTransactionView
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
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.hash_to_address(address_hash) do {:ok, address} <- Chain.hash_to_address(address_hash) do
full_options = full_options =
@ -28,14 +30,45 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options) internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options)
{internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one) {internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one)
next_page_path =
case next_page_params(next_page, internal_transactions, params) do
nil ->
nil
next_page_params ->
address_internal_transaction_path(conn, :index, address_hash, Map.delete(next_page_params, "type"))
end
internal_transactions_json =
Enum.map(internal_transactions, fn internal_transaction ->
View.render_to_string(
InternalTransactionView,
"_tile.html",
current_address: address,
internal_transaction: internal_transaction
)
end)
json(conn, %{items: internal_transactions_json, next_page_path: next_page_path})
else
:error ->
not_found(conn)
{:error, :not_found} ->
not_found(conn)
end
end
def index(conn, %{"address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
render( render(
conn, conn,
"index.html", "index.html",
address: address, address: address,
next_page_params: next_page_params(next_page, internal_transactions, params), current_path: current_path(conn),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"], filter: params["filter"],
internal_transactions: internal_transactions,
transaction_count: transaction_count(address), transaction_count: transaction_count(address),
validation_count: validation_count(address) validation_count: validation_count(address)
) )

@ -7,7 +7,7 @@
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> <%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
</div> </div>
<div class="card-body"> <div class="card-body" data-async-listing="<%= @current_path %>">
<div data-selector="channel-batching-message" style="display: none;"> <div data-selector="channel-batching-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-info"> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More internal transactions have come in" %></a> <a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More internal transactions have come in" %></a>
@ -54,31 +54,33 @@
) %> ) %>
</div> </div>
</div> </div>
<h2 class="card-title"><%= gettext "Internal Transactions" %></h2> <h2 class="card-title"><%= gettext "Internal Transactions" %></h2>
<%= if Enum.count(@internal_transactions) > 0 do %> <button data-error-message class="alert alert-danger col-12 text-left" style="display: none;">
<span data-selector="internal-transactions-list"> <span href="#" class="alert-link"><%= gettext("Something went wrong, click to reload.") %></span>
<%= for internal_transaction <- @internal_transactions do %> </button>
<%= render BlockScoutWeb.InternalTransactionView, "_tile.html", current_address: @address, internal_transaction: internal_transaction %> <div data-empty-response-message style="display: none;">
<% end %>
</span>
<% else %>
<div class="tile tile-muted text-center"> <div class="tile tile-muted text-center">
<span data-selector="empty-internal-transactions-list"><%= gettext "There are no internal transactions for this address." %></span> <span data-selector="empty-internal-transactions-list"><%= gettext "There are no internal transactions for this address." %></span>
</div> </div>
<% end %> </div>
<div> <div data-loading-message class="tile tile-muted text-center mt-3">
<%= if @next_page_params do %> <span class="loading-spinner-small mr-2">
<%= link( <span class="loading-spinner-block-1"></span>
gettext("Older"), <span class="loading-spinner-block-2"></span>
class: "button button-secondary button-sm float-right mt-3", </span>
to: address_internal_transaction_path( <%= gettext("Loading") %>...
@conn, </div>
:index, <div data-items></div>
@address, <a href="#" class="button button-secondary button-small float-right mt-4" data-next-page-button style="display: none;">
@next_page_params <%= gettext("Older") %>
) </a>
) %> <div class="button button-secondary button-small float-right mt-4" data-loading-button style="display: none;">
<% end %> <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> </div>
</div> </div>

@ -1,7 +1,8 @@
defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [address_internal_transaction_path: 3] import BlockScoutWeb.Router.Helpers,
only: [address_internal_transaction_path: 3, address_internal_transaction_path: 4]
alias Explorer.Chain.{Block, InternalTransaction, Transaction} alias Explorer.Chain.{Block, InternalTransaction, Transaction}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
@ -21,6 +22,14 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
assert html_response(conn, 404) assert html_response(conn, 404)
end end
test "includes USD exchange rate value for address in assigns", %{conn: conn} do
address = insert(:address)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash))
assert %Token{} = conn.assigns.exchange_rate
end
test "returns internal transactions for the address", %{conn: conn} do test "returns internal transactions for the address", %{conn: conn} do
address = insert(:address) address = insert(:address)
@ -47,29 +56,17 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction.index transaction_index: transaction.index
) )
path = address_internal_transaction_path(conn, :index, address) path = address_internal_transaction_path(conn, :index, address, %{"type" => "JSON"})
conn = get(conn, path) conn = get(conn, path)
actual_internal_transaction_primary_keys = internal_transaction_tiles = json_response(conn, 200)["items"]
Enum.map(conn.assigns.internal_transactions, &{&1.transaction_hash, &1.index})
assert Enum.member?(
actual_internal_transaction_primary_keys,
{from_internal_transaction.transaction_hash, from_internal_transaction.index}
)
assert Enum.member?( assert Enum.all?([from_internal_transaction, to_internal_transaction], fn internal_transaction ->
actual_internal_transaction_primary_keys, Enum.any?(internal_transaction_tiles, fn tile ->
{to_internal_transaction.transaction_hash, to_internal_transaction.index} String.contains?(tile, to_string(internal_transaction.transaction_hash)) &&
) String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"")
end end)
end)
test "includes USD exchange rate value for address in assigns", %{conn: conn} do
address = insert(:address)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash))
assert %Token{} = conn.assigns.exchange_rate
end end
test "returns next page of results based on last seen internal transaction", %{conn: conn} do test "returns next page of results based on last seen internal transaction", %{conn: conn} do
@ -105,7 +102,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_1.index transaction_index: transaction_1.index
) )
end) end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
transaction_2_hashes = transaction_2_hashes =
1..20 1..20
@ -119,7 +115,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_2.index transaction_index: transaction_2.index
) )
end) end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
transaction_3_hashes = transaction_3_hashes =
1..10 1..10
@ -133,9 +128,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_3.index transaction_index: transaction_3.index
) )
end) end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
second_page_hashes = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes second_page = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes
%InternalTransaction{index: index} = %InternalTransaction{index: index} =
insert( insert(
@ -151,15 +145,18 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash), %{ get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash), %{
"block_number" => Integer.to_string(b_block.number), "block_number" => Integer.to_string(b_block.number),
"transaction_index" => Integer.to_string(transaction_3.index), "transaction_index" => Integer.to_string(transaction_3.index),
"index" => Integer.to_string(index) "index" => Integer.to_string(index),
"type" => "JSON"
}) })
actual_hashes = internal_transaction_tiles = json_response(conn, 200)["items"]
conn.assigns.internal_transactions
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
|> Enum.reverse()
assert second_page_hashes == actual_hashes assert Enum.all?(second_page, fn internal_transaction ->
Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"")
end)
end)
end end
test "next_page_params exist if not on last page", %{conn: conn} do test "next_page_params exist if not on last page", %{conn: conn} do
@ -184,10 +181,17 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
) )
end) end)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash)) conn =
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{"type" => "JSON"}))
expected_response =
address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{
"block_number" => number,
"index" => 11,
"transaction_index" => transaction_index
})
assert %{"block_number" => ^number, "index" => 11, "transaction_index" => ^transaction_index} = assert expected_response == json_response(conn, 200)["next_page_path"]
conn.assigns.next_page_params
end end
test "next_page_params are empty if on last page", %{conn: conn} do test "next_page_params are empty if on last page", %{conn: conn} do
@ -208,9 +212,10 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
) )
end) end)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash)) conn =
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{"type" => "JSON"}))
refute conn.assigns.next_page_params assert %{"next_page_path" => nil} = json_response(conn, 200)
end end
end end
end end

Loading…
Cancel
Save