Add a paginated blocks index

pull/2/head
Doc Ritezel 7 years ago
parent f5dd228551
commit 06057e36ef
  1. 1
      assets/css/components/_all.scss
  2. 42
      assets/css/components/_blocks.scss
  3. 32
      assets/css/components/_header.scss
  4. 45
      assets/css/components/_pagination.scss
  5. 3
      assets/static/images/block.svg
  6. 21
      doc/dependency_decisions.yml
  7. 1
      lib/explorer/repo.ex
  8. 8
      lib/explorer_web/controllers/block_controller.ex
  9. 6
      lib/explorer_web/controllers/chain_controller.ex
  10. 6
      lib/explorer_web/router.ex
  11. 34
      lib/explorer_web/templates/block/index.html.eex
  12. 0
      lib/explorer_web/templates/chain/show.html.eex
  13. 9
      lib/explorer_web/templates/layout/_header.html.eex
  14. 1
      lib/explorer_web/views/block_view.ex
  15. 2
      lib/explorer_web/views/chain_view.ex
  16. 8
      mix.exs
  17. 4
      mix.lock
  18. 10
      test/explorer_web/controllers/block_controller_test.exs
  19. 5
      test/explorer_web/controllers/chain_controller_test.exs
  20. 2
      test/explorer_web/features/contributor_browsing_test.exs
  21. 2
      test/explorer_web/views/chain_view_test.exs
  22. 2
      test/support/factories/transaction_factory.ex

@ -6,5 +6,6 @@
@import "container";
@import "footer";
@import "header";
@import "pagination";
@import "transaction";
@import "transactions";

@ -7,19 +7,59 @@
&--title { padding-top: explorer-size(0); }
}
&__headline-title,
&__title {
@include explorer-typography("title");
color: explorer-color("slate", "100");
margin: 0;
}
&__headline-title { line-height: 18px; }
&__table {
@extend %table;
@include explorer-typography("body1");
color: explorer-color("slate", "100");
}
&__column-header { @include explorer-typography("body1"); }
&__column-header {
@include explorer-typography("body1");
&--optional { display: none; }
}
&__column {
&--optional { display: none; }
}
&__link { color: explorer-color("blue", "500"); }
&__pagination {
margin: explorer-size(-1) 0 explorer-size(-2) 0;
.pagination { text-align: right; }
}
}
@media (min-width: $explorer-breakpoint-sm) {
.blocks {
&__column-header {
&--optional { display: table-cell; }
}
&__column {
&--optional { display: table-cell; }
}
}
}
@media (min-width: $explorer-breakpoint-md) {
.blocks {
&__headline {
display: flex;
align-items: center;
justify-content: center;
}
&__headline-title { flex: 1; }
&__pagination {
margin: explorer-size(-1) 0 explorer-size(-2) 0;
}
}
}

@ -13,6 +13,11 @@
&__cell {
@extend %explorer-header-height;
&--logo { @extend %explorer-header-square-width; }
&--links {
display: flex;
flex-direction: row-reverse;
padding-right: explorer-size(0);
}
}
&__logo-link {
@ -29,4 +34,31 @@
height: 36px;
display: block;
}
&__link {
@extend %explorer-header-height;
display: block;
text-decoration: none;
border-top: explorer-size(-4) solid transparent;
border-left: explorer-size(-4) solid transparent;
border-right: explorer-size(-4) solid transparent;
padding: explorer-size(-3);
color: explorer-color("slate", "100");
&:hover {
border-top-color: explorer-color("blue", "500");
color: explorer-color("blue", "500");
}
}
&__link-name {
@include explorer-typography("body1");
margin-left: explorer-size(-3);
}
&__link-image, &__link-name {
display: inline-block;
vertical-align: middle;
}
}

