diff --git a/package.json b/package.json old mode 100755 new mode 100644 index d4af55c..8e0d808 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "livepeer" ], "dependencies": { + "axios": "^0.18.0", "bootstrap": "^4.1.3", "react": "^16.6.3", "react-dom": "^16.6.3", diff --git a/src/App.js b/src/App.js index 1a719b8..4511022 100755 --- a/src/App.js +++ b/src/App.js @@ -7,8 +7,18 @@ import withWeb3Provider from './Components/Common/Hoc/Web3Provider/Web3Provider' import Spinner from './Components/Common/UI/Spinner/Spinner' export class App extends Component { + shouldComponentUpdate(nextProps, nextState, nextContext) { + console.log('[App.js] shouldComponentUpdate') + let shouldUpdate = + this.props.render !== nextProps.render || + this.props.userData.authenticated !== nextProps.userData.authenticated || + this.props.userData.address !== nextProps.userData.address || + this.props.userData.currentNetwork !== nextProps.userData.currentNetwork + return shouldUpdate + } + render() { - console.log('[App.js] props', this.props) + console.log('[App.js] render, web3 address: ', this.props.userData.address) let content = if (this.props.render) { content = ( @@ -23,6 +33,8 @@ export class App extends Component { authenticated={this.props.userData.authenticated} exact path="/account" + web3={this.props.web3} + userData={this.props.userData} component={AccountSummaryComponent} /> diff --git a/src/Components/AccountSummary/AccountSummary.js b/src/Components/AccountSummary/AccountSummary.js new file mode 100755 index 0000000..44304a9 --- /dev/null +++ b/src/Components/AccountSummary/AccountSummary.js @@ -0,0 +1,288 @@ +import React, { Component } from 'react' +import axios from 'axios' +import Spinner from '../Common/UI/Spinner/Spinner' +import * as displayTexts from './AccountSummaryTexts' +import UserSubscribed from './UserSubscribed/UserSubscribed' +import { toast, ToastContainer } from 'react-toastify' + +export class AccountSummaryComponent extends Component { + state = { + userData: { + address: null, + isSubscribed: false, + activated: null, + id: null, + email: 'test@altoros.com', + frequency: 'weekly', + activatedCode: null, + createdAt: null + }, + summary: { + bondedAmount: '', + fees: '', + lastClaimRound: '', + startRound: '', + status: 'Bonded', + withdrawRound: '', + stake: '' + }, + render: false, + displayMsg: displayTexts.LOADING_USER_DATA, + toastId: 1, + error: false + } + + initState = async () => { + this.setState({ + userData: { + ...this.state.userData, + address: this.props.userData.address, + ethBalance: this.props.userData.ethBalance + } + }) + } + + componentDidMount = async () => { + console.log('[AccountSummaryComponent.js] componentDidMount') + let userDataPromise, summaryPromise + await this.initState() + try { + userDataPromise = axios.get('/address/' + this.state.userData.address) + summaryPromise = axios.get('/summary/' + this.state.userData.address) + let resultValues = await Promise.all([userDataPromise, summaryPromise]) + let userData = resultValues[0] + let summaryData = resultValues[1] + console.log('Promise all finished') + this.setState( + { + userData: { + ...this.state.userData, + isSubscribed: true, + activated: userData.data.activated, + id: userData.data._id, + activatedCode: userData.data.activatedCode, + createdAt: userData.data.createdAt, + email: userData.data.email, + frequency: userData.data.frequency + }, + summary: { + bondedAmount: summaryData.data.summary.bondedAmount, + fees: summaryData.data.summary.fees, + lastClaimRound: summaryData.data.summary.lastClaimRound, + startRound: summaryData.data.summary.startRound, + status: summaryData.data.summary.status, + withdrawRound: summaryData.data.summary.withdrawRound, + stake: summaryData.data.summary.totalStake + }, + render: true, + displayMsg: displayTexts.WELCOME_AGAIN + this.state.userData.email, + error: false, + lpBalance: summaryData.data.balance + }, + () => console.log('setting state finished ', this.state) + ) + } catch (error) { + /** Subscription not found **/ + console.log('subscription not found') + if (error.response && error.response.status === 404) { + console.log('Subscription not found') + this.setState({ + userData: { + ...this.state.userData, + isSubscribed: false + }, + render: true, + displayMsg: displayTexts.WELCOME_NOT_SUBSCRIBED, + error: true + }) + await this.fetchAccountSummaryData() + } else { + console.log('[AccountSummary.js] exception on getRequest', error) + this.setState( + { + render: true, + displayMsg: displayTexts.FAIL_NO_REASON, + error: true + }, + () => this.sendToast() + ) + } + } + } + + sendToast = toastTime => { + console.log('Sending toast') + let time = 6000 + if (toastTime) { + time = toastTime + } + let displayMsg = this.state.displayMsg + + if (!toast.isActive(this.state.toastId) && this.state.render) { + if (this.state.error) { + toast.error(displayMsg, { + position: toast.POSITION.TOP_RIGHT, + progressClassName: 'Toast-progress-bar', + autoClose: time, + toastId: this.state.toastId + }) + } else { + toast.success(displayMsg, { + position: toast.POSITION.TOP_RIGHT, + progressClassName: 'Toast-progress-bar', + autoClose: time, + toastId: this.state.toastId + }) + } + } + } + + fetchAccountSummaryData = async () => { + try { + let summaryData = await axios.get('/summary/' + this.state.userData.address) + console.log('summary data ', summaryData) + this.setState({ + summary: { + bondedAmount: summaryData.data.summary.bondedAmount, + fees: summaryData.data.summary.fees, + lastClaimRound: summaryData.data.summary.lastClaimRound, + startRound: summaryData.data.summary.startRound, + status: summaryData.data.summary.status, + withdrawRound: summaryData.data.summary.withdrawRound, + stake: summaryData.data.summary.totalStake + }, + lpBalance: summaryData.data.balance + }) + } catch (exception) { + console.log('Exception fetching account summary ', exception) + } + } + + onSubscribeBtnHandler = async () => { + console.log('[AccountSummary.js] subscribe btnHandler') + let response + this.setState({ + render: false, + displayMsg: displayTexts.LOADING_SUBSCRIPTION + }) + const data = { + email: this.state.userData.email, + address: this.state.userData.address, + frequency: this.state.userData.frequency + } + try { + console.log('Creating new subscriber with data: ', data) + response = await axios.post('', data) + this.setState({ + userData: { + ...this.state.userData, + activated: response.data.activated, + id: response.data._id, + activatedCode: response.data.activated, + createdAt: response.data.createdAt, + isSubscribed: true + }, + render: true, + error: false, + displayMsg: displayTexts.WELCOME_NEW_SUBSCRIBER + this.state.userData.email + }) + } catch (exception) { + console.log('[AccountSummary.js] exception on postSubscription', exception) + /** TODO -- PARSE WHEN EMAIL ALREADY EXISTS **/ + this.setState( + { + render: true, + displayMsg: displayTexts.FAIL_NO_REASON, + error: true + }, + () => this.sendToast() + ) + } + } + + onUnSubscribeBtnHandler = async () => { + console.log('[AccountSummary.js] unsubscribe btnHandler') + this.setState({ + render: false, + displayMsg: displayTexts.LOADING_UNSUBSCRIPTION + }) + const data = { + username: 'test' + } + try { + await axios.delete('/' + this.state.userData.id, data) + console.log('User unsubscribed') + this.setState( + { + render: true, + displayMsg: displayTexts.UNSUBSCRIPTION_SUCCESSFUL, + userData: { + ...this.state.userData, + isSubscribed: false, + activated: null, + id: null, + activatedCode: null, + createdAt: null, + error: false + } + }, + () => this.sendToast() + ) + } catch (exception) { + console.log('[AccountSummary.js] exception on deleteSubscription') + if (exception.response.status === 404) { + /** User with that id not found **/ + this.setState( + { + render: true, + displayMsg: displayTexts.WELCOME_NOT_SUBSCRIBED, + error: true + }, + () => this.sendToast() + ) + } else { + this.setState( + { + render: true, + displayMsg: displayTexts.FAIL_NO_REASON, + error: true + }, + () => this.sendToast() + ) + } + } + } + + onSubscriptionChangeHandler = () => { + console.log('[AccountSummary.js] onSubscriptionChangeHandler') + } + + render() { + let content = ( + <> +

{this.state.displayMsg}

+ + + ) + if (this.state.render) { + content = ( + <> + + + ) + } + return ( +
+ {content} + +
+ ) + } +} diff --git a/src/Components/AccountSummary/AccountSummary.test.js b/src/Components/AccountSummary/AccountSummary.test.js new file mode 100644 index 0000000..c8b682e --- /dev/null +++ b/src/Components/AccountSummary/AccountSummary.test.js @@ -0,0 +1,80 @@ +import React from 'react' +import { configure, mount, shallow } from 'enzyme' +import Adapter from 'enzyme-adapter-react-16' +import * as jest from 'jest' +import axios from 'axios' +import { AccountSummaryComponent } from './AccountSummary' +import Spinner from '../Common/UI/Spinner/Spinner' +import * as displayTexts from './AccountSummaryTexts' +import UserSubscribed from './AccountSummary' + +configure({ adapter: new Adapter() }) + +const props = { + summary: { + bondedAmount: 0, + fees: 0, + status: 'Bounded', + lastClaimRound: 0, + startRound: 0, + withdrawRound: 0 + }, + userData: { + address: '0x4d3F9184Fc32A43BAD2641b1536B52a076FBBDcE', + email: 'test@altoros.com', + frequency: 'weekly', + ethBalance: 'ethBalance', + activated: 1 + }, + render: true, + onSubscriptionChangeHandler: () => {}, + onUnSubscribeBtnHandler: () => {}, + web3: [] +} + +const response = { + data: { + activated: 0, + _id: 0, + email: 'test@altoros.com', + address: '0x9D45EECe52F0b8Ae0238dCb0a42da9928f4b9c4f', + frequency: 'weekly', + activatedCode: '741208730162732000000', + createdAt: '2019-01-02T14:51:17.800Z' + } +} + +jest.mock('axios') +describe('test', () => { + it('test', () => { + expect(true) + }) +}) +/* +describe('Renders AccountSummary data', () => { + it('Renders Loading spinner when fetching data', () => { + let wrapper = shallow() + wrapper.update() + expect(wrapper.contains()).toBe(true) + }) + it('Requests user to be on bounded status if the user does not have this status', () => { + const message = displayTexts.WELCOME_AGAIN + axios.get.mockResolvedValue(response) + let wrapper = mount() + /!** TODO -- After conditional rendering bug in enzyme is solve, change the way we do this **!/ + wrapper = wrapper.setProps({}) + console.log('testing debug wrapper', wrapper.debug()) + // expect(wrapper.contains(message)).toEqual(true) + expect( + wrapper.contains( + + ) + ).toBe(true) + }) +}) +*/ diff --git a/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.css b/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.css new file mode 100644 index 0000000..390ff50 --- /dev/null +++ b/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.css @@ -0,0 +1,3 @@ +.accountSummaryDataTable{ + +} \ No newline at end of file diff --git a/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.js b/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.js new file mode 100644 index 0000000..e0f6456 --- /dev/null +++ b/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.js @@ -0,0 +1,46 @@ +import React from 'react' +import './AccountSummaryData.css' +const AccountSummaryData = props => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Summary Information
BondedAmount{props.summary.bondedAmount}
Fees{props.summary.fees}
Status{props.summary.status}
LastClaimRound{props.summary.lastClaimRound}
StartRound{props.summary.startRound}
WithdrawRound{props.summary.withdrawRound}
Stake{props.summary.totalStake}
+ + ) +} +export default AccountSummaryData diff --git a/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.test.js b/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.test.js new file mode 100644 index 0000000..52f456f --- /dev/null +++ b/src/Components/AccountSummary/AccountSummaryData/AccountSummaryData.test.js @@ -0,0 +1,50 @@ +import React from 'react' +import { configure, shallow } from 'enzyme' +import Adapter from 'enzyme-adapter-react-16' +import AccountSummaryData from './AccountSummaryData' + +configure({ adapter: new Adapter() }) + +const props = { + summary: { + bondedAmount: 0, + fees: 0, + status: 'Bounded', + lastClaimRound: 0, + startRound: 0, + withdrawRound: 0 + } +} + +describe('Renders account summary data', () => { + it('Shows bondedAmount', () => { + const message = 'BondedAmount' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows Fees', () => { + const message = 'Fees' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows Status', () => { + const message = 'Status' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows LastClaimRound', () => { + const message = 'LastClaimRound' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows StartRound', () => { + const message = 'StartRound' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows WithdrawRound', () => { + const message = 'WithdrawRound' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) +}) diff --git a/src/Components/AccountSummary/AccountSummaryTexts.js b/src/Components/AccountSummary/AccountSummaryTexts.js new file mode 100644 index 0000000..068bd19 --- /dev/null +++ b/src/Components/AccountSummary/AccountSummaryTexts.js @@ -0,0 +1,11 @@ +export const LOADING_USER_DATA = 'Loading user data' +export const WELCOME_AGAIN = 'Welcome Again! ' +export const WELCOME_NEW_SUBSCRIBER = 'Welcome to Livepeer! ' +export const WELCOME_NOT_SUBSCRIBED = 'Welcome to Livepeer!' +export const FAIL_NO_REASON = 'Something went wrong!, please try again later!' +export const LOADING_UNSUBSCRIPTION = 'Please wait while we process your unsubscription' +export const LOADING_SUBSCRIPTION = 'Please wait while we process your subscription' +export const LOADING_SUBSCRIPTION_DATA = + 'Please wait while we get the information of your subscription' +export const UNSUBSCRIPTION_SUCCESSFUL = 'You were successful unsubscribed!' +export const BOUNDED_STATUS_NEEDED = 'In order to subscribe you need to be on BOUNDED status' diff --git a/src/Components/AccountSummary/UserSubscribed/UserSubscribed.css b/src/Components/AccountSummary/UserSubscribed/UserSubscribed.css new file mode 100644 index 0000000..0b08374 --- /dev/null +++ b/src/Components/AccountSummary/UserSubscribed/UserSubscribed.css @@ -0,0 +1,7 @@ +.subscriptionBtn { + +} +.subscriberInfoTable{ + margin-bottom: 30px; + +} diff --git a/src/Components/AccountSummary/UserSubscribed/UserSubscribed.js b/src/Components/AccountSummary/UserSubscribed/UserSubscribed.js new file mode 100644 index 0000000..520134d --- /dev/null +++ b/src/Components/AccountSummary/UserSubscribed/UserSubscribed.js @@ -0,0 +1,65 @@ +import React from 'react' +import * as displayTexts from '../AccountSummaryTexts' +import './UserSubscribed.css' +import Button from '../../Common/UI/Button/Button' +import AccountSummaryData from '../AccountSummaryData/AccountSummaryData' + +const UserSubscribed = props => { + let disabledBtn = props.summary.status !== 'Bonded' + let subscriptionBtn + if (props.userData.isSubscribed) { + subscriptionBtn = ( + <> + + + + ) + } else { + subscriptionBtn = ( + + ) + } + + return ( + <> +

{displayTexts.WELCOME_AGAIN}

+ + + + + + + + + + + + +
Account Summary
+ + + + + + + + + + + + + + + +
Address{props.userData.address}
ETH Balance{props.userData.ethBalance}
LivePeer Balance{props.lpBalance}
+
+ +
+ +
{subscriptionBtn}
+ + ) +} +export default UserSubscribed diff --git a/src/Components/AccountSummary/UserSubscribed/UserSubscribed.test.js b/src/Components/AccountSummary/UserSubscribed/UserSubscribed.test.js new file mode 100644 index 0000000..cdfd717 --- /dev/null +++ b/src/Components/AccountSummary/UserSubscribed/UserSubscribed.test.js @@ -0,0 +1,108 @@ +import React from 'react' +import { configure, shallow } from 'enzyme' +import Adapter from 'enzyme-adapter-react-16' +import UserSubscribed from './UserSubscribed' +import Spinner from '../../Common/UI/Spinner/Spinner' +import * as jest from 'jest' +import axios from 'axios' + +configure({ adapter: new Adapter() }) + +const props = { + summary: { + bondedAmount: 0, + fees: 0, + status: 'Bounded', + lastClaimRound: 0, + startRound: 0, + withdrawRound: 0 + }, + userData: { + address: '0x4d3F9184Fc32A43BAD2641b1536B52a076FBBDcE', + email: 'test@altoros.com', + frequency: 'weekly', + ethBalance: 'ethBalance', + activated: 1 + }, + render: true, + onSubscriptionChangeHandler: () => {}, + onUnSubscribeBtnHandler: () => {} +} + +const response = { + data: { + bondedAmount: 0, + fees: 0, + lastClaimRound: 0, + startRound: 0, + status: 0, + withdrawRound: 0, + totalStake: 0 + } +} + +jest.mock('axios') +describe('test', () => { + it('test', () => { + expect(true) + }) +}) +/** TODO -- Enable again when the enzyme bug of conditional rendering is solved **/ +describe('Renders userSubscribed data', () => { + /* it('Shows Welcome Message', () => { + const message = displayTexts.WELCOME_AGAIN + axios.get.mockResolvedValue(response) + let wrapper = shallow() + wrapper = wrapper.update() + console.log("testing debug wrapper" , wrapper.debug()) + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows address', () => { + const message = 'Address' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows Email', () => { + const message = 'Email' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows Activated status', () => { + const message = 'Activated' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows created at', () => { + const message = 'Created at' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows Subscription frequency', () => { + const message = 'Subscription frequency' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Shows ETH Balance', () => { + const message = 'ETH Balance' + let wrapper = shallow() + expect(wrapper.contains(message)).toEqual(true) + }) + it('Renders account summary data child component', () => { + let wrapper = shallow() + expect(wrapper.contains()).toBe(true) + }) + it('Renders change subscription button', () => { + let wrapper = shallow() + expect( + wrapper.contains( + + ) + ).toBe(true) + }) + it('Renders delete subscription button', () => { + let wrapper = shallow() + expect( + wrapper.contains() + ).toBe(true) + })*/ +}) diff --git a/src/Components/AccountSummary/accountSummary.js b/src/Components/AccountSummary/accountSummary.js deleted file mode 100755 index 5027eef..0000000 --- a/src/Components/AccountSummary/accountSummary.js +++ /dev/null @@ -1,15 +0,0 @@ -import React, { Component } from 'react' - -export class AccountSummaryComponent extends Component { - render() { - const web3 = this.props - console.log('[AccountSummaryComponent]', web3.eth) - - return ( -
-

Account Summary Component

- -
- ) - } -} diff --git a/src/Components/Common/Hoc/PrivateRoute/PrivateRoute.js b/src/Components/Common/Hoc/PrivateRoute/PrivateRoute.js index 0798d95..1043fa4 100755 --- a/src/Components/Common/Hoc/PrivateRoute/PrivateRoute.js +++ b/src/Components/Common/Hoc/PrivateRoute/PrivateRoute.js @@ -4,7 +4,7 @@ import { Route, Redirect } from 'react-router-dom' const PrivateRoute = ({ component: Component, authenticated, ...rest }) => ( (authenticated ? : )} + render={props => (authenticated ? : )} /> ) export default PrivateRoute diff --git a/src/Components/Common/Hoc/Web3Provider/Web3Provider.js b/src/Components/Common/Hoc/Web3Provider/Web3Provider.js index 29919d2..7d5b888 100755 --- a/src/Components/Common/Hoc/Web3Provider/Web3Provider.js +++ b/src/Components/Common/Hoc/Web3Provider/Web3Provider.js @@ -16,79 +16,15 @@ const withWeb3Provider = WrappedComponent => { } loadWeb3 = async () => { - let web3Instance - let userAddress - let userNetwork console.log('[Web3Provider.js] loadWeb3') /** We check if metamask is installed, either the new version or the legacy one **/ if (window.ethereum) { - console.log('[Web3Provider.js] getting web3 new instance') - web3Instance = new Web3(window.ethereum) - try { - /** Request access to the user **/ - console.log('[Web3Provider.js] requesting user permissions') - await window.ethereum.enable() - Promise.all([web3Instance.eth.getAccounts(), web3Instance.eth.net.getId()]).then( - results => { - userAddress = results[0] - userNetwork = results[0] - console.log('[Web3Provider.js] user with web3 ethereum authenticated') - /** The user accepted the app, now it's authenticated **/ - this.setState({ - web3: web3Instance, - userData: { - authenticated: true, - address: userAddress, - currentNetwork: userNetwork - }, - render: true - }) - /** We subscribe to the event that detects if the user has changed the account **/ - web3Instance.currentProvider.publicConfigStore.on( - 'update', - this.onUserAccountChangeHandler - ) - } - ) - } catch (error) { - /** The user denied the app, it's not authenticated **/ - console.log('[Web3Provider.js] user denied access') - this.setState({ - web3: web3Instance, - userData: { - authenticated: false, - reason: failReasons.NO_PERMISSIONS - }, - render: true - }) - console.log('[Web3Provider.js] user with ethereum denied the access') - } + await this.loadWeb3LastVersion() } else if (window.web3) { - console.log('[Web3Provider.js] getting web3 legacy instance') - web3Instance = new Web3(window.web3.currentProvider) - Promise.all([web3Instance.eth.getAccounts(), web3Instance.eth.net.getId()]).then( - results => { - userAddress = results[0] - userNetwork = results[0] - this.setState({ - web3: web3Instance, - userData: { - authenticated: true, - address: userAddress, - currentNetwork: userNetwork - }, - render: true - }) - console.log('[Web3Provider.js] user with web3 legacy authenticated') - /** We subscribe to the event that detects if the user has changed the account **/ - web3Instance.currentProvider.publicConfigStore.on( - 'update', - this.onUserAccountChangeHandler - ) - } - ) + await this.loadWeb3Legacy() } else { /** The user does not have web3 **/ + console.log('[Web3Provider.js] user does not have web3') this.setState({ render: true, userData: { @@ -96,18 +32,84 @@ const withWeb3Provider = WrappedComponent => { reason: failReasons.NO_WEB3 } }) - console.log('[Web3Provider.js] user does not have web3') } } - onUserAccountChangeHandler = userAuthData => { - //console.log('[Web3Provider.js] onUserAccountChangeHandler with data', userAuthData) - this.setState({ - userData: { - ...this.state.userData, - address: userAuthData.selectedAddress, - currentNetwork: userAuthData.networkVersion - } + loadWeb3Legacy = async () => { + console.log('[Web3Provider.js] getting web3 legacy instance') + let web3Instance + web3Instance = new Web3(window.web3.currentProvider) + await this.loadUserDataFromWeb3(web3Instance) + } + + loadWeb3LastVersion = async () => { + console.log('[Web3Provider.js] getting web3 new instance') + let web3Instance + web3Instance = new Web3(window.ethereum) + try { + /** Request access to the user **/ + console.log('[Web3Provider.js] requesting user permissions') + await window.ethereum.enable() + console.log('[Web3Provider.js] user with web3 ethereum authenticated') + /** The user accepted the app, now it's authenticated **/ + await this.loadUserDataFromWeb3(web3Instance) + } catch (error) { + /** The user denied the app, it's not authenticated **/ + console.log('[Web3Provider.js] user denied access') + this.setState({ + web3: web3Instance, + userData: { + authenticated: false, + reason: failReasons.NO_PERMISSIONS + }, + render: true + }) + console.log('[Web3Provider.js] user with ethereum denied the access') + } + } + + loadUserDataFromWeb3 = async web3Instance => { + let userAddress + let userNetwork + Promise.all([web3Instance.eth.getAccounts(), web3Instance.eth.net.getId()]).then(results => { + userAddress = results[0] + userNetwork = results[1] + web3Instance.eth.getBalance(userAddress[0]).then(balance => { + this.setState({ + web3: web3Instance, + userData: { + authenticated: true, + address: userAddress[0], + currentNetwork: userNetwork, + ethBalance: balance + }, + render: true + }) + + /** We subscribe to the event that detects if the user has changed the account **/ + window.ethereum.on('accountsChanged', accounts => { + console.log('ETHEREUM CHANGED') + console.log('ACCOUNTS ', accounts) + this.setState({ + userData: { + ...this.state.userData, + address: accounts[0] + } + }) + }) + + /** We subscribe to the event that detects if the user has changed the network **/ + window.ethereum.on('networkChanged', network => { + console.log('NETWORK CHANGED') + console.log('NETWORK ', network) + this.setState({ + userData: { + ...this.state.userData, + currentNetwork: network + } + }) + }) + }) }) } diff --git a/src/Components/Common/UI/Button/Button.css b/src/Components/Common/UI/Button/Button.css new file mode 100644 index 0000000..1c27de5 --- /dev/null +++ b/src/Components/Common/UI/Button/Button.css @@ -0,0 +1,28 @@ +.Button { + background-color: transparent; + border: none; + color: white; + outline: none; + cursor: pointer; + font: inherit; + padding: 10px; + margin: 10px; + font-weight: bold; +} + +.Button:first-of-type { + margin-left: 0; + padding-left: 0; +} + +.Success { + color: #5C9210; +} + +.Danger { + color: #944317; +} +.Button:disabled{ + color: #ccc; + cursor: not-allowed; +} \ No newline at end of file diff --git a/src/Components/Common/UI/Button/Button.js b/src/Components/Common/UI/Button/Button.js new file mode 100644 index 0000000..9324f88 --- /dev/null +++ b/src/Components/Common/UI/Button/Button.js @@ -0,0 +1,8 @@ +import React from 'react' +import './Button.css' +const button = props => ( + +) +export default button diff --git a/src/Components/Common/UI/Button/Button.test.js b/src/Components/Common/UI/Button/Button.test.js new file mode 100644 index 0000000..1639636 --- /dev/null +++ b/src/Components/Common/UI/Button/Button.test.js @@ -0,0 +1,14 @@ +import React from 'react' +import Adapter from 'enzyme-adapter-react-16' +import { configure, shallow } from 'enzyme' +import Button from './Button' + +configure({ adapter: new Adapter() }) + +describe('Spinner test ', () => { + it(`should render Button component with class name 'Button'`, () => { + const wrapper = shallow(