Skip to content

Commit

Permalink
Merge pull request #2716 from LiskHQ/2510-implement-load-new-blocks-b…
Browse files Browse the repository at this point in the history
…utton

Implement new blocks and transactions buttons - Closes #2510 #2513
  • Loading branch information
reyraa committed Jan 7, 2020
2 parents 64d4483 + 95ca819 commit 6e0a163
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 16 deletions.
2 changes: 2 additions & 0 deletions i18n/locales/en/common.json
Expand Up @@ -278,7 +278,9 @@
"My Account": "My Account",
"Name is already taken!": "Name is already taken!",
"Network switcher": "Network switcher",
"New blocks": "New blocks",
"New bookmark": "New bookmark",
"New transactions": "New transactions",
"Next": "Next",
"Nickname is too long.": "Nickname is too long.",
"No Bookmarks added yet": "No Bookmarks added yet",
Expand Down
16 changes: 16 additions & 0 deletions src/assets/images/icons/click-to-update.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/components/screens/monitor/blocks/blocks.js
Expand Up @@ -13,6 +13,7 @@ import BoxEmptyState from '../../../toolbox/box/emptyState';
import FilterBar from '../../../shared/filterBar';
import Illustration from '../../../toolbox/illustration';
import LiskAmount from '../../../shared/liskAmount';
import LoadLatestButton from '../../../shared/loadLatestButton';
import MonitorHeader from '../header';
import Table from '../../../toolbox/table';
import Tooltip from '../../../toolbox/tooltip/tooltip';
Expand Down Expand Up @@ -41,6 +42,8 @@ const Blocks = ({
}));
};

const loadLastBlocks = () => applyFilters(filters);

