import $ from 'jquery' import omit from 'lodash/omit' import last from 'lodash/last' import min from 'lodash/min' import max from 'lodash/max' import keys from 'lodash/keys' import rangeRight from 'lodash/rangeRight' import humps from 'humps' import socket from '../socket' import { connectElements } from '../lib/redux_helpers.js' import { createAsyncLoadStore } from '../lib/async_listing_load' import '../app' export const initialState = { channelDisconnected: false } export const blockReducer = withMissingBlocks(baseReducer) function baseReducer (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 }) } case 'RECEIVED_NEW_BLOCK': { if (state.channelDisconnected || state.beyondPageOne || state.blockType !== 'block') return state const blockNumber = getBlockNumber(action.msg.blockHtml) const minBlock = getBlockNumber(last(state.items)) if (state.items.length && blockNumber < minBlock) return state return Object.assign({}, state, { items: [action.msg.blockHtml, ...state.items] }) } default: return state } } const elements = { '[data-selector="channel-disconnected-message"]': { render ($el, state) { if (state.channelDisconnected && !window.loading) $el.show() } } } function getBlockNumber (blockHtml) { return $(blockHtml).data('blockNumber') } function withMissingBlocks (reducer) { return (...args) => { const result = reducer(...args) if (result.items.length < 2) return result const blockNumbersToItems = result.items.reduce((acc, item) => { const blockNumber = getBlockNumber(item) acc[blockNumber] = acc[blockNumber] || item return acc }, {}) const blockNumbers = keys(blockNumbersToItems).map(x => parseInt(x, 10)) const minBlock = min(blockNumbers) const maxBlock = max(blockNumbers) return Object.assign({}, result, { items: rangeRight(minBlock, maxBlock + 1) .map((blockNumber) => blockNumbersToItems[blockNumber] || placeHolderBlock(blockNumber)) }) } } const $blockListPage = $('[data-page="block-list"]') const $uncleListPage = $('[data-page="uncle-list"]') const $reorgListPage = $('[data-page="reorg-list"]') if ($blockListPage.length || $uncleListPage.length || $reorgListPage.length) { window.onbeforeunload = () => { window.loading = true } const blockType = $blockListPage.length ? 'block' : $uncleListPage.length ? 'uncle' : 'reorg' const store = createAsyncLoadStore( $blockListPage.length ? blockReducer : baseReducer, Object.assign({}, initialState, { blockType }), 'dataset.blockNumber' ) connectElements({ store, elements }) const blocksChannel = socket.channel('blocks:new_block', {}) blocksChannel.join() blocksChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) blocksChannel.on('new_block', (msg) => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) })) } export function placeHolderBlock (blockNumber) { return `