diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 5f94ac5da8..132e2b9999 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -419,22 +419,21 @@ defmodule Explorer.Chain do end @doc """ - Finds all `t:Explorer.Chain.Transaction.t/0` in the `t:Explorer.Chain.Block.t/0`. + Finds all `t:Explorer.Chain.Transaction.t/0`s in the `t:Explorer.Chain.Block.t/0`. ## Options * `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is `:required`, and the `t:Explorer.Chain.Transaction.t/0` has no associated record for that association, then the `t:Explorer.Chain.Transaction.t/0` will not be included in the page `entries`. - * `:pagination` - pagination params to pass to scrivener. + * `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` and + `:key` (a tuple of the lowest/oldest {index}) and. Results will be the transactions older than + the index that are passed. """ - @spec block_to_transactions(Block.t()) :: %Scrivener.Page{entries: [Transaction.t()]} - @spec block_to_transactions(Block.t(), [necessity_by_association_option | pagination_option]) :: %Scrivener.Page{ - entries: [Transaction.t()] - } + @spec block_to_transactions(Block.t(), [paging_options | necessity_by_association_option]) :: [Transaction.t()] def block_to_transactions(%Block{hash: block_hash}, options \\ []) when is_list(options) do necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) - pagination = Keyword.get(options, :pagination, %{}) + paging_options = Keyword.get(options, :paging_options, %PagingOptions{page_size: 50}) Transaction |> load_contract_creation() @@ -443,9 +442,11 @@ defmodule Explorer.Chain do }) |> join(:inner, [transaction], block in assoc(transaction, :block)) |> where([_, _, block], block.hash == ^block_hash) - |> order_by([transaction], desc: transaction.inserted_at, desc: transaction.hash) + |> page_transaction(paging_options) + |> limit(^paging_options.page_size) + |> order_by([transaction], desc: transaction.index) |> join_associations(necessity_by_association) - |> Repo.paginate(pagination) + |> Repo.all() end @doc """ @@ -2550,6 +2551,11 @@ defmodule Explorer.Chain do ) end + defp page_transaction(query, %PagingOptions{key: {index}}) do + query + |> where([transaction], transaction.index < ^index) + end + defp run_addresses(multi, ecto_schema_module_to_changes_list, options) when is_map(ecto_schema_module_to_changes_list) and is_list(options) do case ecto_schema_module_to_changes_list do diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 43b9710b0b..dbfa352a22 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -122,11 +122,7 @@ defmodule Explorer.ChainTest do assert Repo.aggregate(Transaction, :count, :hash) == 0 - assert %Scrivener.Page{ - entries: [], - page_number: 1, - total_entries: 0 - } = Chain.block_to_transactions(block) + assert [] = Chain.block_to_transactions(block) end test "with transactions" do @@ -135,40 +131,28 @@ defmodule Explorer.ChainTest do |> insert() |> with_block() - assert %Scrivener.Page{ - entries: [%Transaction{hash: ^transaction_hash}], - page_number: 1, - total_entries: 1 - } = Chain.block_to_transactions(block) + assert [%Transaction{hash: ^transaction_hash}] = Chain.block_to_transactions(block) end test "with transactions can be paginated" do block = insert(:block) - transactions = - Enum.map(0..1, fn _ -> - :transaction - |> insert() - |> with_block(block) - end) - - [%Transaction{hash: first_transaction_hash}, %Transaction{hash: second_transaction_hash}] = transactions + second_page_hashes = + 50 + |> insert_list(:transaction) + |> with_block(block) + |> Enum.map(& &1.hash) - assert %Scrivener.Page{ - entries: [%Transaction{hash: ^second_transaction_hash}], - page_number: 1, - page_size: 1, - total_entries: 2, - total_pages: 2 - } = Chain.block_to_transactions(block, pagination: %{page_size: 1}) + %Transaction{block_number: block_number, index: index} = + :transaction + |> insert() + |> with_block(block) - assert %Scrivener.Page{ - entries: [%Transaction{hash: ^first_transaction_hash}], - page_number: 2, - page_size: 1, - total_entries: 2, - total_pages: 2 - } = Chain.block_to_transactions(block, pagination: %{page: 2, page_size: 1}) + assert second_page_hashes == + block + |> Chain.block_to_transactions(paging_options: %PagingOptions{key: {block_number, index}, page_size: 50}) + |> Enum.map(& &1.hash) + |> Enum.reverse() end end diff --git a/apps/explorer_web/lib/explorer_web/controllers/block_transaction_controller.ex b/apps/explorer_web/lib/explorer_web/controllers/block_transaction_controller.ex index 8e575aa294..c6859ea2c6 100644 --- a/apps/explorer_web/lib/explorer_web/controllers/block_transaction_controller.ex +++ b/apps/explorer_web/lib/explorer_web/controllers/block_transaction_controller.ex @@ -3,24 +3,37 @@ defmodule ExplorerWeb.BlockTransactionController do import ExplorerWeb.Chain, only: [param_to_block_number: 1] - alias Explorer.Chain + alias Explorer.{Chain, PagingOptions} + + @page_size 50 + @default_paging_options %PagingOptions{page_size: @page_size + 1} def index(conn, %{"block_id" => formatted_block_number} = params) do with {:ok, block_number} <- param_to_block_number(formatted_block_number), {:ok, block} <- Chain.number_to_block(block_number, necessity_by_association: %{miner: :required}), block_transaction_count <- Chain.block_to_transaction_count(block) do - page = - Chain.block_to_transactions( - block, + full_options = + [ necessity_by_association: %{ block: :required, from_address: :required, to_address: :optional - }, - pagination: params - ) + } + ] + |> Keyword.merge(paging_options(params)) + + transactions_plus_one = Chain.block_to_transactions(block, full_options) + + {transactions, next_page} = Enum.split(transactions_plus_one, @page_size) - render(conn, "index.html", block: block, block_transaction_count: block_transaction_count, page: page) + render( + conn, + "index.html", + block: block, + block_transaction_count: block_transaction_count, + next_page_params: next_page_params(next_page, transactions), + transactions: transactions + ) else {:error, :invalid} -> not_found(conn) @@ -29,4 +42,21 @@ defmodule ExplorerWeb.BlockTransactionController do not_found(conn) end end + + defp next_page_params([], _transactions), do: nil + + defp next_page_params(_, transactions) do + last = List.last(transactions) + %{block_number: last.block_number, index: last.index} + end + + defp paging_options(params) do + with %{"index" => index_string} <- params, + {index, ""} <- Integer.parse(index_string) do + [paging_options: %{@default_paging_options | key: {index}}] + else + _ -> + [paging_options: @default_paging_options] + end + end end diff --git a/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex index 57bc038200..1fb821c53e 100644 --- a/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex @@ -129,7 +129,7 @@
- <%= if Enum.count(@page) > 0 do %> + <%= if Enum.count(@transactions) > 0 do %> @@ -146,7 +146,7 @@ - <%= for transaction <- @page do %> + <%= for transaction <- @transactions do %>
@@ -195,21 +195,17 @@ <% end %> - - <%= if Enum.count(@page) > 0 do %> -
- <%= pagination_links( + <%= if @next_page_params do %> + <%= link( + gettext("Older"), + class: "button button--secondary button--sm u-float-right mt-3", + to: transaction_path( @conn, - @page, - ["en", @conn.params["block_id"]], - distance: 1, - first: true, - next: Phoenix.HTML.raw("›"), - path: &block_transaction_path/5, - previous: Phoenix.HTML.raw("‹"), - view_style: :bulma - ) %> -
+ :index, + @conn.assigns.locale, + @next_page_params + ) + ) %> <% end %> diff --git a/apps/explorer_web/test/explorer_web/controllers/address_transaction_controller_test.exs b/apps/explorer_web/test/explorer_web/controllers/address_transaction_controller_test.exs index 6058891f18..8106cbc7db 100644 --- a/apps/explorer_web/test/explorer_web/controllers/address_transaction_controller_test.exs +++ b/apps/explorer_web/test/explorer_web/controllers/address_transaction_controller_test.exs @@ -114,16 +114,7 @@ defmodule ExplorerWeb.AddressTransactionControllerTest do |> insert(from_address_hash: address.hash) |> with_block() - %Transaction{block_number: block_number, index: index} = - :transaction - |> insert(from_address_hash: address.hash) - |> with_block() - - conn = - get(conn, address_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash), %{ - "block_number" => Integer.to_string(block_number), - "index" => Integer.to_string(index) - }) + conn = get(conn, address_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash)) refute conn.assigns.next_page_params end diff --git a/apps/explorer_web/test/explorer_web/controllers/block_transaction_controller_test.exs b/apps/explorer_web/test/explorer_web/controllers/block_transaction_controller_test.exs index aad12fdbb7..0d4ba7f7d1 100644 --- a/apps/explorer_web/test/explorer_web/controllers/block_transaction_controller_test.exs +++ b/apps/explorer_web/test/explorer_web/controllers/block_transaction_controller_test.exs @@ -25,7 +25,7 @@ defmodule ExplorerWeb.BlockTransactionControllerTest do conn = get(conn, block_transaction_path(ExplorerWeb.Endpoint, :index, :en, block.number)) assert html_response(conn, 200) - assert 2 == Enum.count(conn.assigns.page) + assert 2 == Enum.count(conn.assigns.transactions) end test "does not return unrelated transactions", %{conn: conn} do @@ -35,7 +35,7 @@ defmodule ExplorerWeb.BlockTransactionControllerTest do conn = get(conn, block_transaction_path(ExplorerWeb.Endpoint, :index, :en, block)) assert html_response(conn, 200) - assert Enum.empty?(conn.assigns.page) + assert Enum.empty?(conn.assigns.transactions) end test "does not return related transactions without a block", %{conn: conn} do @@ -45,7 +45,31 @@ defmodule ExplorerWeb.BlockTransactionControllerTest do conn = get(conn, block_transaction_path(ExplorerWeb.Endpoint, :index, :en, block)) assert html_response(conn, 200) - assert Enum.empty?(conn.assigns.page) + assert Enum.empty?(conn.assigns.transactions) + end + + test "next_page_params exist if not on last page", %{conn: conn} do + block = insert(:block) + + 60 + |> insert_list(:transaction) + |> with_block(block) + + conn = get(conn, block_transaction_path(ExplorerWeb.Endpoint, :index, :en, block)) + + assert conn.assigns.next_page_params + end + + test "next_page_params are empty if on last page", %{conn: conn} do + block = insert(:block) + + :transaction + |> insert() + |> with_block(block) + + conn = get(conn, block_transaction_path(ExplorerWeb.Endpoint, :index, :en, block)) + + refute conn.assigns.next_page_params end end end