diff --git a/apps/block_scout_web/assets/.eslintrc b/apps/block_scout_web/assets/.eslintrc index e3578aadfd..535509b69a 100644 --- a/apps/block_scout_web/assets/.eslintrc +++ b/apps/block_scout_web/assets/.eslintrc @@ -1,3 +1,6 @@ { - "extends": "standard" + "extends": "standard", + "env": { + "browser": true + } } diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js index 9fa5c2a6d3..8ff4aa19f9 100644 --- a/apps/block_scout_web/assets/js/app.js +++ b/apps/block_scout_web/assets/js/app.js @@ -44,6 +44,7 @@ import './lib/stop_propagation' import './lib/token_balance_dropdown' import './lib/token_balance_dropdown_search' import './lib/token_transfers_toggle' +import './lib/async_listing_load' import './lib/tooltip' import './lib/try_api' import './lib/swappable_item' diff --git a/apps/block_scout_web/assets/js/lib/async_listing_load.js b/apps/block_scout_web/assets/js/lib/async_listing_load.js new file mode 100644 index 0000000000..f2ffe5fdb7 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/async_listing_load.js @@ -0,0 +1,85 @@ +import $ from 'jquery' +/** + * This script is a generic function to load list within a tab async. See token transfers tab at Token's page as example. + * + * To get it working the markup must follow the pattern below: + * + *
+ *
message
+ *
message
+ *
message
+ *
+ * button text + *
loading text
+ *
+ * + */ +const $element = $('[data-async-listing]') + +function asyncListing (element, path) { + const $mainElement = $(element) + const $items = $mainElement.find('[data-items]') + const $loading = $mainElement.find('[data-loading-message]') + const $nextPageButton = $mainElement.find('[data-next-page-button]') + const $loadingButton = $mainElement.find('[data-loading-button]') + const $errorMessage = $mainElement.find('[data-error-message]') + const $emptyResponseMessage = $mainElement.find('[data-empty-response-message]') + + $.getJSON(path, {type: 'JSON'}) + .done(response => { + if (!response.items || response.items.length === 0) { + $emptyResponseMessage.show() + $items.empty() + } else { + $items.html(response.items) + } + if (response.next_page_path) { + $nextPageButton.attr('href', response.next_page_path) + $nextPageButton.show() + } else { + $nextPageButton.hide() + } + }) + .fail(() => $errorMessage.show()) + .always(() => { + $loading.hide() + $loadingButton.hide() + }) +} + +if ($element.length === 1) { + $element.on('click', '[data-next-page-button]', (event) => { + event.preventDefault() + + const $button = $(event.target) + const path = $button.attr('href') + const $loadingButton = $element.find('[data-loading-button]') + + // change url to the next page link before loading the next page + history.pushState({}, null, path) + $button.hide() + $loadingButton.show() + + asyncListing($element, path) + }) + + $element.on('click', '[data-error-message]', (event) => { + event.preventDefault() + + // event.target had a weird behavior here + // it hid the tag but left the red div showing + const $link = $element.find('[data-error-message]') + const $loading = $element.find('[data-loading-message]') + const path = $element.data('async-listing') + + $link.hide() + $loading.show() + + asyncListing($element, path) + }) + + // force browser to reload when the user goes back a page + $(window).on('popstate', () => location.reload()) + + asyncListing($element, $element.data('async-listing')) +} diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex index cdbd764313..0a0129a191 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex @@ -1,25 +1,59 @@ defmodule BlockScoutWeb.Tokens.TransferController do use BlockScoutWeb, :controller + alias BlockScoutWeb.Tokens.TransferView alias Explorer.Chain + alias Phoenix.View import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] - def index(conn, %{"token_id" => address_hash_string} = params) do + def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, token} <- Chain.token_from_address_hash(address_hash), token_transfers <- Chain.fetch_token_transfers_from_token_hash(address_hash, paging_options(params)) do {token_transfers_paginated, next_page} = split_list_by_page(token_transfers) + next_page_path = + case next_page_params(next_page, token_transfers_paginated, params) do + nil -> + nil + + next_page_params -> + token_transfer_path(conn, :index, token.contract_address_hash, Map.delete(next_page_params, "type")) + end + + transfers_json = + Enum.map(token_transfers_paginated, fn transfer -> + View.render_to_string( + TransferView, + "_token_transfer.html", + conn: conn, + token: token, + transfer: transfer + ) + end) + + json(conn, %{items: transfers_json, next_page_path: next_page_path}) + else + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index(conn, %{"token_id" => address_hash_string}) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token} <- Chain.token_from_address_hash(address_hash) do render( conn, "index.html", - transfers: token_transfers_paginated, + current_path: current_path(conn), token: token, holders_count_consolidation_enabled: Chain.token_holders_counter_consolidation_enabled?(), total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash), - total_token_holders: Chain.count_token_holders_from_token_hash(address_hash), - next_page_params: next_page_params(next_page, token_transfers_paginated, params) + total_token_holders: Chain.count_token_holders_from_token_hash(address_hash) ) else :error -> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex index fa661cc332..3d2d08b6f8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex @@ -15,28 +15,35 @@ <%= render OverviewView, "_tabs.html", assigns %> -
+

<%= gettext "Token Transfers" %>

- - <%= if Enum.any?(@transfers) do %> - <%= for transfer <- @transfers do %> - <%= render("_token_transfer.html", conn: @conn, token: @token, transfer: transfer) %> - <% end %> - <% else %> -
- - <%= gettext "There are no transfers for this Token." %> - -
- <% end %> - - <%= if @next_page_params do %> - <%= link( - gettext("Older"), - class: "button button-secondary button-small float-right mt-4", - to: token_path(@conn, :show, @token.contract_address_hash, @next_page_params) - ) %> - <% end %> + + +
+ + + + + <%= gettext("Loading") %>... +
+
+
+
+
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 9ae3578b23..07e1143075 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -643,7 +643,7 @@ msgstr "" #: 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/pending_transaction/index.html.eex:41 -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:35 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:38 #: lib/block_scout_web/templates/transaction/index.html.eex:41 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:24 msgid "Older" @@ -861,7 +861,7 @@ msgid "There are no transactions for this block." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:28 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:25 msgid "There are no transfers for this Token." msgstr "" @@ -1217,6 +1217,8 @@ msgstr "" #: lib/block_scout_web/templates/address_transaction/index.html.eex:61 #: lib/block_scout_web/templates/block/index.html.eex:22 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:33 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:33 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:45 #: lib/block_scout_web/templates/transaction/index.html.eex:33 msgid "Loading" msgstr "" @@ -1405,3 +1407,8 @@ msgstr "" #: lib/block_scout_web/templates/transaction_log/index.html.eex:54 msgid "Log Data" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 +msgid "Something went wrong, click to reload." +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 5e81eec55b..dbedb19301 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -643,7 +643,7 @@ msgstr "" #: 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/pending_transaction/index.html.eex:41 -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:35 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:38 #: lib/block_scout_web/templates/transaction/index.html.eex:41 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:24 msgid "Older" @@ -861,7 +861,7 @@ msgid "There are no transactions for this block." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:28 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:25 msgid "There are no transfers for this Token." msgstr "" @@ -1217,6 +1217,8 @@ msgstr "" #: lib/block_scout_web/templates/address_transaction/index.html.eex:61 #: lib/block_scout_web/templates/block/index.html.eex:22 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:33 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:33 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:45 #: lib/block_scout_web/templates/transaction/index.html.eex:33 msgid "Loading" msgstr "" @@ -1405,3 +1407,8 @@ msgstr "" #: lib/block_scout_web/templates/transaction_log/index.html.eex:54 msgid "Log Data" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 +msgid "Something went wrong, click to reload." +msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex index 6db529aff9..bd3a5ec02d 100644 --- a/apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex +++ b/apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex @@ -10,7 +10,11 @@ defmodule BlockScoutWeb.TokenPage do end def visit_page(session, contract_address_hash) do - visit(session, "tokens/#{contract_address_hash}") + visit(session, "tokens/#{contract_address_hash}/token_holders") + end + + def token_holders_tab(count: count) do + css("[data-test='token_holders_tab']", count: count) end def click_tokens_holders(session) do diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs index 06f1255964..d896266848 100644 --- a/apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs @@ -15,7 +15,6 @@ defmodule BlockScoutWeb.ViewingTokensTest do session |> TokenPage.visit_page(token.contract_address) - |> TokenPage.click_tokens_holders() |> assert_has(TokenPage.token_holders(count: 2)) end end