listMorph all the things

pull/1049/head
jimmay5469 6 years ago
parent 8ca3cbcdf4
commit 0251d00e95
  1. 74
      apps/block_scout_web/assets/__tests__/pages/pending_transactions.js
  2. 41
      apps/block_scout_web/assets/__tests__/pages/transactions.js
  3. 2
      apps/block_scout_web/assets/js/pages/address.js
  4. 25
      apps/block_scout_web/assets/js/pages/blocks.js
  5. 59
      apps/block_scout_web/assets/js/pages/chain.js
  6. 83
      apps/block_scout_web/assets/js/pages/pending_transactions.js
  7. 53
      apps/block_scout_web/assets/js/pages/transactions.js
  8. 13
      apps/block_scout_web/assets/js/utils.js
  9. 4
      apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex
  10. 2
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex

@ -1,3 +1,4 @@
import _ from 'lodash'
import { reducer, initialState } from '../../js/pages/pending_transactions' import { reducer, initialState } from '../../js/pages/pending_transactions'
test('CHANNEL_DISCONNECTED', () => { test('CHANNEL_DISCONNECTED', () => {
@ -22,8 +23,11 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactions).toEqual(['test']) expect(output.pendingTransactions).toEqual([{
expect(output.newPendingTransactionHashesBatch.length).toEqual(0) transactionHash: '0x00',
transactionHtml: 'test'
}])
expect(output.pendingTransactionsBatch.length).toEqual(0)
expect(output.pendingTransactionCount).toEqual(1) expect(output.pendingTransactionCount).toEqual(1)
}) })
test('large batch of transactions', () => { test('large batch of transactions', () => {
@ -67,13 +71,16 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactions).toEqual([]) expect(output.pendingTransactions).toEqual([])
expect(output.newPendingTransactionHashesBatch.length).toEqual(11) expect(output.pendingTransactionsBatch.length).toEqual(11)
expect(output.pendingTransactionCount).toEqual(11) expect(output.pendingTransactionCount).toEqual(11)
}) })
test('single transaction after single transaction', () => { test('single transaction after single transaction', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
newPendingTransactions: ['test 1'], pendingTransactions: [{
transactionHash: '0x01',
transactionHtml: 'test 1'
}],
pendingTransactionCount: 1 pendingTransactionCount: 1
}) })
const action = { const action = {
@ -85,13 +92,16 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactions).toEqual(['test 1', 'test 2']) expect(output.pendingTransactions).toEqual([
expect(output.newPendingTransactionHashesBatch.length).toEqual(0) { transactionHash: '0x02', transactionHtml: 'test 2' },
{ transactionHash: '0x01', transactionHtml: 'test 1' }
])
expect(output.pendingTransactionsBatch.length).toEqual(0)
expect(output.pendingTransactionCount).toEqual(2) expect(output.pendingTransactionCount).toEqual(2)
}) })
test('single transaction after large batch of transactions', () => { test('single transaction after large batch of transactions', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
newPendingTransactionHashesBatch: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'] pendingTransactionsBatch: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']
}) })
const action = { const action = {
type: 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH', type: 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH',
@ -102,12 +112,12 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactions).toEqual([]) expect(output.pendingTransactions).toEqual([])
expect(output.newPendingTransactionHashesBatch.length).toEqual(12) expect(output.pendingTransactionsBatch.length).toEqual(12)
}) })
test('large batch of transactions after large batch of transactions', () => { test('large batch of transactions after large batch of transactions', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
newPendingTransactionHashesBatch: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'] pendingTransactionsBatch: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']
}) })
const action = { const action = {
type: 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH', type: 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH',
@ -148,8 +158,8 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactions).toEqual([]) expect(output.pendingTransactions).toEqual([])
expect(output.newPendingTransactionHashesBatch.length).toEqual(22) expect(output.pendingTransactionsBatch.length).toEqual(22)
}) })
test('after disconnection', () => { test('after disconnection', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
@ -164,7 +174,7 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactions).toEqual([]) expect(output.pendingTransactions).toEqual([])
}) })
test('on page 2+', () => { test('on page 2+', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
@ -180,28 +190,50 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactions).toEqual([]) expect(output.pendingTransactions).toEqual([])
expect(output.pendingTransactionCount).toEqual(2) expect(output.pendingTransactionCount).toEqual(2)
}) })
}) })
describe('RECEIVED_NEW_TRANSACTION', () => { describe('RECEIVED_NEW_TRANSACTION', () => {
test('single transaction collated', () => { test('single transaction collated', () => {
const state = { ...initialState, pendingTransactionCount: 2 } const state = Object.assign({}, initialState, {
pendingTransactionCount: 2,
pendingTransactions: [{
transactionHash: '0x00',
transactionHtml: 'old'
}]
})
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION', type: 'RECEIVED_NEW_TRANSACTION',
msg: { msg: {
transactionHash: '0x00' transactionHash: '0x00',
transactionHtml: 'new'
} }
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.pendingTransactionCount).toBe(1) expect(output.pendingTransactionCount).toBe(1)
expect(output.newTransactionHashes).toEqual(['0x00']) expect(output.pendingTransactions).toEqual([{
transactionHash: '0x00',
transactionHtml: 'new'
}])
}) })
test('single transaction collated after batch', () => { test('single transaction collated after batch', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
newPendingTransactionHashesBatch: ['0x01', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'] pendingTransactionsBatch: [
{ transactionHash: '0x01' },
{ transactionHash: '2' },
{ transactionHash: '3' },
{ transactionHash: '4' },
{ transactionHash: '5' },
{ transactionHash: '6' },
{ transactionHash: '7' },
{ transactionHash: '8' },
{ transactionHash: '9' },
{ transactionHash: '10' },
{ transactionHash: '11' }
]
}) })
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION', type: 'RECEIVED_NEW_TRANSACTION',
@ -211,8 +243,8 @@ describe('RECEIVED_NEW_TRANSACTION', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newPendingTransactionHashesBatch.length).toEqual(10) expect(output.pendingTransactionsBatch.length).toEqual(10)
expect(output.newPendingTransactionHashesBatch).not.toContain('0x01') expect(_.map(output.pendingTransactionsBatch, 'transactionHash')).not.toContain('0x01')
}) })
test('on page 2+', () => { test('on page 2+', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {

@ -8,7 +8,7 @@ test('CHANNEL_DISCONNECTED', () => {
const output = reducer(state, action) const output = reducer(state, action)
expect(output.channelDisconnected).toBe(true) expect(output.channelDisconnected).toBe(true)
expect(output.batchCountAccumulator).toBe(0) expect(output.transactionsBatch.length).toBe(0)
}) })
describe('RECEIVED_NEW_TRANSACTION_BATCH', () => { describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
@ -22,8 +22,8 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual(['test']) expect(output.transactions).toEqual([{ transactionHtml: 'test' }])
expect(output.batchCountAccumulator).toEqual(0) expect(output.transactionsBatch.length).toEqual(0)
expect(output.transactionCount).toEqual(1) expect(output.transactionCount).toEqual(1)
}) })
test('large batch of transactions', () => { test('large batch of transactions', () => {
@ -56,13 +56,15 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual([]) expect(output.transactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(11) expect(output.transactionsBatch.length).toEqual(11)
expect(output.transactionCount).toEqual(11) expect(output.transactionCount).toEqual(11)
}) })
test('single transaction after single transaction', () => { test('single transaction after single transaction', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
newTransactions: ['test 1'] transactions: [{
transactionHtml: 'test 1'
}]
}) })
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH', type: 'RECEIVED_NEW_TRANSACTION_BATCH',
@ -72,12 +74,15 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual(['test 1', 'test 2']) expect(output.transactions).toEqual([
expect(output.batchCountAccumulator).toEqual(0) { transactionHtml: 'test 2' },
{ transactionHtml: 'test 1' }
])
expect(output.transactionsBatch.length).toEqual(0)
}) })
test('single transaction after large batch of transactions', () => { test('single transaction after large batch of transactions', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
batchCountAccumulator: 11 transactionsBatch: [1,2,3,4,5,6,7,8,9,10,11]
}) })
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH', type: 'RECEIVED_NEW_TRANSACTION_BATCH',
@ -87,12 +92,12 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual([]) expect(output.transactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(12) expect(output.transactionsBatch.length).toEqual(12)
}) })
test('large batch of transactions after large batch of transactions', () => { test('large batch of transactions after large batch of transactions', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
batchCountAccumulator: 11 transactionsBatch: [1,2,3,4,5,6,7,8,9,10,11]
}) })
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH', type: 'RECEIVED_NEW_TRANSACTION_BATCH',
@ -122,8 +127,8 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual([]) expect(output.transactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(22) expect(output.transactionsBatch.length).toEqual(22)
}) })
test('after disconnection', () => { test('after disconnection', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
@ -137,8 +142,8 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual([]) expect(output.transactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(0) expect(output.transactionsBatch.length).toEqual(0)
}) })
test('on page 2+', () => { test('on page 2+', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
@ -153,8 +158,8 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
} }
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual([]) expect(output.transactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(0) expect(output.transactionsBatch.length).toEqual(0)
expect(output.transactionCount).toEqual(2) expect(output.transactionCount).toEqual(2)
}) })
}) })

