From 2e7e5d19321d2e47bfc00d3ed67cdc6d7deed4b7 Mon Sep 17 00:00:00 2001 From: jimmay5469 Date: Fri, 26 Oct 2018 11:23:21 -0400 Subject: [PATCH] Use listMorph on address page --- .../assets/js/pages/address.js | 210 ++++++++++-------- .../channels/address_channel.ex | 1 + .../address_transaction/index.html.eex | 12 +- 3 files changed, 122 insertions(+), 101 deletions(-) diff --git a/apps/block_scout_web/assets/js/pages/address.js b/apps/block_scout_web/assets/js/pages/address.js index c3ed5a9ba7..63726d5ad1 100644 --- a/apps/block_scout_web/assets/js/pages/address.js +++ b/apps/block_scout_web/assets/js/pages/address.js @@ -4,27 +4,30 @@ import URI from 'urijs' import humps from 'humps' import numeral from 'numeral' import socket from '../socket' -import { batchChannel, initRedux, slideDownPrepend, slideUpRemove } from '../utils' -import { updateAllAges } from '../lib/from_now' +import { batchChannel, initRedux, listMorph } from '../utils' import { updateAllCalculatedUsdValues } from '../lib/currency.js' import { loadTokenBalanceDropdown } from '../lib/token_balance_dropdown' const BATCH_THRESHOLD = 10 +const TRANSACTION_VALIDATED_MOVE_DELAY = 1000 export const initialState = { - addressHash: null, - balance: null, - batchCountAccumulator: 0, - beyondPageOne: null, channelDisconnected: false, + + addressHash: null, filter: null, - newBlock: null, - newInternalTransactions: [], - newPendingTransactions: [], - newTransactions: [], - pendingTransactionHashes: [], + + balance: null, transactionCount: null, - validationCount: null + validationCount: null, + + pendingTransactions: [], + transactions: [], + internalTransactions: [], + internalTransactionsBatch: [], + validatedBlocks: [], + + beyondPageOne: null } export function reducer (state = initialState, action) { @@ -32,11 +35,18 @@ export function reducer (state = initialState, action) { case 'PAGE_LOAD': { return Object.assign({}, state, { addressHash: action.addressHash, - beyondPageOne: action.beyondPageOne, filter: action.filter, - pendingTransactionHashes: action.pendingTransactionHashes, + + balance: action.balance, transactionCount: numeral(action.transactionCount).value(), - validationCount: action.validationCount ? numeral(action.validationCount).value() : null + validationCount: action.validationCount ? numeral(action.validationCount).value() : null, + + pendingTransactions: action.pendingTransactions, + transactions: action.transactions, + internalTransactions: action.internalTransactions, + validatedBlocks: action.validatedBlocks, + + beyondPageOne: action.beyondPageOne }) } case 'CHANNEL_DISCONNECTED': { @@ -44,7 +54,7 @@ export function reducer (state = initialState, action) { return Object.assign({}, state, { channelDisconnected: true, - batchCountAccumulator: 0 + internalTransactionsBatch: [] }) } case 'RECEIVED_NEW_BLOCK': { @@ -54,7 +64,10 @@ export function reducer (state = initialState, action) { if (state.beyondPageOne) return Object.assign({}, state, { validationCount }) return Object.assign({}, state, { - newBlock: action.msg.blockHtml, + validatedBlocks: [ + action.msg, + ...state.validatedBlocks + ], validationCount }) } @@ -68,16 +81,19 @@ export function reducer (state = initialState, action) { (state.filter === 'from' && fromAddressHash === state.addressHash) )) - if (!state.batchCountAccumulator && incomingInternalTransactions.length < BATCH_THRESHOLD) { + if (!state.internalTransactionsBatch.length && incomingInternalTransactions.length < BATCH_THRESHOLD) { return Object.assign({}, state, { - newInternalTransactions: [ - ...state.newInternalTransactions, - ..._.map(incomingInternalTransactions, 'internalTransactionHtml') + internalTransactions: [ + ...incomingInternalTransactions.reverse(), + ...state.internalTransactions ] }) } else { return Object.assign({}, state, { - batchCountAccumulator: state.batchCountAccumulator + incomingInternalTransactions.length + internalTransactionsBatch: [ + ...incomingInternalTransactions.reverse(), + ...state.internalTransactionsBatch + ] }) } } @@ -90,16 +106,19 @@ export function reducer (state = initialState, action) { } return Object.assign({}, state, { - newPendingTransactions: [ - ...state.newPendingTransactions, - action.msg.transactionHtml - ], - pendingTransactionHashes: [ - ...state.pendingTransactionHashes, - action.msg.transactionHash + pendingTransactions: [ + action.msg, + ...state.pendingTransactions ] }) } + case 'REMOVE_PENDING_TRANSACTION': { + if (state.channelDisconnected) return state + + return Object.assign({}, state, { + pendingTransactions: state.pendingTransactions.filter((transaction) => action.msg.transactionHash !== transaction.transactionHash) + }) + } case 'RECEIVED_NEW_TRANSACTION': { if (state.channelDisconnected) return state @@ -111,15 +130,12 @@ export function reducer (state = initialState, action) { return Object.assign({}, state, { transactionCount }) } - const updatedPendingTransactionHashes = - _.without(state.pendingTransactionHashes, action.msg.transactionHash) - return Object.assign({}, state, { - newTransactions: [ - ...state.newTransactions, - action.msg + pendingTransactions: state.pendingTransactions.map((transaction) => action.msg.transactionHash === transaction.transactionHash ? Object.assign({}, action.msg, { validated: true }) : transaction), + transactions: [ + action.msg, + ...state.transactions ], - pendingTransactionHashes: updatedPendingTransactionHashes, transactionCount: transactionCount }) } @@ -142,13 +158,32 @@ if ($addressDetailsPage.length) { const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true)) store.dispatch({ type: 'PAGE_LOAD', + addressHash, - beyondPageOne: !!blockNumber, filter, - pendingTransactionHashes: $('[data-selector="pending-transactions-list"]').children() - .map((index, el) => el.dataset.transactionHash).toArray(), + + balance: $('[data-selector="balance-card"]').html(), transactionCount: $('[data-selector="transaction-count"]').text(), - validationCount: $('[data-selector="validation-count"]') ? $('[data-selector="validation-count"]').text() : null + validationCount: $('[data-selector="validation-count"]') ? $('[data-selector="validation-count"]').text() : null, + + pendingTransactions: $('[data-selector="pending-transactions-list"]').children().map((index, el) => ({ + transactionHash: el.dataset.transactionHash, + transactionHtml: el.outerHTML + })).toArray(), + transactions: $('[data-selector="transactions-list"]').children().map((index, el) => ({ + transactionHash: el.dataset.transactionHash, + transactionHtml: el.outerHTML + })).toArray(), + internalTransactions: $('[data-selector="internal-transactions-list"]').children().map((index, el) => ({ + internalTransactionId: el.dataset.internalTransactionId, + internalTransactionHtml: el.outerHTML + })).toArray(), + validatedBlocks: $('[data-selector="validations-list"]').children().map((index, el) => ({ + blockNumber: parseInt(el.dataset.blockNumber), + blockHtml: el.outerHTML + })).toArray(), + + beyondPageOne: !!blockNumber }) addressChannel.join() addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) @@ -159,77 +194,60 @@ if ($addressDetailsPage.length) { store.dispatch({ type: 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH', msgs: humps.camelizeKeys(msgs) }) )) addressChannel.on('pending_transaction', (msg) => store.dispatch({ type: 'RECEIVED_NEW_PENDING_TRANSACTION', msg: humps.camelizeKeys(msg) })) - addressChannel.on('transaction', (msg) => store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION', msg: humps.camelizeKeys(msg) })) + addressChannel.on('transaction', (msg) => { + store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION', msg: humps.camelizeKeys(msg) }) + setTimeout(() => store.dispatch({ type: 'REMOVE_PENDING_TRANSACTION', msg: humps.camelizeKeys(msg) }), TRANSACTION_VALIDATED_MOVE_DELAY) + }) 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', msg: humps.camelizeKeys(msg) })) }, render (state, oldState) { - const $balance = $('[data-selector="balance-card"]') - const $channelBatching = $('[data-selector="channel-batching-message"]') - const $channelBatchingCount = $('[data-selector="channel-batching-count"]') - const $channelDisconnected = $('[data-selector="channel-disconnected-message"]') - const $emptyInternalTransactionsList = $('[data-selector="empty-internal-transactions-list"]') - const $emptyTransactionsList = $('[data-selector="empty-transactions-list"]') - const $internalTransactionsList = $('[data-selector="internal-transactions-list"]') - const $pendingTransactionsCount = $('[data-selector="pending-transactions-count"]') - const $pendingTransactionsList = $('[data-selector="pending-transactions-list"]') - const $transactionCount = $('[data-selector="transaction-count"]') - const $transactionsList = $('[data-selector="transactions-list"]') - const $validationCount = $('[data-selector="validation-count"]') - const $validationsList = $('[data-selector="validations-list"]') - - if ($emptyInternalTransactionsList.length && state.newInternalTransactions.length) window.location.reload() - if ($emptyTransactionsList.length && state.newTransactions.length) window.location.reload() - if (state.channelDisconnected) $channelDisconnected.show() + if (state.channelDisconnected) $('[data-selector="channel-disconnected-message"]').show() + if (oldState.balance !== state.balance) { - $balance.empty().append(state.balance) + $('[data-selector="balance-card"]').empty().append(state.balance) loadTokenBalanceDropdown() updateAllCalculatedUsdValues() } - if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format()) - if (oldState.validationCount !== state.validationCount) $validationCount.empty().append(numeral(state.validationCount).format()) - if (state.batchCountAccumulator) { - $channelBatching.show() - $channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format() - } else { - $channelBatching.hide() + if (oldState.transactionCount !== state.transactionCount) $('[data-selector="transaction-count"]').empty().append(numeral(state.transactionCount).format()) + if (oldState.validationCount !== state.validationCount) $('[data-selector="validation-count"]').empty().append(numeral(state.validationCount).format()) + + if (oldState.pendingTransactions !== state.pendingTransactions) { + const container = $('[data-selector="pending-transactions-list"]')[0] + const newElements = _.map(state.pendingTransactions, ({ transactionHtml }) => $(transactionHtml)[0]) + listMorph(container, newElements, { key: 'dataset.transactionHash' }) + if($('[data-selector="pending-transactions-count"]').length) $('[data-selector="pending-transactions-count"]')[0].innerHTML = numeral(state.pendingTransactions.filter(({ validated }) => !validated).length).format() } - if (oldState.newInternalTransactions !== state.newInternalTransactions && $internalTransactionsList.length) { - slideDownPrepend($internalTransactionsList, state.newInternalTransactions.slice(oldState.newInternalTransactions.length).reverse().join('')) - updateAllAges() + function updateTransactions () { + const container = $('[data-selector="transactions-list"]')[0] + const newElements = _.map(state.transactions, ({ transactionHtml }) => $(transactionHtml)[0]) + listMorph(container, newElements, { key: 'dataset.transactionHash' }) } - if (oldState.pendingTransactionHashes.length !== state.pendingTransactionHashes.length && $pendingTransactionsCount.length) { - $pendingTransactionsCount[0].innerHTML = numeral(state.pendingTransactionHashes.length).format() + if (oldState.transactions !== state.transactions) { + if ($('[data-selector="pending-transactions-list"]').is(':visible')) { + setTimeout(updateTransactions, TRANSACTION_VALIDATED_MOVE_DELAY + 400) + } else { + updateTransactions() + } } - if (oldState.newPendingTransactions !== state.newPendingTransactions && $pendingTransactionsList.length) { - slideDownPrepend($pendingTransactionsList, state.newPendingTransactions.slice(oldState.newPendingTransactions.length).reverse().join('')) - updateAllAges() + if (oldState.internalTransactions !== state.internalTransactions) { + const container = $('[data-selector="internal-transactions-list"]')[0] + const newElements = _.map(state.internalTransactions, ({ internalTransactionHtml }) => $(internalTransactionHtml)[0]) + listMorph(container, newElements, { key: 'dataset.internalTransactionId' }) } - if (oldState.newTransactions !== state.newTransactions && $transactionsList.length) { - const newlyValidatedTransactions = state.newTransactions.slice(oldState.newTransactions.length).reverse() - newlyValidatedTransactions.forEach(({ transactionHash, transactionHtml }) => { - let $transaction = $(`[data-selector="pending-transactions-list"] [data-transaction-hash="${transactionHash}"]`) - $transaction.html($(transactionHtml).html()) - if ($transaction.is(':visible')) { - setTimeout(() => { - $transaction.addClass('shrink-out') - setTimeout(() => { - slideUpRemove($transaction) - slideDownPrepend($transactionsList, transactionHtml) - }, 400) - }, 1000) - } else { - $transaction.remove() - slideDownPrepend($transactionsList, transactionHtml) - } - }) - updateAllAges() + const $channelBatching = $('[data-selector="channel-batching-message"]') + if (state.internalTransactionsBatch.length) { + $channelBatching.show() + $('[data-selector="channel-batching-count"]')[0].innerHTML = numeral(state.internalTransactionsBatch.length).format() + } else { + $channelBatching.hide() } - if (oldState.newBlock !== state.newBlock) { - slideDownPrepend($validationsList, state.newBlock) - updateAllAges() + if (oldState.validatedBlocks !== state.validatedBlocks) { + const container = $('[data-selector="validations-list"]')[0] + const newElements = _.map(state.validatedBlocks, ({ blockHtml }) => $(blockHtml)[0]) + listMorph(container, newElements, { key: 'dataset.blockNumber' }) } } }) diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index 6213b6aaf5..383dcf1897 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -55,6 +55,7 @@ defmodule BlockScoutWeb.AddressChannel do push(socket, "internal_transaction", %{ to_address_hash: to_string(internal_transaction.to_address_hash), from_address_hash: to_string(internal_transaction.from_address_hash), + internal_transaction_id: to_string(internal_transaction.id), internal_transaction_html: rendered_internal_transaction }) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex index 9dda749f2c..ac1c69eee6 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex @@ -52,17 +52,19 @@

<%= gettext "Transactions" %>

- <%= link to: "#pending-transactions", class: "d-inline-block mb-3", "data-toggle": "collapse" do %> + <%= link to: "#pending-transactions-container", class: "d-inline-block mb-3", "data-toggle": "collapse" do %> <%= gettext("Show") %> <%= gettext("Hide") %> <%= length(@pending_transactions) %> <%= gettext("Pending Transactions") %> <% end %> -
- <%= for pending_transaction <- @pending_transactions do %> - <%= render(BlockScoutWeb.TransactionView, "_tile.html", current_address: @address, transaction: pending_transaction) %> - <% end %> +
+
+ <%= for pending_transaction <- @pending_transactions do %> + <%= render(BlockScoutWeb.TransactionView, "_tile.html", current_address: @address, transaction: pending_transaction) %> + <% end %> +