diff --git a/apps/block_scout_web/assets/css/app.scss b/apps/block_scout_web/assets/css/app.scss index f855fffa48..32aa84d2df 100644 --- a/apps/block_scout_web/assets/css/app.scss +++ b/apps/block_scout_web/assets/css/app.scss @@ -76,7 +76,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "components/badge"; @import "components/description-list"; @import "components/nounderline-link"; - +@import "components/token-balance-dropdown"; :export { primary: $primary; diff --git a/apps/block_scout_web/assets/css/components/_token-balance-dropdown.scss b/apps/block_scout_web/assets/css/components/_token-balance-dropdown.scss new file mode 100644 index 0000000000..fd3cde0bcd --- /dev/null +++ b/apps/block_scout_web/assets/css/components/_token-balance-dropdown.scss @@ -0,0 +1,35 @@ +.token-balance-dropdown { + min-width: 14.375rem; + margin-top: 1rem; + background-color: $gray-100; + box-shadow: 0px 2px 3px 2px $gray-200; + border: none; + + // Overriding style added by Bootstrap dropdown via JS. + left: -17px !important; + + .dropdown-items { + overflow-y: auto; + max-height: 18.5rem; + + .dropdown-item:hover { + color: $white; + } + } + + &:after, &:before { + bottom: 100%; + left: 14%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + } + + &:before { + border-bottom-color: $gray-100; + border-width: 1rem; + margin-left: -1rem; + } +} diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js index d70012eff8..6ac7d9cebf 100644 --- a/apps/block_scout_web/assets/js/app.js +++ b/apps/block_scout_web/assets/js/app.js @@ -27,6 +27,7 @@ import './lib/tooltip' import './lib/smart_contract/read_only_functions' import './lib/pretty_json' import './lib/try_api' +import './lib/token_balance_dropdown' import './pages/address' import './pages/block' diff --git a/apps/block_scout_web/assets/js/lib/token_balance_dropdown.js b/apps/block_scout_web/assets/js/lib/token_balance_dropdown.js new file mode 100644 index 0000000000..c1e8759859 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/token_balance_dropdown.js @@ -0,0 +1,19 @@ +import $ from 'jquery' + +const tokenBalanceDropdown = (element) => { + const $element = $(element) + const $loading = $element.find('[data-loading]') + const $errorMessage = $element.find('[data-error-message]') + const apiPath = element.dataset.api_path + + $loading.show() + + $.get(apiPath) + .done(response => $element.html(response)) + .fail(() => { + $loading.hide() + $errorMessage.show() + }) +} + +$('[data-token-balance-dropdown]').each((_index, element) => tokenBalanceDropdown(element)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex new file mode 100644 index 0000000000..ddadeac55c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex @@ -0,0 +1,24 @@ +defmodule BlockScoutWeb.AddressTokenBalanceController do + use BlockScoutWeb, :controller + + alias Explorer.Chain + alias Explorer.Token.BalanceReader + + def index(conn, %{"address_id" => address_hash_string}) do + with true <- ajax?(conn), + {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do + token_balances = + address_hash + |> Chain.fetch_tokens_from_address_hash() + |> BalanceReader.fetch_token_balances_without_error(address_hash_string) + + conn + |> put_status(200) + |> put_layout(false) + |> render("_token_balances.html", tokens: token_balances) + else + _ -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/router.ex b/apps/block_scout_web/lib/block_scout_web/router.ex index a2ff629bae..5db35e6b50 100644 --- a/apps/block_scout_web/lib/block_scout_web/router.ex +++ b/apps/block_scout_web/lib/block_scout_web/router.ex @@ -95,6 +95,13 @@ defmodule BlockScoutWeb.Router do only: [:index, :show], as: :read_contract ) + + resources( + "/token_balances", + AddressTokenBalanceController, + only: [:index], + as: :token_balance + ) end resources "/tokens", Tokens.TokenController, only: [:show], as: :token do diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_token_holdings.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_token_holdings.html.eex new file mode 100644 index 0000000000..4e80ebcb81 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_token_holdings.html.eex @@ -0,0 +1,20 @@ +
+ + <%= gettext("Fetching tokens...") %> +
+ + +<%= token_name(token) %>
++ <%= format_according_to_decimals(token.balance, token.decimals) %> <%= token.symbol %> +
+ <% end %> +