@ -233,7 +233,7 @@ const elements = {
} }
}, },
'[data-selector="transactions-list"]': { '[data-selector="transactions-list"]': {
load ($el, store) { load ($el) {
return { return {
transactions: $el.children().map((index, el) => ({ transactions: $el.children().map((index, el) => ({
transactionHash: el.dataset.transactionHash, transactionHash: el.dataset.transactionHash,

@ -3,13 +3,12 @@ import _ from 'lodash'
import URI from 'urijs' import URI from 'urijs'
import humps from 'humps' import humps from 'humps'
import socket from '../socket' import socket from '../socket'
import { updateAllAges } from '../lib/from_now' import { createStore, connectElements, listMorph } from '../utils'
import { createStore, connectElements, slideDownPrepend } from '../utils'
export const initialState = { export const initialState = {
channelDisconnected: false, channelDisconnected: false,
newBlock: null, blocks: [],
beyondPageOne: null beyondPageOne: null
} }
@ -29,7 +28,10 @@ export function reducer (state = initialState, action) {
if (state.channelDisconnected || state.beyondPageOne) return state if (state.channelDisconnected || state.beyondPageOne) return state
return Object.assign({}, state, { return Object.assign({}, state, {
newBlock: action.msg.blockHtml blocks: [
action.msg,
...state.blocks
]
}) })
} }
default: default:
@ -44,10 +46,19 @@ const elements = {
} }
}, },
'[data-selector="blocks-list"]': { '[data-selector="blocks-list"]': {
load ($el) {
return {
blocks: $el.children().map((index, el) => ({
blockNumber: parseInt(el.dataset.blockNumber),
blockHtml: el.outerHTML
})).toArray()
}
},
render ($el, state, oldState) { render ($el, state, oldState) {
if (oldState.newBlock === state.newBlock) return if (oldState.blocks === state.blocks) return
slideDownPrepend($el, state.newBlock) const container = $el[0]
updateAllAges() const newElements = _.map(state.blocks, ({ blockHtml }) => $(blockHtml)[0])
listMorph(container, newElements, { key: 'dataset.blockNumber' })
} }
} }
} }

@ -3,9 +3,8 @@ import _ from 'lodash'
import humps from 'humps' import humps from 'humps'
import numeral from 'numeral' import numeral from 'numeral'
import socket from '../socket' import socket from '../socket'
import { updateAllAges } from '../lib/from_now'
import { exchangeRateChannel, formatUsdValue } from '../lib/currency' import { exchangeRateChannel, formatUsdValue } from '../lib/currency'
import { createStore, connectElements, slideDownPrepend } from '../utils' import { createStore, connectElements, listMorph } from '../utils'
import { createMarketHistoryChart } from '../lib/market_history_chart' import { createMarketHistoryChart } from '../lib/market_history_chart'
export const initialState = { export const initialState = {
@ -13,8 +12,8 @@ export const initialState = {
availableSupply: null, availableSupply: null,
averageBlockTime: null, averageBlockTime: null,
marketHistoryData: null, marketHistoryData: null,
newBlock: null, blocks: null,
newTransaction: null, transactions: null,
transactionCount: null, transactionCount: null,
usdMarketCap: null usdMarketCap: null
} }
@ -32,7 +31,10 @@ export function reducer (state = initialState, action) {
case 'RECEIVED_NEW_BLOCK': { case 'RECEIVED_NEW_BLOCK': {
return Object.assign({}, state, { return Object.assign({}, state, {
averageBlockTime: action.msg.averageBlockTime, averageBlockTime: action.msg.averageBlockTime,
newBlock: action.msg.chainBlockHtml blocks: [
action.msg,
...state.blocks.slice(0, -1)
]
}) })
} }
case 'RECEIVED_NEW_EXCHANGE_RATE': { case 'RECEIVED_NEW_EXCHANGE_RATE': {
@ -44,8 +46,11 @@ export function reducer (state = initialState, action) {
} }
case 'RECEIVED_NEW_TRANSACTION': { case 'RECEIVED_NEW_TRANSACTION': {
return Object.assign({}, state, { return Object.assign({}, state, {
newTransaction: action.msg.transactionHtml, transactionCount: state.transactionCount + 1,
transactionCount: state.transactionCount + 1 transactions: [
action.msg,
...state.transactions.slice(0, -1)
]
}) })
} }
default: default:
@ -92,19 +97,35 @@ const elements = {
} }
}, },
'[data-selector="chain-block-list"]': { '[data-selector="chain-block-list"]': {
load ($el) {
return {
blocks: $el.children().map((index, el) => ({
blockNumber: parseInt(el.dataset.blockNumber),
chainBlockHtml: el.outerHTML
})).toArray()
}
},
render ($el, state, oldState) { render ($el, state, oldState) {
if (oldState.newBlock === state.newBlock) return if (oldState.blocks === state.blocks) return
$el.children().last().remove() const container = $el[0]
$el.prepend(newBlockHtml(state.newBlock)) const newElements = _.map(state.blocks, ({ chainBlockHtml }) => $(chainBlockHtml)[0])
updateAllAges() listMorph(container, newElements, { key: 'dataset.blockNumber', horizontal: true })
} }
}, },
'[data-selector="transactions-list"]': { '[data-selector="transactions-list"]': {
load ($el) {
return {
transactions: $el.children().map((index, el) => ({
transactionHash: el.dataset.transactionHash,
transactionHtml: el.outerHTML
})).toArray()
}
},
render ($el, state, oldState) { render ($el, state, oldState) {
if (oldState.newTransaction === state.newTransaction) return if (oldState.transactions === state.transactions) return
$el.children().last().remove() const container = $el[0]
slideDownPrepend($el, state.newTransaction) const newElements = _.map(state.transactions, ({ transactionHtml }) => $(transactionHtml)[0])
updateAllAges() listMorph(container, newElements, { key: 'dataset.transactionHash' })
} }
} }
} }
@ -140,11 +161,3 @@ if ($chainDetailsPage.length) {
msg: humps.camelizeKeys(msg) msg: humps.camelizeKeys(msg)
})) }))
} }
function newBlockHtml (blockHtml) {
return `
<div class="col-lg-3 fade-up-blocks-chain">
${blockHtml}
</div>
`
}