@ -0,0 +1,45 @@
.pagination {
display: block;
&-list {
list-style: none;
margin: 0;
padding: 0;
white-space: nowrap;
}
li {
display: inline-block;
margin: 0;
margin-right: explorer-size(-3);
&:first-of-type, &:last-of-type { display: none; }
&:last-of-type { margin-right: 0; }
}
&-link {
@include explorer-typography("body1");
display: block;
line-height: 1;
color: lighten(explorer-color("gray", "300"), 10);
text-decoration: none;
padding: explorer-size(-3) explorer-size(-3) + explorer-size(-5);
border: 1px solid lighten(explorer-color("gray", "300"), 10);
border-radius: explorer-size(-5);
&:hover,
&.is-current {
border-color: explorer-color("blue", "500");
background: explorer-color("blue", "500");
color: explorer-color("white");
}
}
}
@media (min-width: $explorer-breakpoint-sm) {
.pagination {
li {
&:first-of-type { display: inline-block; }
&:last-of-type { display: inline-block; }
}
}
}

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="24">
<path fill="#BAC4CB" fill-rule="evenodd" d="M21.998 17.936c0 .006-.004.011-.004.017.009.185-.013.374-.105.547a.892.892 0 0 1-.816.485c-.019.002-.036.011-.055.011l-9.467 4.887a1.11 1.11 0 0 1-.519.111A.113.113 0 0 1 11 24c-.011 0-.021-.006-.032-.006-.052 0-.104-.009-.156-.014-.018-.004-.038-.001-.056-.005a.984.984 0 0 1-.308-.092l-9.425-4.888c-.008 0-.015.005-.023.005-.03 0-.055-.013-.084-.015a.895.895 0 0 1-.805-.485 1.038 1.038 0 0 1-.105-.547c-.002-.01-.006-.019-.006-.03v-.046c.001-.031-.005-.061 0-.092V6.188c-.005-.031.001-.061 0-.092v-.019l.002-.013c0-.043.004-.086.01-.128a.788.788 0 0 1 .114-.459.98.98 0 0 1 .407-.336.928.928 0 0 1 .18-.079c.038-.01.074-.017.112-.024.053-.01.103-.03.157-.034L10.448.117c.156-.081.323-.103.49-.105C10.96.011 10.978 0 11 0s.04.011.062.012c.167.002.334.024.489.104l9.537 4.902c.179.021.343.09.483.199a.613.613 0 0 1 .058.042c.042.037.08.073.116.116.042.044.095.078.129.131.072.111.098.23.109.349A.826.826 0 0 1 22 6.16v11.652c.005.031-.001.062 0 .093v.018l-.002.013zM2 16.159l1.633-1.025c.44-.276 1.002-.112 1.256.366s.103 1.09-.337 1.366l-1.565.983L10 21.486v-8.645l-1.633 1.025c-.44.276-1.002.112-1.256-.366s-.103-1.09.337-1.366l1.573-.988L2 7.522v8.637zM12 2.492V4a1 1 0 0 1-2 0V2.503L3.226 6 10 9.497V8a1 1 0 0 1 2 0v1.508L18.825 6 12 2.492zm8 5.05l-7.017 3.606 1.569.986c.44.276.591.888.337 1.366-.254.478-.816.642-1.256.366L12 12.841v8.656l7.037-3.633-1.589-.998c-.44-.276-.591-.888-.337-1.366.254-.478.816-.642 1.256-.366L20 16.159V7.542z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -275,3 +275,24 @@
:why:
:versions: []
:when: 2018-02-01 07:44:03.250381000 Z
- - :license
- math
- Apache 2.0
- :who:
:why:
:versions: []
:when: 2018-02-02 09:32:25.722843000 Z
- - :license
- scrivener
- MIT
- :who:
:why:
:versions: []
:when: 2018-02-02 09:32:41.813374000 Z
- - :license
- scrivener_ecto
- MIT
- :who:
:why:
:versions: []
:when: 2018-02-02 09:32:51.070166000 Z

