Prepend broadcasted transaction to address transactions list

Co-authored-by: Stamates <stamates@hotmail.com>
Co-authored-by: Tim Mecklem <timothy@mecklem.com>
pull/333/head
Tim Mecklem 7 years ago committed by jimmay5469
parent 56b6010d60
commit 5041f8fa2c
  1. 6
      apps/explorer_web/assets/js/app.js
  2. 65
      apps/explorer_web/assets/js/socket.js
  3. 15
      apps/explorer_web/lib/explorer_web/channels/address_channel.ex
  4. 27
      apps/explorer_web/lib/explorer_web/channels/user_socket.ex
  5. 1
      apps/explorer_web/lib/explorer_web/router.ex
  6. 2
      apps/explorer_web/lib/explorer_web/templates/address/_link.html.eex
  7. 4
      apps/explorer_web/lib/explorer_web/templates/address/overview.html.eex
  8. 6
      apps/explorer_web/lib/explorer_web/templates/address_internal_transaction/index.html.eex
  9. 35
      apps/explorer_web/lib/explorer_web/templates/address_transaction/_transaction.html.eex
  10. 12
      apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex
  11. 6
      apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex
  12. 2
      apps/explorer_web/lib/explorer_web/templates/chain/_blocks.html.eex
  13. 6
      apps/explorer_web/lib/explorer_web/templates/chain/_transactions.html.eex
  14. 6
      apps/explorer_web/lib/explorer_web/templates/pending_transaction/index.html.eex
  15. 2
      apps/explorer_web/lib/explorer_web/templates/transaction/_link.html.eex
  16. 6
      apps/explorer_web/lib/explorer_web/templates/transaction/index.html.eex
  17. 4
      apps/explorer_web/lib/explorer_web/templates/transaction_internal_transaction/index.html.eex
  18. 17
      apps/explorer_web/test/explorer_web/features/viewing_addresses_test.exs

@ -18,7 +18,13 @@ import 'bootstrap'
// Local files can be imported directly using relative // Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket". // paths "./socket" or full ones "web/static/js/socket".
<<<<<<< HEAD
// import socket from "./socket" // import socket from "./socket"
=======
import './socket'
import './lib/sidebar'
import './lib/market_history_chart'
>>>>>>> Prepend broadcasted transaction to address transactions list
import './lib/card_flip' import './lib/card_flip'
import './lib/clipboard_buttons' import './lib/clipboard_buttons'
import './lib/from_now' import './lib/from_now'

@ -1,62 +1,21 @@
// NOTE: The contents of this file will only be executed if
// you uncomment its entry in "assets/js/app.js".
// To use Phoenix channels, the first step is to import Socket
// and connect at the socket path in "lib/web/endpoint.ex":
import {Socket} from 'phoenix' import {Socket} from 'phoenix'
import $ from 'jquery'
let socket = new Socket('/socket', {params: {token: window.userToken}}) let socket = new Socket('/socket', {params: {locale: window.locale}})
// When you connect, you'll often need to authenticate the client.
// For example, imagine you have an authentication plug, `MyAuth`,
// which authenticates the session and assigns a `:current_user`.
// If the current user exists you can assign the user's token in
// the connection for use in the layout.
//
// In your "lib/web/router.ex":
//
// pipeline :browser do
// ...
// plug MyAuth
// plug :put_user_token
// end
//
// defp put_user_token(conn, _) do
// if current_user = conn.assigns[:current_user] do
// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
// assign(conn, :user_token, token)
// else
// conn
// end
// end
//
// Now you need to pass this token to JavaScript. You can do so
// inside a script tag in "lib/web/templates/layout/app.html.eex":
//
// <script>window.userToken = "<%= assigns[:user_token] %>";</script>
//
// You will need to verify the user token in the "connect/2" function
// in "lib/web/channels/user_socket.ex":
//
// def connect(%{"token" => token}, socket) do
// # max_age: 1209600 is equivalent to two weeks in seconds
// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
// {:ok, user_id} ->
// {:ok, assign(socket, :user, user_id)}
// {:error, reason} ->
// :error
// end
// end
//
// Finally, pass the token on connect as below. Or remove it
// from connect if you don't care about authentication.
socket.connect() socket.connect()
// Now that you are connected, you can join channels with a topic:
let channel = socket.channel('topic:subtopic', {})
// addresses channel
let channel = socket.channel(`addresses:${window.addressHash}`, {})
channel.join() channel.join()
.receive('ok', resp => { console.log('Joined successfully', resp) }) .receive('ok', resp => { console.log('Joined successfully', resp) })
.receive('error', resp => { console.log('Unable to join', resp) }) .receive('error', resp => { console.log('Unable to join', resp) })
channel.on('transaction', (msg) => {
$('[data-selector="transactions-list"]').prepend(msg.transaction)
})
export default socket export default socket

