parent
b20995a5e4
commit
7227fc971a
@ -0,0 +1,87 @@ |
||||
import $ from 'jquery' |
||||
import omit from 'lodash/omit' |
||||
import URI from 'urijs' |
||||
import humps from 'humps' |
||||
import { subscribeChannel } from '../../socket' |
||||
import { connectElements } from '../../lib/redux_helpers.js' |
||||
import { createAsyncLoadStore } from '../../lib/async_listing_load' |
||||
import '../address' |
||||
|
||||
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_TOKEN_TRANSFER': { |
||||
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.tokenTransferHtml, ...state.items] }) |
||||
} |
||||
case 'RECEIVED_NEW_REWARD': { |
||||
if (state.channelDisconnected) return state |
||||
|
||||
return Object.assign({}, state, { items: [action.msg.rewardHtml, ...state.items] }) |
||||
} |
||||
default: |
||||
return state |
||||
} |
||||
} |
||||
|
||||
const elements = { |
||||
'[data-selector="channel-disconnected-message"]': { |
||||
render ($el, state) { |
||||
if (state.channelDisconnected) $el.show() |
||||
} |
||||
} |
||||
} |
||||
|
||||
if ($('[data-page="address-token-transfers"]').length) { |
||||
const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') |
||||
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 = subscribeChannel(`addresses:${addressHash}`) |
||||
addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) |
||||
addressChannel.on('token_transfer', (msg) => { |
||||
store.dispatch({ |
||||
type: 'RECEIVED_NEW_TOKEN_TRANSFER', |
||||
msg: humps.camelizeKeys(msg) |
||||
}) |
||||
}) |
||||
|
||||
const rewardsChannel = subscribeChannel(`rewards:${addressHash}`) |
||||
rewardsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) |
||||
rewardsChannel.on('new_reward', (msg) => { |
||||
store.dispatch({ |
||||
type: 'RECEIVED_NEW_REWARD', |
||||
msg: humps.camelizeKeys(msg) |
||||
}) |
||||
}) |
||||
} |
@ -0,0 +1,80 @@ |
||||
import $ from 'jquery' |
||||
import omit from 'lodash/omit' |
||||
import humps from 'humps' |
||||
import socket from '../socket' |
||||
import { createAsyncLoadStore } from '../lib/async_listing_load' |
||||
import { batchChannel } from '../lib/utils' |
||||
import '../app' |
||||
|
||||
const BATCH_THRESHOLD = 10 |
||||
|
||||
export const initialState = { |
||||
channelDisconnected: false, |
||||
tokenTransferCount: null, |
||||
tokenTransfersBatch: [] |
||||
} |
||||
|
||||
export function reducer (state = initialState, action) { |
||||
switch (action.type) { |
||||
case 'ELEMENTS_LOAD': { |
||||
return Object.assign({}, state, omit(action, 'type')) |
||||
} |
||||
case 'CHANNEL_DISCONNECTED': { |
||||
return Object.assign({}, state, { |
||||
channelDisconnected: true, |
||||
tokenTransfersBatch: [] |
||||
}) |
||||
} |
||||
case 'RECEIVED_NEW_TOKEN_TRANSFER_BATCH': { |
||||
if (state.channelDisconnected) return state |
||||
|
||||
const tokenTransferCount = state.tokenTransferCount + action.msgs.length |
||||
|
||||
const tokenTransfersLength = state.items.length + action.msgs.length |
||||
if (tokenTransfersLength < BATCH_THRESHOLD) { |
||||
return Object.assign({}, state, { |
||||
items: [ |
||||
...action.msgs.map(msg => msg.tokenTransferHtml).reverse(), |
||||
...state.items |
||||
], |
||||
tokenTransferCount |
||||
}) |
||||
} else if (!state.tokenTransfersBatch.length && action.msgs.length < BATCH_THRESHOLD) { |
||||
return Object.assign({}, state, { |
||||
items: [ |
||||
...action.msgs.map(msg => msg.tokenTransferHtml).reverse(), |
||||
...state.items.slice(0, -1 * action.msgs.length) |
||||
], |
||||
tokenTransferCount |
||||
}) |
||||
} else { |
||||
return Object.assign({}, state, { |
||||
tokenTransfersBatch: [ |
||||
...action.msgs.reverse(), |
||||
...state.tokenTransfersBatch |
||||
], |
||||
tokenTransferCount |
||||
}) |
||||
} |
||||
} |
||||
default: |
||||
return state |
||||
} |
||||
} |
||||
|
||||
const $tokenTransferListPage = $('[data-page="token-transfer-list"]') |
||||
if ($tokenTransferListPage.length) { |
||||
const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') |
||||
|
||||
const tokenTransfersChannel = socket.channel('token_transfers:new_token_transfer') |
||||
tokenTransfersChannel.join() |
||||
tokenTransfersChannel.onError(() => store.dispatch({ |
||||
type: 'CHANNEL_DISCONNECTED' |
||||
})) |
||||
tokenTransfersChannel.on('token_transfer', batchChannel((msgs) => { |
||||
store.dispatch({ |
||||
type: 'RECEIVED_NEW_TOKEN_TRANSFER_BATCH', |
||||
msgs: humps.camelizeKeys(msgs) |
||||
}) |
||||
})) |
||||
} |
@ -0,0 +1,45 @@ |
||||
defmodule BlockScoutWeb.TokenTransferChannel do |
||||
@moduledoc """ |
||||
Establishes pub/sub channel for live updates of token transfer events. |
||||
""" |
||||
use BlockScoutWeb, :channel |
||||
|
||||
alias BlockScoutWeb.Tokens.TransferView |
||||
alias Explorer.Chain |
||||
alias Explorer.Chain.Hash |
||||
alias Phoenix.View |
||||
|
||||
intercept(["token_transfer"]) |
||||
|
||||
{:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") |
||||
@burn_address_hash burn_address_hash |
||||
|
||||
def join("token_transfers:new_token_transfer", _params, socket) do |
||||
{:ok, %{}, socket} |
||||
end |
||||
|
||||
def join("token_transfers:" <> _transaction_hash, _params, socket) do |
||||
{:ok, %{}, socket} |
||||
end |
||||
|
||||
def handle_out("token_transfer", %{token_transfer: token_transfer}, socket) do |
||||
Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) |
||||
|
||||
rendered_token_transfer = |
||||
View.render_to_string( |
||||
TransferView, |
||||
"_token_transfer.html", |
||||
conn: socket, |
||||
token: token_transfer.token, |
||||
token_transfer: token_transfer, |
||||
burn_address_hash: @burn_address_hash |
||||
) |
||||
|
||||
push(socket, "token_transfer", %{ |
||||
token_transfer_hash: Hash.to_string(token_transfer.transaction_hash), |
||||
token_transfer_html: rendered_token_transfer |
||||
}) |
||||
|
||||
{:noreply, socket} |
||||
end |
||||
end |
Loading…
Reference in new issue