From 41a0ff5518da4d7bc154c3a7c43dd90715cb93b1 Mon Sep 17 00:00:00 2001 From: alon muroch Date: Tue, 4 Oct 2016 19:26:48 +0700 Subject: [PATCH 1/8] created balances container and component --- app/components/Balances/index.js | 22 +++++++++++ app/components/Balances/messages.js | 13 +++++++ app/components/Balances/styles.css | 3 ++ app/components/Balances/tests/index.test.js | 11 ++++++ app/containers/BalancesContainer/actions.js | 15 +++++++ app/containers/BalancesContainer/constants.js | 7 ++++ app/containers/BalancesContainer/index.js | 39 +++++++++++++++++++ app/containers/BalancesContainer/messages.js | 13 +++++++ app/containers/BalancesContainer/reducer.js | 23 +++++++++++ app/containers/BalancesContainer/sagas.js | 11 ++++++ app/containers/BalancesContainer/selectors.js | 25 ++++++++++++ app/containers/BalancesContainer/styles.css | 3 ++ .../BalancesContainer/tests/actions.test.js | 18 +++++++++ .../BalancesContainer/tests/index.test.js | 11 ++++++ .../BalancesContainer/tests/reducer.test.js | 9 +++++ .../BalancesContainer/tests/sagas.test.js | 15 +++++++ .../BalancesContainer/tests/selectors.test.js | 11 ++++++ 17 files changed, 249 insertions(+) create mode 100644 app/components/Balances/index.js create mode 100644 app/components/Balances/messages.js create mode 100644 app/components/Balances/styles.css create mode 100644 app/components/Balances/tests/index.test.js create mode 100644 app/containers/BalancesContainer/actions.js create mode 100644 app/containers/BalancesContainer/constants.js create mode 100644 app/containers/BalancesContainer/index.js create mode 100644 app/containers/BalancesContainer/messages.js create mode 100644 app/containers/BalancesContainer/reducer.js create mode 100644 app/containers/BalancesContainer/sagas.js create mode 100644 app/containers/BalancesContainer/selectors.js create mode 100644 app/containers/BalancesContainer/styles.css create mode 100644 app/containers/BalancesContainer/tests/actions.test.js create mode 100644 app/containers/BalancesContainer/tests/index.test.js create mode 100644 app/containers/BalancesContainer/tests/reducer.test.js create mode 100644 app/containers/BalancesContainer/tests/sagas.test.js create mode 100644 app/containers/BalancesContainer/tests/selectors.test.js diff --git a/app/components/Balances/index.js b/app/components/Balances/index.js new file mode 100644 index 0000000..40fae88 --- /dev/null +++ b/app/components/Balances/index.js @@ -0,0 +1,22 @@ +/** +* +* Balances +* +*/ + +import React from 'react'; + +import { FormattedMessage } from 'react-intl'; +import messages from './messages'; + +import styles from './styles.css'; + +function Balances() { + return ( +
+ +
+ ); +} + +export default Balances; diff --git a/app/components/Balances/messages.js b/app/components/Balances/messages.js new file mode 100644 index 0000000..0e25bd4 --- /dev/null +++ b/app/components/Balances/messages.js @@ -0,0 +1,13 @@ +/* + * Balances Messages + * + * This contains all the text for the Balances component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + header: { + id: 'app.components.Balances.header', + defaultMessage: 'This is the Balances component !', + }, +}); diff --git a/app/components/Balances/styles.css b/app/components/Balances/styles.css new file mode 100644 index 0000000..1e23dc2 --- /dev/null +++ b/app/components/Balances/styles.css @@ -0,0 +1,3 @@ +.balances { /* stylelint-disable */ + +} diff --git a/app/components/Balances/tests/index.test.js b/app/components/Balances/tests/index.test.js new file mode 100644 index 0000000..23b58a4 --- /dev/null +++ b/app/components/Balances/tests/index.test.js @@ -0,0 +1,11 @@ +// import Balances from '../index'; + +import expect from 'expect'; +// import { shallow } from 'enzyme'; +// import React from 'react'; + +describe('', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/BalancesContainer/actions.js b/app/containers/BalancesContainer/actions.js new file mode 100644 index 0000000..920b2bc --- /dev/null +++ b/app/containers/BalancesContainer/actions.js @@ -0,0 +1,15 @@ +/* + * + * BalancesContainer actions + * + */ + +import { + DEFAULT_ACTION, +} from './constants'; + +export function defaultAction() { + return { + type: DEFAULT_ACTION, + }; +} diff --git a/app/containers/BalancesContainer/constants.js b/app/containers/BalancesContainer/constants.js new file mode 100644 index 0000000..3dccaef --- /dev/null +++ b/app/containers/BalancesContainer/constants.js @@ -0,0 +1,7 @@ +/* + * + * BalancesContainer constants + * + */ + +export const DEFAULT_ACTION = 'app/BalancesContainer/DEFAULT_ACTION'; diff --git a/app/containers/BalancesContainer/index.js b/app/containers/BalancesContainer/index.js new file mode 100644 index 0000000..48db709 --- /dev/null +++ b/app/containers/BalancesContainer/index.js @@ -0,0 +1,39 @@ +/* + * + * BalancesContainer + * + */ + +import React from 'react'; +import { connect } from 'react-redux'; +import Helmet from 'react-helmet'; +import selectBalancesContainer from './selectors'; +import { FormattedMessage } from 'react-intl'; +import messages from './messages'; +import styles from './styles.css'; + +export class BalancesContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function + render() { + return ( +
+ + +
+ ); + } +} + +const mapStateToProps = selectBalancesContainer(); + +function mapDispatchToProps(dispatch) { + return { + dispatch, + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(BalancesContainer); diff --git a/app/containers/BalancesContainer/messages.js b/app/containers/BalancesContainer/messages.js new file mode 100644 index 0000000..fd7ecce --- /dev/null +++ b/app/containers/BalancesContainer/messages.js @@ -0,0 +1,13 @@ +/* + * BalancesContainer Messages + * + * This contains all the text for the BalancesContainer component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + header: { + id: 'app.containers.BalancesContainer.header', + defaultMessage: 'This is BalancesContainer container !', + }, +}); diff --git a/app/containers/BalancesContainer/reducer.js b/app/containers/BalancesContainer/reducer.js new file mode 100644 index 0000000..e537ff9 --- /dev/null +++ b/app/containers/BalancesContainer/reducer.js @@ -0,0 +1,23 @@ +/* + * + * BalancesContainer reducer + * + */ + +import { fromJS } from 'immutable'; +import { + DEFAULT_ACTION, +} from './constants'; + +const initialState = fromJS({}); + +function balancesContainerReducer(state = initialState, action) { + switch (action.type) { + case DEFAULT_ACTION: + return state; + default: + return state; + } +} + +export default balancesContainerReducer; diff --git a/app/containers/BalancesContainer/sagas.js b/app/containers/BalancesContainer/sagas.js new file mode 100644 index 0000000..4401e77 --- /dev/null +++ b/app/containers/BalancesContainer/sagas.js @@ -0,0 +1,11 @@ +// import { take, call, put, select } from 'redux-saga/effects'; + +// Individual exports for testing +export function* defaultSaga() { + return; +} + +// All sagas to be loaded +export default [ + defaultSaga, +]; diff --git a/app/containers/BalancesContainer/selectors.js b/app/containers/BalancesContainer/selectors.js new file mode 100644 index 0000000..2853ee8 --- /dev/null +++ b/app/containers/BalancesContainer/selectors.js @@ -0,0 +1,25 @@ +import { createSelector } from 'reselect'; + +/** + * Direct selector to the balancesContainer state domain + */ +const selectBalancesContainerDomain = () => (state) => state.get('balancesContainer'); + +/** + * Other specific selectors + */ + + +/** + * Default selector used by BalancesContainer + */ + +const selectBalancesContainer = () => createSelector( + selectBalancesContainerDomain(), + (substate) => substate.toJS() +); + +export default selectBalancesContainer; +export { + selectBalancesContainerDomain, +}; diff --git a/app/containers/BalancesContainer/styles.css b/app/containers/BalancesContainer/styles.css new file mode 100644 index 0000000..3d4f979 --- /dev/null +++ b/app/containers/BalancesContainer/styles.css @@ -0,0 +1,3 @@ +.balancesContainer { /* stylelint-disable */ + +} diff --git a/app/containers/BalancesContainer/tests/actions.test.js b/app/containers/BalancesContainer/tests/actions.test.js new file mode 100644 index 0000000..8996b91 --- /dev/null +++ b/app/containers/BalancesContainer/tests/actions.test.js @@ -0,0 +1,18 @@ +import expect from 'expect'; +import { + defaultAction, +} from '../actions'; +import { + DEFAULT_ACTION, +} from '../constants'; + +describe('BalancesContainer actions', () => { + describe('Default Action', () => { + it('has a type of DEFAULT_ACTION', () => { + const expected = { + type: DEFAULT_ACTION, + }; + expect(defaultAction()).toEqual(expected); + }); + }); +}); diff --git a/app/containers/BalancesContainer/tests/index.test.js b/app/containers/BalancesContainer/tests/index.test.js new file mode 100644 index 0000000..67cfea9 --- /dev/null +++ b/app/containers/BalancesContainer/tests/index.test.js @@ -0,0 +1,11 @@ +// import BalancesContainer from '../index'; + +import expect from 'expect'; +// import { shallow } from 'enzyme'; +// import React from 'react'; + +describe('', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/BalancesContainer/tests/reducer.test.js b/app/containers/BalancesContainer/tests/reducer.test.js new file mode 100644 index 0000000..3d0d2aa --- /dev/null +++ b/app/containers/BalancesContainer/tests/reducer.test.js @@ -0,0 +1,9 @@ +import expect from 'expect'; +import balancesContainerReducer from '../reducer'; +import { fromJS } from 'immutable'; + +describe('balancesContainerReducer', () => { + it('returns the initial state', () => { + expect(balancesContainerReducer(undefined, {})).toEqual(fromJS({})); + }); +}); diff --git a/app/containers/BalancesContainer/tests/sagas.test.js b/app/containers/BalancesContainer/tests/sagas.test.js new file mode 100644 index 0000000..ec8febe --- /dev/null +++ b/app/containers/BalancesContainer/tests/sagas.test.js @@ -0,0 +1,15 @@ +/** + * Test sagas + */ + +import expect from 'expect'; +// import { take, call, put, select } from 'redux-saga/effects'; +// import { defaultSaga } from '../sagas'; + +// const generator = defaultSaga(); + +describe('defaultSaga Saga', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/BalancesContainer/tests/selectors.test.js b/app/containers/BalancesContainer/tests/selectors.test.js new file mode 100644 index 0000000..76bd1cc --- /dev/null +++ b/app/containers/BalancesContainer/tests/selectors.test.js @@ -0,0 +1,11 @@ +// import { selectBalancesContainerDomain } from '../selectors'; +// import { fromJS } from 'immutable'; +import expect from 'expect'; + +// const selector = selectBalancesContainerDomain(); + +describe('selectBalancesContainerDomain', () => { + it('Expect to have unit tests specified', () => { + expect('Test case').toEqual(false); + }); +}); From a9cb8b6350133937fab479123df67f521208b545 Mon Sep 17 00:00:00 2001 From: alon muroch Date: Tue, 4 Oct 2016 21:20:45 +0700 Subject: [PATCH 2/8] listing hard coded tokens --- app/containers/BalancesContainer/AjaxReq.js | 90 +++++++++++++++++++++ app/containers/BalancesContainer/Token.js | 38 +++++++++ app/containers/BalancesContainer/index.js | 68 ++++++++++++++-- app/containers/Dashboard/index.js | 8 +- 4 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 app/containers/BalancesContainer/AjaxReq.js create mode 100644 app/containers/BalancesContainer/Token.js diff --git a/app/containers/BalancesContainer/AjaxReq.js b/app/containers/BalancesContainer/AjaxReq.js new file mode 100644 index 0000000..a2eff96 --- /dev/null +++ b/app/containers/BalancesContainer/AjaxReq.js @@ -0,0 +1,90 @@ +'use strict'; +var http; +var ajaxReq = function() {} +ajaxReq.http = null; +ajaxReq.postSerializer = null; +ajaxReq.SERVERURL = "https://rpc.myetherwallet.com:8443/api.mew"; +ajaxReq.COINMARKETCAPAPI = "https://coinmarketcap-nexuist.rhcloud.com/api/"; +ajaxReq.pendingPosts = []; +ajaxReq.config = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } +}; +ajaxReq.getBalance = function(addr, callback) { + this.post({ + balance: addr, + isClassic: false + }, callback); +} +ajaxReq.getClassicBalance = function(addr, callback) { + this.post({ + balance: addr, + isClassic: true + }, callback); +} +ajaxReq.getTransactionData = function(addr, callback) { + this.post({ + txdata: addr, + isClassic: false + }, callback); +} +ajaxReq.getClassicTransactionData = function(addr, callback) { + this.post({ + txdata: addr, + isClassic: true + }, callback); +} +ajaxReq.sendRawTx = function(rawTx, callback) { + this.post({ + rawtx: rawTx, + isClassic: false + }, callback); +} +ajaxReq.sendClassicRawTx = function(rawTx, callback) { + this.post({ + rawtx: rawTx, + isClassic: true + }, callback); +} +ajaxReq.getEstimatedGas = function(txobj, callback) { + this.post({ + estimatedGas: txobj, + isClassic: false + }, callback); +} +ajaxReq.getEthCall = function(txobj, callback) { + this.post({ + ethCall: txobj, + isClassic: false + }, callback); +} +ajaxReq.queuePost = function() { + var data = this.pendingPosts[0].data; + var callback = this.pendingPosts[0].callback; + this.http.post(this.SERVERURL, this.postSerializer(data), this.config).then(function(data) { + callback(data.data); + ajaxReq.pendingPosts.splice(0, 1); + if (ajaxReq.pendingPosts.length > 0) ajaxReq.queuePost(); + }); +} +ajaxReq.post = function(data, callback) { + this.pendingPosts.push({ + data: data, + callback: callback + }); + if (this.pendingPosts.length == 1) this.queuePost(); +} +ajaxReq.getETHvalue = function(callback) { + var prefix = "eth"; + this.http.get(this.COINMARKETCAPAPI + prefix).then(function(data) { + data = data['data']['price']; + var priceObj = { + usd: data['usd'].toFixed(6), + eur: data['eur'].toFixed(6), + btc: data['btc'].toFixed(6) + }; + callback(priceObj); + }); +} +module.exports = ajaxReq; \ No newline at end of file diff --git a/app/containers/BalancesContainer/Token.js b/app/containers/BalancesContainer/Token.js new file mode 100644 index 0000000..62e4f42 --- /dev/null +++ b/app/containers/BalancesContainer/Token.js @@ -0,0 +1,38 @@ +export class Token { + constructor(contractAddress, userAddress, symbol, decimal) { + this.contractAddress = contractAddress; + this.userAddress = userAddress + this.symbol = symbol; + this.decimal = decimal; + } + + prettyName() { + return this.symbol; + } + + // const hardCodedTokens = [{ + // "address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + // "symbol": "DAO", + // "decimal": 16 + // }, + // { + // "address": "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a", + // "symbol": "DGD", + // "decimal": 9 + // }, + // { + // "address": "0xc66ea802717bfb9833400264dd12c2bceaa34a6d", + // "symbol": "MKR", + // "decimal": 18 + // }, + // { + // "address": "0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7", + // "symbol": "🦄 Unicorn", + // "decimal": 0 + // }, + // { + // "address": "0x74c1e4b8cae59269ec1d85d3d4f324396048f4ac", + // "symbol": "🍺 BeerCoin", + // "decimal": 0 + // }]; +} \ No newline at end of file diff --git a/app/containers/BalancesContainer/index.js b/app/containers/BalancesContainer/index.js index 48db709..5fef227 100644 --- a/app/containers/BalancesContainer/index.js +++ b/app/containers/BalancesContainer/index.js @@ -11,18 +11,70 @@ import selectBalancesContainer from './selectors'; import { FormattedMessage } from 'react-intl'; import messages from './messages'; import styles from './styles.css'; +import { Token } from './Token' export class BalancesContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function + constructor(props) { + super(props); + this.state = { + tokens: [], + title: "Loading" + }; + } + + componentDidMount() { + + var hardCodedTokens = [{ + "address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "symbol": "DAO", + "decimal": 16 + }, + { + "address": "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a", + "symbol": "DGD", + "decimal": 9 + }, + { + "address": "0xc66ea802717bfb9833400264dd12c2bceaa34a6d", + "symbol": "MKR", + "decimal": 18 + }, + { + "address": "0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7", + "symbol": "🦄 Unicorn", + "decimal": 0 + }, + { + "address": "0x74c1e4b8cae59269ec1d85d3d4f324396048f4ac", + "symbol": "🍺 BeerCoin", + "decimal": 0 + }]; + var _tokens = []; + for (var i = 0; i < hardCodedTokens.length; i++) { + _tokens.push(new Token(hardCodedTokens[i].address, + "d7e10d75cf87abc5a2f34a83ccf27cd54108cbc3", + hardCodedTokens[i].symbol, + hardCodedTokens[i].decimal) + ); + } + this.setState({tokens: _tokens}) + } + render() { return ( -
- - +
+ Addresses: +
+
    + {this.state.tokens.map(function(token) { + return ( +
  • + {token.prettyName()} +
  • + ); + })} +
+
); } diff --git a/app/containers/Dashboard/index.js b/app/containers/Dashboard/index.js index 6aacc9f..b1045c8 100644 --- a/app/containers/Dashboard/index.js +++ b/app/containers/Dashboard/index.js @@ -10,13 +10,17 @@ import selectDashboard from './selectors'; import { FormattedMessage } from 'react-intl'; import messages from './messages'; import styles from './styles.css'; +import { BalancesContainer } from '../BalancesContainer/index'; export class Dashboard extends React.Component { // eslint-disable-line react/prefer-stateless-function render() { return ( -
- +
+
+ //
+ // + //
); } } From 8da4aa1f44c8fb1568b3fc78a18e0cc4b48dfa50 Mon Sep 17 00:00:00 2001 From: alon muroch Date: Wed, 5 Oct 2016 11:12:42 +0700 Subject: [PATCH 3/8] balances working --- app/components/BalanceCell/index.js | 23 ++++ app/components/BalanceCell/messages.js | 13 ++ app/components/BalanceCell/styles.css | 3 + .../tests/index.test.js | 4 +- app/components/Balances/index.js | 22 ---- app/components/Balances/messages.js | 13 -- app/components/Balances/styles.css | 3 - app/containers/BalanceContainer/actions.js | 15 +++ app/containers/BalanceContainer/constants.js | 7 ++ app/containers/BalanceContainer/index.js | 78 ++++++++++++ app/containers/BalanceContainer/messages.js | 13 ++ app/containers/BalanceContainer/reducer.js | 23 ++++ app/containers/BalanceContainer/sagas.js | 11 ++ app/containers/BalanceContainer/selectors.js | 25 ++++ app/containers/BalanceContainer/styles.css | 3 + .../BalanceContainer/tests/actions.test.js | 18 +++ .../BalanceContainer/tests/index.test.js | 11 ++ .../BalanceContainer/tests/reducer.test.js | 9 ++ .../BalanceContainer/tests/sagas.test.js | 15 +++ .../BalanceContainer/tests/selectors.test.js | 11 ++ app/containers/BalancesContainer/AjaxReq.js | 90 -------------- app/containers/BalancesContainer/Token.js | 113 ++++++++++++++---- app/containers/BalancesContainer/index.js | 9 +- package.json | 2 + 24 files changed, 377 insertions(+), 157 deletions(-) create mode 100644 app/components/BalanceCell/index.js create mode 100644 app/components/BalanceCell/messages.js create mode 100644 app/components/BalanceCell/styles.css rename app/components/{Balances => BalanceCell}/tests/index.test.js (72%) delete mode 100644 app/components/Balances/index.js delete mode 100644 app/components/Balances/messages.js delete mode 100644 app/components/Balances/styles.css create mode 100644 app/containers/BalanceContainer/actions.js create mode 100644 app/containers/BalanceContainer/constants.js create mode 100644 app/containers/BalanceContainer/index.js create mode 100644 app/containers/BalanceContainer/messages.js create mode 100644 app/containers/BalanceContainer/reducer.js create mode 100644 app/containers/BalanceContainer/sagas.js create mode 100644 app/containers/BalanceContainer/selectors.js create mode 100644 app/containers/BalanceContainer/styles.css create mode 100644 app/containers/BalanceContainer/tests/actions.test.js create mode 100644 app/containers/BalanceContainer/tests/index.test.js create mode 100644 app/containers/BalanceContainer/tests/reducer.test.js create mode 100644 app/containers/BalanceContainer/tests/sagas.test.js create mode 100644 app/containers/BalanceContainer/tests/selectors.test.js delete mode 100644 app/containers/BalancesContainer/AjaxReq.js diff --git a/app/components/BalanceCell/index.js b/app/components/BalanceCell/index.js new file mode 100644 index 0000000..3d6fc96 --- /dev/null +++ b/app/components/BalanceCell/index.js @@ -0,0 +1,23 @@ +/** +* +* BalanceCell +* +*/ + +import React from 'react'; + +import { FormattedMessage } from 'react-intl'; +import messages from './messages'; +import styles from './styles.css'; + +class BalanceCell extends React.Component { // eslint-disable-line react/prefer-stateless-function + render() { + return ( +
+ { this.props.name + ": " + this.props.balance } +
+ ); + } +} + +export default BalanceCell; diff --git a/app/components/BalanceCell/messages.js b/app/components/BalanceCell/messages.js new file mode 100644 index 0000000..f21dadd --- /dev/null +++ b/app/components/BalanceCell/messages.js @@ -0,0 +1,13 @@ +/* + * BalanceCell Messages + * + * This contains all the text for the BalanceCell component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + header: { + id: 'app.components.BalanceCell.header', + defaultMessage: 'This is the BalanceCell component !', + }, +}); diff --git a/app/components/BalanceCell/styles.css b/app/components/BalanceCell/styles.css new file mode 100644 index 0000000..281be39 --- /dev/null +++ b/app/components/BalanceCell/styles.css @@ -0,0 +1,3 @@ +.balanceCell { /* stylelint-disable */ + +} diff --git a/app/components/Balances/tests/index.test.js b/app/components/BalanceCell/tests/index.test.js similarity index 72% rename from app/components/Balances/tests/index.test.js rename to app/components/BalanceCell/tests/index.test.js index 23b58a4..d61df5d 100644 --- a/app/components/Balances/tests/index.test.js +++ b/app/components/BalanceCell/tests/index.test.js @@ -1,10 +1,10 @@ -// import Balances from '../index'; +// import BalanceCell from '../index'; import expect from 'expect'; // import { shallow } from 'enzyme'; // import React from 'react'; -describe('', () => { +describe('', () => { it('Expect to have unit tests specified', () => { expect(true).toEqual(false); }); diff --git a/app/components/Balances/index.js b/app/components/Balances/index.js deleted file mode 100644 index 40fae88..0000000 --- a/app/components/Balances/index.js +++ /dev/null @@ -1,22 +0,0 @@ -/** -* -* Balances -* -*/ - -import React from 'react'; - -import { FormattedMessage } from 'react-intl'; -import messages from './messages'; - -import styles from './styles.css'; - -function Balances() { - return ( -
- -
- ); -} - -export default Balances; diff --git a/app/components/Balances/messages.js b/app/components/Balances/messages.js deleted file mode 100644 index 0e25bd4..0000000 --- a/app/components/Balances/messages.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Balances Messages - * - * This contains all the text for the Balances component. - */ -import { defineMessages } from 'react-intl'; - -export default defineMessages({ - header: { - id: 'app.components.Balances.header', - defaultMessage: 'This is the Balances component !', - }, -}); diff --git a/app/components/Balances/styles.css b/app/components/Balances/styles.css deleted file mode 100644 index 1e23dc2..0000000 --- a/app/components/Balances/styles.css +++ /dev/null @@ -1,3 +0,0 @@ -.balances { /* stylelint-disable */ - -} diff --git a/app/containers/BalanceContainer/actions.js b/app/containers/BalanceContainer/actions.js new file mode 100644 index 0000000..c80fb60 --- /dev/null +++ b/app/containers/BalanceContainer/actions.js @@ -0,0 +1,15 @@ +/* + * + * BalanceContainer actions + * + */ + +import { + DEFAULT_ACTION, +} from './constants'; + +export function defaultAction() { + return { + type: DEFAULT_ACTION, + }; +} diff --git a/app/containers/BalanceContainer/constants.js b/app/containers/BalanceContainer/constants.js new file mode 100644 index 0000000..2f1d3dc --- /dev/null +++ b/app/containers/BalanceContainer/constants.js @@ -0,0 +1,7 @@ +/* + * + * BalanceContainer constants + * + */ + +export const DEFAULT_ACTION = 'app/BalanceContainer/DEFAULT_ACTION'; diff --git a/app/containers/BalanceContainer/index.js b/app/containers/BalanceContainer/index.js new file mode 100644 index 0000000..ed1c21e --- /dev/null +++ b/app/containers/BalanceContainer/index.js @@ -0,0 +1,78 @@ +/* + * + * BalanceContainer + * + */ + +import React from 'react'; +import { connect } from 'react-redux'; +import Helmet from 'react-helmet'; +import selectBalanceContainer from './selectors'; +import { FormattedMessage } from 'react-intl'; +import messages from './messages'; +import styles from './styles.css'; +import BalanceCell from 'components/BalanceCell'; +import $ from 'jquery'; +import BigNumber from 'bignumber.js'; + +export class BalanceContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function + constructor(props) { + super(props); + + this.state = { + balance: 0, + name: "" + }; + } + + componentDidMount() { + if (this.props.token == null) { + return; + } + + this.setState({name: this.props.token.symbol}); + + var data = $.param(this.props.token.balanceCallData()); + var serverUrl = "https://rpc.myetherwallet.com:8443/api.mew"; + var parentObj = this; + $.ajax({ + url: serverUrl, + headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, + type: 'POST', + data: data, + success: function(data) { + if (!data.error) { + if (parentObj.props.token.symbol == "ETH") { + parentObj.props.token.balance = data.data.balance; + var _balance = parentObj.props.token.weiBalance(); + this.setState({balance: _balance}); + } + else { + var decimal = parentObj.props.token.decimal; + var _balance = new BigNumber(data.data).div(new BigNumber(10).pow(decimal)); + this.setState({balance: _balance}); + } + } + }.bind(this), + error: function(xhr, status, err) { + console.error("ERRORRRR", status, err.toString()); + }.bind(this) + }); + } + + render() { + return ( + + ); + } +} + +const mapStateToProps = selectBalanceContainer(); + +function mapDispatchToProps(dispatch) { + return { + dispatch, + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(BalanceContainer); diff --git a/app/containers/BalanceContainer/messages.js b/app/containers/BalanceContainer/messages.js new file mode 100644 index 0000000..f37835f --- /dev/null +++ b/app/containers/BalanceContainer/messages.js @@ -0,0 +1,13 @@ +/* + * BalanceContainer Messages + * + * This contains all the text for the BalanceContainer component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + header: { + id: 'app.containers.BalanceContainer.header', + defaultMessage: 'This is BalanceContainer container !', + }, +}); diff --git a/app/containers/BalanceContainer/reducer.js b/app/containers/BalanceContainer/reducer.js new file mode 100644 index 0000000..0513b35 --- /dev/null +++ b/app/containers/BalanceContainer/reducer.js @@ -0,0 +1,23 @@ +/* + * + * BalanceContainer reducer + * + */ + +import { fromJS } from 'immutable'; +import { + DEFAULT_ACTION, +} from './constants'; + +const initialState = fromJS({}); + +function balanceContainerReducer(state = initialState, action) { + switch (action.type) { + case DEFAULT_ACTION: + return state; + default: + return state; + } +} + +export default balanceContainerReducer; diff --git a/app/containers/BalanceContainer/sagas.js b/app/containers/BalanceContainer/sagas.js new file mode 100644 index 0000000..4401e77 --- /dev/null +++ b/app/containers/BalanceContainer/sagas.js @@ -0,0 +1,11 @@ +// import { take, call, put, select } from 'redux-saga/effects'; + +// Individual exports for testing +export function* defaultSaga() { + return; +} + +// All sagas to be loaded +export default [ + defaultSaga, +]; diff --git a/app/containers/BalanceContainer/selectors.js b/app/containers/BalanceContainer/selectors.js new file mode 100644 index 0000000..f219d1c --- /dev/null +++ b/app/containers/BalanceContainer/selectors.js @@ -0,0 +1,25 @@ +import { createSelector } from 'reselect'; + +/** + * Direct selector to the balanceContainer state domain + */ +const selectBalanceContainerDomain = () => (state) => state.get('balanceContainer'); + +/** + * Other specific selectors + */ + + +/** + * Default selector used by BalanceContainer + */ + +const selectBalanceContainer = () => createSelector( + selectBalanceContainerDomain(), + (substate) => substate.toJS() +); + +export default selectBalanceContainer; +export { + selectBalanceContainerDomain, +}; diff --git a/app/containers/BalanceContainer/styles.css b/app/containers/BalanceContainer/styles.css new file mode 100644 index 0000000..492b482 --- /dev/null +++ b/app/containers/BalanceContainer/styles.css @@ -0,0 +1,3 @@ +.balanceContainer { /* stylelint-disable */ + +} diff --git a/app/containers/BalanceContainer/tests/actions.test.js b/app/containers/BalanceContainer/tests/actions.test.js new file mode 100644 index 0000000..a919743 --- /dev/null +++ b/app/containers/BalanceContainer/tests/actions.test.js @@ -0,0 +1,18 @@ +import expect from 'expect'; +import { + defaultAction, +} from '../actions'; +import { + DEFAULT_ACTION, +} from '../constants'; + +describe('BalanceContainer actions', () => { + describe('Default Action', () => { + it('has a type of DEFAULT_ACTION', () => { + const expected = { + type: DEFAULT_ACTION, + }; + expect(defaultAction()).toEqual(expected); + }); + }); +}); diff --git a/app/containers/BalanceContainer/tests/index.test.js b/app/containers/BalanceContainer/tests/index.test.js new file mode 100644 index 0000000..b961d4a --- /dev/null +++ b/app/containers/BalanceContainer/tests/index.test.js @@ -0,0 +1,11 @@ +// import BalanceContainer from '../index'; + +import expect from 'expect'; +// import { shallow } from 'enzyme'; +// import React from 'react'; + +describe('', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/BalanceContainer/tests/reducer.test.js b/app/containers/BalanceContainer/tests/reducer.test.js new file mode 100644 index 0000000..3e8ff40 --- /dev/null +++ b/app/containers/BalanceContainer/tests/reducer.test.js @@ -0,0 +1,9 @@ +import expect from 'expect'; +import balanceContainerReducer from '../reducer'; +import { fromJS } from 'immutable'; + +describe('balanceContainerReducer', () => { + it('returns the initial state', () => { + expect(balanceContainerReducer(undefined, {})).toEqual(fromJS({})); + }); +}); diff --git a/app/containers/BalanceContainer/tests/sagas.test.js b/app/containers/BalanceContainer/tests/sagas.test.js new file mode 100644 index 0000000..ec8febe --- /dev/null +++ b/app/containers/BalanceContainer/tests/sagas.test.js @@ -0,0 +1,15 @@ +/** + * Test sagas + */ + +import expect from 'expect'; +// import { take, call, put, select } from 'redux-saga/effects'; +// import { defaultSaga } from '../sagas'; + +// const generator = defaultSaga(); + +describe('defaultSaga Saga', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/BalanceContainer/tests/selectors.test.js b/app/containers/BalanceContainer/tests/selectors.test.js new file mode 100644 index 0000000..7661a37 --- /dev/null +++ b/app/containers/BalanceContainer/tests/selectors.test.js @@ -0,0 +1,11 @@ +// import { selectBalanceContainerDomain } from '../selectors'; +// import { fromJS } from 'immutable'; +import expect from 'expect'; + +// const selector = selectBalanceContainerDomain(); + +describe('selectBalanceContainerDomain', () => { + it('Expect to have unit tests specified', () => { + expect('Test case').toEqual(false); + }); +}); diff --git a/app/containers/BalancesContainer/AjaxReq.js b/app/containers/BalancesContainer/AjaxReq.js deleted file mode 100644 index a2eff96..0000000 --- a/app/containers/BalancesContainer/AjaxReq.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; -var http; -var ajaxReq = function() {} -ajaxReq.http = null; -ajaxReq.postSerializer = null; -ajaxReq.SERVERURL = "https://rpc.myetherwallet.com:8443/api.mew"; -ajaxReq.COINMARKETCAPAPI = "https://coinmarketcap-nexuist.rhcloud.com/api/"; -ajaxReq.pendingPosts = []; -ajaxReq.config = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' - } -}; -ajaxReq.getBalance = function(addr, callback) { - this.post({ - balance: addr, - isClassic: false - }, callback); -} -ajaxReq.getClassicBalance = function(addr, callback) { - this.post({ - balance: addr, - isClassic: true - }, callback); -} -ajaxReq.getTransactionData = function(addr, callback) { - this.post({ - txdata: addr, - isClassic: false - }, callback); -} -ajaxReq.getClassicTransactionData = function(addr, callback) { - this.post({ - txdata: addr, - isClassic: true - }, callback); -} -ajaxReq.sendRawTx = function(rawTx, callback) { - this.post({ - rawtx: rawTx, - isClassic: false - }, callback); -} -ajaxReq.sendClassicRawTx = function(rawTx, callback) { - this.post({ - rawtx: rawTx, - isClassic: true - }, callback); -} -ajaxReq.getEstimatedGas = function(txobj, callback) { - this.post({ - estimatedGas: txobj, - isClassic: false - }, callback); -} -ajaxReq.getEthCall = function(txobj, callback) { - this.post({ - ethCall: txobj, - isClassic: false - }, callback); -} -ajaxReq.queuePost = function() { - var data = this.pendingPosts[0].data; - var callback = this.pendingPosts[0].callback; - this.http.post(this.SERVERURL, this.postSerializer(data), this.config).then(function(data) { - callback(data.data); - ajaxReq.pendingPosts.splice(0, 1); - if (ajaxReq.pendingPosts.length > 0) ajaxReq.queuePost(); - }); -} -ajaxReq.post = function(data, callback) { - this.pendingPosts.push({ - data: data, - callback: callback - }); - if (this.pendingPosts.length == 1) this.queuePost(); -} -ajaxReq.getETHvalue = function(callback) { - var prefix = "eth"; - this.http.get(this.COINMARKETCAPAPI + prefix).then(function(data) { - data = data['data']['price']; - var priceObj = { - usd: data['usd'].toFixed(6), - eur: data['eur'].toFixed(6), - btc: data['btc'].toFixed(6) - }; - callback(priceObj); - }); -} -module.exports = ajaxReq; \ No newline at end of file diff --git a/app/containers/BalancesContainer/Token.js b/app/containers/BalancesContainer/Token.js index 62e4f42..97164c1 100644 --- a/app/containers/BalancesContainer/Token.js +++ b/app/containers/BalancesContainer/Token.js @@ -1,38 +1,101 @@ + +import BigNumber from 'bignumber.js'; + export class Token { + constructor(contractAddress, userAddress, symbol, decimal) { this.contractAddress = contractAddress; this.userAddress = userAddress this.symbol = symbol; this.decimal = decimal; + this.balance = 0; + this.balanceHex = "0x70a08231"; + + this.unitMap = { + 'wei': '1', + 'kwei': '1000', + 'ada': '1000', + 'femtoether': '1000', + 'mwei': '1000000', + 'babbage': '1000000', + 'picoether': '1000000', + 'gwei': '1000000000', + 'shannon': '1000000000', + 'nanoether': '1000000000', + 'nano': '1000000000', + 'szabo': '1000000000000', + 'microether': '1000000000000', + 'micro': '1000000000000', + 'finney': '1000000000000000', + 'milliether': '1000000000000000', + 'milli': '1000000000000000', + 'ether': '1000000000000000000', + 'kether': '1000000000000000000000', + 'grand': '1000000000000000000000', + 'einstein': '1000000000000000000000', + 'mether': '1000000000000000000000000', + 'gether': '1000000000000000000000000000', + 'tether': '1000000000000000000000000000000' + }; } prettyName() { return this.symbol; } - // const hardCodedTokens = [{ - // "address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", - // "symbol": "DAO", - // "decimal": 16 - // }, - // { - // "address": "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a", - // "symbol": "DGD", - // "decimal": 9 - // }, - // { - // "address": "0xc66ea802717bfb9833400264dd12c2bceaa34a6d", - // "symbol": "MKR", - // "decimal": 18 - // }, - // { - // "address": "0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7", - // "symbol": "🦄 Unicorn", - // "decimal": 0 - // }, - // { - // "address": "0x74c1e4b8cae59269ec1d85d3d4f324396048f4ac", - // "symbol": "🍺 BeerCoin", - // "decimal": 0 - // }]; + getDataObj(to, func, arrVals) { + var val=""; + for(var i=0;i= width ? n : new Array(width - n.length + 1).join(z) + n; + } + + getNakedAddress() { + return this.userAddress.toLowerCase().replace('0x', ''); + } + + balanceCallData() { + if (this.symbol != "ETH") { + var data = this.getDataObj(this.contractAddress, + this.balanceHex, + [this.getNakedAddress()] + ); + + return { + ethCall: data, + isClassic: false + }; + } + + // ETH + return { + balance: this.userAddress, + isClassic: false + }; + } + + + // balance + weiBalance() { + // wei + var wei = new BigNumber(String(this.balance)).times(this.getValueOfUnit('wei')); + wei = wei.toString(10); + + var returnValue = new BigNumber(wei).div(this.getValueOfUnit('ether')); + return returnValue.toString(10); + } + + getValueOfUnit(unit) { + unit = unit ? unit.toLowerCase() : 'ether'; + var unitValue = this.unitMap[unit]; + if (unitValue === undefined) { + throw 0; + } + return new BigNumber(unitValue, 10); + } } \ No newline at end of file diff --git a/app/containers/BalancesContainer/index.js b/app/containers/BalancesContainer/index.js index 5fef227..4203f4f 100644 --- a/app/containers/BalancesContainer/index.js +++ b/app/containers/BalancesContainer/index.js @@ -12,6 +12,7 @@ import { FormattedMessage } from 'react-intl'; import messages from './messages'; import styles from './styles.css'; import { Token } from './Token' +import { BalanceContainer } from '../BalanceContainer/index' export class BalancesContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function constructor(props) { @@ -25,6 +26,11 @@ export class BalancesContainer extends React.Component { // eslint-disable-line componentDidMount() { var hardCodedTokens = [{ + "address": "", + "symbol" : "ETH", + "decimal" : 0 + }, + { "address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "symbol": "DAO", "decimal": 16 @@ -63,13 +69,12 @@ export class BalancesContainer extends React.Component { // eslint-disable-line render() { return (
- Addresses:
    {this.state.tokens.map(function(token) { return (
  • - {token.prettyName()} +
  • ); })} diff --git a/package.json b/package.json index 3923b14..0a4d7ad 100644 --- a/package.json +++ b/package.json @@ -201,6 +201,7 @@ }, "dependencies": { "babel-polyfill": "6.13.0", + "bignumber.js": "^2.4.0", "chalk": "1.1.3", "compression": "1.6.2", "cross-env": "2.0.1", @@ -210,6 +211,7 @@ "intl": "1.2.4", "invariant": "2.2.1", "ip": "1.1.3", + "jquery": "^3.1.1", "lodash": "4.15.0", "minimist": "1.2.0", "react": "15.3.1", From 598b44838c2ef70510bd40cdccfd77fc7e8058b8 Mon Sep 17 00:00:00 2001 From: alon muroch Date: Wed, 5 Oct 2016 19:00:23 +0700 Subject: [PATCH 4/8] add balance works --- app/components/AddTokenForm/index.js | 50 +++++++++ app/components/AddTokenForm/messages.js | 13 +++ app/components/AddTokenForm/styles.css | 3 + .../AddTokenForm/tests/index.test.js | 11 ++ app/containers/BalancesContainer/index.js | 60 ++++------ app/utils/Wallet/Token.js | 103 ++++++++++++++++++ app/utils/Wallet/Wallet.js | 95 ++++++++++++++++ package.json | 2 + 8 files changed, 296 insertions(+), 41 deletions(-) create mode 100644 app/components/AddTokenForm/index.js create mode 100644 app/components/AddTokenForm/messages.js create mode 100644 app/components/AddTokenForm/styles.css create mode 100644 app/components/AddTokenForm/tests/index.test.js create mode 100644 app/utils/Wallet/Token.js create mode 100644 app/utils/Wallet/Wallet.js diff --git a/app/components/AddTokenForm/index.js b/app/components/AddTokenForm/index.js new file mode 100644 index 0000000..62d05da --- /dev/null +++ b/app/components/AddTokenForm/index.js @@ -0,0 +1,50 @@ +/** +* +* AddTokenForm +* +*/ + +import React from 'react'; + +import { FormattedMessage } from 'react-intl'; +import messages from './messages'; +import styles from './styles.css'; +import Form from "react-jsonschema-form"; + +const schema = { + title: "Fill token details", + type: "object", + required: ["Symbol", "ContractAddress", "Decimal"], + properties: { + Symbol: {type: "string", title: "Symbol: ", default: ""}, + ContractAddress: {type: "string", title: "Contract Address: ", default: "0x"}, + Decimal: {type: "string", title: "Decimal: ", default: "0"}, + } +}; + +class AddTokenForm extends React.Component { // eslint-disable-line react/prefer-stateless-function + constructor(props) { + super(props); + } + + render() { + return ( +
    + ); + } + + onFormSubmit(formData) { + this.props.onSubmited(formData.formData.Symbol, + formData.formData.ContractAddress, + formData.formData.Decimal); + } + + onFormError(errors) { + console.log("error:"); + console.log(errors); + } +} + +export default AddTokenForm; diff --git a/app/components/AddTokenForm/messages.js b/app/components/AddTokenForm/messages.js new file mode 100644 index 0000000..97a6d2c --- /dev/null +++ b/app/components/AddTokenForm/messages.js @@ -0,0 +1,13 @@ +/* + * AddTokenForm Messages + * + * This contains all the text for the AddTokenForm component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + header: { + id: 'app.components.AddTokenForm.header', + defaultMessage: 'This is the AddTokenForm component !', + }, +}); diff --git a/app/components/AddTokenForm/styles.css b/app/components/AddTokenForm/styles.css new file mode 100644 index 0000000..723571b --- /dev/null +++ b/app/components/AddTokenForm/styles.css @@ -0,0 +1,3 @@ +.addTokenForm { /* stylelint-disable */ + +} diff --git a/app/components/AddTokenForm/tests/index.test.js b/app/components/AddTokenForm/tests/index.test.js new file mode 100644 index 0000000..bcf87fe --- /dev/null +++ b/app/components/AddTokenForm/tests/index.test.js @@ -0,0 +1,11 @@ +// import AddTokenForm from '../index'; + +import expect from 'expect'; +// import { shallow } from 'enzyme'; +// import React from 'react'; + +describe('', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/BalancesContainer/index.js b/app/containers/BalancesContainer/index.js index 4203f4f..2084706 100644 --- a/app/containers/BalancesContainer/index.js +++ b/app/containers/BalancesContainer/index.js @@ -11,8 +11,11 @@ import selectBalancesContainer from './selectors'; import { FormattedMessage } from 'react-intl'; import messages from './messages'; import styles from './styles.css'; -import { Token } from './Token' +import { Token } from '../../utils/Wallet/Token' +import W from '../../utils/Wallet/Wallet' import { BalanceContainer } from '../BalanceContainer/index' +import SkyLight from 'react-skylight'; +import AddTokenForm from 'components/AddTokenForm'; export class BalancesContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function constructor(props) { @@ -24,46 +27,7 @@ export class BalancesContainer extends React.Component { // eslint-disable-line } componentDidMount() { - - var hardCodedTokens = [{ - "address": "", - "symbol" : "ETH", - "decimal" : 0 - }, - { - "address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", - "symbol": "DAO", - "decimal": 16 - }, - { - "address": "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a", - "symbol": "DGD", - "decimal": 9 - }, - { - "address": "0xc66ea802717bfb9833400264dd12c2bceaa34a6d", - "symbol": "MKR", - "decimal": 18 - }, - { - "address": "0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7", - "symbol": "🦄 Unicorn", - "decimal": 0 - }, - { - "address": "0x74c1e4b8cae59269ec1d85d3d4f324396048f4ac", - "symbol": "🍺 BeerCoin", - "decimal": 0 - }]; - var _tokens = []; - for (var i = 0; i < hardCodedTokens.length; i++) { - _tokens.push(new Token(hardCodedTokens[i].address, - "d7e10d75cf87abc5a2f34a83ccf27cd54108cbc3", - hardCodedTokens[i].symbol, - hardCodedTokens[i].decimal) - ); - } - this.setState({tokens: _tokens}) + this.setState({tokens: W.getTokens()}) } render() { @@ -80,9 +44,23 @@ export class BalancesContainer extends React.Component { // eslint-disable-line })}
