Refactor address.js to use async_load_listing redux

pull/1137/head
Felipe Renan 6 years ago
parent 87579dfd63
commit e993356744
  1. 57
      apps/block_scout_web/assets/__tests__/pages/address.js
  2. 124
      apps/block_scout_web/assets/__tests__/pages/address/transactions.js
  3. 1
      apps/block_scout_web/assets/js/app.js
  4. 43
      apps/block_scout_web/assets/js/pages/address.js
  5. 73
      apps/block_scout_web/assets/js/pages/address/transactions.js
  6. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex

@ -145,61 +145,36 @@ describe('RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH', () => {
}) })
describe('RECEIVED_NEW_TRANSACTION', () => { describe('RECEIVED_NEW_TRANSACTION', () => {
test('with new transaction', () => { test('increment the transactions count', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
transactions: [{ transactionHash: 1, transactionHtml: 'test 1' }] addressHash: "0x001",
transactionCount: 1
}) })
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: { transactionHash: 2, transactionHtml: 'test 2' }
}
const output = reducer(state, action)
expect(output.transactions).toEqual([
{ transactionHash: 2, transactionHtml: 'test 2' },
{ transactionHash: 1, transactionHtml: 'test 1' }
])
})
test('when channel has been disconnected', () => {
const state = Object.assign({}, initialState, {
channelDisconnected: true,
transactions: [{ transactionHash: 1, transactionHtml: 'test 1' }]
})
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION', type: 'RECEIVED_NEW_TRANSACTION',
msg: { transactionHash: 2, transactionHtml: 'test 2' } msg: { fromAddressHash: "0x001", transactionHash: 2, transactionHtml: 'test 2' }
} }
const output = reducer(state, action)
expect(output.transactions).toEqual([ const newState = reducer(state, action)
{ transactionHash: 1, transactionHtml: 'test 1' }
])
})
test('beyond page one', () => {
const state = Object.assign({}, initialState, {
beyondPageOne: true,
transactions: [{ transactionHash: 1, transactionHtml: 'test 1' }]
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: { transactionHash: 2, transactionHtml: 'test 2' }
}
const output = reducer(state, action)
expect(output.transactions).toEqual([ expect(newState.transactionCount).toEqual(2)
{ transactionHash: 1, transactionHtml: 'test 1' }
])
}) })
test('with filtered out transaction', () => {
test('does not increment the count if the channel is disconnected', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
filter: 'to' addressHash: "0x001",
transactionCount: 1,
channelDisconnected: true
}) })
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION', type: 'RECEIVED_NEW_TRANSACTION',
msg: { transactionHash: 2, transactionHtml: 'test 2' } msg: { fromAddressHash: "0x001", transactionHash: 2, transactionHtml: 'test 2' }
} }
const output = reducer(state, action)
expect(output.transactions).toEqual([]) const newState = reducer(state, action)
expect(newState.transactionCount).toEqual(1)
}) })
}) })

