diff --git a/src/actions/loading.js b/src/actions/loading.js
new file mode 100644
index 000000000..6a948d34b
--- /dev/null
+++ b/src/actions/loading.js
@@ -0,0 +1,19 @@
+import actionTypes from '../constants/actions';
+
+/**
+ * An action to dispatch loadingStarted
+ *
+ */
+export const loadingStarted = data => ({
+ data,
+ type: actionTypes.loadingStarted,
+});
+
+/**
+ * An action to dispatch loadingFinished
+ *
+ */
+export const loadingFinished = data => ({
+ data,
+ type: actionTypes.loadingFinished,
+});
diff --git a/src/actions/loding.test.js b/src/actions/loding.test.js
new file mode 100644
index 000000000..ab9fa12ae
--- /dev/null
+++ b/src/actions/loding.test.js
@@ -0,0 +1,33 @@
+import { expect } from 'chai';
+import actionTypes from '../constants/actions';
+import {
+ loadingStarted,
+ loadingFinished,
+} from './loading';
+
+
+describe('actions: loading', () => {
+ describe('loadingStarted', () => {
+ it('should create an action to show loading bar', () => {
+ const data = 'test';
+
+ const expectedAction = {
+ data,
+ type: actionTypes.loadingStarted,
+ };
+ expect(loadingStarted(data)).to.be.deep.equal(expectedAction);
+ });
+ });
+
+ describe('loadingFinished', () => {
+ it('should create an action to hide loading bar', () => {
+ const data = 'test';
+
+ const expectedAction = {
+ data,
+ type: actionTypes.loadingFinished,
+ };
+ expect(loadingFinished(data)).to.be.deep.equal(expectedAction);
+ });
+ });
+});
diff --git a/src/components/app/index.js b/src/components/app/index.js
index 686b95904..b22952cad 100644
--- a/src/components/app/index.js
+++ b/src/components/app/index.js
@@ -11,7 +11,7 @@ import styles from './app.css';
import Metronome from '../../utils/metronome';
import Dialog from '../dialog';
import Tabs from '../tabs';
- // temporary, will be deleted with #347
+import LoadingBar from '../loadingBar';
// start dispatching sync ticks
const metronome = new Metronome();
@@ -33,6 +33,7 @@ const App = () => (
+
);
diff --git a/src/components/loadingBar/index.js b/src/components/loadingBar/index.js
new file mode 100644
index 000000000..86a17267f
--- /dev/null
+++ b/src/components/loadingBar/index.js
@@ -0,0 +1,10 @@
+
+import { connect } from 'react-redux';
+import LoadingBar from './loadingBar';
+
+const mapStateToProps = state => ({
+ loading: state.loading,
+});
+
+export default connect(mapStateToProps)(LoadingBar);
+
diff --git a/src/components/loadingBar/index.test.js b/src/components/loadingBar/index.test.js
new file mode 100644
index 000000000..52e8e58ce
--- /dev/null
+++ b/src/components/loadingBar/index.test.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import { expect } from 'chai';
+import { mount } from 'enzyme';
+import { Provider } from 'react-redux';
+import LoadingBar from './';
+import store from '../../store';
+
+
+describe('LoadingBar Container', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount();
+ });
+
+ it('should render Send', () => {
+ expect(wrapper.find('LoadingBar')).to.have.lengthOf(1);
+ });
+});
diff --git a/src/components/loadingBar/loadingBar.css b/src/components/loadingBar/loadingBar.css
new file mode 100644
index 000000000..537c6cc38
--- /dev/null
+++ b/src/components/loadingBar/loadingBar.css
@@ -0,0 +1,7 @@
+.fixedAtTop {
+ position: fixed;
+ top: -11px;
+ right: 0;
+ width: 100vw;
+ z-index: 201;
+}
diff --git a/src/components/loadingBar/loadingBar.js b/src/components/loadingBar/loadingBar.js
new file mode 100644
index 000000000..99827c14d
--- /dev/null
+++ b/src/components/loadingBar/loadingBar.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import ProgressBar from 'react-toolbox/lib/progress_bar';
+import styles from './loadingBar.css';
+
+const LoadingBar = props => (
+
+ {props.loading && props.loading.length ?
+
:
+ null
+ }
+
+);
+
+export default LoadingBar;
diff --git a/src/components/loadingBar/loadingBar.test.js b/src/components/loadingBar/loadingBar.test.js
new file mode 100644
index 000000000..4f366b24d
--- /dev/null
+++ b/src/components/loadingBar/loadingBar.test.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import { expect } from 'chai';
+import { mount } from 'enzyme';
+import LoadingBar from './loadingBar';
+
+
+describe('LoadingBar Container', () => {
+ it('should show ProgresBar if props.loading.length is not 0', () => {
+ const wrapper = mount();
+ expect(wrapper.find('ProgressBar')).to.have.lengthOf(1);
+ });
+
+ it('should not show ProgresBar if props.loading.length is 0', () => {
+ const wrapper = mount();
+ expect(wrapper.find('ProgressBar')).to.have.lengthOf(0);
+ });
+});
diff --git a/src/constants/actions.js b/src/constants/actions.js
index 11fdbca0f..da81e2b76 100644
--- a/src/constants/actions.js
+++ b/src/constants/actions.js
@@ -8,6 +8,8 @@ const actionTypes = {
dialogHidden: 'DIALOG_HIDDEN',
forgedBlocksUpdated: 'FORGED_BLOCKS_UPDATED',
forgingStatsUpdated: 'FORGING_STATS_UPDATED',
+ loadingStarted: 'LOADING_STARTED',
+ loadingFinished: 'LOADING_FINISHED',
};
export default actionTypes;
diff --git a/src/store/reducers/index.js b/src/store/reducers/index.js
index 9dca54638..d2d18cd81 100644
--- a/src/store/reducers/index.js
+++ b/src/store/reducers/index.js
@@ -2,4 +2,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';
+export { default as loading } from './loading';
diff --git a/src/store/reducers/loading.js b/src/store/reducers/loading.js
new file mode 100644
index 000000000..2d3ab5bdd
--- /dev/null
+++ b/src/store/reducers/loading.js
@@ -0,0 +1,19 @@
+import actionTypes from '../../constants/actions';
+
+/**
+ *
+ * @param {Array} state
+ * @param {Object} action
+ */
+const dialog = (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.loadingStarted:
+ return [...state, action.data];
+ case actionTypes.loadingFinished:
+ return state.filter(item => item !== action.data);
+ default:
+ return state;
+ }
+};
+
+export default dialog;
diff --git a/src/store/reducers/loding.test.js b/src/store/reducers/loding.test.js
new file mode 100644
index 000000000..2b13bda28
--- /dev/null
+++ b/src/store/reducers/loding.test.js
@@ -0,0 +1,34 @@
+import chai, { expect } from 'chai';
+import sinonChai from 'sinon-chai';
+import loading from './loading';
+import actionTypes from '../../constants/actions';
+
+
+chai.use(sinonChai);
+
+describe('Reducer: loading(state, action)', () => {
+ let state;
+
+ beforeEach(() => {
+ state = ['test1', 'test2'];
+ });
+
+ it('should return loading array with the new loading if action.type = actionTypes.loadingStarted', () => {
+ const action = {
+ type: actionTypes.loadingStarted,
+ data: 'test3',
+ };
+ const changedState = loading(state, action);
+ expect(changedState).to.deep.equal([...state, action.data]);
+ });
+
+ it('should return loading array without action.data if action.type = actionTypes.loadingFinished', () => {
+ const action = {
+ type: actionTypes.loadingFinished,
+ data: 'test1',
+ };
+ const changedState = loading(state, action);
+ expect(changedState).to.deep.equal(['test2']);
+ });
+});
+
diff --git a/src/utils/api/peers.js b/src/utils/api/peers.js
index fd94c4018..76d32f0bb 100644
--- a/src/utils/api/peers.js
+++ b/src/utils/api/peers.js
@@ -1,12 +1,16 @@
+import { loadingStarted, loadingFinished } from '../../utils/loading';
+
/* eslint-disable */
export const requestToActivePeer = (activePeer, path, urlParams) =>
new Promise((resolve, reject) => {
+ loadingStarted(path);
activePeer.sendRequest(path, urlParams, (data) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
+ loadingFinished(path);
});
});
/* eslint-enable */
diff --git a/src/utils/loading.js b/src/utils/loading.js
new file mode 100644
index 000000000..793699a2e
--- /dev/null
+++ b/src/utils/loading.js
@@ -0,0 +1,7 @@
+import { loadingStarted as loadingStartedAction, loadingFinished as loadingFinishedAction } from '../actions/loading';
+import store from '../store';
+
+
+export const loadingStarted = data => store.dispatch(loadingStartedAction(data));
+
+export const loadingFinished = data => store.dispatch(loadingFinishedAction(data));