From ed3a6212803c709d2cbad6e3554889ce5e5e9b30 Mon Sep 17 00:00:00 2001 From: William Sanches Date: Mon, 3 Dec 2018 11:38:22 -0200 Subject: [PATCH 1/3] Refactor controller and template for async load --- .../address_validation_controller.ex | 53 ++++++++++++++++--- .../address_validation/index.html.eex | 47 +++++++++------- 2 files changed, 74 insertions(+), 26 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex index 6d39df12b5..90944822ab 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex @@ -5,12 +5,16 @@ defmodule BlockScoutWeb.AddressValidationController do use BlockScoutWeb, :controller import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1] - import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] - alias Explorer.{Chain, Market} + import BlockScoutWeb.Chain, + only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + alias BlockScoutWeb.BlockView alias Explorer.ExchangeRates.Token + alias Explorer.{Chain, Market} + alias Phoenix.View - def index(conn, %{"address_id" => address_hash_string} = params) do + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash) do full_options = @@ -28,15 +32,52 @@ defmodule BlockScoutWeb.AddressValidationController do blocks_plus_one = Chain.get_blocks_validated_by_address(full_options, address) {blocks, next_page} = split_list_by_page(blocks_plus_one) + next_page_path = + case next_page_params(next_page, blocks, params) do + nil -> + nil + + next_page_params -> + address_validation_path( + conn, + :index, + address_hash_string, + Map.delete(next_page_params, "type") + ) + end + + items = + Enum.map(blocks, fn block -> + View.render_to_string( + BlockView, + "_tile.html", + conn: conn, + block: block, + block_type: BlockView.block_type(block) + ) + end) + + json(conn, %{items: items, next_page_path: next_page_path}) + else + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index(conn, %{"address_id" => address_hash_string}) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash) do render( conn, "index.html", address: address, - blocks: blocks, + current_path: current_path(conn), transaction_count: transaction_count(address), validation_count: validation_count(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), - next_page_params: next_page_params(next_page, blocks, params) + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() ) else :error -> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex index f9028ba3e6..f677724b5d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex @@ -1,7 +1,7 @@
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %> -
+
@@ -98,7 +98,7 @@
-
+

<%=gettext("Blocks Validated")%>