@ -1,5 +1,6 @@
defmodule Explorer.Repo do
use Ecto.Repo, otp_app: :explorer
use Scrivener, page_size: 10
@dialyzer {:nowarn_function, rollback: 1}
@doc """

@ -5,6 +5,14 @@ defmodule ExplorerWeb.BlockController do
alias Explorer.Repo
alias Explorer.BlockForm
def index(conn, params) do
blocks = Block
|> order_by(desc: :number)
|> preload(:transactions)
|> Repo.paginate(params)
render(conn, "index.html", blocks: blocks)
end
def show(conn, params) do
block = Block
|> where(number: ^params["id"])

@ -1,4 +1,4 @@
defmodule ExplorerWeb.PageController do
defmodule ExplorerWeb.ChainController do
use ExplorerWeb, :controller
import Ecto.Query
alias Explorer.Block
@ -7,7 +7,7 @@ defmodule ExplorerWeb.PageController do
alias Explorer.BlockForm
alias Explorer.TransactionForm
def index(conn, _params) do
def show(conn, _params) do
blocks = Block
|> order_by(desc: :number)
|> limit(5)
@ -22,6 +22,6 @@ defmodule ExplorerWeb.PageController do
|> Repo.preload(:block)
|> Enum.map(&TransactionForm.build/1)
render(conn, "index.html", blocks: blocks, transactions: transactions)
render(conn, "show.html", blocks: blocks, transactions: transactions)
end
end

@ -24,13 +24,13 @@ defmodule ExplorerWeb.Router do
scope "/", ExplorerWeb do
pipe_through :browser
get "/", PageController, :dummy
resources "/", ChainController, only: [:show], singleton: true, as: :chain
end
scope "/:locale", ExplorerWeb do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
resources "/blocks", BlockController, only: [:show]
resources "/", ChainController, only: [:show], singleton: true, as: :chain
resources "/blocks", BlockController, only: [:index, :show]
resources "/transactions", TransactionController, only: [:show]
resources "/addresses", AddressController, only: [:show]
end

@ -0,0 +1,34 @@
<section class="container__section">
<div class="blocks__headline">
<h1 class="blocks__headline-title"><%= gettext("Showing #%{start_block} to #%{end_block}", start_block: List.last(@blocks.entries).number, end_block: List.first(@blocks.entries).number) %></h1>
<div class="blocks__pagination"><%= pagination_links @conn, @blocks, ["en"], view_style: :bulma, first: true, distance: 1, previous: Phoenix.HTML.raw("&lsaquo;"), next: Phoenix.HTML.raw("&rsaquo;"), path: &block_path/4 %></div>
</div>
<div class="blocks">
<div class="blocks__container">
<table class="blocks__table">
<thead class="blocks__header">
<tr>
<th class="blocks__column-header"><%= gettext "Height" %></th>
<th class="blocks__column-header"><%= gettext "Age" %></th>
<th class="blocks__column-header"><%= gettext "Transactions" %></th>
<th class="blocks__column-header blocks__column-header--optional"><%= gettext "Gas Used" %></th>
<th class="blocks__column-header blocks__column-header--optional"><%= gettext "Gas Limit" %></th>
<th class="blocks__column-header blocks__column-header--optional"><%= gettext "Gas Price" %></th>
</tr>
</thead>
<tbody>
<%= for block <- @blocks do %>
<tr class="blocks__row">
<td class="blocks__column blocks__column--height"><%= link(block.number, to: block_path(@conn, :show, @conn.assigns.locale, block.number), class: "blocks__link") %></td>
<td class="transactions__column transactions__column--age"><%= block.timestamp |> Timex.from_now %></td>
<td class="blocks__column blocks__column--transactions-count"><%= gettext("%{count} transactions", count: block.transactions |> Enum.count) %></td>
<td class="blocks__column blocks__column--optional blocks__column--gas-used"><%= block.gas_used |> Cldr.Number.to_string! %> (<%= block.gas_used / block.gas_limit |> Cldr.Number.to_string!(format: "#.#%") %>)</td>
<td class="blocks__column blocks__column--optional blocks__column--gas-limit"><%= block.gas_limit |> Cldr.Number.to_string! %></td>
<td class="blocks__column blocks__column--optional blocks__column--gas-price"><%= block.transactions |> Enum.map(fn(transaction) -> Decimal.to_float(Decimal.div(Decimal.new(transaction.gas_price), Decimal.new(1_000_000_000))) end) |> Math.Enum.mean || 0 |> Cldr.Number.to_string! %> Gwei</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</section>

@ -2,11 +2,16 @@
<table class="header__container" cellspacing="0" cellpadding="0" border="0">
<tr class="header__row">
<td class="header__cell header__cell--logo">
<a href="<%= ExplorerWeb.Endpoint.url() %>" class="header__logo-link">
<a href="<%= chain_path(@conn, :show) %>" class="header__logo-link">
<%= logo_image(@conn, alt: gettext("POA Network Explorer"), class: "header__logo") %>
</a>
</td>
<td class="header__cell" align="right"></td>
<td class="header__cell header__cell--links" align="right">
<a href="<%= block_path(@conn, :index, Gettext.get_locale) %>" class="header__link">
<img class="header__link-image" src="<%= static_path(@conn, "/images/block.svg") %>" />
<div class="header__link-name"><%= gettext("Blocks") %></div>
</a>
</td>
</tr>
</table>
</header>

@ -1,4 +1,5 @@
defmodule ExplorerWeb.BlockView do
use ExplorerWeb, :view
import Scrivener.HTML
@dialyzer :no_match
end

@ -1,4 +1,4 @@
defmodule ExplorerWeb.PageView do
defmodule ExplorerWeb.ChainView do
use ExplorerWeb, :view
@dialyzer :no_match
end

@ -37,7 +37,10 @@ defmodule Explorer.Mixfile do
# Specifies extra applications to start per environment
defp extra_applications(:prod), do: [:phoenix_pubsub_redis, :new_relixir | extra_applications()]
defp extra_applications(_), do: extra_applications()
defp extra_applications, do: [:ex_cldr, :ex_jasmine, :ethereumex, :timex, :timex_ecto, :set_locale, :logger, :runtime_tools]
defp extra_applications, do: [
:scrivener_ecto, :scrivener_html, :ex_cldr, :ex_jasmine, :ethereumex,
:timex, :timex_ecto, :set_locale, :logger, :runtime_tools
]
# Specifies your project dependencies.
#
@ -55,6 +58,7 @@ defmodule Explorer.Mixfile do
{:gettext, "~> 0.11"},
{:ex_jasmine, github: "minifast/ex_jasmine", branch: "master"},
{:junit_formatter, ">= 0.0.0"},
{:math, "~> 0.3.0"},
{:new_relixir, "~> 0.4.0", only: [:prod]},
{:phoenix, "~> 1.3.0"},
{:phoenix_ecto, "~> 3.2"},
@ -64,6 +68,8 @@ defmodule Explorer.Mixfile do
{:phoenix_pubsub_redis, "~> 2.1.0", only: [:prod]},
{:postgrex, ">= 0.0.0"},
{:quantum, "~> 2.2.1"},
{:scrivener_ecto, "~> 1.0"},
{:scrivener_html, "~> 1.7"},
{:set_locale, github: "minifast/set_locale", branch: "master"}, # Waiting on https://github.com/smeevil/set_locale/pull/9
{:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:timex, "~> 3.1.24"},

@ -28,6 +28,7 @@
"idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"},
"junit_formatter": {:hex, :junit_formatter, "2.1.0", "ff03d2bbe9a67041f2488d8e72180ddcf4dff9e9c8a39b79eac9828fcb9e9bbf", [:mix], [], "hexpm"},
"math": {:hex, :math, "0.3.0", "e14e7291115201cb155a3567e66d196bf5088a6f55b030d598107d7ae934a11c", [:mix], [], "hexpm"},
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"},
@ -47,6 +48,9 @@
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
"redix": {:hex, :redix, "0.6.1", "20986b0e02f02b13e6f53c79a1ae70aa83147488c408f40275ec261f5bb0a6d0", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"redix_pubsub": {:hex, :redix_pubsub, "0.4.1", "26e6a69129072ac2226be49139019bdf951bb1e9e210a773c1372acf88100936", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:redix, "~> 0.6.0", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm"},
"scrivener": {:hex, :scrivener, "2.5.0", "e1f78c62b6806d91cc9c4778deef1ea4e80aa9fadfce2c16831afe0468cc8a2c", [:mix], [], "hexpm"},
"scrivener_ecto": {:hex, :scrivener_ecto, "1.3.0", "69698428e22810ac8a47abc12d1df5b2f5d8f6b36dc5d5bfe6dd93fde857c576", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.11.0 or ~> 0.12.0 or ~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm"},
"scrivener_html": {:hex, :scrivener_html, "1.7.1", "afa35128fb36184bc469e4531bb1ef61b2d91bb29e373157068c62332291485f", [:mix], [{:phoenix, "~> 1.0-pre and < 1.4.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.2", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.1", [hex: :plug, repo: "hexpm", optional: false]}, {:scrivener, "~> 1.2 or ~> 2.0", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm"},
"set_locale": {:git, "https://github.com/minifast/set_locale.git", "da9ae029642bc0fbd9212c2aaf86c0adca70c084", [branch: "master"]},
"sobelow": {:hex, :sobelow, "0.6.6", "04a2850fe31d241ef8c53b83ab9216f7659c19eb94bfb271d2b664e6953b4965", [:mix], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"},

@ -1,11 +1,19 @@
defmodule ExplorerWeb.BlockControllerTest do
use ExplorerWeb.ConnCase
describe "GET show/3" do
describe "GET show/2" do
test "returns a block", %{conn: conn} do
block = insert(:block, number: 3)
conn = get(conn, "/en/blocks/3")
assert conn.assigns.block.id == block.id
end
end
describe "GET index/2" do
test "returns all blocks", %{conn: conn} do
block_ids = insert_list(4, :block) |> Enum.map(fn (block) -> block.number end) |> Enum.reverse
conn = get(conn, "/en/blocks")
assert conn.assigns.blocks |> Enum.map(fn (block) -> block.number end) == block_ids
end
end
end

@ -1,4 +1,4 @@
defmodule ExplorerWeb.PageControllerTest do
defmodule ExplorerWeb.ChainControllerTest do
use ExplorerWeb.ConnCase
def build_transaction(block \\ nil) do
@ -19,13 +19,14 @@ defmodule ExplorerWeb.PageControllerTest do
describe "GET index/2 with a locale" do
test "returns a welcome message", %{conn: conn} do
conn = get conn, "/en"
conn = get conn, ExplorerWeb.Router.Helpers.chain_path(ExplorerWeb.Endpoint, :show, %{locale: :en})
assert html_response(conn, 200) =~ "POA"
end
test "returns a block", %{conn: conn} do
insert(:block, %{number: 23})
conn = get conn, "/en"
assert(List.first(conn.assigns.blocks).number == 23)
end

@ -39,6 +39,8 @@ defmodule ExplorerWeb.UserListTest do
|> assert_has(css(".blocks__column--gas-used", count: 5, text: "10"))
session
|> click(link("Blocks"))
|> assert_has(css(".blocks__column--height", text: "311"))
|> click(link("311"))
|> assert_has(css(".block__item", text: "0xMrCoolBlock"))
|> assert_has(css(".block__item", text: "Heathcliff"))

@ -1,3 +1,3 @@
defmodule ExplorerWeb.PageViewTest do
defmodule ExplorerWeb.ChainViewTest do
use ExplorerWeb.ConnCase, async: true
end

@ -7,7 +7,7 @@ defmodule Explorer.TransactionFactory do
hash: sequence("0x"),
value: Enum.random(1..100_000),
gas: Enum.random(21_000..100_000),
gas_price: Enum.random(1..100_000),
gas_price: Enum.random(10..99) * 1_000_000_00,
input: sequence("0x"),
nonce: Enum.random(1..1_000),
public_key: sequence("0x"),

Loading…
Cancel
Save