+
+ + +

+ +
+
); } + + + onSubmitedToken(symbol, address, decimal) { + W.addToken(symbol, address, parseInt(decimal)); + this.setState({tokens: W.getTokens()}) + } + } const mapStateToProps = selectBalancesContainer(); diff --git a/app/utils/Wallet/Token.js b/app/utils/Wallet/Token.js new file mode 100644 index 0000000..0398602 --- /dev/null +++ b/app/utils/Wallet/Token.js @@ -0,0 +1,103 @@ + +import BigNumber from 'bignumber.js'; + +export class Token { + + constructor(contractAddress, userAddress, symbol, decimal) { + this.contractAddress = contractAddress; + this.userAddress = userAddress + this.symbol = symbol; + this.decimal = decimal; + this.balance = 0; + this.balanceHex = "0x70a08231"; + + this.unitMap = { + 'wei': '1', + 'kwei': '1000', + 'ada': '1000', + 'femtoether': '1000', + 'mwei': '1000000', + 'babbage': '1000000', + 'picoether': '1000000', + 'gwei': '1000000000', + 'shannon': '1000000000', + 'nanoether': '1000000000', + 'nano': '1000000000', + 'szabo': '1000000000000', + 'microether': '1000000000000', + 'micro': '1000000000000', + 'finney': '1000000000000000', + 'milliether': '1000000000000000', + 'milli': '1000000000000000', + 'ether': '1000000000000000000', + 'kether': '1000000000000000000000', + 'grand': '1000000000000000000000', + 'einstein': '1000000000000000000000', + 'mether': '1000000000000000000000000', + 'gether': '1000000000000000000000000000', + 'tether': '1000000000000000000000000000000' + }; + } + + // uitl + prettyName() { + return this.symbol; + } + + // ajax utils + getDataObj(to, func, arrVals) { + var val=""; + for(var i=0;i= width ? n : new Array(width - n.length + 1).join(z) + n; + } + + getNakedAddress() { + return this.userAddress.toLowerCase().replace('0x', ''); + } + + balanceCallData() { + if (this.symbol != "ETH") { + var data = this.getDataObj(this.contractAddress, + this.balanceHex, + [this.getNakedAddress()] + ); + + return { + ethCall: data, + isClassic: false + }; + } + + // ETH + return { + balance: this.userAddress, + isClassic: false + }; + } + + + // balance + weiBalance() { + // wei + var wei = new BigNumber(String(this.balance)).times(this.getValueOfUnit('wei')); + wei = wei.toString(10); + + var returnValue = new BigNumber(wei).div(this.getValueOfUnit('ether')); + return returnValue.toString(10); + } + + getValueOfUnit(unit) { + unit = unit ? unit.toLowerCase() : 'ether'; + var unitValue = this.unitMap[unit]; + if (unitValue === undefined) { + throw 0; + } + return new BigNumber(unitValue, 10); + } +} \ No newline at end of file diff --git a/app/utils/Wallet/Wallet.js b/app/utils/Wallet/Wallet.js new file mode 100644 index 0000000..6b52797 --- /dev/null +++ b/app/utils/Wallet/Wallet.js @@ -0,0 +1,95 @@ +import { Token } from './Token' + +export class Wallet { + static walletFromDisk() { + return new Wallet(); + } + + constructor(props) { + this.walletAddress = "d7e10d75cf87abc5a2f34a83ccf27cd54108cbc3"; + } + + // tokens + getTokens() { + var allTokens = Wallet.allTokens(); + var _tokens = []; + for (var i = 0; i < allTokens.length; i++) { + let address = allTokens[i].address; + let symbol = allTokens[i].symbol; + let decimal = allTokens[i].decimal; + + console.log("token symbol: " + symbol + ", address: " + address + ", decimal: " + decimal); + + + if (address == null || symbol == null || decimal == null) continue; + + _tokens.push(new Token(allTokens[i].address, + this.walletAddress, + allTokens[i].symbol, + allTokens[i].decimal) + ); + } + return _tokens; + } + + addToken(token) { + this.addToken(token.symbol, token.contractAddress, token.decimal); + } + + addToken(symbol, address, decimal) { + console.log("adding token with symbol: " + symbol + ", address: " + address + ", decimal: " + decimal); + var tokens = Wallet.savedTokens() + tokens.push({ + address: address, + symbol: symbol, + decimal: decimal + }); + localStorage.setItem("localTokens",JSON.stringify(tokens)); + } + + static allTokens() { + return Wallet.savedTokens().concat(Wallet.hardcodedTokes()); + } + + + static savedTokens() { + return localStorage.getItem("localTokens") != null ? JSON.parse(localStorage.getItem("localTokens")) : []; + } + + static hardcodedTokes() { + return [{ + "address": "", + "symbol" : "ETH", + "decimal" : 0 + }, + { + "address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "symbol": "DAO", + "decimal": 16 + }, + { + "address": "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a", + "symbol": "DGD", + "decimal": 9 + }, + { + "address": "0xc66ea802717bfb9833400264dd12c2bceaa34a6d", + "symbol": "MKR", + "decimal": 18 + }, + { + "address": "0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7", + "symbol": "🦄 Unicorn", + "decimal": 0 + }, + { + "address": "0x74c1e4b8cae59269ec1d85d3d4f324396048f4ac", + "symbol": "🍺 BeerCoin", + "decimal": 0 + }]; + } +} + +const W = Wallet.walletFromDisk(); +Object.freeze(W); +export default W; \ No newline at end of file diff --git a/package.json b/package.json index 0a4d7ad..e41a223 100644 --- a/package.json +++ b/package.json @@ -219,10 +219,12 @@ "react-fa": "^4.1.2", "react-helmet": "3.1.0", "react-intl": "2.1.5", + "react-jsonschema-form": "^0.40.0", "react-redux": "4.4.5", "react-router": "2.8.0", "react-router-redux": "4.0.5", "react-router-scroll": "0.3.2", + "react-skylight": "^0.4.1", "redux": "3.6.0", "redux-immutable": "3.0.8", "redux-saga": "0.11.1", From 4f42638f7e868bcf384f0414d774842256593f20 Mon Sep 17 00:00:00 2001 From: alon muroch Date: Wed, 5 Oct 2016 22:56:23 +0700 Subject: [PATCH 5/8] fixes --- app/components/AddTokenForm/messages.js | 13 --- app/components/AddTokenForm/styles.css | 3 - .../AddTokenForm/tests/index.test.js | 11 -- .../BalanceCell/tests/index.test.js | 6 +- .../{AddTokenForm => TokenForm}/index.js | 6 +- app/components/TokenForm/messages.js | 13 +++ app/components/TokenForm/styles.css | 3 + app/components/TokenForm/tests/index.test.js | 11 ++ app/containers/BalanceContainer/index.js | 8 +- app/containers/BalancesContainer/Token.js | 101 ------------------ app/containers/BalancesContainer/index.js | 4 +- app/containers/Dashboard/index.js | 2 +- app/utils/Wallet/Wallet.js | 1 - 13 files changed, 40 insertions(+), 142 deletions(-) delete mode 100644 app/components/AddTokenForm/messages.js delete mode 100644 app/components/AddTokenForm/styles.css delete mode 100644 app/components/AddTokenForm/tests/index.test.js rename app/components/{AddTokenForm => TokenForm}/index.js (87%) create mode 100644 app/components/TokenForm/messages.js create mode 100644 app/components/TokenForm/styles.css create mode 100644 app/components/TokenForm/tests/index.test.js delete mode 100644 app/containers/BalancesContainer/Token.js diff --git a/app/components/AddTokenForm/messages.js b/app/components/AddTokenForm/messages.js deleted file mode 100644 index 97a6d2c..0000000 --- a/app/components/AddTokenForm/messages.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * AddTokenForm Messages - * - * This contains all the text for the AddTokenForm component. - */ -import { defineMessages } from 'react-intl'; - -export default defineMessages({ - header: { - id: 'app.components.AddTokenForm.header', - defaultMessage: 'This is the AddTokenForm component !', - }, -}); diff --git a/app/components/AddTokenForm/styles.css b/app/components/AddTokenForm/styles.css deleted file mode 100644 index 723571b..0000000 --- a/app/components/AddTokenForm/styles.css +++ /dev/null @@ -1,3 +0,0 @@ -.addTokenForm { /* stylelint-disable */ - -} diff --git a/app/components/AddTokenForm/tests/index.test.js b/app/components/AddTokenForm/tests/index.test.js deleted file mode 100644 index bcf87fe..0000000 --- a/app/components/AddTokenForm/tests/index.test.js +++ /dev/null @@ -1,11 +0,0 @@ -// import AddTokenForm from '../index'; - -import expect from 'expect'; -// import { shallow } from 'enzyme'; -// import React from 'react'; - -describe('', () => { - it('Expect to have unit tests specified', () => { - expect(true).toEqual(false); - }); -}); diff --git a/app/components/BalanceCell/tests/index.test.js b/app/components/BalanceCell/tests/index.test.js index d61df5d..32c9cc0 100644 --- a/app/components/BalanceCell/tests/index.test.js +++ b/app/components/BalanceCell/tests/index.test.js @@ -5,7 +5,7 @@ import expect from 'expect'; // import React from 'react'; describe('', () => { - it('Expect to have unit tests specified', () => { - expect(true).toEqual(false); - }); + // it('Expect to have unit tests specified', () => { + // expect(true).toEqual(false); + // }); }); diff --git a/app/components/AddTokenForm/index.js b/app/components/TokenForm/index.js similarity index 87% rename from app/components/AddTokenForm/index.js rename to app/components/TokenForm/index.js index 62d05da..53a4918 100644 --- a/app/components/AddTokenForm/index.js +++ b/app/components/TokenForm/index.js @@ -1,6 +1,6 @@ /** * -* AddTokenForm +* TokenForm * */ @@ -22,7 +22,7 @@ const schema = { } }; -class AddTokenForm extends React.Component { // eslint-disable-line react/prefer-stateless-function +class TokenForm extends React.Component { // eslint-disable-line react/prefer-stateless-function constructor(props) { super(props); } @@ -47,4 +47,4 @@ class AddTokenForm extends React.Component { // eslint-disable-line react/prefer } } -export default AddTokenForm; +export default TokenForm; diff --git a/app/components/TokenForm/messages.js b/app/components/TokenForm/messages.js new file mode 100644 index 0000000..7e9a632 --- /dev/null +++ b/app/components/TokenForm/messages.js @@ -0,0 +1,13 @@ +/* + * TokenForm Messages + * + * This contains all the text for the TokenForm component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + header: { + id: 'app.components.TokenForm.header', + defaultMessage: 'This is the TokenForm component !', + }, +}); diff --git a/app/components/TokenForm/styles.css b/app/components/TokenForm/styles.css new file mode 100644 index 0000000..dd37465 --- /dev/null +++ b/app/components/TokenForm/styles.css @@ -0,0 +1,3 @@ +.tokenForm { /* stylelint-disable */ + +} diff --git a/app/components/TokenForm/tests/index.test.js b/app/components/TokenForm/tests/index.test.js new file mode 100644 index 0000000..d6d86b3 --- /dev/null +++ b/app/components/TokenForm/tests/index.test.js @@ -0,0 +1,11 @@ +// import TokenForm from '../index'; + +import expect from 'expect'; +// import { shallow } from 'enzyme'; +// import React from 'react'; + +// describe('', () => { +// it('Expect to have unit tests specified', () => { +// expect(true).toEqual(false); +// }); +// }); diff --git a/app/containers/BalanceContainer/index.js b/app/containers/BalanceContainer/index.js index ed1c21e..19b3acc 100644 --- a/app/containers/BalanceContainer/index.js +++ b/app/containers/BalanceContainer/index.js @@ -26,15 +26,15 @@ export class BalanceContainer extends React.Component { // eslint-disable-line r } componentDidMount() { - if (this.props.token == null) { + if (!this.props.token) { return; } this.setState({name: this.props.token.symbol}); - var data = $.param(this.props.token.balanceCallData()); - var serverUrl = "https://rpc.myetherwallet.com:8443/api.mew"; - var parentObj = this; + let data = $.param(this.props.token.balanceCallData()); + let serverUrl = "https://rpc.myetherwallet.com:8443/api.mew"; + let parentObj = this; $.ajax({ url: serverUrl, headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, diff --git a/app/containers/BalancesContainer/Token.js b/app/containers/BalancesContainer/Token.js deleted file mode 100644 index 97164c1..0000000 --- a/app/containers/BalancesContainer/Token.js +++ /dev/null @@ -1,101 +0,0 @@ - -import BigNumber from 'bignumber.js'; - -export class Token { - - constructor(contractAddress, userAddress, symbol, decimal) { - this.contractAddress = contractAddress; - this.userAddress = userAddress - this.symbol = symbol; - this.decimal = decimal; - this.balance = 0; - this.balanceHex = "0x70a08231"; - - this.unitMap = { - 'wei': '1', - 'kwei': '1000', - 'ada': '1000', - 'femtoether': '1000', - 'mwei': '1000000', - 'babbage': '1000000', - 'picoether': '1000000', - 'gwei': '1000000000', - 'shannon': '1000000000', - 'nanoether': '1000000000', - 'nano': '1000000000', - 'szabo': '1000000000000', - 'microether': '1000000000000', - 'micro': '1000000000000', - 'finney': '1000000000000000', - 'milliether': '1000000000000000', - 'milli': '1000000000000000', - 'ether': '1000000000000000000', - 'kether': '1000000000000000000000', - 'grand': '1000000000000000000000', - 'einstein': '1000000000000000000000', - 'mether': '1000000000000000000000000', - 'gether': '1000000000000000000000000000', - 'tether': '1000000000000000000000000000000' - }; - } - - prettyName() { - return this.symbol; - } - - getDataObj(to, func, arrVals) { - var val=""; - for(var i=0;i= width ? n : new Array(width - n.length + 1).join(z) + n; - } - - getNakedAddress() { - return this.userAddress.toLowerCase().replace('0x', ''); - } - - balanceCallData() { - if (this.symbol != "ETH") { - var data = this.getDataObj(this.contractAddress, - this.balanceHex, - [this.getNakedAddress()] - ); - - return { - ethCall: data, - isClassic: false - }; - } - - // ETH - return { - balance: this.userAddress, - isClassic: false - }; - } - - - // balance - weiBalance() { - // wei - var wei = new BigNumber(String(this.balance)).times(this.getValueOfUnit('wei')); - wei = wei.toString(10); - - var returnValue = new BigNumber(wei).div(this.getValueOfUnit('ether')); - return returnValue.toString(10); - } - - getValueOfUnit(unit) { - unit = unit ? unit.toLowerCase() : 'ether'; - var unitValue = this.unitMap[unit]; - if (unitValue === undefined) { - throw 0; - } - return new BigNumber(unitValue, 10); - } -} \ No newline at end of file diff --git a/app/containers/BalancesContainer/index.js b/app/containers/BalancesContainer/index.js index 2084706..425bc8a 100644 --- a/app/containers/BalancesContainer/index.js +++ b/app/containers/BalancesContainer/index.js @@ -15,7 +15,7 @@ import { Token } from '../../utils/Wallet/Token' import W from '../../utils/Wallet/Wallet' import { BalanceContainer } from '../BalanceContainer/index' import SkyLight from 'react-skylight'; -import AddTokenForm from 'components/AddTokenForm'; +import TokenForm from 'components/TokenForm'; export class BalancesContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function constructor(props) { @@ -48,7 +48,7 @@ export class BalancesContainer extends React.Component { // eslint-disable-line

- +
diff --git a/app/containers/Dashboard/index.js b/app/containers/Dashboard/index.js index b1045c8..f2afa26 100644 --- a/app/containers/Dashboard/index.js +++ b/app/containers/Dashboard/index.js @@ -15,7 +15,7 @@ import { BalancesContainer } from '../BalancesContainer/index'; export class Dashboard extends React.Component { // eslint-disable-line react/prefer-stateless-function render() { return ( -
+
//
diff --git a/app/utils/Wallet/Wallet.js b/app/utils/Wallet/Wallet.js index 6b52797..62b1300 100644 --- a/app/utils/Wallet/Wallet.js +++ b/app/utils/Wallet/Wallet.js @@ -91,5 +91,4 @@ export class Wallet { } const W = Wallet.walletFromDisk(); -Object.freeze(W); export default W; \ No newline at end of file From ac918eb29ae2d9ce967836f921221435cdd7b72e Mon Sep 17 00:00:00 2001 From: alon muroch Date: Sat, 15 Oct 2016 20:44:12 +0700 Subject: [PATCH 6/8] removed jquery --- app/containers/BalanceContainer/index.js | 55 +++++++++++++----------- app/utils/AjaxUtils.js | 33 ++++++++++++++ 2 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 app/utils/AjaxUtils.js diff --git a/app/containers/BalanceContainer/index.js b/app/containers/BalanceContainer/index.js index 19b3acc..0391a81 100644 --- a/app/containers/BalanceContainer/index.js +++ b/app/containers/BalanceContainer/index.js @@ -12,8 +12,8 @@ import { FormattedMessage } from 'react-intl'; import messages from './messages'; import styles from './styles.css'; import BalanceCell from 'components/BalanceCell'; -import $ from 'jquery'; import BigNumber from 'bignumber.js'; +import { AjaxUtils } from '../../utils/AjaxUtils' export class BalanceContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function constructor(props) { @@ -32,32 +32,37 @@ export class BalanceContainer extends React.Component { // eslint-disable-line r this.setState({name: this.props.token.symbol}); - let data = $.param(this.props.token.balanceCallData()); + let data = AjaxUtils.queryParams(this.props.token.balanceCallData()); + let data2 = $.param(this.props.token.balanceCallData()); let serverUrl = "https://rpc.myetherwallet.com:8443/api.mew"; let parentObj = this; - $.ajax({ - url: serverUrl, - headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, - type: 'POST', - data: data, - success: function(data) { - if (!data.error) { - if (parentObj.props.token.symbol == "ETH") { - parentObj.props.token.balance = data.data.balance; - var _balance = parentObj.props.token.weiBalance(); - this.setState({balance: _balance}); - } - else { - var decimal = parentObj.props.token.decimal; - var _balance = new BigNumber(data.data).div(new BigNumber(10).pow(decimal)); - this.setState({balance: _balance}); - } - } - }.bind(this), - error: function(xhr, status, err) { - console.error("ERRORRRR", status, err.toString()); - }.bind(this) - }); + + fetch(serverUrl, { + method: 'post', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: data + }) + .then((response) => response.json()) + .then((data) => { + console.log(data); + if (!data.error) { + if (parentObj.props.token.symbol == "ETH") { + parentObj.props.token.balance = data.data.balance; + var _balance = parentObj.props.token.weiBalance(); + this.setState({balance: _balance}); + } + else { + var decimal = parentObj.props.token.decimal; + var _balance = new BigNumber(data.data).div(new BigNumber(10).pow(decimal)); + this.setState({balance: _balance}); + } + } + }) + .catch((error) => { + console.error(error); + }); } render() { diff --git a/app/utils/AjaxUtils.js b/app/utils/AjaxUtils.js new file mode 100644 index 0000000..e4e9ee1 --- /dev/null +++ b/app/utils/AjaxUtils.js @@ -0,0 +1,33 @@ +export class AjaxUtils { + static queryParams(source) { + var array = []; + + for(var key in source) { + let data = source[key]; + if (Object.prototype.isPrototypeOf(data)) { + array.push(AjaxUtils.encodeSubObject(key, data)); + } + else { + array.push(encodeURIComponent(key) + "=" + encodeURIComponent(data)); + } + } + + return array.join("&"); + } + + static encodeSubObject(originalKey, object) { + var array = []; + + for(var key in object) { + let data = object[key]; + if (Object.prototype.isPrototypeOf(data)) { + array.push("%5B" + key + "%5D" + AjaxUtils.encodeSubObject(data)); + } + else { + array.push(originalKey + "%5B" + key + "%5D=" + encodeURIComponent(data)); + } + } + + return array.join("&"); + } +} \ No newline at end of file From d80ec6ec5ae8f71bee0d3e7578e3aef4c4dd66f2 Mon Sep 17 00:00:00 2001 From: alon muroch Date: Tue, 18 Oct 2016 07:22:38 +0300 Subject: [PATCH 7/8] small fixes --- app/containers/BalanceContainer/index.js | 1 - app/containers/BalancesContainer/index.js | 6 +++--- app/utils/AjaxUtils.js | 8 ++++---- app/utils/Wallet/Wallet.js | 12 ++++++------ 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/containers/BalanceContainer/index.js b/app/containers/BalanceContainer/index.js index 0391a81..6e4537b 100644 --- a/app/containers/BalanceContainer/index.js +++ b/app/containers/BalanceContainer/index.js @@ -33,7 +33,6 @@ export class BalanceContainer extends React.Component { // eslint-disable-line r this.setState({name: this.props.token.symbol}); let data = AjaxUtils.queryParams(this.props.token.balanceCallData()); - let data2 = $.param(this.props.token.balanceCallData()); let serverUrl = "https://rpc.myetherwallet.com:8443/api.mew"; let parentObj = this; diff --git a/app/containers/BalancesContainer/index.js b/app/containers/BalancesContainer/index.js index 425bc8a..d677892 100644 --- a/app/containers/BalancesContainer/index.js +++ b/app/containers/BalancesContainer/index.js @@ -12,7 +12,7 @@ import { FormattedMessage } from 'react-intl'; import messages from './messages'; import styles from './styles.css'; import { Token } from '../../utils/Wallet/Token' -import W from '../../utils/Wallet/Wallet' +import Wallet from '../../utils/Wallet/Wallet' import { BalanceContainer } from '../BalanceContainer/index' import SkyLight from 'react-skylight'; import TokenForm from 'components/TokenForm'; @@ -27,7 +27,7 @@ export class BalancesContainer extends React.Component { // eslint-disable-line } componentDidMount() { - this.setState({tokens: W.getTokens()}) + this.setState({tokens: Wallet.getTokens()}) } render() { @@ -58,7 +58,7 @@ export class BalancesContainer extends React.Component { // eslint-disable-line onSubmitedToken(symbol, address, decimal) { W.addToken(symbol, address, parseInt(decimal)); - this.setState({tokens: W.getTokens()}) + this.setState({tokens: Wallet.getTokens()}) } } diff --git a/app/utils/AjaxUtils.js b/app/utils/AjaxUtils.js index e4e9ee1..1cbb4b1 100644 --- a/app/utils/AjaxUtils.js +++ b/app/utils/AjaxUtils.js @@ -1,8 +1,8 @@ export class AjaxUtils { static queryParams(source) { - var array = []; + let array = []; - for(var key in source) { + for(let key in source) { let data = source[key]; if (Object.prototype.isPrototypeOf(data)) { array.push(AjaxUtils.encodeSubObject(key, data)); @@ -16,9 +16,9 @@ export class AjaxUtils { } static encodeSubObject(originalKey, object) { - var array = []; + let array = []; - for(var key in object) { + for(let key in object) { let data = object[key]; if (Object.prototype.isPrototypeOf(data)) { array.push("%5B" + key + "%5D" + AjaxUtils.encodeSubObject(data)); diff --git a/app/utils/Wallet/Wallet.js b/app/utils/Wallet/Wallet.js index 62b1300..ee9fb0f 100644 --- a/app/utils/Wallet/Wallet.js +++ b/app/utils/Wallet/Wallet.js @@ -1,8 +1,8 @@ import { Token } from './Token' -export class Wallet { +export class ETHWallet { static walletFromDisk() { - return new Wallet(); + return new ETHWallet(); } constructor(props) { @@ -11,7 +11,7 @@ export class Wallet { // tokens getTokens() { - var allTokens = Wallet.allTokens(); + var allTokens = ETHWallet.allTokens(); var _tokens = []; for (var i = 0; i < allTokens.length; i++) { let address = allTokens[i].address; @@ -48,7 +48,7 @@ export class Wallet { } static allTokens() { - return Wallet.savedTokens().concat(Wallet.hardcodedTokes()); + return ETHWallet.savedTokens().concat(ETHWallet.hardcodedTokes()); } @@ -90,5 +90,5 @@ export class Wallet { } } -const W = Wallet.walletFromDisk(); -export default W; \ No newline at end of file +const Wallet = ETHWallet.walletFromDisk(); +export default Wallet; \ No newline at end of file From 244dddcc0cbe603c1ba00f9d88a8a1284e24026e Mon Sep 17 00:00:00 2001 From: alon muroch Date: Tue, 18 Oct 2016 07:52:50 +0300 Subject: [PATCH 8/8] added transactions page --- app/components/SideNav/index.js | 5 +++ app/components/SideNav/messages.js | 4 ++ app/containers/Transactions/actions.js | 15 +++++++ app/containers/Transactions/constants.js | 7 ++++ app/containers/Transactions/index.js | 39 +++++++++++++++++++ app/containers/Transactions/messages.js | 13 +++++++ app/containers/Transactions/reducer.js | 23 +++++++++++ app/containers/Transactions/sagas.js | 11 ++++++ app/containers/Transactions/selectors.js | 25 ++++++++++++ app/containers/Transactions/styles.css | 3 ++ .../Transactions/tests/actions.test.js | 18 +++++++++ .../Transactions/tests/index.test.js | 11 ++++++ .../Transactions/tests/reducer.test.js | 9 +++++ .../Transactions/tests/sagas.test.js | 15 +++++++ .../Transactions/tests/selectors.test.js | 11 ++++++ app/routes.js | 20 ++++++++++ 16 files changed, 229 insertions(+) create mode 100644 app/containers/Transactions/actions.js create mode 100644 app/containers/Transactions/constants.js create mode 100644 app/containers/Transactions/index.js create mode 100644 app/containers/Transactions/messages.js create mode 100644 app/containers/Transactions/reducer.js create mode 100644 app/containers/Transactions/sagas.js create mode 100644 app/containers/Transactions/selectors.js create mode 100644 app/containers/Transactions/styles.css create mode 100644 app/containers/Transactions/tests/actions.test.js create mode 100644 app/containers/Transactions/tests/index.test.js create mode 100644 app/containers/Transactions/tests/reducer.test.js create mode 100644 app/containers/Transactions/tests/sagas.test.js create mode 100644 app/containers/Transactions/tests/selectors.test.js diff --git a/app/components/SideNav/index.js b/app/components/SideNav/index.js index f3f0e69..68e4c15 100644 --- a/app/components/SideNav/index.js +++ b/app/components/SideNav/index.js @@ -22,6 +22,11 @@ const buttons = [ id: 'investments', route: '/investments', text: messages.investments + }, + { + id: 'transactions', + route: '/transactions', + text: messages.transactions } ]; diff --git a/app/components/SideNav/messages.js b/app/components/SideNav/messages.js index e9f3e0e..8b960c7 100644 --- a/app/components/SideNav/messages.js +++ b/app/components/SideNav/messages.js @@ -13,5 +13,9 @@ export default defineMessages({ investments: { id: 'app.components.SideNav.investments', defaultMessage: 'Investments', + }, + transactions: { + id: 'app.components.SideNav.transactions', + defaultMessage: 'Transactions', } }); diff --git a/app/containers/Transactions/actions.js b/app/containers/Transactions/actions.js new file mode 100644 index 0000000..2f0edac --- /dev/null +++ b/app/containers/Transactions/actions.js @@ -0,0 +1,15 @@ +/* + * + * Transactions actions + * + */ + +import { + DEFAULT_ACTION, +} from './constants'; + +export function defaultAction() { + return { + type: DEFAULT_ACTION, + }; +} diff --git a/app/containers/Transactions/constants.js b/app/containers/Transactions/constants.js new file mode 100644 index 0000000..7bf89ef --- /dev/null +++ b/app/containers/Transactions/constants.js @@ -0,0 +1,7 @@ +/* + * + * Transactions constants + * + */ + +export const DEFAULT_ACTION = 'app/Transactions/DEFAULT_ACTION'; diff --git a/app/containers/Transactions/index.js b/app/containers/Transactions/index.js new file mode 100644 index 0000000..f35f08f --- /dev/null +++ b/app/containers/Transactions/index.js @@ -0,0 +1,39 @@ +/* + * + * Transactions + * + */ + +import React from 'react'; +import { connect } from 'react-redux'; +import Helmet from 'react-helmet'; +import selectTransactions from './selectors'; +import { FormattedMessage } from 'react-intl'; +import messages from './messages'; +import styles from './styles.css'; + +export class Transactions extends React.Component { // eslint-disable-line react/prefer-stateless-function + render() { + return ( +
+ + +
+ ); + } +} + +const mapStateToProps = selectTransactions(); + +function mapDispatchToProps(dispatch) { + return { + dispatch, + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Transactions); diff --git a/app/containers/Transactions/messages.js b/app/containers/Transactions/messages.js new file mode 100644 index 0000000..4d39413 --- /dev/null +++ b/app/containers/Transactions/messages.js @@ -0,0 +1,13 @@ +/* + * Transactions Messages + * + * This contains all the text for the Transactions component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + header: { + id: 'app.containers.Transactions.header', + defaultMessage: 'This is Transactions container !', + }, +}); diff --git a/app/containers/Transactions/reducer.js b/app/containers/Transactions/reducer.js new file mode 100644 index 0000000..373fe8f --- /dev/null +++ b/app/containers/Transactions/reducer.js @@ -0,0 +1,23 @@ +/* + * + * Transactions reducer + * + */ + +import { fromJS } from 'immutable'; +import { + DEFAULT_ACTION, +} from './constants'; + +const initialState = fromJS({}); + +function transactionsReducer(state = initialState, action) { + switch (action.type) { + case DEFAULT_ACTION: + return state; + default: + return state; + } +} + +export default transactionsReducer; diff --git a/app/containers/Transactions/sagas.js b/app/containers/Transactions/sagas.js new file mode 100644 index 0000000..4401e77 --- /dev/null +++ b/app/containers/Transactions/sagas.js @@ -0,0 +1,11 @@ +// import { take, call, put, select } from 'redux-saga/effects'; + +// Individual exports for testing +export function* defaultSaga() { + return; +} + +// All sagas to be loaded +export default [ + defaultSaga, +]; diff --git a/app/containers/Transactions/selectors.js b/app/containers/Transactions/selectors.js new file mode 100644 index 0000000..b890268 --- /dev/null +++ b/app/containers/Transactions/selectors.js @@ -0,0 +1,25 @@ +import { createSelector } from 'reselect'; + +/** + * Direct selector to the transactions state domain + */ +const selectTransactionsDomain = () => (state) => state.get('transactions'); + +/** + * Other specific selectors + */ + + +/** + * Default selector used by Transactions + */ + +const selectTransactions = () => createSelector( + selectTransactionsDomain(), + (substate) => substate.toJS() +); + +export default selectTransactions; +export { + selectTransactionsDomain, +}; diff --git a/app/containers/Transactions/styles.css b/app/containers/Transactions/styles.css new file mode 100644 index 0000000..a0b410d --- /dev/null +++ b/app/containers/Transactions/styles.css @@ -0,0 +1,3 @@ +.transactions { /* stylelint-disable */ + +} diff --git a/app/containers/Transactions/tests/actions.test.js b/app/containers/Transactions/tests/actions.test.js new file mode 100644 index 0000000..42f4291 --- /dev/null +++ b/app/containers/Transactions/tests/actions.test.js @@ -0,0 +1,18 @@ +import expect from 'expect'; +import { + defaultAction, +} from '../actions'; +import { + DEFAULT_ACTION, +} from '../constants'; + +describe('Transactions actions', () => { + describe('Default Action', () => { + it('has a type of DEFAULT_ACTION', () => { + const expected = { + type: DEFAULT_ACTION, + }; + expect(defaultAction()).toEqual(expected); + }); + }); +}); diff --git a/app/containers/Transactions/tests/index.test.js b/app/containers/Transactions/tests/index.test.js new file mode 100644 index 0000000..cb3fc67 --- /dev/null +++ b/app/containers/Transactions/tests/index.test.js @@ -0,0 +1,11 @@ +// import Transactions from '../index'; + +import expect from 'expect'; +// import { shallow } from 'enzyme'; +// import React from 'react'; + +describe('', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/Transactions/tests/reducer.test.js b/app/containers/Transactions/tests/reducer.test.js new file mode 100644 index 0000000..42a4ad2 --- /dev/null +++ b/app/containers/Transactions/tests/reducer.test.js @@ -0,0 +1,9 @@ +import expect from 'expect'; +import transactionsReducer from '../reducer'; +import { fromJS } from 'immutable'; + +describe('transactionsReducer', () => { + it('returns the initial state', () => { + expect(transactionsReducer(undefined, {})).toEqual(fromJS({})); + }); +}); diff --git a/app/containers/Transactions/tests/sagas.test.js b/app/containers/Transactions/tests/sagas.test.js new file mode 100644 index 0000000..ec8febe --- /dev/null +++ b/app/containers/Transactions/tests/sagas.test.js @@ -0,0 +1,15 @@ +/** + * Test sagas + */ + +import expect from 'expect'; +// import { take, call, put, select } from 'redux-saga/effects'; +// import { defaultSaga } from '../sagas'; + +// const generator = defaultSaga(); + +describe('defaultSaga Saga', () => { + it('Expect to have unit tests specified', () => { + expect(true).toEqual(false); + }); +}); diff --git a/app/containers/Transactions/tests/selectors.test.js b/app/containers/Transactions/tests/selectors.test.js new file mode 100644 index 0000000..1a8c972 --- /dev/null +++ b/app/containers/Transactions/tests/selectors.test.js @@ -0,0 +1,11 @@ +// import { selectTransactionsDomain } from '../selectors'; +// import { fromJS } from 'immutable'; +import expect from 'expect'; + +// const selector = selectTransactionsDomain(); + +describe('selectTransactionsDomain', () => { + it('Expect to have unit tests specified', () => { + expect('Test case').toEqual(false); + }); +}); diff --git a/app/routes.js b/app/routes.js index f06e14e..25cf36d 100644 --- a/app/routes.js +++ b/app/routes.js @@ -61,6 +61,26 @@ export default function createRoutes(store) { .then(loadModule(cb)) .catch(errorLoading); }, + }, { + path: '/transactions', + name: 'transactions', + getComponent(location, cb) { + const importModules = Promise.all([ + System.import('containers/Transactions/reducer'), + System.import('containers/Transactions/sagas'), + System.import('containers/Transactions'), + ]); + + const renderRoute = loadModule(cb); + + importModules.then(([reducer, sagas, component]) => { + injectReducer('transactions', reducer.default); + injectSagas(sagas.default); + renderRoute(component); + }); + + importModules.catch(errorLoading); + }, }, { path: '*', name: 'notfound',