- - <%= for block <- @blocks do %> - <%= render BlockScoutWeb.BlockView, "_tile.html", block: block, block_type: BlockScoutWeb.BlockView.block_type(block)%> - <% end %> - -
- <%= if @next_page_params do %> - <%= link( - gettext("Older"), - class: "button button-secondary button-sm float-right mt-3", - to: address_validation_path( - @conn, - :index, - @address, - @next_page_params - ) - ) %> - <% end %> +
+ + + + + <%= gettext("Loading...") %> +
+ + +
+ +
From f49fbbf9869edb3e40135f9a5209ec3a4f1e9aff Mon Sep 17 00:00:00 2001 From: William Sanches Date: Mon, 3 Dec 2018 11:38:33 -0200 Subject: [PATCH 2/3] Refactor JS for async load --- .../assets/__tests__/pages/address.js | 40 ++---------- .../__tests__/pages/address/validations.js | 46 +++++++++++++ apps/block_scout_web/assets/js/app.js | 1 + .../assets/js/pages/address.js | 27 +------- .../assets/js/pages/address/validations.js | 64 +++++++++++++++++++ 5 files changed, 118 insertions(+), 60 deletions(-) create mode 100644 apps/block_scout_web/assets/__tests__/pages/address/validations.js create mode 100644 apps/block_scout_web/assets/js/pages/address/validations.js diff --git a/apps/block_scout_web/assets/__tests__/pages/address.js b/apps/block_scout_web/assets/__tests__/pages/address.js index fe6506e22b..5e87b5b73d 100644 --- a/apps/block_scout_web/assets/__tests__/pages/address.js +++ b/apps/block_scout_web/assets/__tests__/pages/address.js @@ -1,56 +1,28 @@ import { reducer, initialState } from '../../js/pages/address' describe('RECEIVED_NEW_BLOCK', () => { - test('with new block', () => { - const state = Object.assign({}, initialState, { - validationCount: 30, - validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }] - }) + test('increases validation count', () => { + const state = Object.assign({}, initialState, { validationCount: 30 }) const action = { type: 'RECEIVED_NEW_BLOCK', - msg: { blockNumber: 2, blockHtml: 'test 2' } + blockHtml: 'test 2' } const output = reducer(state, action) expect(output.validationCount).toEqual(31) - expect(output.validatedBlocks).toEqual([ - { blockNumber: 2, blockHtml: 'test 2' }, - { blockNumber: 1, blockHtml: 'test 1' } - ]) }) - test('when channel has been disconnected', () => { + test('when channel has been disconnected does not increase validation count', () => { const state = Object.assign({}, initialState, { channelDisconnected: true, - validationCount: 30, - validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }] + validationCount: 30 }) const action = { type: 'RECEIVED_NEW_BLOCK', - msg: { blockNumber: 2, blockHtml: 'test 2' } + blockHtml: 'test 2' } const output = reducer(state, action) expect(output.validationCount).toEqual(30) - expect(output.validatedBlocks).toEqual([ - { blockNumber: 1, blockHtml: 'test 1' } - ]) - }) - test('beyond page one', () => { - const state = Object.assign({}, initialState, { - beyondPageOne: true, - validationCount: 30, - validatedBlocks: [{ blockNumber: 1, blockHtml: 'test 1' }] - }) - const action = { - type: 'RECEIVED_NEW_BLOCK', - msg: { blockNumber: 2, blockHtml: 'test 2' } - } - const output = reducer(state, action) - - expect(output.validationCount).toEqual(31) - expect(output.validatedBlocks).toEqual([ - { blockNumber: 1, blockHtml: 'test 1' } - ]) }) }) diff --git a/apps/block_scout_web/assets/__tests__/pages/address/validations.js b/apps/block_scout_web/assets/__tests__/pages/address/validations.js new file mode 100644 index 0000000000..f8bf284029 --- /dev/null +++ b/apps/block_scout_web/assets/__tests__/pages/address/validations.js @@ -0,0 +1,46 @@ +import { reducer, initialState } from '../../../js/pages/address/validations' + +describe('RECEIVED_NEW_BLOCK', () => { + test('adds new block to the top of the list', () => { + const state = Object.assign({}, initialState, { + items: ['test 1'] + }) + const action = { + type: 'RECEIVED_NEW_BLOCK', + blockHtml: 'test 2' + } + const output = reducer(state, action) + + expect(output.items).toEqual(['test 2', 'test 1']) + }) + + test('does nothing beyond page one', () => { + const state = Object.assign({}, initialState, { + beyondPageOne: true, + channelDisconnected: false, + items: ['test 1'] + }) + const action = { + type: 'RECEIVED_NEW_BLOCK', + blockHtml: 'test 2' + } + const output = reducer(state, action) + + expect(output.items).toEqual(['test 1']) + }) + + test('does nothing when channel has been disconnected', () => { + const state = Object.assign({}, initialState, { + channelDisconnected: true, + items: ['test 1'] + }) + const action = { + type: 'RECEIVED_NEW_BLOCK', + blockHtml: 'test 2' + } + const output = reducer(state, action) + + expect(output.items).toEqual(['test 1']) + }) +}) + diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js index 8f084b5521..e1fcc24b45 100644 --- a/apps/block_scout_web/assets/js/app.js +++ b/apps/block_scout_web/assets/js/app.js @@ -21,6 +21,7 @@ import 'bootstrap' import './locale' import './pages/address' +import './pages/address/validations' import './pages/blocks' import './pages/chain' import './pages/pending_transactions' diff --git a/apps/block_scout_web/assets/js/pages/address.js b/apps/block_scout_web/assets/js/pages/address.js index 683fc66a57..ce790e04b5 100644 --- a/apps/block_scout_web/assets/js/pages/address.js +++ b/apps/block_scout_web/assets/js/pages/address.js @@ -26,7 +26,6 @@ export const initialState = { transactions: [], internalTransactions: [], internalTransactionsBatch: [], - validatedBlocks: [], beyondPageOne: null, @@ -53,15 +52,7 @@ function baseReducer (state = initialState, action) { if (state.channelDisconnected) return state const validationCount = state.validationCount + 1 - - if (state.beyondPageOne) return Object.assign({}, state, { validationCount }) - return Object.assign({}, state, { - validatedBlocks: [ - action.msg, - ...state.validatedBlocks - ], - validationCount - }) + return Object.assign({}, state, { validationCount }) } case 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH': { if (state.channelDisconnected || state.beyondPageOne) return state @@ -209,22 +200,6 @@ const elements = { $channelBatching.show() $el[0].innerHTML = numeral(state.internalTransactionsBatch.length).format() } - }, - '[data-selector="validations-list"]': { - load ($el) { - return { - validatedBlocks: $el.children().map((index, el) => ({ - blockNumber: parseInt(el.dataset.blockNumber), - blockHtml: el.outerHTML - })).toArray() - } - }, - render ($el, state, oldState) { - if (oldState.validatedBlocks === state.validatedBlocks) return - const container = $el[0] - const newElements = _.map(state.validatedBlocks, ({ blockHtml }) => $(blockHtml)[0]) - listMorph(container, newElements, { key: 'dataset.blockNumber' }) - } } } diff --git a/apps/block_scout_web/assets/js/pages/address/validations.js b/apps/block_scout_web/assets/js/pages/address/validations.js new file mode 100644 index 0000000000..6aca07a98b --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/validations.js @@ -0,0 +1,64 @@ +import $ from 'jquery' +import _ from 'lodash' +import humps from 'humps' +import socket from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load.js' + +export const initialState = { + addressHash: null, + channelDisconnected: false +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, _.omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + return Object.assign({}, state, { channelDisconnected: true }) + } + case 'RECEIVED_NEW_BLOCK': { + if (state.channelDisconnected) return state + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + items: [ + action.blockHtml, + ...state.items + ] + }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected) $el.show() + } + } +} + +if ($('[data-page="blocks-validated"]').length) { + const store = createAsyncLoadStore(reducer, initialState, 'dataset.blockNumber') + connectElements({ store, elements }) + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + store.dispatch({ + type: 'PAGE_LOAD', + addressHash + }) + + const blocksChannel = socket.channel(`blocks:${addressHash}`, {}) + blocksChannel.join() + blocksChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + blocksChannel.on('new_block', (msg) => store.dispatch({ + type: 'RECEIVED_NEW_BLOCK', + blockHtml: humps.camelizeKeys(msg).blockHtml + })) +} From e0d422175de2c3844c634d9ccfe25e826ebfbd7b Mon Sep 17 00:00:00 2001 From: William Sanches Date: Mon, 3 Dec 2018 11:59:01 -0200 Subject: [PATCH 3/3] Gettext --- apps/block_scout_web/priv/gettext/default.pot | 10 +++++++++- .../priv/gettext/en/LC_MESSAGES/default.po | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index d73265cc8d..0687a0e605 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -639,7 +639,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31 -#: lib/block_scout_web/templates/address_validation/index.html.eex:117 +#: lib/block_scout_web/templates/address_validation/index.html.eex:126 #: 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 @@ -1188,6 +1188,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 +#: lib/block_scout_web/templates/address_validation/index.html.eex:114 +#: lib/block_scout_web/templates/address_validation/index.html.eex:133 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 msgid "Loading..." msgstr "" @@ -1417,7 +1419,13 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26 +#: lib/block_scout_web/templates/address_validation/index.html.eex:121 #: lib/block_scout_web/templates/tokens/holder/index.html.eex:23 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 msgid "Something went wrong, click to reload." msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_validation/index.html.eex:117 +msgid "There are no blocks validated by this address." +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 2b41acfd7f..f16f8735bb 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 @@ -639,7 +639,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31 -#: lib/block_scout_web/templates/address_validation/index.html.eex:117 +#: lib/block_scout_web/templates/address_validation/index.html.eex:126 #: 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 @@ -1188,6 +1188,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 +#: lib/block_scout_web/templates/address_validation/index.html.eex:114 +#: lib/block_scout_web/templates/address_validation/index.html.eex:133 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 msgid "Loading..." msgstr "" @@ -1417,7 +1419,13 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26 +#: lib/block_scout_web/templates/address_validation/index.html.eex:121 #: lib/block_scout_web/templates/tokens/holder/index.html.eex:23 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 msgid "Something went wrong, click to reload." msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_validation/index.html.eex:117 +msgid "There are no blocks validated by this address." +msgstr ""