@ -0,0 +1,124 @@
import { reducer, initialState } from '../../../js/pages/address/transactions'
describe('RECEIVED_NEW_TRANSACTION', () => {
test('with new transaction', () => {
const state = Object.assign({}, initialState, {
items: ['transaction html']
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: { transactionHtml: 'another transaction html' }
}
const output = reducer(state, action)
expect(output.items).toEqual([ 'another transaction html', 'transaction html' ])
})
test('when channel has been disconnected', () => {
const state = Object.assign({}, initialState, {
channelDisconnected: true,
items: ['transaction html']
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: { transactionHtml: 'another transaction html' }
}
const output = reducer(state, action)
expect(output.items).toEqual(['transaction html'])
})
test('beyond page one', () => {
const state = Object.assign({}, initialState, {
beyondPageOne: true,
items: ['transaction html']
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: { transactionHtml: 'another transaction html' }
}
const output = reducer(state, action)
expect(output.items).toEqual([ 'transaction html' ])
})
test('adds the new transaction to state even when it is filtered by to', () => {
const state = Object.assign({}, initialState, {
addressHash: '0x001',
filter: 'to',
items: []
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: {
fromAddressHash: '0x002',
transactionHtml: 'transaction html',
toAddressHash: '0x001'
}
}
const output = reducer(state, action)
expect(output.items).toEqual(['transaction html'])
})
test(
'does nothing when it is filtered by to but the toAddressHash is different from addressHash',
() => {
const state = Object.assign({}, initialState, {
addressHash: '0x001',
filter: 'to',
items: []
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: {
fromAddressHash: '0x003',
transactionHtml: 'transaction html',
toAddressHash: '0x002'
}
}
const output = reducer(state, action)
expect(output.items).toEqual([])
})
test('adds the new transaction to state even when it is filtered by from', () => {
const state = Object.assign({}, initialState, {
addressHash: '0x001',
filter: 'from',
items: []
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: {
fromAddressHash: '0x001',
transactionHtml: 'transaction html',
toAddressHash: '0x002'
}
}
const output = reducer(state, action)
expect(output.items).toEqual(['transaction html'])
})
test(
'does nothing when it is filtered by from but the fromAddressHash is different from addressHash',
() => {
const state = Object.assign({}, initialState, {
addressHash: '0x001',
filter: 'to',
items: []
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION',
msg: {
addressHash: '0x001',
transactionHtml: 'transaction html',
fromAddressHash: '0x002'
}
}
const output = reducer(state, action)
expect(output.items).toEqual([])
})
})

@ -21,6 +21,7 @@ import 'bootstrap'
import './locale' import './locale'
import './pages/address' import './pages/address'
import './pages/address/transactions'
import './pages/address/validations' import './pages/address/validations'
import './pages/blocks' import './pages/blocks'
import './pages/chain' import './pages/chain'

@ -22,11 +22,10 @@ export const initialState = {
transactionCount: null, transactionCount: null,
validationCount: null, validationCount: null,
transactions: [],
internalTransactions: [], internalTransactions: [],
internalTransactionsBatch: [], internalTransactionsBatch: [],
validatedBlocks: [],
beyondPageOne: null beyondPageOne: false
} }
export function reducer (state = initialState, action) { export function reducer (state = initialState, action) {
@ -80,20 +79,8 @@ export function reducer (state = initialState, action) {
const transactionCount = (action.msg.fromAddressHash === state.addressHash) ? state.transactionCount + 1 : state.transactionCount const transactionCount = (action.msg.fromAddressHash === state.addressHash) ? state.transactionCount + 1 : state.transactionCount
if (state.beyondPageOne ||
(state.filter === 'to' && action.msg.toAddressHash !== state.addressHash) ||
(state.filter === 'from' && action.msg.fromAddressHash !== state.addressHash)) {
return Object.assign({}, state, { transactionCount }) return Object.assign({}, state, { transactionCount })
} }
return Object.assign({}, state, {
transactions: [
action.msg,
...state.transactions
],
transactionCount: transactionCount
})
}
case 'RECEIVED_UPDATED_BALANCE': { case 'RECEIVED_UPDATED_BALANCE': {
return Object.assign({}, state, { return Object.assign({}, state, {
balance: action.msg.balance balance: action.msg.balance
@ -139,32 +126,6 @@ const elements = {
$el.empty().append(numeral(state.validationCount).format()) $el.empty().append(numeral(state.validationCount).format())
} }
}, },
'[data-selector="empty-transactions-list"]': {
render ($el, state) {
if (state.transactions.length || state.loadingNextPage || state.pagingError) {
$el.hide()
} else {
$el.show()
}
}
},
'[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) {
if (oldState.transactions === state.transactions) return
const container = $el[0]
const newElements = _.map(state.transactions, ({ transactionHtml }) => $(transactionHtml)[0])
return listMorph(container, newElements, { key: 'dataset.transactionHash' })
}
},
'[data-selector="internal-transactions-list"]': { '[data-selector="internal-transactions-list"]': {
load ($el) { load ($el) {
return { return {

@ -0,0 +1,73 @@
import $ from 'jquery'
import _ from 'lodash'
import URI from 'urijs'
import humps from 'humps'
import socket from '../../socket'
import { connectElements } from '../../lib/redux_helpers.js'
import { createAsyncLoadStore } from '../../lib/async_listing_load'
export const initialState = {
addressHash: null,
channelDisconnected: false,
filter: null
}
export function reducer (state, action) {
switch (action.type) {
case 'PAGE_LOAD':
case 'ELEMENTS_LOAD': {
return Object.assign({}, state, _.omit(action, 'type'))
}
case 'CHANNEL_DISCONNECTED': {
if (state.beyondPageOne) return state
return Object.assign({}, state, { channelDisconnected: true })
}
case 'RECEIVED_NEW_TRANSACTION': {
if (state.channelDisconnected) return state
if (state.beyondPageOne ||
(state.filter === 'to' && action.msg.toAddressHash !== state.addressHash) ||
(state.filter === 'from' && action.msg.fromAddressHash !== state.addressHash)) {
return state
}
return Object.assign({}, state, { items: [ action.msg.transactionHtml, ...state.items ] })
}
default:
return state
}
}
const elements = {
'[data-selector="channel-disconnected-message"]': {
render ($el, state) {
if (state.channelDisconnected) $el.show()
}
}
}
if ($('[data-page="address-transactions"]').length) {
const store = createAsyncLoadStore(reducer, initialState, 'dataset.transactionHash')
const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash
const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true))
connectElements({ store, elements })
store.dispatch({
type: 'PAGE_LOAD',
addressHash,
filter,
beyondPageOne: !!blockNumber
})
const addressChannel = socket.channel(`addresses:${addressHash}`, {})
addressChannel.join()
addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
addressChannel.on('transaction', (msg) => {
store.dispatch({
type: 'RECEIVED_NEW_TRANSACTION',
msg: humps.camelizeKeys(msg)
})
})
}

@ -2,7 +2,7 @@
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %> <%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<section> <section data-page="address-transactions">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> <%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
@ -69,7 +69,7 @@
<%= gettext("Loading") %>... <%= gettext("Loading") %>...
</div> </div>
<div data-selector="transactions-list" data-items></div> <div data-items></div>
<a href="#" class="button button-secondary button-small float-right mt-4" data-next-page-button style="display: none;"> <a href="#" class="button button-secondary button-small float-right mt-4" data-next-page-button style="display: none;">
<%= gettext("Older") %> <%= gettext("Older") %>

Loading…
Cancel
Save