From e5078ff51b5c5d0fa2d6bc09a75c4fcef11c96f2 Mon Sep 17 00:00:00 2001 From: Pierre Bertet Date: Fri, 5 Apr 2019 19:05:57 +0200 Subject: [PATCH] Finance: use aragonAPI for React (#765) Only contains the changes needed to support aragonAPI for React: this is not a full conversion to React Hooks. --- apps/finance/app/package.json | 3 +- apps/finance/app/src/App.js | 124 ++---------------- apps/finance/app/src/app-state-reducer.js | 66 ++++++++++ apps/finance/app/src/components/AppLayout.js | 18 ++- apps/finance/app/src/components/Download.js | 2 +- .../app/src/components/MenuButton/IconMenu.js | 15 --- .../src/components/MenuButton/MenuButton.js | 4 +- .../app/src/components/NewTransfer/Deposit.js | 24 +++- .../components/NewTransfer/PanelContent.js | 12 +- .../components/NewTransfer/TokenSelector.js | 7 +- .../app/src/components/ToggleFiltersButton.js | 2 +- .../finance/app/src/components/TransferRow.js | 7 +- apps/finance/app/src/index.js | 50 ++----- apps/finance/app/src/lib/provideNetwork.js | 18 --- 14 files changed, 134 insertions(+), 218 deletions(-) create mode 100644 apps/finance/app/src/app-state-reducer.js delete mode 100644 apps/finance/app/src/components/MenuButton/IconMenu.js delete mode 100644 apps/finance/app/src/lib/provideNetwork.js diff --git a/apps/finance/app/package.json b/apps/finance/app/package.json index 3ce0e5ec7a..958acaca96 100644 --- a/apps/finance/app/package.json +++ b/apps/finance/app/package.json @@ -5,13 +5,14 @@ "license": "AGPL-3.0-or-later", "dependencies": { "@aragon/api": "^1.0.0", + "@aragon/api-react": "^1.0.0-beta.2", "@aragon/templates-tokens": "^1.1.1", "@aragon/ui": "^0.33.0", "@babel/polyfill": "^7.0.0", "bn.js": "^4.11.8", "date-fns": "2.0.0-alpha.22", "lodash.throttle": "^4.1.1", - "prop-types": "^15.6.0", + "prop-types": "^15.7.2", "qrcode.react": "^0.8.0", "react": "^16.8.4", "react-display-name": "^0.2.3", diff --git a/apps/finance/app/src/App.js b/apps/finance/app/src/App.js index 6e347b9017..7f14338f2f 100644 --- a/apps/finance/app/src/App.js +++ b/apps/finance/app/src/App.js @@ -1,15 +1,13 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import BN from 'bn.js' -import { map } from 'rxjs/operators' -import { EmptyStateCard, Main, SidePanel, observe } from '@aragon/ui' +import { EmptyStateCard, Main, SidePanel } from '@aragon/ui' +import { useAragonApi } from '@aragon/api-react' import Balances from './components/Balances' import NewTransferPanelContent from './components/NewTransfer/PanelContent' import Transfers from './components/Transfers' import AppLayout from './components/AppLayout' import NewTransferIcon from './components/NewTransferIcon' -import { networkContextType } from './lib/provideNetwork' import { ETHER_TOKEN_FAKE_ADDRESS } from './lib/token-utils' import { IdentityProvider } from './components/IdentityManager/IdentityManager' @@ -17,31 +15,17 @@ import addFundsIcon from './components/assets/add-funds-icon.svg' class App extends React.Component { static propTypes = { - app: PropTypes.object.isRequired, - sendMessageToWrapper: PropTypes.func.isRequired, - proxyAddress: PropTypes.string, + api: PropTypes.object, + appState: PropTypes.object, } static defaultProps = { balances: [], transactions: [], tokens: [], - network: {}, - userAccount: '', - } - static childContextTypes = { - network: networkContextType, } state = { newTransferOpened: false, } - getChildContext() { - const { network } = this.props - return { - network: { - type: network.type, - }, - } - } handleNewTransferOpen = () => { this.setState({ newTransferOpened: true }) } @@ -50,7 +34,7 @@ class App extends React.Component { } handleWithdraw = (tokenAddress, recipient, amount, reference) => { // Immediate, one-time payment - this.props.app.newPayment( + this.props.api.newPayment( tokenAddress, recipient, amount, @@ -62,7 +46,7 @@ class App extends React.Component { this.handleNewTransferClose() } handleDeposit = async (tokenAddress, amount, reference) => { - const { app, periodDuration, periods } = this.props + const { api, periodDuration, periods } = this.props let intentParams if (tokenAddress === ETHER_TOKEN_FAKE_ADDRESS) { @@ -90,32 +74,23 @@ class App extends React.Component { } } - app.deposit(tokenAddress, amount, reference, intentParams) + api.deposit(tokenAddress, amount, reference, intentParams) this.handleNewTransferClose() } - handleMenuPanelOpen = () => { - this.props.sendMessageToWrapper('menuPanel', true) - } handleResolveLocalIdentity = address => { - return this.props.app.resolveAddressIdentity(address).toPromise() + return this.props.api.resolveAddressIdentity(address).toPromise() } handleShowLocalIdentityModal = address => { - return this.props.app + return this.props.api .requestAddressIdentityModification(address) .toPromise() } render() { - const { - app, - balances, - transactions, - tokens, - proxyAddress, - userAccount, - } = this.props + const { appState } = this.props const { newTransferOpened } = this.state + const { balances, transactions, tokens, proxyAddress } = appState return (
@@ -126,7 +101,6 @@ class App extends React.Component { > , @@ -162,13 +136,11 @@ class App extends React.Component { title="New Transfer" > @@ -192,75 +164,7 @@ const SpacedBlock = styled.div` } ` -// Use this function to sort by ETH and then token symbol -const compareBalancesByEthAndSymbol = (tokenA, tokenB) => { - if (tokenA.address === ETHER_TOKEN_FAKE_ADDRESS) { - return -1 - } - if (tokenB.address === ETHER_TOKEN_FAKE_ADDRESS) { - return 1 - } - return tokenA.symbol.localeCompare(tokenB.symbol) +export default () => { + const { api, appState } = useAragonApi() + return } - -export default observe( - observable => - observable.pipe( - map(state => { - const { balances, transactions } = state || {} - - const balancesBn = balances - ? balances - .map(balance => ({ - ...balance, - amount: new BN(balance.amount), - decimals: new BN(balance.decimals), - // Note that numbers in `numData` are not safe for accurate - // computations (but are useful for making divisions easier). - numData: { - amount: parseInt(balance.amount, 10), - decimals: parseInt(balance.decimals, 10), - }, - })) - .sort(compareBalancesByEthAndSymbol) - : [] - - const transactionsBn = transactions - ? transactions.map(transaction => ({ - ...transaction, - amount: new BN(transaction.amount), - numData: { - amount: parseInt(transaction.amount, 10), - }, - })) - : [] - - return { - ...state, - - tokens: balancesBn.map( - ({ - address, - name, - symbol, - numData: { amount, decimals }, - verified, - }) => ({ - address, - amount, - decimals, - name, - symbol, - verified, - }) - ), - - // Filter out empty balances - balances: balancesBn.filter(balance => !balance.amount.isZero()), - - transactions: transactionsBn, - } - }) - ), - {} -)(App) diff --git a/apps/finance/app/src/app-state-reducer.js b/apps/finance/app/src/app-state-reducer.js new file mode 100644 index 0000000000..762c17c453 --- /dev/null +++ b/apps/finance/app/src/app-state-reducer.js @@ -0,0 +1,66 @@ +import BN from 'bn.js' +import { ETHER_TOKEN_FAKE_ADDRESS } from './lib/token-utils' + +// Use this function to sort by ETH and then token symbol +const compareBalancesByEthAndSymbol = (tokenA, tokenB) => { + if (tokenA.address === ETHER_TOKEN_FAKE_ADDRESS) { + return -1 + } + if (tokenB.address === ETHER_TOKEN_FAKE_ADDRESS) { + return 1 + } + return tokenA.symbol.localeCompare(tokenB.symbol) +} + +function appStateReducer(state) { + const { balances, transactions } = state || {} + + const balancesBn = balances + ? balances + .map(balance => ({ + ...balance, + amount: new BN(balance.amount), + decimals: new BN(balance.decimals), + + // Note that numbers in `numData` are not safe for accurate + // computations (but are useful for making divisions easier). + numData: { + amount: parseInt(balance.amount, 10), + decimals: parseInt(balance.decimals, 10), + }, + })) + .sort(compareBalancesByEthAndSymbol) + : [] + + const transactionsBn = transactions + ? transactions.map(transaction => ({ + ...transaction, + amount: new BN(transaction.amount), + numData: { + amount: parseInt(transaction.amount, 10), + }, + })) + : [] + + return { + ...state, + + tokens: balancesBn.map( + ({ address, name, symbol, numData: { amount, decimals }, verified }) => ({ + address, + amount, + decimals, + name, + symbol, + verified, + }) + ), + + // Filter out empty balances + balances: balancesBn.filter(balance => !balance.amount.isZero()), + + transactions: transactionsBn, + } +} + +export default appStateReducer diff --git a/apps/finance/app/src/components/AppLayout.js b/apps/finance/app/src/components/AppLayout.js index db2b8dfd7a..4ad8ce07e7 100644 --- a/apps/finance/app/src/components/AppLayout.js +++ b/apps/finance/app/src/components/AppLayout.js @@ -2,6 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' import { AppBar, AppView, Button, ButtonIcon, Viewport, font } from '@aragon/ui' +import { useAragonApi } from '@aragon/api-react' import MenuButton from './MenuButton/MenuButton' const AppLayout = ({ @@ -10,9 +11,9 @@ const AppLayout = ({ afterTitle, smallViewPadding, largeViewPadding, - onMenuOpen, mainButton, }) => { + const { requestMenu, displayMenuButton } = useAragonApi() return ( {({ below }) => ( @@ -21,18 +22,24 @@ const AppLayout = ({ appBar={ - {below('medium') && <MenuButton onClick={onMenuOpen} />} - <TitleLabel>{title}</TitleLabel> + {displayMenuButton && <MenuButton onClick={requestMenu} />} + <TitleLabel + css={` + margin-left: ${displayMenuButton ? '0' : '20px'}; + `} + > + {title} + </TitleLabel> {afterTitle} {mainButton && (below('medium') ? ( ( diff --git a/apps/finance/app/src/components/MenuButton/IconMenu.js b/apps/finance/app/src/components/MenuButton/IconMenu.js deleted file mode 100644 index 9e852a883e..0000000000 --- a/apps/finance/app/src/components/MenuButton/IconMenu.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' - -const Menu = props => ( - - - - - - - - - -) - -export default Menu diff --git a/apps/finance/app/src/components/MenuButton/MenuButton.js b/apps/finance/app/src/components/MenuButton/MenuButton.js index 029e9512e9..df35bc9e08 100644 --- a/apps/finance/app/src/components/MenuButton/MenuButton.js +++ b/apps/finance/app/src/components/MenuButton/MenuButton.js @@ -1,6 +1,5 @@ import React from 'react' -import { ButtonIcon } from '@aragon/ui' -import IconMenu from './IconMenu' +import { ButtonIcon, IconMenu } from '@aragon/ui' export default props => ( ( padding: 0 10px 0 20px; margin-right: 8px; `} + label="Open menu" > diff --git a/apps/finance/app/src/components/NewTransfer/Deposit.js b/apps/finance/app/src/components/NewTransfer/Deposit.js index b90a97c5fe..997c6cc8d9 100644 --- a/apps/finance/app/src/components/NewTransfer/Deposit.js +++ b/apps/finance/app/src/components/NewTransfer/Deposit.js @@ -12,12 +12,12 @@ import { TextInput, theme, } from '@aragon/ui' +import { useAragonApi } from '@aragon/api-react' import QRCode from 'qrcode.react' import tokenBalanceOfAbi from '../../abi/token-balanceof.json' import tokenDecimalsAbi from '../../abi/token-decimals.json' import tokenSymbolAbi from '../../abi/token-symbol.json' import { fromDecimals, toDecimals } from '../../lib/math-utils' -import provideNetwork from '../../lib/provideNetwork' import { ETHER_TOKEN_FAKE_ADDRESS, tokenDataFallback, @@ -124,12 +124,12 @@ class Deposit extends React.Component { return selectedToken.value && !selectedToken.data.loading } loadTokenData(address) { - const { app, network, userAccount } = this.props + const { api, network, connectedAccount } = this.props // ETH if (addressesEqual(address, ETHER_TOKEN_FAKE_ADDRESS)) { return new Promise((resolve, reject) => - app.web3Eth('getBalance', userAccount).subscribe( + api.web3Eth('getBalance', connectedAccount).subscribe( ethBalance => resolve({ decimals: 18, @@ -143,10 +143,10 @@ class Deposit extends React.Component { } // Tokens - const token = app.external(address, tokenAbi) + const token = api.external(address, tokenAbi) return new Promise(async (resolve, reject) => { - const userBalance = await token.balanceOf(userAccount).toPromise() + const userBalance = await token.balanceOf(connectedAccount).toPromise() const decimalsFallback = tokenDataFallback(address, 'decimals', network.type) || '0' @@ -161,7 +161,7 @@ class Deposit extends React.Component { } const [tokenSymbol, tokenDecimals] = await Promise.all([ - getTokenSymbol(app, address), + getTokenSymbol(api, address), token.decimals().toPromise(), ]) @@ -401,4 +401,14 @@ const ValidationError = ({ message }) => ( ) -export default provideNetwork(Deposit) +export default props => { + const { api, connectedAccount, network } = useAragonApi() + return network && api ? ( + + ) : null +} diff --git a/apps/finance/app/src/components/NewTransfer/PanelContent.js b/apps/finance/app/src/components/NewTransfer/PanelContent.js index 5607f60131..eadd40b377 100644 --- a/apps/finance/app/src/components/NewTransfer/PanelContent.js +++ b/apps/finance/app/src/components/NewTransfer/PanelContent.js @@ -32,15 +32,7 @@ class PanelContent extends React.Component { render() { const { screenIndex } = this.state - const { - app, - opened, - tokens, - onWithdraw, - onDeposit, - proxyAddress, - userAccount, - } = this.props + const { opened, tokens, onWithdraw, onDeposit, proxyAddress } = this.props return (
@@ -53,11 +45,9 @@ class PanelContent extends React.Component { {screenIndex === 0 && ( )} {screenIndex === 1 && ( diff --git a/apps/finance/app/src/components/NewTransfer/TokenSelector.js b/apps/finance/app/src/components/NewTransfer/TokenSelector.js index 8b1f4137d8..48f9c20455 100644 --- a/apps/finance/app/src/components/NewTransfer/TokenSelector.js +++ b/apps/finance/app/src/components/NewTransfer/TokenSelector.js @@ -1,6 +1,6 @@ import React from 'react' import { DropDown, Field, TextInput } from '@aragon/ui' -import provideNetwork from '../../lib/provideNetwork' +import { useNetwork } from '@aragon/api-react' import { ETHER_TOKEN_VERIFIED_BY_SYMBOL } from '../../lib/verified-tokens' import { isAddress } from '../../lib/web3-utils' import TokenSelectorInstance from './TokenSelectorInstance' @@ -119,4 +119,7 @@ class TokenSelector extends React.Component { } } -export default provideNetwork(TokenSelector) +export default props => { + const network = useNetwork() + return +} diff --git a/apps/finance/app/src/components/ToggleFiltersButton.js b/apps/finance/app/src/components/ToggleFiltersButton.js index 3b10975e00..5339c648f1 100644 --- a/apps/finance/app/src/components/ToggleFiltersButton.js +++ b/apps/finance/app/src/components/ToggleFiltersButton.js @@ -2,7 +2,7 @@ import React from 'react' import { ButtonIcon } from '@aragon/ui' export default props => ( - + { + const network = useNetwork() + return +} diff --git a/apps/finance/app/src/index.js b/apps/finance/app/src/index.js index b289442054..f9c5a81945 100644 --- a/apps/finance/app/src/index.js +++ b/apps/finance/app/src/index.js @@ -2,47 +2,13 @@ import '@babel/polyfill' import React from 'react' import ReactDOM from 'react-dom' -import Aragon, { providers } from '@aragon/api' +import { AragonApi } from '@aragon/api-react' +import appStateReducer from './app-state-reducer' import App from './App' -class ConnectedApp extends React.Component { - state = { - app: new Aragon(new providers.WindowMessage(window.parent)), - observable: null, - network: {}, - } - componentDidMount() { - window.addEventListener('message', this.handleWrapperMessage) - } - componentWillUnmount() { - window.removeEventListener('message', this.handleWrapperMessage) - } - handleWrapperMessage = ({ data }) => { - if (data.from !== 'wrapper') { - return - } - if (data.name === 'ready') { - const { app } = this.state - this.sendMessageToWrapper('ready', true) - this.setState({ - observable: app.state(), - }) - app.accounts().subscribe(accounts => { - this.setState({ userAccount: accounts[0] || '' }) - }) - app.network().subscribe(network => { - this.setState({ network }) - }) - } - } - sendMessageToWrapper = (name, value) => { - window.parent.postMessage({ from: 'app', name, value }, '*') - } - render() { - return ( - - ) - } -} - -ReactDOM.render(, document.getElementById('root')) +ReactDOM.render( + + + , + document.getElementById('root') +) diff --git a/apps/finance/app/src/lib/provideNetwork.js b/apps/finance/app/src/lib/provideNetwork.js deleted file mode 100644 index fa272bd524..0000000000 --- a/apps/finance/app/src/lib/provideNetwork.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import getDisplayName from 'react-display-name' - -export const networkContextType = PropTypes.shape({ - type: PropTypes.string, -}) - -const provideNetwork = Component => { - const GetNetwork = (props, context) => - GetNetwork.contextTypes = { - network: networkContextType, - } - GetNetwork.displayName = `GetNetwork(${getDisplayName(Component)})` - return GetNetwork -} - -export default provideNetwork