diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex index 36c32740e7..08be14a9fc 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex @@ -8,10 +8,12 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do 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] + alias BlockScoutWeb.InternalTransactionView alias Explorer.{Chain, Market} 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), {:ok, address} <- Chain.hash_to_address(address_hash) do full_options = @@ -28,14 +30,45 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options) {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( conn, "index.html", 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(), filter: params["filter"], - internal_transactions: internal_transactions, transaction_count: transaction_count(address), validation_count: validation_count(address) ) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex index e866c4ae19..2883e278f2 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex @@ -7,7 +7,7 @@ <%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> -
+
+

<%= gettext "Internal Transactions" %>

- <%= if Enum.count(@internal_transactions) > 0 do %> - - <%= for internal_transaction <- @internal_transactions do %> - <%= render BlockScoutWeb.InternalTransactionView, "_tile.html", current_address: @address, internal_transaction: internal_transaction %> - <% end %> - - <% else %> + +
<%= gettext "There are no internal transactions for this address." %>
- <% end %> -
- <%= if @next_page_params do %> - <%= link( - gettext("Older"), - class: "button button-secondary button-sm float-right mt-3", - to: address_internal_transaction_path( - @conn, - :index, - @address, - @next_page_params - ) - ) %> - <% end %> +
+
+ + + + + <%= gettext("Loading") %>... +
+
+ +
diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs index 19d03d9cdd..398501294a 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs @@ -1,7 +1,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do 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.ExchangeRates.Token @@ -21,6 +22,14 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do assert html_response(conn, 404) 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 address = insert(:address) @@ -47,29 +56,17 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do 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) - actual_internal_transaction_primary_keys = - 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} - ) + internal_transaction_tiles = json_response(conn, 200)["items"] - assert Enum.member?( - actual_internal_transaction_primary_keys, - {to_internal_transaction.transaction_hash, to_internal_transaction.index} - ) - 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 + assert Enum.all?([from_internal_transaction, to_internal_transaction], 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 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 ) end) - |> Enum.map(&"#{&1.transaction_hash}.#{&1.index}") transaction_2_hashes = 1..20 @@ -119,7 +115,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do transaction_index: transaction_2.index ) end) - |> Enum.map(&"#{&1.transaction_hash}.#{&1.index}") transaction_3_hashes = 1..10 @@ -133,9 +128,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do transaction_index: transaction_3.index ) 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} = insert( @@ -151,15 +145,18 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash), %{ "block_number" => Integer.to_string(b_block.number), "transaction_index" => Integer.to_string(transaction_3.index), - "index" => Integer.to_string(index) + "index" => Integer.to_string(index), + "type" => "JSON" }) - actual_hashes = - conn.assigns.internal_transactions - |> Enum.map(&"#{&1.transaction_hash}.#{&1.index}") - |> Enum.reverse() + internal_transaction_tiles = json_response(conn, 200)["items"] - 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 test "next_page_params exist if not on last page", %{conn: conn} do @@ -184,10 +181,17 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do ) 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} = - conn.assigns.next_page_params + assert expected_response == json_response(conn, 200)["next_page_path"] end test "next_page_params are empty if on last page", %{conn: conn} do @@ -208,9 +212,10 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do ) 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