@ -0,0 +1,15 @@
defmodule ExplorerWeb.AddressChannel do
use ExplorerWeb, :channel
intercept ["transaction"]
def join("addresses:" <> _address_hash, _params, socket) do
{:ok, %{}, socket}
end
def handle_out("transaction", %{transaction: transaction}, socket) do
rendered = Phoenix.View.render_to_string(ExplorerWeb.AddressTransactionView, "_transaction.html", locale: socket.assigns.locale, transaction: transaction)
push socket, "transaction", %{transaction: rendered}
{:noreply, socket}
end
end

@ -1,35 +1,14 @@
defmodule ExplorerWeb.UserSocket do defmodule ExplorerWeb.UserSocket do
use Phoenix.Socket use Phoenix.Socket
# channel "room:*", ExplorerWeb.RoomChannel channel "addresses:*", ExplorerWeb.AddressChannel
transport(:websocket, Phoenix.Transports.WebSocket, timeout: 45_000) transport(:websocket, Phoenix.Transports.WebSocket, timeout: 45_000)
# transport :longpoll, Phoenix.Transports.LongPoll # transport :longpoll, Phoenix.Transports.LongPoll
# Socket params are passed from the client and can def connect(%{"locale" => locale}, socket) do
# be used to verify and authenticate a user. After {:ok, assign(socket, :locale, locale)}
# verification, you can put default assigns into
# the socket that will be set for all channels, ie
#
# {:ok, assign(socket, :user_id, verified_user_id)}
#
# To deny connection, return `:error`.
#
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
def connect(_params, socket) do
{:ok, socket}
end end
# Socket ids are topics that allow you to identify all sockets for a user:
#
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
#
# Would allow you to broadcast a "disconnect" event and terminate
# all active sockets and channels for a given user:
#
# ExplorerWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
#
# Returning `nil` makes this socket anonymous.
def id(_socket), do: nil def id(_socket), do: nil
end end

