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