Visible accounts for dapps (default whitelist) (#3898)
* Add APIs for Dapp management * Move AddDapps modal * Add DappsPermissions Modal (basics) * Allow whitelist editing * Add select/unselect tests * Case * Case * Modal render/non-render tests * UI made slightly prettier * Adjust spacing * Allow get/set of null for default whitelist (all) * Allow null = all for selection * Adjust selected background * Address valid comment on formatters location
This commit is contained in:
		
							parent
							
								
									80eae8cc49
								
							
						
					
					
						commit
						b27c809c64
					
				| @ -24,6 +24,10 @@ export function inAddress (address) { | |||||||
|   return inHex(address); |   return inHex(address); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function inAddresses (addresses) { | ||||||
|  |   return (addresses || []).map(inAddress); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export function inBlockNumber (blockNumber) { | export function inBlockNumber (blockNumber) { | ||||||
|   if (isString(blockNumber)) { |   if (isString(blockNumber)) { | ||||||
|     switch (blockNumber) { |     switch (blockNumber) { | ||||||
|  | |||||||
| @ -42,6 +42,10 @@ export function outAddress (address) { | |||||||
|   return toChecksumAddress(address); |   return toChecksumAddress(address); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function outAddresses (addresses) { | ||||||
|  |   return (addresses || []).map(outAddress); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export function outBlock (block) { | export function outBlock (block) { | ||||||
|   if (block) { |   if (block) { | ||||||
|     Object.keys(block).forEach((key) => { |     Object.keys(block).forEach((key) => { | ||||||
|  | |||||||
| @ -14,8 +14,8 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import { inAddress, inData, inHex, inNumber16, inOptions } from '../../format/input'; | import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions } from '../../format/input'; | ||||||
| import { outAccountInfo, outAddress, outChainStatus, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output'; | import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output'; | ||||||
| 
 | 
 | ||||||
| export default class Parity { | export default class Parity { | ||||||
|   constructor (transport) { |   constructor (transport) { | ||||||
| @ -128,6 +128,18 @@ export default class Parity { | |||||||
|       .execute('parity_generateSecretPhrase'); |       .execute('parity_generateSecretPhrase'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   getDappsAddresses (dappId) { | ||||||
|  |     return this._transport | ||||||
|  |       .execute('parity_getDappsAddresses', dappId) | ||||||
|  |       .then(outAddresses); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getNewDappsWhitelist () { | ||||||
|  |     return this._transport | ||||||
|  |       .execute('parity_getNewDappsWhitelist') | ||||||
|  |       .then((addresses) => addresses ? addresses.map(outAddress) : null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   hashContent (url) { |   hashContent (url) { | ||||||
|     return this._transport |     return this._transport | ||||||
|       .execute('parity_hashContent', url); |       .execute('parity_hashContent', url); | ||||||
| @ -135,8 +147,8 @@ export default class Parity { | |||||||
| 
 | 
 | ||||||
|   importGethAccounts (accounts) { |   importGethAccounts (accounts) { | ||||||
|     return this._transport |     return this._transport | ||||||
|       .execute('parity_importGethAccounts', (accounts || []).map(inAddress)) |       .execute('parity_importGethAccounts', inAddresses) | ||||||
|       .then((accounts) => (accounts || []).map(outAddress)); |       .then(outAddresses); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   killAccount (account, password) { |   killAccount (account, password) { | ||||||
| @ -144,6 +156,11 @@ export default class Parity { | |||||||
|       .execute('parity_killAccount', inAddress(account), password); |       .execute('parity_killAccount', inAddress(account), password); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   listRecentDapps () { | ||||||
|  |     return this._transport | ||||||
|  |       .execute('parity_listRecentDapps'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   removeAddress (address) { |   removeAddress (address) { | ||||||
|     return this._transport |     return this._transport | ||||||
|       .execute('parity_removeAddress', inAddress(address)); |       .execute('parity_removeAddress', inAddress(address)); | ||||||
| @ -152,7 +169,7 @@ export default class Parity { | |||||||
|   listGethAccounts () { |   listGethAccounts () { | ||||||
|     return this._transport |     return this._transport | ||||||
|       .execute('parity_listGethAccounts') |       .execute('parity_listGethAccounts') | ||||||
|       .then((accounts) => (accounts || []).map(outAddress)); |       .then(outAddresses); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   localTransactions () { |   localTransactions () { | ||||||
| @ -289,6 +306,11 @@ export default class Parity { | |||||||
|       .execute('parity_setAuthor', inAddress(address)); |       .execute('parity_setAuthor', inAddress(address)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   setDappsAddresses (dappId, addresses) { | ||||||
|  |     return this._transport | ||||||
|  |       .execute('parity_setDappsAddresses', dappId, inAddresses(addresses)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   setExtraData (data) { |   setExtraData (data) { | ||||||
|     return this._transport |     return this._transport | ||||||
|       .execute('parity_setExtraData', inData(data)); |       .execute('parity_setExtraData', inData(data)); | ||||||
| @ -309,6 +331,11 @@ export default class Parity { | |||||||
|       .execute('parity_setMode', mode); |       .execute('parity_setMode', mode); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   setNewDappsWhitelist (addresses) { | ||||||
|  |     return this._transport | ||||||
|  |       .execute('parity_setNewDappsWhitelist', addresses ? inAddresses(addresses) : null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   setTransactionsLimit (quantity) { |   setTransactionsLimit (quantity) { | ||||||
|     return this._transport |     return this._transport | ||||||
|       .execute('parity_setTransactionsLimit', inNumber16(quantity)); |       .execute('parity_setTransactionsLimit', inNumber16(quantity)); | ||||||
|  | |||||||
| @ -236,6 +236,29 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|  |   getDappsAddresses: { | ||||||
|  |     desc: 'Returns the list of accounts available to a specific dapp', | ||||||
|  |     params: [ | ||||||
|  |       { | ||||||
|  |         type: String, | ||||||
|  |         desc: 'Dapp Id' | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     returns: { | ||||||
|  |       type: Array, | ||||||
|  |       desc: 'The list of available accounts' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   getNewDappsWhitelist: { | ||||||
|  |     desc: 'Returns the list of accounts available to a new dapps', | ||||||
|  |     params: [], | ||||||
|  |     returns: { | ||||||
|  |       type: Array, | ||||||
|  |       desc: 'The list of available accounts' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|   hashContent: { |   hashContent: { | ||||||
|     desc: 'Creates a hash of the file as retrieved', |     desc: 'Creates a hash of the file as retrieved', | ||||||
|     params: [ |     params: [ | ||||||
| @ -282,6 +305,15 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|  |   listRecentDapps: { | ||||||
|  |     desc: 'Returns a list of the most recent active dapps', | ||||||
|  |     params: [], | ||||||
|  |     returns: { | ||||||
|  |       type: Array, | ||||||
|  |       desc: 'Array of Dapp Ids' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|   removeAddress: { |   removeAddress: { | ||||||
|     desc: 'Removes an address from the addressbook', |     desc: 'Removes an address from the addressbook', | ||||||
|     params: [ |     params: [ | ||||||
| @ -586,6 +618,24 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|  |   setDappsAddresses: { | ||||||
|  |     desc: 'Sets the available addresses for a dapp', | ||||||
|  |     params: [ | ||||||
|  |       { | ||||||
|  |         type: String, | ||||||
|  |         desc: 'Dapp Id' | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         type: Array, | ||||||
|  |         desc: 'Array of available accounts available to the dapp' | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     returns: { | ||||||
|  |       type: Boolean, | ||||||
|  |       desc: 'True if the call succeeded' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|   setExtraData: { |   setExtraData: { | ||||||
|     desc: 'Changes extra data for newly mined blocks', |     desc: 'Changes extra data for newly mined blocks', | ||||||
|     params: [ |     params: [ | ||||||
| @ -645,6 +695,20 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|  |   setNewDappsWhitelist: { | ||||||
|  |     desc: 'Sets the list of accounts available to new dapps', | ||||||
|  |     params: [ | ||||||
|  |       { | ||||||
|  |         type: Array, | ||||||
|  |         desc: 'List of accounts available by default' | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     returns: { | ||||||
|  |       type: Boolean, | ||||||
|  |       desc: 'True if the call succeeded' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|   setTransactionsLimit: { |   setTransactionsLimit: { | ||||||
|     desc: 'Changes limit for transactions in queue.', |     desc: 'Changes limit for transactions in queue.', | ||||||
|     params: [ |     params: [ | ||||||
|  | |||||||
| @ -14,16 +14,16 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
|  | import { Checkbox } from 'material-ui'; | ||||||
|  | import { List, ListItem } from 'material-ui/List'; | ||||||
|  | import { observer } from 'mobx-react'; | ||||||
| import React, { Component, PropTypes } from 'react'; | import React, { Component, PropTypes } from 'react'; | ||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
| import { observer } from 'mobx-react'; |  | ||||||
| import DoneIcon from 'material-ui/svg-icons/action/done'; |  | ||||||
| import { List, ListItem } from 'material-ui/List'; |  | ||||||
| import Checkbox from 'material-ui/Checkbox'; |  | ||||||
| 
 | 
 | ||||||
| import { Modal, Button } from '~/ui'; | import { Modal, Button } from '~/ui'; | ||||||
|  | import { DoneIcon } from '~/ui/Icons'; | ||||||
| 
 | 
 | ||||||
| import styles from './AddDapps.css'; | import styles from './addDapps.css'; | ||||||
| 
 | 
 | ||||||
| @observer | @observer | ||||||
| export default class AddDapps extends Component { | export default class AddDapps extends Component { | ||||||
| @ -40,25 +40,24 @@ export default class AddDapps extends Component { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Modal |       <Modal | ||||||
|         visible |         actions={ [ | ||||||
|  |           <Button | ||||||
|  |             icon={ <DoneIcon /> } | ||||||
|  |             key='done' | ||||||
|  |             label={ | ||||||
|  |               <FormattedMessage | ||||||
|  |                 id='dapps.add.button.done' | ||||||
|  |                 defaultMessage='Done' /> | ||||||
|  |             } | ||||||
|  |             onClick={ store.closeModal } /> | ||||||
|  |         ] } | ||||||
|         compact |         compact | ||||||
|         title={ |         title={ | ||||||
|           <FormattedMessage |           <FormattedMessage | ||||||
|             id='dapps.add.label' |             id='dapps.add.label' | ||||||
|             defaultMessage='visible applications' /> |             defaultMessage='visible applications' /> | ||||||
|         } |         } | ||||||
|         actions={ [ |         visible> | ||||||
|           <Button |  | ||||||
|             label={ |  | ||||||
|               <FormattedMessage |  | ||||||
|                 id='dapps.add.button.done' |  | ||||||
|                 defaultMessage='Done' /> |  | ||||||
|             } |  | ||||||
|             key='done' |  | ||||||
|             onClick={ store.closeModal } |  | ||||||
|             icon={ <DoneIcon /> } |  | ||||||
|           /> |  | ||||||
|         ] }> |  | ||||||
|         <div className={ styles.warning } /> |         <div className={ styles.warning } /> | ||||||
|         { |         { | ||||||
|           this.renderList(store.sortedLocal, |           this.renderList(store.sortedLocal, | ||||||
							
								
								
									
										46
									
								
								js/src/modals/AddDapps/addDapps.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								js/src/modals/AddDapps/addDapps.spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | // Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity is distributed in the hope that it will be useful,
 | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||||
|  | // GNU General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | import { shallow } from 'enzyme'; | ||||||
|  | import React from 'react'; | ||||||
|  | 
 | ||||||
|  | import AddDapps from './'; | ||||||
|  | 
 | ||||||
|  | function renderShallow (store = {}) { | ||||||
|  |   return shallow( | ||||||
|  |     <AddDapps store={ store } /> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | describe('modals/AddDapps', () => { | ||||||
|  |   describe('rendering', () => { | ||||||
|  |     it('renders defaults', () => { | ||||||
|  |       expect(renderShallow()).to.be.ok; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('does not render the modal with modalOpen = false', () => { | ||||||
|  |       expect( | ||||||
|  |         renderShallow({ modalOpen: false }).find('Connect(Modal)') | ||||||
|  |       ).to.have.length(0); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('does render the modal with modalOpen = true', () => { | ||||||
|  |       expect( | ||||||
|  |         renderShallow({ modalOpen: true }).find('Connect(Modal)') | ||||||
|  |       ).to.have.length(1); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @ -14,4 +14,4 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| export default from './AddDapps'; | export default from './addDapps'; | ||||||
							
								
								
									
										47
									
								
								js/src/modals/DappPermissions/dappPermissions.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								js/src/modals/DappPermissions/dappPermissions.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | /* Copyright 2015, 2016 Parity Technologies (UK) Ltd. | ||||||
|  | /* This file is part of Parity. | ||||||
|  | /* | ||||||
|  | /* Parity is free software: you can redistribute it and/or modify | ||||||
|  | /* it under the terms of the GNU General Public License as published by | ||||||
|  | /* the Free Software Foundation, either version 3 of the License, or | ||||||
|  | /* (at your option) any later version. | ||||||
|  | /* | ||||||
|  | /* Parity is distributed in the hope that it will be useful, | ||||||
|  | /* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | /* GNU General Public License for more details. | ||||||
|  | /* | ||||||
|  | /* You should have received a copy of the GNU General Public License | ||||||
|  | /* along with Parity.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .item { | ||||||
|  |   .info { | ||||||
|  |     display: inline-block; | ||||||
|  | 
 | ||||||
|  |     .address { | ||||||
|  |       opacity: 0.75; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .description { | ||||||
|  |       margin-top: 0.5em; | ||||||
|  |       opacity: 0.75; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .name { | ||||||
|  |       margin: 0.5em 0; | ||||||
|  |       text-transform: uppercase; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .selected, .unselected { | ||||||
|  |   margin-bottom: 0.25em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .selected { | ||||||
|  |   background: rgba(255, 255, 255, 0.15); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .unselected { | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								js/src/modals/DappPermissions/dappPermissions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								js/src/modals/DappPermissions/dappPermissions.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | |||||||
|  | // Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity is distributed in the hope that it will be useful,
 | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||||
|  | // GNU General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | import { Checkbox } from 'material-ui'; | ||||||
|  | import { List, ListItem } from 'material-ui/List'; | ||||||
|  | import { observer } from 'mobx-react'; | ||||||
|  | import React, { Component, PropTypes } from 'react'; | ||||||
|  | import { FormattedMessage } from 'react-intl'; | ||||||
|  | 
 | ||||||
|  | import { Button, IdentityIcon, Modal } from '~/ui'; | ||||||
|  | import { DoneIcon } from '~/ui/Icons'; | ||||||
|  | 
 | ||||||
|  | import styles from './dappPermissions.css'; | ||||||
|  | 
 | ||||||
|  | @observer | ||||||
|  | export default class DappPermissions extends Component { | ||||||
|  |   static propTypes = { | ||||||
|  |     store: PropTypes.object.isRequired | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   render () { | ||||||
|  |     const { store } = this.props; | ||||||
|  | 
 | ||||||
|  |     if (!store.modalOpen) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |       <Modal | ||||||
|  |         actions={ [ | ||||||
|  |           <Button | ||||||
|  |             icon={ <DoneIcon /> } | ||||||
|  |             key='done' | ||||||
|  |             label={ | ||||||
|  |               <FormattedMessage | ||||||
|  |                 id='dapps.permissions.button.done' | ||||||
|  |                 defaultMessage='Done' /> | ||||||
|  |             } | ||||||
|  |             onClick={ store.closeModal } /> | ||||||
|  |         ] } | ||||||
|  |         compact | ||||||
|  |         title={ | ||||||
|  |           <FormattedMessage | ||||||
|  |             id='dapps.permissions.label' | ||||||
|  |             defaultMessage='visible dapp accounts' /> | ||||||
|  |         } | ||||||
|  |         visible> | ||||||
|  |         <List> | ||||||
|  |           { this.renderListItems() } | ||||||
|  |         </List> | ||||||
|  |       </Modal> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   renderListItems () { | ||||||
|  |     const { store } = this.props; | ||||||
|  | 
 | ||||||
|  |     return store.accounts.map((account) => { | ||||||
|  |       const onCheck = () => { | ||||||
|  |         store.selectAccount(account.address); | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       // TODO: Once new modal & account selection is in, this should be updated
 | ||||||
|  |       // to conform to the new (as of this code WIP) look & feel for selection.
 | ||||||
|  |       // For now in the current/old style, not as pretty but consistent.
 | ||||||
|  |       return ( | ||||||
|  |         <ListItem | ||||||
|  |           className={ | ||||||
|  |             account.checked | ||||||
|  |               ? styles.selected | ||||||
|  |               : styles.unselected | ||||||
|  |           } | ||||||
|  |           key={ account.address } | ||||||
|  |           leftCheckbox={ | ||||||
|  |             <Checkbox | ||||||
|  |               checked={ account.checked } | ||||||
|  |               onCheck={ onCheck } | ||||||
|  |             /> | ||||||
|  |           } | ||||||
|  |           primaryText={ | ||||||
|  |             <div className={ styles.item }> | ||||||
|  |               <IdentityIcon address={ account.address } /> | ||||||
|  |               <div className={ styles.info }> | ||||||
|  |                 <h3 className={ styles.name }> | ||||||
|  |                   { account.name } | ||||||
|  |                 </h3> | ||||||
|  |                 <div className={ styles.address }> | ||||||
|  |                   { account.address } | ||||||
|  |                 </div> | ||||||
|  |                 <div className={ styles.description }> | ||||||
|  |                   { account.description } | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           } /> | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								js/src/modals/DappPermissions/dappPermissions.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								js/src/modals/DappPermissions/dappPermissions.spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | // Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity is distributed in the hope that it will be useful,
 | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||||
|  | // GNU General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | import { shallow } from 'enzyme'; | ||||||
|  | import React from 'react'; | ||||||
|  | 
 | ||||||
|  | import DappPermissions from './'; | ||||||
|  | 
 | ||||||
|  | function renderShallow (store = {}) { | ||||||
|  |   return shallow( | ||||||
|  |     <DappPermissions store={ store } /> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | describe('modals/DappPermissions', () => { | ||||||
|  |   describe('rendering', () => { | ||||||
|  |     it('renders defaults', () => { | ||||||
|  |       expect(renderShallow()).to.be.ok; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('does not render the modal with modalOpen = false', () => { | ||||||
|  |       expect( | ||||||
|  |         renderShallow({ modalOpen: false }).find('Connect(Modal)') | ||||||
|  |       ).to.have.length(0); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('does render the modal with modalOpen = true', () => { | ||||||
|  |       expect( | ||||||
|  |         renderShallow({ modalOpen: true, accounts: [] }).find('Connect(Modal)') | ||||||
|  |       ).to.have.length(1); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										17
									
								
								js/src/modals/DappPermissions/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								js/src/modals/DappPermissions/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | // Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity is distributed in the hope that it will be useful,
 | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||||
|  | // GNU General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | export default from './dappPermissions'; | ||||||
							
								
								
									
										94
									
								
								js/src/modals/DappPermissions/store.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								js/src/modals/DappPermissions/store.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | |||||||
|  | // Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity is distributed in the hope that it will be useful,
 | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||||
|  | // GNU General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | import { action, observable, transaction } from 'mobx'; | ||||||
|  | 
 | ||||||
|  | export default class Store { | ||||||
|  |   @observable accounts = []; | ||||||
|  |   @observable modalOpen = false; | ||||||
|  |   @observable whitelist = []; | ||||||
|  | 
 | ||||||
|  |   constructor (api) { | ||||||
|  |     this._api = api; | ||||||
|  | 
 | ||||||
|  |     this.loadWhitelist(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @action closeModal = () => { | ||||||
|  |     transaction(() => { | ||||||
|  |       const accounts = this.accounts | ||||||
|  |         .filter((account) => account.checked) | ||||||
|  |         .map((account) => account.address); | ||||||
|  | 
 | ||||||
|  |       this.modalOpen = false; | ||||||
|  |       this.updateWhitelist(accounts.length === this.accounts.length ? null : accounts); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @action openModal = (accounts) => { | ||||||
|  |     transaction(() => { | ||||||
|  |       this.accounts = Object | ||||||
|  |         .values(accounts) | ||||||
|  |         .map((account) => { | ||||||
|  |           return { | ||||||
|  |             address: account.address, | ||||||
|  |             checked: this.whitelist | ||||||
|  |               ? this.whitelist.includes(account.address) | ||||||
|  |               : true, | ||||||
|  |             description: account.meta.description, | ||||||
|  |             name: account.name | ||||||
|  |           }; | ||||||
|  |         }); | ||||||
|  |       this.modalOpen = true; | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @action selectAccount = (address) => { | ||||||
|  |     this.accounts = this.accounts.map((account) => { | ||||||
|  |       if (account.address === address) { | ||||||
|  |         account.checked = !account.checked; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       return account; | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @action setWhitelist = (whitelist) => { | ||||||
|  |     this.whitelist = whitelist; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   loadWhitelist () { | ||||||
|  |     return this._api.parity | ||||||
|  |       .getNewDappsWhitelist() | ||||||
|  |       .then((whitelist) => { | ||||||
|  |         this.setWhitelist(whitelist); | ||||||
|  |       }) | ||||||
|  |       .catch((error) => { | ||||||
|  |         console.warn('loadWhitelist', error); | ||||||
|  |       }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   updateWhitelist (whitelist) { | ||||||
|  |     return this._api.parity | ||||||
|  |       .setNewDappsWhitelist(whitelist) | ||||||
|  |       .then(() => { | ||||||
|  |         this.setWhitelist(whitelist); | ||||||
|  |       }) | ||||||
|  |       .catch((error) => { | ||||||
|  |         console.warn('updateWhitelist', error); | ||||||
|  |       }); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										100
									
								
								js/src/modals/DappPermissions/store.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								js/src/modals/DappPermissions/store.spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | |||||||
|  | // Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity is distributed in the hope that it will be useful,
 | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||||
|  | // GNU General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | import sinon from 'sinon'; | ||||||
|  | 
 | ||||||
|  | import Store from './store'; | ||||||
|  | 
 | ||||||
|  | const ACCOUNTS = { | ||||||
|  |   '123': { address: '123', name: '123', meta: { description: '123' } }, | ||||||
|  |   '456': { address: '456', name: '456', meta: { description: '456' } }, | ||||||
|  |   '789': { address: '789', name: '789', meta: { description: '789' } } | ||||||
|  | }; | ||||||
|  | const WHITELIST = ['123', '456']; | ||||||
|  | 
 | ||||||
|  | describe('modals/DappPermissions/store', () => { | ||||||
|  |   let api; | ||||||
|  |   let store; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     api = { | ||||||
|  |       parity: { | ||||||
|  |         getNewDappsWhitelist: sinon.stub().resolves(WHITELIST), | ||||||
|  |         setNewDappsWhitelist: sinon.stub().resolves(true) | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     store = new Store(api); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('constructor', () => { | ||||||
|  |     it('retrieves the whitelist via api', () => { | ||||||
|  |       expect(api.parity.getNewDappsWhitelist).to.be.calledOnce; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('sets the retrieved whitelist', () => { | ||||||
|  |       expect(store.whitelist.peek()).to.deep.equal(WHITELIST); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('@actions', () => { | ||||||
|  |     describe('openModal', () => { | ||||||
|  |       beforeEach(() => { | ||||||
|  |         store.openModal(ACCOUNTS); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('sets the modalOpen status', () => { | ||||||
|  |         expect(store.modalOpen).to.be.true; | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('sets accounts with checked interfaces', () => { | ||||||
|  |         expect(store.accounts.peek()).to.deep.equal([ | ||||||
|  |           { address: '123', name: '123', description: '123', checked: true }, | ||||||
|  |           { address: '456', name: '456', description: '456', checked: true }, | ||||||
|  |           { address: '789', name: '789', description: '789', checked: false } | ||||||
|  |         ]); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     describe('closeModal', () => { | ||||||
|  |       beforeEach(() => { | ||||||
|  |         store.openModal(ACCOUNTS); | ||||||
|  |         store.selectAccount('789'); | ||||||
|  |         store.closeModal(); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('calls setNewDappsWhitelist', () => { | ||||||
|  |         expect(api.parity.setNewDappsWhitelist).to.have.been.calledOnce; | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     describe('selectAccount', () => { | ||||||
|  |       beforeEach(() => { | ||||||
|  |         store.openModal(ACCOUNTS); | ||||||
|  |         store.selectAccount('123'); | ||||||
|  |         store.selectAccount('789'); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('unselects previous selected accounts', () => { | ||||||
|  |         expect(store.accounts.find((account) => account.address === '123').checked).to.be.false; | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('selects previous unselected accounts', () => { | ||||||
|  |         expect(store.accounts.find((account) => account.address === '789').checked).to.be.true; | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @ -16,8 +16,10 @@ | |||||||
| 
 | 
 | ||||||
| import AddAddress from './AddAddress'; | import AddAddress from './AddAddress'; | ||||||
| import AddContract from './AddContract'; | import AddContract from './AddContract'; | ||||||
|  | import AddDapps from './AddDapps'; | ||||||
| import CreateAccount from './CreateAccount'; | import CreateAccount from './CreateAccount'; | ||||||
| import CreateWallet from './CreateWallet'; | import CreateWallet from './CreateWallet'; | ||||||
|  | import DappPermissions from './DappPermissions'; | ||||||
| import DeleteAccount from './DeleteAccount'; | import DeleteAccount from './DeleteAccount'; | ||||||
| import DeployContract from './DeployContract'; | import DeployContract from './DeployContract'; | ||||||
| import EditMeta from './EditMeta'; | import EditMeta from './EditMeta'; | ||||||
| @ -35,8 +37,10 @@ import WalletSettings from './WalletSettings'; | |||||||
| export { | export { | ||||||
|   AddAddress, |   AddAddress, | ||||||
|   AddContract, |   AddContract, | ||||||
|  |   AddDapps, | ||||||
|   CreateAccount, |   CreateAccount, | ||||||
|   CreateWallet, |   CreateWallet, | ||||||
|  |   DappPermissions, | ||||||
|   DeleteAccount, |   DeleteAccount, | ||||||
|   DeployContract, |   DeployContract, | ||||||
|   EditMeta, |   EditMeta, | ||||||
|  | |||||||
| @ -20,10 +20,12 @@ import CheckIcon from 'material-ui/svg-icons/navigation/check'; | |||||||
| import CloseIcon from 'material-ui/svg-icons/navigation/close'; | import CloseIcon from 'material-ui/svg-icons/navigation/close'; | ||||||
| import ContractIcon from 'material-ui/svg-icons/action/code'; | import ContractIcon from 'material-ui/svg-icons/action/code'; | ||||||
| import DoneIcon from 'material-ui/svg-icons/action/done-all'; | import DoneIcon from 'material-ui/svg-icons/action/done-all'; | ||||||
| import PrevIcon from 'material-ui/svg-icons/navigation/arrow-back'; | import LockedIcon from 'material-ui/svg-icons/action/lock-outline'; | ||||||
| import NextIcon from 'material-ui/svg-icons/navigation/arrow-forward'; | import NextIcon from 'material-ui/svg-icons/navigation/arrow-forward'; | ||||||
|  | import PrevIcon from 'material-ui/svg-icons/navigation/arrow-back'; | ||||||
| import SaveIcon from 'material-ui/svg-icons/content/save'; | import SaveIcon from 'material-ui/svg-icons/content/save'; | ||||||
| import SnoozeIcon from 'material-ui/svg-icons/av/snooze'; | import SnoozeIcon from 'material-ui/svg-icons/av/snooze'; | ||||||
|  | import VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye'; | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
|   AddIcon, |   AddIcon, | ||||||
| @ -32,8 +34,10 @@ export { | |||||||
|   CloseIcon, |   CloseIcon, | ||||||
|   ContractIcon, |   ContractIcon, | ||||||
|   DoneIcon, |   DoneIcon, | ||||||
|   PrevIcon, |   LockedIcon, | ||||||
|   NextIcon, |   NextIcon, | ||||||
|  |   PrevIcon, | ||||||
|   SaveIcon, |   SaveIcon, | ||||||
|   SnoozeIcon |   SnoozeIcon, | ||||||
|  |   VisibleIcon | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -14,29 +14,35 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import React, { Component, PropTypes } from 'react'; |  | ||||||
| import { FormattedMessage } from 'react-intl'; |  | ||||||
| import { Checkbox } from 'material-ui'; | import { Checkbox } from 'material-ui'; | ||||||
| import { observer } from 'mobx-react'; | import { observer } from 'mobx-react'; | ||||||
|  | import React, { Component, PropTypes } from 'react'; | ||||||
|  | import { FormattedMessage } from 'react-intl'; | ||||||
|  | import { connect } from 'react-redux'; | ||||||
|  | import { bindActionCreators } from 'redux'; | ||||||
| 
 | 
 | ||||||
| import { Actionbar, Page } from '~/ui'; | import { AddDapps, DappPermissions } from '~/modals'; | ||||||
| import FlatButton from 'material-ui/FlatButton'; | import PermissionStore from '~/modals/DappPermissions/store'; | ||||||
| import EyeIcon from 'material-ui/svg-icons/image/remove-red-eye'; | import { Actionbar, Button, Page } from '~/ui'; | ||||||
|  | import { LockedIcon, VisibleIcon } from '~/ui/Icons'; | ||||||
| 
 | 
 | ||||||
| import DappsStore from './dappsStore'; | import DappsStore from './dappsStore'; | ||||||
| 
 |  | ||||||
| import AddDapps from './AddDapps'; |  | ||||||
| import Summary from './Summary'; | import Summary from './Summary'; | ||||||
| 
 | 
 | ||||||
| import styles from './dapps.css'; | import styles from './dapps.css'; | ||||||
| 
 | 
 | ||||||
| @observer | @observer | ||||||
| export default class Dapps extends Component { | class Dapps extends Component { | ||||||
|   static contextTypes = { |   static contextTypes = { | ||||||
|     api: PropTypes.object.isRequired |     api: PropTypes.object.isRequired | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   static propTypes = { | ||||||
|  |     accounts: PropTypes.object.isRequired | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|   store = DappsStore.get(this.context.api); |   store = DappsStore.get(this.context.api); | ||||||
|  |   permissionStore = new PermissionStore(this.context.api); | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     let externalOverlay = null; |     let externalOverlay = null; | ||||||
| @ -68,6 +74,7 @@ export default class Dapps extends Component { | |||||||
|     return ( |     return ( | ||||||
|       <div> |       <div> | ||||||
|         <AddDapps store={ this.store } /> |         <AddDapps store={ this.store } /> | ||||||
|  |         <DappPermissions store={ this.permissionStore } /> | ||||||
|         <Actionbar |         <Actionbar | ||||||
|           className={ styles.toolbar } |           className={ styles.toolbar } | ||||||
|           title={ |           title={ | ||||||
| @ -76,30 +83,31 @@ export default class Dapps extends Component { | |||||||
|               defaultMessage='Decentralized Applications' /> |               defaultMessage='Decentralized Applications' /> | ||||||
|           } |           } | ||||||
|           buttons={ [ |           buttons={ [ | ||||||
|             <FlatButton |             <Button | ||||||
|  |               icon={ <VisibleIcon /> } | ||||||
|  |               key='edit' | ||||||
|               label={ |               label={ | ||||||
|                 <FormattedMessage |                 <FormattedMessage | ||||||
|                   id='dapps.button.edit' |                   id='dapps.button.edit' | ||||||
|                   defaultMessage='edit' /> |                   defaultMessage='edit' /> | ||||||
|               } |               } | ||||||
|               key='edit' |               onClick={ this.store.openModal } | ||||||
|               icon={ <EyeIcon /> } |             />, | ||||||
|               onTouchTap={ this.store.openModal } |             <Button | ||||||
|             /> |               icon={ <LockedIcon /> } | ||||||
|  |               key='permissions' | ||||||
|  |               label={ | ||||||
|  |                 <FormattedMessage | ||||||
|  |                   id='dapps.button.permissions' | ||||||
|  |                   defaultMessage='permissions' /> | ||||||
|  |               } | ||||||
|  |               onClick={ this.openPermissionsModal } /> | ||||||
|           ] } |           ] } | ||||||
|         /> |         /> | ||||||
|         <Page> |         <Page> | ||||||
|           <div> |           <div>{ this.renderList(this.store.visibleLocal) }</div> | ||||||
|             { this.renderList(this.store.visibleLocal) } |           <div>{ this.renderList(this.store.visibleBuiltin) }</div> | ||||||
|           </div> |           <div>{ this.renderList(this.store.visibleNetwork, externalOverlay) }</div> | ||||||
| 
 |  | ||||||
|           <div> |  | ||||||
|             { this.renderList(this.store.visibleBuiltin) } |  | ||||||
|           </div> |  | ||||||
| 
 |  | ||||||
|           <div> |  | ||||||
|             { this.renderList(this.store.visibleNetwork, externalOverlay) } |  | ||||||
|           </div> |  | ||||||
|         </Page> |         </Page> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
| @ -131,4 +139,27 @@ export default class Dapps extends Component { | |||||||
|   onClickAcceptExternal = () => { |   onClickAcceptExternal = () => { | ||||||
|     this.store.closeExternalOverlay(); |     this.store.closeExternalOverlay(); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   openPermissionsModal = () => { | ||||||
|  |     const { accounts } = this.props; | ||||||
|  | 
 | ||||||
|  |     this.permissionStore.openModal(accounts); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | function mapStateToProps (state) { | ||||||
|  |   const { accounts } = state.personal; | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     accounts | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function mapDispatchToProps (dispatch) { | ||||||
|  |   return bindActionCreators({}, dispatch); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default connect( | ||||||
|  |   mapStateToProps, | ||||||
|  |   mapDispatchToProps | ||||||
|  | )(Dapps); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user