diff --git a/package.json b/package.json
index 93542befb..bb93424f3 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"postcss-cssnext": "=2.11.0",
"prop-types": "=15.5.10",
"react": "=15.6.x",
+ "react-circular-progressbar": "=0.1.5",
"react-dom": "=15.6.x",
"react-redux": "=5.0.3",
"react-redux-toastr": "=7.0.0",
diff --git a/src/actions/forging.js b/src/actions/forging.js
new file mode 100644
index 000000000..f58e06b08
--- /dev/null
+++ b/src/actions/forging.js
@@ -0,0 +1,11 @@
+import actionTypes from '../constants/actions';
+
+export const updateForgedBlocks = data => ({
+ data,
+ type: actionTypes.forgedBlocksUpdated,
+});
+
+export const updateForgingStats = data => ({
+ data,
+ type: actionTypes.forgingStatsUpdated,
+});
diff --git a/src/actions/forging.test.js b/src/actions/forging.test.js
new file mode 100644
index 000000000..2cf3e4bcc
--- /dev/null
+++ b/src/actions/forging.test.js
@@ -0,0 +1,27 @@
+import { expect } from 'chai';
+import actionTypes from '../constants/actions';
+import { updateForgedBlocks, updateForgingStats } from './forging';
+
+describe('actions', () => {
+ it('should create an action to update forged blocks', () => {
+ const data = {
+ online: true,
+ };
+
+ const expectedAction = {
+ data,
+ type: actionTypes.forgedBlocksUpdated,
+ };
+ expect(updateForgedBlocks(data)).to.be.deep.equal(expectedAction);
+ });
+
+ it('should create an action to update forging stats', () => {
+ const data = { last7d: 1000 };
+
+ const expectedAction = {
+ data,
+ type: actionTypes.forgingStatsUpdated,
+ };
+ expect(updateForgingStats(data)).to.be.deep.equal(expectedAction);
+ });
+});
diff --git a/src/components/account/address.js b/src/components/account/address.js
index e5fa9a402..4b862f179 100644
--- a/src/components/account/address.js
+++ b/src/components/account/address.js
@@ -6,7 +6,7 @@ const Address = (props) => {
const content = props.isDelegate ?
(
- {props.username}
+ {props.delegate.username}
{props.address}
diff --git a/src/components/account/address.test.js b/src/components/account/address.test.js
index f103ae13f..bf577f452 100644
--- a/src/components/account/address.test.js
+++ b/src/components/account/address.test.js
@@ -18,7 +18,9 @@ describe('Address', () => {
const inputValue = {
isDelegate: true,
address: '16313739661670634666L',
- username: 'lisk-nano',
+ delegate: {
+ username: 'lisk-nano',
+ },
};
const expectedHeaderValue = 'Delegate';
const wrapper = shallow(
);
@@ -29,7 +31,9 @@ describe('Address', () => {
const inputValue = {
isDelegate: true,
address: '16313739661670634666L',
- username: 'lisk-nano',
+ delegate: {
+ username: 'lisk-nano',
+ },
};
const expectedValue = 'lisk-nano';
const wrapper = shallow(
);
diff --git a/src/components/app/index.js b/src/components/app/index.js
index c7eef125a..d5af8bfe5 100644
--- a/src/components/app/index.js
+++ b/src/components/app/index.js
@@ -23,6 +23,9 @@ const App = () => {
(
+ Transactions
+ Voting
+ Forging
@@ -31,10 +34,6 @@ const App = () => {
- Login
- Transactions
- Voting
- Forging
);
diff --git a/src/components/forging/circularProgressbar.css b/src/components/forging/circularProgressbar.css
new file mode 100644
index 000000000..2a3958944
--- /dev/null
+++ b/src/components/forging/circularProgressbar.css
@@ -0,0 +1,24 @@
+:global .CircularProgressbar {
+ /*
+ * This fixes an issue where the CircularProgressbar svg has
+ * 0 width inside a "display: flex" container, and thus not visible.
+ *
+ * If you're not using "display: flex", you can remove this style.
+ */
+ width: 100%;
+}
+
+:global .CircularProgressbar .CircularProgressbar-path {
+ stroke: rgb(2, 136, 209);
+ transition: stroke-dashoffset 0.5s ease 0s;
+}
+
+:global .CircularProgressbar .CircularProgressbar-trail {
+ stroke: #d6d6d6;
+}
+
+:global .CircularProgressbar .CircularProgressbar-text {
+ font-size: 20px;
+ dominant-baseline: middle;
+ text-anchor: middle;
+}
diff --git a/src/components/forging/delegateStats.js b/src/components/forging/delegateStats.js
new file mode 100644
index 000000000..f9e72401f
--- /dev/null
+++ b/src/components/forging/delegateStats.js
@@ -0,0 +1,50 @@
+import React from 'react';
+import { Card, CardText } from 'react-toolbox/lib/card';
+import CircularProgressbar from 'react-circular-progressbar';
+import grid from '../../../node_modules/flexboxgrid/dist/flexboxgrid.css';
+import style from './forging.css';
+
+const identity = x => (x);
+const addPercentSign = x => (`${x}%`);
+
+const progressCircleCardList = [
+ {
+ key: 'rate',
+ label: 'Rank',
+ percentageTransform: percentage => (Math.max(0, 101 - percentage)),
+ textForPercentage: identity,
+ }, {
+ key: 'productivity',
+ label: 'Productivity',
+ percentageTransform: identity,
+ textForPercentage: addPercentSign,
+ }, {
+ key: 'approval',
+ label: 'Approval',
+ percentageTransform: identity,
+ textForPercentage: addPercentSign,
+ },
+];
+
+const DelegateStats = props => (
+
+ {progressCircleCardList.map(cardItem => (
+
+ ))}
+
+);
+
+export default DelegateStats;
diff --git a/src/components/forging/delegateStats.test.js b/src/components/forging/delegateStats.test.js
new file mode 100644
index 000000000..1ce83b70c
--- /dev/null
+++ b/src/components/forging/delegateStats.test.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import { mount } from 'enzyme';
+import DelegateStats from './delegateStats';
+
+chai.use(sinonChai);
+
+describe('DelegateStats', () => {
+ const delegate = {
+ username: 'genesis_17',
+ rate: 19,
+ approval: 30,
+ productivity: 99.2,
+ };
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('should render 3 Card components', () => {
+ expect(wrapper.find('Card')).to.have.lengthOf(3);
+ });
+
+ it('should render 3 CircularProgressbar components', () => {
+ expect(wrapper.find('svg.CircularProgressbar')).to.have.lengthOf(3);
+ });
+});
diff --git a/src/components/forging/forgedBlocks.js b/src/components/forging/forgedBlocks.js
new file mode 100644
index 000000000..d43ee5808
--- /dev/null
+++ b/src/components/forging/forgedBlocks.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import { Card, CardTitle } from 'react-toolbox/lib/card';
+import { Table, TableHead, TableRow, TableCell } from 'react-toolbox/lib/table';
+import { TooltipTime } from '../timestamp';
+import LiskAmount from '../liskAmount';
+import FormattedNumber from '../formattedNumber';
+import grid from '../../../node_modules/flexboxgrid/dist/flexboxgrid.css';
+import style from './forging.css';
+
+
+const ForgedBlocks = props => (
+
+
+ Forged Blocks
+
+
+
+
+ Block height
+ Block Id
+ Timestamp
+ Total fee
+ Reward
+
+ {props.forgedBlocks.map((block, idx) => (
+
+
+ {block.id}
+
+
+
+
+ ))}
+
+
+
+);
+
+export default ForgedBlocks;
diff --git a/src/components/forging/forgedBlocks.test.js b/src/components/forging/forgedBlocks.test.js
new file mode 100644
index 000000000..3babb2c14
--- /dev/null
+++ b/src/components/forging/forgedBlocks.test.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import { mount } from 'enzyme';
+import ForgedBlocks from './forgedBlocks';
+
+chai.use(sinonChai);
+
+describe('ForgedBlocks', () => {
+ const forgedBlocks = [{
+ id: '16113150790072764126',
+ timestamp: 36280810,
+ height: 29394,
+ totalFee: 0,
+ reward: 0,
+ },
+ {
+ id: '13838471839278892195',
+ version: 0,
+ timestamp: 36280700,
+ height: 29383,
+ totalFee: 0,
+ reward: 0,
+ },
+ {
+ id: '5654150596698663763',
+ version: 0,
+ timestamp: 36279700,
+ height: 29283,
+ totalFee: 0,
+ reward: 0,
+ },
+ ];
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('should render 1 Table component', () => {
+ expect(wrapper.find('Table')).to.have.lengthOf(1);
+ });
+
+ it('should render TableHead component with 5 TableCell componenets', () => {
+ expect(wrapper.find('TableHead')).to.have.lengthOf(1);
+ expect(wrapper.find('TableHead').find('TableCell')).to.have.lengthOf(5);
+ });
+
+ it('should render 3 TableRow components', () => {
+ expect(wrapper.find('TableRow')).to.have.lengthOf(3);
+ });
+});
diff --git a/src/components/forging/forging.css b/src/components/forging/forging.css
new file mode 100644
index 000000000..e455602da
--- /dev/null
+++ b/src/components/forging/forging.css
@@ -0,0 +1,20 @@
+@import './circularProgressbar.css';
+
+.delegateName {
+ margin: 0;
+ font-weight: normal;
+}
+
+.grayCard {
+ background: #f7f8f9;
+}
+
+.forgedBlocksTableWrapper {
+ margin: 0 -8px;
+}
+
+.circularProgressTitle {
+ text-align: center;
+ padding-bottom: 16px;
+ width: 100%;
+}
diff --git a/src/components/forging/forgingComponent.js b/src/components/forging/forgingComponent.js
new file mode 100644
index 000000000..7ee452b52
--- /dev/null
+++ b/src/components/forging/forgingComponent.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import { Card } from 'react-toolbox/lib/card';
+import Waypoint from 'react-waypoint';
+import { getForgedBlocks, getForgedStats } from '../../utils/api/forging';
+import ForgingTitle from './forgingTitle';
+import DelegateStats from './delegateStats';
+import ForgingStats from './forgingStats';
+import ForgedBlocks from './forgedBlocks';
+
+class ForgingComponent extends React.Component {
+ loadStats(key, startMoment) {
+ getForgedStats(this.props.peers.data, startMoment, this.props.account.publicKey,
+ ).then((data) => {
+ this.props.onForgingStatsUpdate({ [key]: data.forged });
+ });
+ }
+
+ loadForgedBlocks(activePeer, limit, offset, generatorPublicKey) {
+ getForgedBlocks(activePeer, limit, offset, generatorPublicKey).then((data) => {
+ this.props.onForgedBlocksLoaded(data.blocks);
+ });
+ }
+
+ render() {
+ return (
+
+ {this.props.account && this.props.account.isDelegate ?
+
+
+
+
+
+
+
+
+ this.loadForgedBlocks(
+ this.props.peers.data,
+ 20,
+ this.props.forgedBlocks.length,
+ this.props.account.publicKey,
+ ) } />
+
:
+ null
+ }
+ {this.props.account && this.props.account.delegate && !this.props.account.isDelegate ?
+
+ You need to become a delegate to start forging.
+ If you already registered to become a delegate,
+ your registration hasn't been processed, yet.
+
:
+ null
+ }
+
+ );
+ }
+}
+
+export default ForgingComponent;
diff --git a/src/components/forging/forgingComponent.test.js b/src/components/forging/forgingComponent.test.js
new file mode 100644
index 000000000..d625dcb79
--- /dev/null
+++ b/src/components/forging/forgingComponent.test.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import { mount } from 'enzyme';
+import ForgingComponent from './forgingComponent';
+
+chai.use(sinonChai);
+
+
+describe('ForgingComponent', () => {
+ let wrapper;
+ const props = {
+ account: {
+ delegate: {},
+ isDelegate: true,
+ },
+ peers: {},
+ statistics: {},
+ forgedBlocks: [],
+ loadStats: () => {},
+ loadForgedBlocks: () => {},
+ };
+
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('should render ForgingTitle', () => {
+ expect(wrapper.find('ForgingTitle')).to.have.lengthOf(1);
+ });
+
+ it('should render ForgingStats', () => {
+ expect(wrapper.find('ForgingStats')).to.have.lengthOf(1);
+ });
+
+ it('should render DelegateStats', () => {
+ expect(wrapper.find('DelegateStats')).to.have.lengthOf(1);
+ });
+
+ it('should render ForgedBlocks', () => {
+ expect(wrapper.find('ForgedBlocks')).to.have.lengthOf(1);
+ });
+});
diff --git a/src/components/forging/forgingStats.js b/src/components/forging/forgingStats.js
new file mode 100644
index 000000000..ec0cca590
--- /dev/null
+++ b/src/components/forging/forgingStats.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import { Card, CardText } from 'react-toolbox/lib/card';
+import moment from 'moment';
+import LiskAmount from '../liskAmount';
+import grid from '../../../node_modules/flexboxgrid/dist/flexboxgrid.css';
+import style from './forging.css';
+
+const statCardObjects = [
+ {
+ key: 'last24h',
+ label: 'Last 24 hours',
+ startMoment: moment().subtract(1, 'days'),
+ }, {
+ key: 'last7d',
+ label: 'Last 7 days',
+ startMoment: moment().subtract(7, 'days'),
+ }, {
+ key: 'last30d',
+ label: 'Last 30 days',
+ startMoment: moment().subtract(30, 'days'),
+ }, {
+ key: 'last365d',
+ label: 'Last 365 days',
+ startMoment: moment().subtract(365, 'days'),
+ },
+];
+
+
+class ForgingStats extends React.Component {
+
+ componentDidMount() {
+ statCardObjects.map(obj => this.props.loadStats(obj.key, obj.startMoment));
+ }
+
+ render() {
+ return (
+
+ {statCardObjects.map(cardObj => (
+
+
+
+
+
+ {cardObj.label}
+
+ LSK
+
+
+
+
+
+
+ ))}
+
+ );
+ }
+}
+
+export default ForgingStats;
diff --git a/src/components/forging/forgingStats.test.js b/src/components/forging/forgingStats.test.js
new file mode 100644
index 000000000..900922af6
--- /dev/null
+++ b/src/components/forging/forgingStats.test.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import { mount } from 'enzyme';
+import ForgingStats from './forgingStats';
+
+chai.use(sinonChai);
+
+
+describe('ForgingStats', () => {
+ const account = {
+ delegate: {
+ username: 'genesis_17',
+ rate: 19,
+ approval: 30,
+ productivity: 99.2,
+ },
+ };
+ const statistics = {
+ last24h: 321317,
+ last7d: 3213179124,
+ last30d: 321317912423,
+ last365d: 32131791242342,
+ };
+ const loadStats = () => {};
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('should render 4 Card components', () => {
+ expect(wrapper.find('Card')).to.have.lengthOf(4);
+ });
+
+ it('should render Card component for Last 24 hours', () => {
+ expect(wrapper.find('Card').at(0).text().trim()).to.equal('Last 24 hours 0.00321317 LSK');
+ });
+
+ it('should render Card component for Last 7 days', () => {
+ expect(wrapper.find('Card').at(1).text().trim()).to.equal('Last 7 days 32.13179124 LSK');
+ });
+
+ it('should render Card component for Last 30 days', () => {
+ expect(wrapper.find('Card').at(2).text().trim()).to.equal('Last 30 days 3,213.17912423 LSK');
+ });
+
+ it('should render Card component for Last 365 days', () => {
+ expect(wrapper.find('Card').at(3).text().trim()).to.equal('Last 365 days 321,317.91242342 LSK');
+ });
+});
diff --git a/src/components/forging/forgingTitle.js b/src/components/forging/forgingTitle.js
new file mode 100644
index 000000000..43802ef18
--- /dev/null
+++ b/src/components/forging/forgingTitle.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Card, CardText } from 'react-toolbox/lib/card';
+import moment from 'moment';
+import LiskAmount from '../liskAmount';
+import grid from '../../../node_modules/flexboxgrid/dist/flexboxgrid.css';
+import style from './forging.css';
+
+
+class ForgingTitle extends React.Component {
+
+ componentDidMount() {
+ this.props.loadStats('total', moment('2016-04-24 17:00'));
+ }
+
+ render() {
+ return (
+
+
+
+
+ {this.props.account.delegate.username}
+
+
+ LSK Earned
+
+
+
+
+ );
+ }
+}
+
+export default ForgingTitle;
diff --git a/src/components/forging/forgingTitle.test.js b/src/components/forging/forgingTitle.test.js
new file mode 100644
index 000000000..5581fb46d
--- /dev/null
+++ b/src/components/forging/forgingTitle.test.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import { mount } from 'enzyme';
+import ForgingTitle from './forgingTitle';
+
+chai.use(sinonChai);
+
+
+describe('ForgingTitle', () => {
+ const account = {
+ delegate: {
+ username: 'genesis_17',
+ rate: 19,
+ approval: 30,
+ productivity: 99.2,
+ },
+ };
+ const statistics = {
+ total: 132423,
+ };
+ const loadStats = () => {};
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('should render 1 Card component', () => {
+ expect(wrapper.find('Card')).to.have.lengthOf(1);
+ });
+
+ it('should render h2 with delegate name', () => {
+ expect(wrapper.find('h2').text()).to.equal(account.delegate.username);
+ });
+});
diff --git a/src/components/forging/index.js b/src/components/forging/index.js
index e6a832e56..89766e186 100644
--- a/src/components/forging/index.js
+++ b/src/components/forging/index.js
@@ -1,7 +1,26 @@
-import React from 'react';
+import { connect } from 'react-redux';
+import { updateForgedBlocks, updateForgingStats } from '../../actions/forging';
+import ForgingComponent from './forgingComponent';
-const Forging = () => (
- Forging
-);
+const mapStateToProps = state => ({
+ account: state.account,
+ peers: state.peers,
+ statistics: state.forging.statistics,
+ forgedBlocks: state.forging.forgedBlocks,
+});
+
+const mapDispatchToProps = dispatch => ({
+ onForgedBlocksLoaded: (blocks) => {
+ dispatch(updateForgedBlocks(blocks));
+ },
+ onForgingStatsUpdate: (stats) => {
+ dispatch(updateForgingStats(stats));
+ },
+});
+
+const Forging = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ForgingComponent);
export default Forging;
diff --git a/src/components/forging/index.test.js b/src/components/forging/index.test.js
new file mode 100644
index 000000000..72babc785
--- /dev/null
+++ b/src/components/forging/index.test.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import { mount } from 'enzyme';
+import { Provider } from 'react-redux';
+import Forging from './';
+import store from '../../store';
+
+chai.use(sinonChai);
+
+
+describe('Forging', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('should render ForgingComponent', () => {
+ expect(wrapper.find('ForgingComponent')).to.have.lengthOf(1);
+ });
+});
diff --git a/src/components/login/loginFormComponent.js b/src/components/login/loginFormComponent.js
index 9ef8015c0..ffb79571b 100644
--- a/src/components/login/loginFormComponent.js
+++ b/src/components/login/loginFormComponent.js
@@ -6,6 +6,7 @@ import Dropdown from 'react-toolbox/lib/dropdown';
import Button from 'react-toolbox/lib/button';
import Checkbox from 'react-toolbox/lib/checkbox';
import { getAccount } from '../../utils/api/account';
+import { getDelegate } from '../../utils/api/delegate';
import networksRaw from './networks';
if (global._bitcore) delete global._bitcore;
@@ -94,6 +95,11 @@ class LoginFormComponent extends React.Component {
// redirect to main/transactions
getAccount(this.props.peers.data, accountInfo.address).then((result) => {
onAccountUpdated(result);
+ getDelegate(this.props.peers.data, accountInfo.publicKey).then((data) => {
+ onAccountUpdated({ delegate: data.delegate, isDelegate: true });
+ }).catch(() => {
+ onAccountUpdated({ delegate: {}, isDelegate: false });
+ });
// redirect to main/transactions
this.props.history.push('/main/transactions');
});
diff --git a/src/constants/actions.js b/src/constants/actions.js
index f43e50fbd..11fdbca0f 100644
--- a/src/constants/actions.js
+++ b/src/constants/actions.js
@@ -6,6 +6,8 @@ const actionTypes = {
activePeerReset: 'ACTIVE_PEER_RESET',
dialogDisplayed: 'DIALOG_DISPLAYED',
dialogHidden: 'DIALOG_HIDDEN',
+ forgedBlocksUpdated: 'FORGED_BLOCKS_UPDATED',
+ forgingStatsUpdated: 'FORGING_STATS_UPDATED',
};
export default actionTypes;
diff --git a/src/store/reducers/forging.js b/src/store/reducers/forging.js
new file mode 100644
index 000000000..eb026ee75
--- /dev/null
+++ b/src/store/reducers/forging.js
@@ -0,0 +1,36 @@
+import actionTypes from '../../constants/actions';
+
+/**
+ *
+ * @param {Array} state
+ * @param {Object} action
+ */
+const forging = (state = { forgedBlocks: [], statistics: {} }, action) => {
+ let startTimesamp;
+ let endTimesamp;
+
+ switch (action.type) {
+ case actionTypes.forgedBlocksUpdated:
+ startTimesamp = state.forgedBlocks && state.forgedBlocks.length ?
+ state.forgedBlocks[0].timestamp :
+ 0;
+ endTimesamp = state.forgedBlocks && state.forgedBlocks.length ?
+ state.forgedBlocks[state.forgedBlocks.length - 1].timestamp :
+ 0;
+ return Object.assign({}, state, {
+ forgedBlocks: [
+ ...action.data.filter(block => block.timestamp > startTimesamp),
+ ...state.forgedBlocks,
+ ...action.data.filter(block => block.timestamp < endTimesamp),
+ ],
+ });
+ case actionTypes.forgingStatsUpdated:
+ return Object.assign({}, state, {
+ statistics: Object.assign({}, state.statistics, action.data),
+ });
+ default:
+ return state;
+ }
+};
+
+export default forging;
diff --git a/src/store/reducers/forging.test.js b/src/store/reducers/forging.test.js
new file mode 100644
index 000000000..7fa4ca09a
--- /dev/null
+++ b/src/store/reducers/forging.test.js
@@ -0,0 +1,86 @@
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import forging from './forging';
+import actionTypes from '../../constants/actions';
+
+
+chai.use(sinonChai);
+
+describe('Reducer: forging(state, action)', () => {
+ let state;
+ const blocks = [{
+ id: '16113150790072764126',
+ timestamp: 36280810,
+ height: 29394,
+ totalFee: 0,
+ reward: 0,
+ },
+ {
+ id: '13838471839278892195',
+ version: 0,
+ timestamp: 36280700,
+ height: 29383,
+ totalFee: 0,
+ reward: 0,
+ },
+ {
+ id: '5654150596698663763',
+ version: 0,
+ timestamp: 36279700,
+ height: 29283,
+ totalFee: 0,
+ reward: 0,
+ }];
+
+ it('should set forgedBlocks if action.type = actionTypes.forgedBlocksUpdate and state.forgedBlocks is []', () => {
+ state = {
+ statistics: {},
+ forgedBlocks: [],
+ };
+ const action = {
+ type: actionTypes.forgedBlocksUpdated,
+ data: [blocks[0], blocks[1]],
+ };
+ const changedState = forging(state, action);
+ expect(changedState.forgedBlocks).to.deep.equal(action.data);
+ });
+
+ it('should prepend forgedBlocks with newer blocks if action.type = actionTypes.forgedBlocksUpdated', () => {
+ state = {
+ statistics: {},
+ forgedBlocks: [blocks[2]],
+ };
+ const action = {
+ type: actionTypes.forgedBlocksUpdated,
+ data: [blocks[0], blocks[1]],
+ };
+ const changedState = forging(state, action);
+ expect(changedState.forgedBlocks).to.deep.equal(blocks);
+ });
+
+ it('should append forgedBlocks with older blocks if action.type = actionTypes.forgedBlocksUpdated', () => {
+ state = {
+ statistics: {},
+ forgedBlocks: [blocks[0]],
+ };
+ const action = {
+ type: actionTypes.forgedBlocksUpdated,
+ data: [blocks[1], blocks[2]],
+ };
+ const changedState = forging(state, action);
+ expect(changedState.forgedBlocks).to.deep.equal(blocks);
+ });
+
+ it('should update statistics if action.type = actionTypes.forgingStatsUpdated', () => {
+ state = {
+ statistics: { last7d: 1000 },
+ };
+ const action = {
+ type: actionTypes.forgingStatsUpdated,
+ data: { last24h: 100 },
+ };
+ const changedState = forging(state, action);
+ expect(changedState.statistics).to.deep.equal({ last7d: 1000, last24h: 100 });
+ });
+});
+
diff --git a/src/store/reducers/index.js b/src/store/reducers/index.js
index 018cf688a..9dca54638 100644
--- a/src/store/reducers/index.js
+++ b/src/store/reducers/index.js
@@ -1,4 +1,5 @@
export { default as account } from './account';
export { default as peers } from './peers';
export { default as dialog } from './dialog';
+export { default as forging } from './forging';
diff --git a/src/utils/api/delegate.js b/src/utils/api/delegate.js
index e523d0fb5..7d0143475 100644
--- a/src/utils/api/delegate.js
+++ b/src/utils/api/delegate.js
@@ -7,8 +7,8 @@ export const listAccountDelegates = (activePeer, address) =>
export const listDelegates = (activePeer, options) =>
requestToActivePeer(activePeer, `delegates/${options.q ? 'search' : ''}`, options);
-export const getDelegate = (activePeer, options) =>
- requestToActivePeer(activePeer, 'delegates/get', options);
+export const getDelegate = (activePeer, publicKey) =>
+ requestToActivePeer(activePeer, 'delegates/get', { publicKey });
export const vote = (activePeer, secret, publicKey, voteList, unvoteList, secondSecret = null) =>
requestToActivePeer(activePeer, 'accounts/delegates', {
diff --git a/src/utils/api/delegate.test.js b/src/utils/api/delegate.test.js
index bfc542492..d977c72df 100644
--- a/src/utils/api/delegate.test.js
+++ b/src/utils/api/delegate.test.js
@@ -61,11 +61,11 @@ describe('Utils: Delegate', () => {
describe('getDelegate', () => {
it('should return requestToActivePeer(activePeer, `delegates/get`, options)', () => {
- const options = {};
+ const options = { publicKey: '"86499879448d1b0215d59cbf078836e3d7d9d2782d56a2274a568761bff36f19"' };
const mockedPromise = new Promise((resolve) => { resolve(); });
peersMock.expects('requestToActivePeer').withArgs(activePeer, 'delegates/get', options).returns(mockedPromise);
- const returnedPromise = getDelegate(activePeer, options);
+ const returnedPromise = getDelegate(activePeer, options.publicKey);
expect(returnedPromise).to.equal(mockedPromise);
});
});
diff --git a/src/utils/api/forging.js b/src/utils/api/forging.js
index 35af093d4..bcd798d60 100644
--- a/src/utils/api/forging.js
+++ b/src/utils/api/forging.js
@@ -1,11 +1,6 @@
import moment from 'moment';
import { requestToActivePeer } from './peers';
-export const getDelegate = (activePeer, publicKey) =>
- requestToActivePeer(activePeer, 'delegates/get', {
- publicKey,
- });
-
export const getForgedBlocks = (activePeer, limit = 10, offset = 0, generatorPublicKey) =>
requestToActivePeer(activePeer, 'blocks', {
limit,
diff --git a/src/utils/api/forging.test.js b/src/utils/api/forging.test.js
index 5fc0001d9..861c3635a 100644
--- a/src/utils/api/forging.test.js
+++ b/src/utils/api/forging.test.js
@@ -1,17 +1,11 @@
import chai, { expect } from 'chai';
import sinonChai from 'sinon-chai';
-import { getDelegate, getForgedBlocks, getForgedStats } from './forging';
+import { getForgedBlocks, getForgedStats } from './forging';
chai.use(sinonChai);
-describe('Utils: Forging', () => {
- describe('getDelegate', () => {
- it('should return a promise', () => {
- const promise = getDelegate();
- expect(typeof promise.then).to.be.equal('function');
- });
- });
+describe('Utils: Forging', () => {
describe('getForgedBlocks', () => {
it('should return a promise', () => {
const promise = getForgedBlocks();