@ -4,18 +4,19 @@ import URI from 'urijs'
import humps from 'humps' import humps from 'humps'
import numeral from 'numeral' import numeral from 'numeral'
import socket from '../socket' import socket from '../socket'
import { updateAllAges } from '../lib/from_now' import { createStore, connectElements, batchChannel, listMorph } from '../utils'
import { createStore, connectElements, batchChannel, slideDownPrepend, slideUpRemove } from '../utils'
const BATCH_THRESHOLD = 10 const BATCH_THRESHOLD = 10
export const initialState = { export const initialState = {
newPendingTransactionHashesBatch: [],
beyondPageOne: null,
channelDisconnected: false, channelDisconnected: false,
newPendingTransactions: [],
newTransactionHashes: [], pendingTransactionCount: null,
pendingTransactionCount: null
pendingTransactions: [],
pendingTransactionsBatch: [],
beyondPageOne: null
} }
export function reducer (state = initialState, action) { export function reducer (state = initialState, action) {
@ -33,9 +34,9 @@ export function reducer (state = initialState, action) {
if (state.channelDisconnected) return state if (state.channelDisconnected) return state
return Object.assign({}, state, { return Object.assign({}, state, {
newPendingTransactionHashesBatch: _.without(state.newPendingTransactionHashesBatch, action.msg.transactionHash), pendingTransactions: state.pendingTransactions.map((transaction) => action.msg.transactionHash === transaction.transactionHash ? action.msg : transaction),
pendingTransactionCount: state.pendingTransactionCount - 1, pendingTransactionsBatch: state.pendingTransactionsBatch.filter((transaction) => action.msg.transactionHash !== transaction.transactionHash),
newTransactionHashes: [action.msg.transactionHash] pendingTransactionCount: state.pendingTransactionCount - 1
}) })
} }
case 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH': { case 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH': {
@ -45,24 +46,29 @@ export function reducer (state = initialState, action) {
if (state.beyondPageOne) return Object.assign({}, state, { pendingTransactionCount }) if (state.beyondPageOne) return Object.assign({}, state, { pendingTransactionCount })
if (!state.newPendingTransactionHashesBatch.length && action.msgs.length < BATCH_THRESHOLD) { if (!state.pendingTransactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) {
return Object.assign({}, state, { return Object.assign({}, state, {
newPendingTransactions: [ pendingTransactions: [
...state.newPendingTransactions, ...action.msgs.reverse(),
..._.map(action.msgs, 'transactionHtml') ...state.pendingTransactions
], ],
pendingTransactionCount pendingTransactionCount
}) })
} else { } else {
return Object.assign({}, state, { return Object.assign({}, state, {
newPendingTransactionHashesBatch: [ pendingTransactionsBatch: [
...state.newPendingTransactionHashesBatch, ...action.msgs.reverse(),
..._.map(action.msgs, 'transactionHash') ...state.pendingTransactionsBatch
], ],
pendingTransactionCount pendingTransactionCount
}) })
} }
} }
case 'REMOVE_PENDING_TRANSACTION': {
return Object.assign({}, state, {
pendingTransactions: state.pendingTransactions.filter((transaction) => action.msg.transactionHash !== transaction.transactionHash)
})
}
default: default:
return state return state
} }
@ -77,9 +83,9 @@ const elements = {
'[data-selector="channel-batching-count"]': { '[data-selector="channel-batching-count"]': {
render ($el, state, oldState) { render ($el, state, oldState) {
const $channelBatching = $('[data-selector="channel-batching-message"]') const $channelBatching = $('[data-selector="channel-batching-message"]')
if (state.newPendingTransactionHashesBatch.length) { if (state.pendingTransactionsBatch.length) {
$channelBatching.show() $channelBatching.show()
$el[0].innerHTML = numeral(state.newPendingTransactionHashesBatch.length).format() $el[0].innerHTML = numeral(state.pendingTransactionsBatch.length).format()
} else { } else {
$channelBatching.hide() $channelBatching.hide()
} }
@ -95,24 +101,19 @@ const elements = {
} }
}, },
'[data-selector="transactions-pending-list"]': { '[data-selector="transactions-pending-list"]': {
render ($el, state, oldState) { load ($el) {
if (oldState.newTransactionHashes !== state.newTransactionHashes && state.newTransactionHashes.length > 0) { return {
const $transaction = $(`[data-transaction-hash="${state.newTransactionHashes[0]}"]`) pendingTransactions: $el.children().map((index, el) => ({
$transaction.addClass('shrink-out') transactionHash: el.dataset.transactionHash,
setTimeout(() => { transactionHtml: el.outerHTML
if ($transaction.length === 1 && $transaction.siblings().length === 0 && state.pendingTransactionCount > 0) { })).toArray()
window.location.href = URI(window.location).removeQuery('inserted_at').removeQuery('hash').toString()
} else {
slideUpRemove($transaction)
}
}, 400)
}
if (oldState.newPendingTransactions !== state.newPendingTransactions) {
const newTransactionsToInsert = state.newPendingTransactions.slice(oldState.newPendingTransactions.length)
slideDownPrepend($el, newTransactionsToInsert.reverse().join(''))
updateAllAges()
} }
},
render ($el, state, oldState) {
if (oldState.pendingTransactions === state.pendingTransactions) return
const container = $el[0]
const newElements = _.map(state.pendingTransactions, ({ transactionHtml }) => $(transactionHtml)[0])
listMorph(container, newElements, { key: 'dataset.transactionHash' })
} }
} }
} }
@ -131,10 +132,16 @@ if ($transactionPendingListPage.length) {
transactionsChannel.onError(() => store.dispatch({ transactionsChannel.onError(() => store.dispatch({
type: 'CHANNEL_DISCONNECTED' type: 'CHANNEL_DISCONNECTED'
})) }))
transactionsChannel.on('transaction', (msg) => store.dispatch({ transactionsChannel.on('transaction', (msg) => {
store.dispatch({
type: 'RECEIVED_NEW_TRANSACTION', type: 'RECEIVED_NEW_TRANSACTION',
msg: humps.camelizeKeys(msg) msg: humps.camelizeKeys(msg)
})) })
setTimeout(() => store.dispatch({
type: 'REMOVE_PENDING_TRANSACTION',
msg: humps.camelizeKeys(msg)
}), 1000)
})
const pendingTransactionsChannel = socket.channel(`transactions:new_pending_transaction`) const pendingTransactionsChannel = socket.channel(`transactions:new_pending_transaction`)
pendingTransactionsChannel.join() pendingTransactionsChannel.join()

@ -4,17 +4,19 @@ import URI from 'urijs'
import humps from 'humps' import humps from 'humps'
import numeral from 'numeral' import numeral from 'numeral'
import socket from '../socket' import socket from '../socket'
import { updateAllAges } from '../lib/from_now' import { createStore, connectElements, batchChannel, listMorph } from '../utils'
import { createStore, connectElements, batchChannel, slideDownPrepend } from '../utils'
const BATCH_THRESHOLD = 10 const BATCH_THRESHOLD = 10
export const initialState = { export const initialState = {
batchCountAccumulator: 0,
beyondPageOne: null,
channelDisconnected: false, channelDisconnected: false,
newTransactions: [],
transactionCount: null transactionCount: null,
transactions: [],
transactionsBatch: [],
beyondPageOne: null
} }
export function reducer (state = initialState, action) { export function reducer (state = initialState, action) {
@ -26,7 +28,7 @@ export function reducer (state = initialState, action) {
case 'CHANNEL_DISCONNECTED': { case 'CHANNEL_DISCONNECTED': {
return Object.assign({}, state, { return Object.assign({}, state, {
channelDisconnected: true, channelDisconnected: true,
batchCountAccumulator: 0 transactionsBatch: []
}) })
} }
case 'RECEIVED_NEW_TRANSACTION_BATCH': { case 'RECEIVED_NEW_TRANSACTION_BATCH': {
@ -36,17 +38,20 @@ export function reducer (state = initialState, action) {
if (state.beyondPageOne) return Object.assign({}, state, { transactionCount }) if (state.beyondPageOne) return Object.assign({}, state, { transactionCount })
if (!state.batchCountAccumulator && action.msgs.length < BATCH_THRESHOLD) { if (!state.transactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) {
return Object.assign({}, state, { return Object.assign({}, state, {
newTransactions: [ transactions: [
...state.newTransactions, ...action.msgs.reverse(),
..._.map(action.msgs, 'transactionHtml') ...state.transactions
], ],
transactionCount transactionCount
}) })
} else { } else {
return Object.assign({}, state, { return Object.assign({}, state, {
batchCountAccumulator: state.batchCountAccumulator + action.msgs.length, transactionsBatch: [
...action.msgs.reverse(),
...state.transactionsBatch
],
transactionCount transactionCount
}) })
} }
@ -65,12 +70,9 @@ const elements = {
'[data-selector="channel-batching-count"]': { '[data-selector="channel-batching-count"]': {
render ($el, state, oldState) { render ($el, state, oldState) {
const $channelBatching = $('[data-selector="channel-batching-message"]') const $channelBatching = $('[data-selector="channel-batching-message"]')
if (state.batchCountAccumulator) { if (!state.transactionsBatch.length) return $channelBatching.hide()
$channelBatching.show() $channelBatching.show()
$el[0].innerHTML = numeral(state.batchCountAccumulator).format() $el[0].innerHTML = numeral(state.transactionsBatch.length).format()
} else {
$channelBatching.hide()
}
} }
}, },
'[data-selector="transaction-count"]': { '[data-selector="transaction-count"]': {
@ -83,12 +85,19 @@ const elements = {
} }
}, },
'[data-selector="transactions-list"]': { '[data-selector="transactions-list"]': {
load ($el, store) {
return {
transactions: $el.children().map((index, el) => ({
transactionHash: el.dataset.transactionHash,
transactionHtml: el.outerHTML
})).toArray()
}
},
render ($el, state, oldState) { render ($el, state, oldState) {
if (oldState.newTransactions === state.newTransactions) return if (oldState.transactions === state.transactions) return
const newTransactionsToInsert = state.newTransactions.slice(oldState.newTransactions.length) const container = $el[0]
slideDownPrepend($el, newTransactionsToInsert.reverse().join('')) const newElements = _.map(state.transactions, ({ transactionHtml }) => $(transactionHtml)[0])
listMorph(container, newElements, { key: 'dataset.transactionHash' })
updateAllAges()
} }
} }
} }

@ -47,28 +47,21 @@ export function connectElements ({ elements, store }) {
}) })
} }
export function slideDownPrepend ($container, content) { function slideDownAppend ($container, content) {
smarterSlideDown($(content), {
insert ($el) {
$container.prepend($el)
}
})
}
export function slideDownAppend ($container, content) {
smarterSlideDown($(content), { smarterSlideDown($(content), {
insert ($el) { insert ($el) {
$container.append($el) $container.append($el)
} }
}) })
} }
export function slideDownBefore ($container, content) { function slideDownBefore ($container, content) {
smarterSlideDown($(content), { smarterSlideDown($(content), {
insert ($el) { insert ($el) {
$container.before($el) $container.before($el)
} }
}) })
} }
export function slideUpRemove ($el) { function slideUpRemove ($el) {
smarterSlideUp($el, { smarterSlideUp($el, {
complete () { complete () {
$el.remove() $el.remove()

@ -1,4 +1,5 @@
<div class="tile tile-type-block d-flex flex-column" data-selector="chain-block" data-block-number="<%= @block.number %>"> <div class="col-lg-3 fade-up-blocks-chain" data-selector="chain-block" data-block-number="<%= @block.number %>">
<div class="tile tile-type-block d-flex flex-column">
<%= link( <%= link(
@block, @block,
class: "tile-title", class: "tile-title",
@ -17,4 +18,5 @@
address: @block.miner, address: @block.miner,
contract: false %> contract: false %>
</span> </span>
</div>
</div> </div>

@ -57,9 +57,7 @@
<h2 class="card-title"><%= gettext "Blocks" %></h2> <h2 class="card-title"><%= gettext "Blocks" %></h2>
<div class="row" data-selector="chain-block-list"> <div class="row" data-selector="chain-block-list">
<%= for block <- @blocks do %> <%= for block <- @blocks do %>
<div class="col-lg-3 fade-up-blocks-chain">
<%= render BlockScoutWeb.ChainView, "_block.html", block: block %> <%= render BlockScoutWeb.ChainView, "_block.html", block: block %>
</div>
<% end %> <% end %>
</div> </div>
</div> </div>

Loading…
Cancel
Save