@ -9,6 +9,7 @@ defmodule ExplorerWeb.Router do
plug(:put_secure_browser_headers, %{ plug(:put_secure_browser_headers, %{
"content-security-policy" => "\ "content-security-policy" => "\
connect-src 'self' ws://localhost:*;\
default-src 'self';\ default-src 'self';\
script-src 'self' 'unsafe-inline' 'unsafe-eval';\ script-src 'self' 'unsafe-inline' 'unsafe-eval';\
style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com;\ style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com;\

@ -1,6 +1,6 @@
<div class="address-link"> <div class="address-link">
<%= if @address do %> <%= if @address do %>
<%= link to: address_path(@conn, :show, @conn.assigns.locale, @address), <%= link to: address_path(ExplorerWeb.Endpoint, :show, @locale, @address),
"data-toggle": "tooltip", "data-toggle": "tooltip",
"data-placement": "top", "data-placement": "top",
class: "address-link__font", class: "address-link__font",

@ -1,3 +1,7 @@
<script>
window.addressHash = '<%= @address %>'
</script>
<section> <section>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-2 col-lg-1 order-last order-sm-first"> <div class="col-md-2 col-lg-1 order-last order-sm-first">

@ -91,7 +91,7 @@
<tgroup> <tgroup>
<tr data-test="internal_transaction"> <tr data-test="internal_transaction">
<td> <td>
<%= render ExplorerWeb.TransactionView, "_link.html", conn: @conn, transaction: internal_transaction.transaction %> <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction: internal_transaction.transaction %>
</td> </td>
<td> <td>
<%= link(internal_transaction.transaction.block, <%= link(internal_transaction.transaction.block,
@ -100,7 +100,7 @@
</td> </td>
<td data-from-now="<%= internal_transaction.transaction.block.timestamp %>"></td> <td data-from-now="<%= internal_transaction.transaction.block.timestamp %>"></td>
<td> <td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.from_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: internal_transaction.from_address %>
</td> </td>
<td> <td>
<%= if ExplorerWeb.InternalTransactionView.create?(internal_transaction) do %> <%= if ExplorerWeb.InternalTransactionView.create?(internal_transaction) do %>
@ -113,7 +113,7 @@
title: internal_transaction.created_contract_address_hash title: internal_transaction.created_contract_address_hash
) %> ) %>
<% else %> <% else %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.to_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: internal_transaction.to_address %>
<% end %> <% end %>
</td> </td>
<td><%= ExplorerWeb.TransactionView.value(internal_transaction, include_label: false) %></td> <td><%= ExplorerWeb.TransactionView.value(internal_transaction, include_label: false) %></td>

@ -0,0 +1,35 @@
<tr>
<td><div class="transaction__dot transaction__dot--<%= status(@transaction) %>"></div></td>
<td>
<%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction: @transaction %>
</td>
<td>
<%= link(
@transaction.block,
to: block_path(ExplorerWeb.Endpoint, :show, @locale, @transaction.block)
) %>
</td>
<td><%= @transaction.block.timestamp |> Timex.from_now %></td>
<td class="address-cell">
<%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: @transaction.from_address %>
</td>
<td class="u-text-center"><i class="fas fa-arrow-circle-right"></i></td>
<td>
<%= cond do %>
<% @transaction.to_address_hash != nil -> %>
<%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: @transaction.to_address %>
<% @transaction.created_contract_address_hash != nil -> %>
<i class="fas fa-plus-square"></i>
<%= link(
"Contract Creation",
class: "transaction__link",
"data-address-hash": @transaction.created_contract_address_hash,
to: address_path(ExplorerWeb.Endpoint, :show, @locale, @transaction.created_contract_address_hash),
title: @transaction.created_contract_address_hash
) %>
<% true -> %>
<% end %>
</td>
<td><%= ExplorerWeb.TransactionView.value(@transaction, include_label: false) %></td>
<td><%= ExplorerWeb.TransactionView.formatted_fee(@transaction, denomination: :ether) %></td>
</tr>

@ -93,35 +93,35 @@
<th><%= gettext "Fee" %></th> <th><%= gettext "Fee" %></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody data-selector='transactions-list'>
<%= for transaction <- @transactions do %> <%= for transaction <- @transactions do %>
<tr> <tr>
<td><div class="transaction__dot transaction__dot--<%= status(transaction) %>"></div></td> <td><div class="transaction__dot transaction__dot--<%= status(transaction) %>"></div></td>
<td> <td>
<%= render ExplorerWeb.TransactionView, "_link.html", conn: @conn, transaction: transaction %> <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction: transaction %>
</td> </td>
<td> <td>
<%= link( <%= link(
transaction.block, transaction.block,
to: block_path(@conn, :show, @conn.assigns.locale, transaction.block) to: block_path(@conn, :show, @locale, transaction.block)
) %> ) %>
</td> </td>
<td data-from-now="<%= transaction.block.timestamp %>"></td> <td data-from-now="<%= transaction.block.timestamp %>"></td>
<td class="address-cell"> <td class="address-cell">
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.from_address %>
</td> </td>
<td class="u-text-center"><i class="fas fa-arrow-circle-right"></i></td> <td class="u-text-center"><i class="fas fa-arrow-circle-right"></i></td>
<td> <td>
<%= cond do %> <%= cond do %>
<% transaction.to_address_hash != nil -> %> <% transaction.to_address_hash != nil -> %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.to_address %>
<% transaction.created_contract_address_hash != nil -> %> <% transaction.created_contract_address_hash != nil -> %>
<i class="fas fa-plus-square"></i> <i class="fas fa-plus-square"></i>
<%= link( <%= link(
"Contract Creation", "Contract Creation",
class: "transaction__link", class: "transaction__link",
"data-address-hash": transaction.created_contract_address_hash, "data-address-hash": transaction.created_contract_address_hash,
to: address_path(@conn, :show, @conn.assigns.locale, transaction.created_contract_address_hash), to: address_path(@conn, :show, @locale, transaction.created_contract_address_hash),
title: transaction.created_contract_address_hash title: transaction.created_contract_address_hash
) %> ) %>
<% true -> %> <% true -> %>

@ -152,7 +152,7 @@
<div class="transaction__dot transaction__dot--<%= status(transaction) %>"></div> <div class="transaction__dot transaction__dot--<%= status(transaction) %>"></div>
</td> </td>
<td> <td>
<%= render ExplorerWeb.TransactionView, "_link.html", conn: @conn, transaction: transaction %> <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction: transaction %>
</td> </td>
<td> <td>
<%= link( <%= link(
@ -162,13 +162,13 @@
</td> </td>
<td data-from-now="<%= transaction.block.timestamp %>"></td> <td data-from-now="<%= transaction.block.timestamp %>"></td>
<td class="address-cell"> <td class="address-cell">
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.from_address %>
</td> </td>
<td class="u-text-center"><i class="fas fa-arrow-circle-right"></i></td> <td class="u-text-center"><i class="fas fa-arrow-circle-right"></i></td>
<td> <td>
<%= cond do %> <%= cond do %>
<% transaction.to_address_hash != nil -> %> <% transaction.to_address_hash != nil -> %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.to_address %>
<% transaction.created_contract_address_hash != nil -> %> <% transaction.created_contract_address_hash != nil -> %>
<i class="fas fa-plus-square"></i> <i class="fas fa-plus-square"></i>
<%= link( <%= link(

@ -21,7 +21,7 @@
<td><%= block.transactions |> Enum.count %></td> <td><%= block.transactions |> Enum.count %></td>
<td><%= block.gas_used |> Cldr.Number.to_string! %></td> <td><%= block.gas_used |> Cldr.Number.to_string! %></td>
<td> <td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: block.miner %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: block.miner %>
</td> </td>
</tr> </tr>
<% end %> <% end %>

@ -14,15 +14,15 @@
<%= for transaction <- @chain.transactions do %> <%= for transaction <- @chain.transactions do %>
<tr data-test="chain_transaction"> <tr data-test="chain_transaction">
<td> <td>
<%= render ExplorerWeb.TransactionView, "_link.html", conn: @conn, transaction: transaction %> <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction: transaction %>
</td> </td>
<td> <td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.from_address %>
</td> </td>
<td> <td>
<%= cond do %> <%= cond do %>
<% transaction.to_address_hash != nil -> %> <% transaction.to_address_hash != nil -> %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.to_address %>
<% transaction.created_contract_address_hash != nil -> %> <% transaction.created_contract_address_hash != nil -> %>
<i class="fas fa-plus-square"></i> <i class="fas fa-plus-square"></i>
<%= link( <%= link(

@ -46,14 +46,14 @@
<div class="transaction__dot transaction__dot--pending"></div> <div class="transaction__dot transaction__dot--pending"></div>
</td> </td>
<td> <td>
<%= render ExplorerWeb.TransactionView, "_link.html", conn: @conn, transaction: transaction %> <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction: transaction %>
</td> </td>
<td data-from-now="<%= transaction.updated_at %>"></td> <td data-from-now="<%= transaction.updated_at %>"></td>
<td> <td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.from_address %>
</td> </td>
<td> <td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.to_address %>
</td> </td>
<td> <td>
<%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %> <%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %>

@ -1,5 +1,5 @@
<%= link(@transaction |> hash |> String.slice(2..7), <%= link(@transaction |> hash |> String.slice(2..7),
to: transaction_path(@conn, :show, @conn.assigns.locale, @transaction), to: transaction_path(ExplorerWeb.Endpoint, :show, @locale, @transaction),
"data-toggle": "tooltip", "data-toggle": "tooltip",
"data-placement": "top", "data-placement": "top",
"data-test": "transaction_hash", "data-test": "transaction_hash",

@ -46,7 +46,7 @@
<div class="transaction__dot transaction__dot--<%= status(transaction) %>"></div> <div class="transaction__dot transaction__dot--<%= status(transaction) %>"></div>
</td> </td>
<td> <td>
<%= render ExplorerWeb.TransactionView, "_link.html", conn: @conn, transaction: transaction %> <%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction: transaction %>
</td> </td>
<td> <td>
<%= link( <%= link(
@ -57,12 +57,12 @@
</td> </td>
<td data-from-now="<%= transaction.block.timestamp %>"></td> <td data-from-now="<%= transaction.block.timestamp %>"></td>
<td> <td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.from_address %>
</td> </td>
<td> <td>
<%= cond do %> <%= cond do %>
<% transaction.to_address_hash != nil -> %> <% transaction.to_address_hash != nil -> %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: transaction.to_address %>
<% transaction.created_contract_address_hash != nil -> %> <% transaction.created_contract_address_hash != nil -> %>
<%= link( <%= link(
"Contract Creation", "Contract Creation",

@ -38,7 +38,7 @@
<tr> <tr>
<td><%= internal_transaction.type %></td> <td><%= internal_transaction.type %></td>
<td> <td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.from_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: internal_transaction.from_address %>
</td> </td>
<td> <td>
<%= if ExplorerWeb.InternalTransactionView.create?(internal_transaction) do %> <%= if ExplorerWeb.InternalTransactionView.create?(internal_transaction) do %>
@ -51,7 +51,7 @@
title: internal_transaction.created_contract_address_hash title: internal_transaction.created_contract_address_hash
) %> ) %>
<% else %> <% else %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.to_address %> <%= render ExplorerWeb.AddressView, "_link.html", locale: @locale, address: internal_transaction.to_address %>
<% end %> <% end %>
</td> </td>
<td><%= ExplorerWeb.TransactionView.value(internal_transaction, include_label: false) %></td> <td><%= ExplorerWeb.TransactionView.value(internal_transaction, include_label: false) %></td>

@ -146,4 +146,21 @@ defmodule ExplorerWeb.ViewingAddressesTest do
|> AddressPage.visit_page(addresses.lincoln) |> AddressPage.visit_page(addresses.lincoln)
|> assert_text(AddressPage.transaction_count(), "1,002") |> assert_text(AddressPage.transaction_count(), "1,002")
end end
test "viewing new transactions via live update", %{addresses: addresses, session: session} do
session = session
|> AddressPage.visit_page(addresses.lincoln)
|> assert_has(AddressPage.balance())
transaction =
:transaction
|> insert(from_address_hash: addresses.lincoln.hash)
|> with_block()
|> Repo.preload([:block, :from_address, :to_address])
ExplorerWeb.Endpoint.broadcast!("addresses:#{addresses.lincoln.hash}", "transaction", %{transaction: transaction})
session
|> assert_has(AddressPage.transaction(transaction))
end
end end

Loading…
Cancel
Save