return (
<div>
<MonitorHeader />
Expand All @@ -50,6 +53,12 @@ const Blocks = ({
<h2 className="blocks-header-title">{t('All blocks')}</h2>
<BlockFilterDropdown filters={filters} applyFilters={applyFilters} />
</BoxHeader>
<LoadLatestButton
event="update.block"
onClick={loadLastBlocks}
>
{t('New blocks')}
</LoadLatestButton>
<FilterBar {...{
clearFilter, clearAllFilters, filters, formatters, t,
}}
Expand Down
34 changes: 34 additions & 0 deletions src/components/shared/loadLatestButton/index.js
@@ -0,0 +1,34 @@
import React, { useEffect, useState } from 'react';
import { PrimaryButton } from '../../toolbox/buttons/button';
import Icon from '../../toolbox/icon';
import useServiceSocketUpdates from '../../../hooks/useServiceSocketUpdates';
import styles from './loadLatestButton.css';

const LoadLatestButton = ({ children, onClick, event }) => {
const [isUpdateAvailable, hideUpdateButton] = useServiceSocketUpdates(event);
const [hasUpdatedRecently, didUpdateRecently] = useState(false);

const handleClick = () => {
hideUpdateButton();
const timer = setTimeout(() => {
didUpdateRecently(false);
}, 10000);
didUpdateRecently(timer);
onClick();
};

// willUnmount
useEffect(() => () => clearTimeout(hasUpdatedRecently), []);

return (isUpdateAvailable && hasUpdatedRecently === false
? (
<PrimaryButton onClick={handleClick} className={styles.button}>
<Icon name="arrowUpCircle" className={styles.icon} />
{children}
</PrimaryButton>
)
: null);
};


export default LoadLatestButton;
24 changes: 24 additions & 0 deletions src/components/shared/loadLatestButton/loadLatestButton.css
@@ -0,0 +1,24 @@
@keyframes slideDown {
0% {
transform: translateX(-50%) translateY(0px);
}

100% {
transform: translateX(-50%) translateY(100px);
}
}

.button {
animation: slideDown var(--animation-speed-fast) ease-in;
animation-fill-mode: forwards;
position: absolute;
z-index: 2;
width: 200px;
transform: translateX(-50%);
left: 50%;

& .icon {
vertical-align: bottom;
padding-right: 12px;
}
}
54 changes: 54 additions & 0 deletions src/components/shared/loadLatestButton/loadLatestButton.test.js
@@ -0,0 +1,54 @@
import { act } from 'react-dom/test-utils';
import React from 'react';
import { mount } from 'enzyme';
import LoadLatestButton from '.';
import liskService from '../../../utils/api/lsk/liskService';

jest.mock('../../../utils/api/lsk/liskService');

describe('LoadLatestButton', () => {
let onSocketEvent;
const props = {
onClick: jest.fn(),
event: 'test.event',
children: 'Test load button',
};
const render = () => {
liskService.listenToBlockchainEvents.mockImplementation(({ callback }) => {
onSocketEvent = callback;
});
const wrapper = mount(<LoadLatestButton {...props} />);
return wrapper;
};

it('renders empty by default', () => {
const wrapper = render();
expect(wrapper).toBeEmptyRender();
});

it('shows button on websocket event and hides the button on click', () => {
jest.useFakeTimers();

const wrapper = render();
expect(wrapper).toBeEmptyRender();

act(() => { onSocketEvent(); });
wrapper.update();
expect(wrapper).toContainExactlyOneMatchingElement('button');
expect(wrapper).toHaveText(props.children);

wrapper.find('button').simulate('click');
expect(props.onClick).toHaveBeenCalledWith();
act(() => {
jest.runOnlyPendingTimers();
});
expect(wrapper).toBeEmptyRender();
});

it('clears the timeout before unmounting', () => {
jest.useFakeTimers();
const wrapper = render();
wrapper.unmount();
expect(clearTimeout).toHaveBeenCalledTimes(1);
});
});
37 changes: 24 additions & 13 deletions src/components/shared/transactionsTable/index.js
@@ -1,27 +1,28 @@
import React from 'react';
import { withTranslation } from 'react-i18next';
import React from 'react';
import grid from 'flexboxgrid/dist/flexboxgrid.css';
import { DEFAULT_LIMIT } from '../../../constants/monitor';
import { DateTimeFromTimestamp } from '../../toolbox/timestamp';
import { tokenMap } from '../../../constants/tokens';
import { transactionNames } from '../../../constants/transactionTypes';
import AccountVisualWithAddress from '../accountVisualWithAddress';
import Box from '../../toolbox/box';
import BoxHeader from '../../toolbox/box/header';
import BoxContent from '../../toolbox/box/content';
import BoxFooterButton from '../../toolbox/box/footerButton';
import BoxEmptyState from '../../toolbox/box/emptyState';
import BoxFooterButton from '../../toolbox/box/footerButton';
import BoxHeader from '../../toolbox/box/header';
import FilterBar from '../filterBar';
import FilterDropdownButton from '../filterDropdownButton';
import Icon from '../../toolbox/icon';
import Tooltip from '../../toolbox/tooltip/tooltip';
import LiskAmount from '../liskAmount';
import Illustration from '../../toolbox/illustration';
import LiskAmount from '../liskAmount';
import LoadLatestButton from '../loadLatestButton';
import Table from '../../toolbox/table';
import Tooltip from '../../toolbox/tooltip/tooltip';
import routes from '../../../constants/routes';
import styles from './transactionsTable.css';
import FilterDropdownButton from '../filterDropdownButton';
import withResizeValues from '../../../utils/withResizeValues';
import withFilters from '../../../utils/withFilters';
import FilterBar from '../filterBar';
import routes from '../../../constants/routes';
import Table from '../../toolbox/table';
import AccountVisualWithAddress from '../accountVisualWithAddress';
import { DEFAULT_LIMIT } from '../../../constants/monitor';
import { transactionNames } from '../../../constants/transactionTypes';
import withResizeValues from '../../../utils/withResizeValues';

class TransactionsTable extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -77,6 +78,16 @@ class TransactionsTable extends React.Component {
applyFilters={applyFilters}
/>
</BoxHeader>
{isLoadMoreEnabled
&& (
<LoadLatestButton
event="update.transactions.confirmed"
onClick={transactions.loadData}
>
{t('New transactions')}
</LoadLatestButton>
)
}
<FilterBar {...{
clearFilter, clearAllFilters, filters, formatters, t,
}}
Expand Down
1 change: 1 addition & 0 deletions src/components/toolbox/box/box.css
Expand Up @@ -120,6 +120,7 @@
margin: 0;
padding: 16px 20px;
width: 100%;
z-index: 3;

& h1,
& h2 {
Expand Down
2 changes: 2 additions & 0 deletions src/components/toolbox/icon/index.js
Expand Up @@ -9,6 +9,7 @@ import arrowLeftActive from '../../../assets/images/icons/arrow-left-active.svg'
import arrowLeftInactive from '../../../assets/images/icons/arrow-left-inactive.svg';
import arrowRightActive from '../../../assets/images/icons/arrow-right-active.svg';
import arrowRightInactive from '../../../assets/images/icons/arrow-right-inactive.svg';
import arrowUpCircle from '../../../assets/images/icons/click-to-update.svg';
import balance from '../../../assets/images/icons/balance.svg';
import balanceDark from '../../../assets/images/icons/balance-dark.svg';
import bookmarksIconEmptyState from '../../../assets/images/icons/bookmarks-empty-state.svg';
Expand Down Expand Up @@ -110,6 +111,7 @@ export const icons = {
arrowLeftInactive,
arrowRightActive,
arrowRightInactive,
arrowUpCircle,
balance,
bookmarksIconEmptyState,
btcIcon,
Expand Down
28 changes: 28 additions & 0 deletions src/hooks/useServiceSocketUpdates.js
@@ -0,0 +1,28 @@
import { useSelector } from 'react-redux';
import { useState, useEffect } from 'react';
import liskService from '../utils/api/lsk/liskService';

/**
*
* @param {object} event - Sock event data fired by Lisk Service
* @returns {array} - [boolean, function]
*/
const useServiceSocketUpdates = (event) => {
const networkConfig = useSelector(state => state.network);
const [isUpdateAvailable, setUpdateAvailable] = useState(false);
const reset = () => setUpdateAvailable(false);

useEffect(() => {
const cleanUp = liskService.listenToBlockchainEvents({
networkConfig,
event,
callback: () => setUpdateAvailable(true),
});

return cleanUp;
}, [networkConfig.name]);

return [isUpdateAvailable, reset];
};

export default useServiceSocketUpdates;
25 changes: 22 additions & 3 deletions src/utils/api/lsk/liskService.js
@@ -1,14 +1,21 @@
import * as popsicle from 'popsicle';
import { utils } from '@liskhq/lisk-transactions';
import io from 'socket.io-client';
import * as popsicle from 'popsicle';
import { DEFAULT_LIMIT } from '../../../constants/monitor';
import { getNetworkNameBasedOnNethash } from '../../getNetwork';
import { getTimestampFromFirstBlock } from '../../datetime';
import { version } from '../../../../package.json';
import i18n from '../../../i18n';
import networks from '../../../constants/networks';
import voting from '../../../constants/voting';

const liskServiceUrl = 'https://service.lisk.io';
const liskServiceTestnetUrl = 'https://testnet-service.lisk.io';
const isStaging = () => (
localStorage.getItem('useLiskServiceStaging') || version.includes('beta') || version.includes('rc')
? '-staging' : ''
);

const liskServiceUrl = `https://mainnet-service${isStaging()}.lisk.io`;
const liskServiceTestnetUrl = `https://testnet-service${isStaging()}.lisk.io`;

const getServerUrl = (networkConfig) => {
const name = getNetworkNameBasedOnNethash(networkConfig);
Expand Down Expand Up @@ -179,6 +186,18 @@ const liskServiceApi = {
networkConfig,
path: '/api/v1/network/status',
}),

listenToBlockchainEvents: ({ networkConfig, event, callback }) => {
const socket = io(
`${liskServiceApi.getLiskServiceUrl(networkConfig)}/blockchain`,
{ transports: ['websocket'] },
);
socket.on(event, callback);

return function cleanUp() {
socket.close();
};
},
};

export default liskServiceApi;

0 comments on commit 6e0a163

Please sign in to comment.