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);
|
||||
}
|
||||
|
||||
export function inAddresses (addresses) {
|
||||
return (addresses || []).map(inAddress);
|
||||
}
|
||||
|
||||
export function inBlockNumber (blockNumber) {
|
||||
if (isString(blockNumber)) {
|
||||
switch (blockNumber) {
|
||||
|
@ -42,6 +42,10 @@ export function outAddress (address) {
|
||||
return toChecksumAddress(address);
|
||||
}
|
||||
|
||||
export function outAddresses (addresses) {
|
||||
return (addresses || []).map(outAddress);
|
||||
}
|
||||
|
||||
export function outBlock (block) {
|
||||
if (block) {
|
||||
Object.keys(block).forEach((key) => {
|
||||
|
@ -14,8 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inAddress, inData, inHex, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAccountInfo, outAddress, outChainStatus, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output';
|
||||
import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output';
|
||||
|
||||
export default class Parity {
|
||||
constructor (transport) {
|
||||
@ -128,6 +128,18 @@ export default class Parity {
|
||||
.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) {
|
||||
return this._transport
|
||||
.execute('parity_hashContent', url);
|
||||
@ -135,8 +147,8 @@ export default class Parity {
|
||||
|
||||
importGethAccounts (accounts) {
|
||||
return this._transport
|
||||
.execute('parity_importGethAccounts', (accounts || []).map(inAddress))
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
.execute('parity_importGethAccounts', inAddresses)
|
||||
.then(outAddresses);
|
||||
}
|
||||
|
||||
killAccount (account, password) {
|
||||
@ -144,6 +156,11 @@ export default class Parity {
|
||||
.execute('parity_killAccount', inAddress(account), password);
|
||||
}
|
||||
|
||||
listRecentDapps () {
|
||||
return this._transport
|
||||
.execute('parity_listRecentDapps');
|
||||
}
|
||||
|
||||
removeAddress (address) {
|
||||
return this._transport
|
||||
.execute('parity_removeAddress', inAddress(address));
|
||||
@ -152,7 +169,7 @@ export default class Parity {
|
||||
listGethAccounts () {
|
||||
return this._transport
|
||||
.execute('parity_listGethAccounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
.then(outAddresses);
|
||||
}
|
||||
|
||||
localTransactions () {
|
||||
@ -289,6 +306,11 @@ export default class Parity {
|
||||
.execute('parity_setAuthor', inAddress(address));
|
||||
}
|
||||
|
||||
setDappsAddresses (dappId, addresses) {
|
||||
return this._transport
|
||||
.execute('parity_setDappsAddresses', dappId, inAddresses(addresses));
|
||||
}
|
||||
|
||||
setExtraData (data) {
|
||||
return this._transport
|
||||
.execute('parity_setExtraData', inData(data));
|
||||
@ -309,6 +331,11 @@ export default class Parity {
|
||||
.execute('parity_setMode', mode);
|
||||
}
|
||||
|
||||
setNewDappsWhitelist (addresses) {
|
||||
return this._transport
|
||||
.execute('parity_setNewDappsWhitelist', addresses ? inAddresses(addresses) : null);
|
||||
}
|
||||
|
||||
setTransactionsLimit (quantity) {
|
||||
return this._transport
|
||||
.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: {
|
||||
desc: 'Creates a hash of the file as retrieved',
|
||||
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: {
|
||||
desc: 'Removes an address from the addressbook',
|
||||
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: {
|
||||
desc: 'Changes extra data for newly mined blocks',
|
||||
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: {
|
||||
desc: 'Changes limit for transactions in queue.',
|
||||
params: [
|
||||
|
@ -14,16 +14,16 @@
|
||||
// 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 { 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 { DoneIcon } from '~/ui/Icons';
|
||||
|
||||
import styles from './AddDapps.css';
|
||||
import styles from './addDapps.css';
|
||||
|
||||
@observer
|
||||
export default class AddDapps extends Component {
|
||||
@ -40,25 +40,24 @@ export default class AddDapps extends Component {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
actions={ [
|
||||
<Button
|
||||
icon={ <DoneIcon /> }
|
||||
key='done'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='dapps.add.button.done'
|
||||
defaultMessage='Done' />
|
||||
}
|
||||
onClick={ store.closeModal } />
|
||||
] }
|
||||
compact
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='dapps.add.label'
|
||||
defaultMessage='visible applications' />
|
||||
}
|
||||
actions={ [
|
||||
<Button
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='dapps.add.button.done'
|
||||
defaultMessage='Done' />
|
||||
}
|
||||
key='done'
|
||||
onClick={ store.closeModal }
|
||||
icon={ <DoneIcon /> }
|
||||
/>
|
||||
] }>
|
||||
visible>
|
||||
<div className={ styles.warning } />
|
||||
{
|
||||
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
|
||||
// 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 AddContract from './AddContract';
|
||||
import AddDapps from './AddDapps';
|
||||
import CreateAccount from './CreateAccount';
|
||||
import CreateWallet from './CreateWallet';
|
||||
import DappPermissions from './DappPermissions';
|
||||
import DeleteAccount from './DeleteAccount';
|
||||
import DeployContract from './DeployContract';
|
||||
import EditMeta from './EditMeta';
|
||||
@ -35,8 +37,10 @@ import WalletSettings from './WalletSettings';
|
||||
export {
|
||||
AddAddress,
|
||||
AddContract,
|
||||
AddDapps,
|
||||
CreateAccount,
|
||||
CreateWallet,
|
||||
DappPermissions,
|
||||
DeleteAccount,
|
||||
DeployContract,
|
||||
EditMeta,
|
||||
|
@ -20,10 +20,12 @@ import CheckIcon from 'material-ui/svg-icons/navigation/check';
|
||||
import CloseIcon from 'material-ui/svg-icons/navigation/close';
|
||||
import ContractIcon from 'material-ui/svg-icons/action/code';
|
||||
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 PrevIcon from 'material-ui/svg-icons/navigation/arrow-back';
|
||||
import SaveIcon from 'material-ui/svg-icons/content/save';
|
||||
import SnoozeIcon from 'material-ui/svg-icons/av/snooze';
|
||||
import VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
|
||||
|
||||
export {
|
||||
AddIcon,
|
||||
@ -32,8 +34,10 @@ export {
|
||||
CloseIcon,
|
||||
ContractIcon,
|
||||
DoneIcon,
|
||||
PrevIcon,
|
||||
LockedIcon,
|
||||
NextIcon,
|
||||
PrevIcon,
|
||||
SaveIcon,
|
||||
SnoozeIcon
|
||||
SnoozeIcon,
|
||||
VisibleIcon
|
||||
};
|
||||
|
@ -14,29 +14,35 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// 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 { 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 FlatButton from 'material-ui/FlatButton';
|
||||
import EyeIcon from 'material-ui/svg-icons/image/remove-red-eye';
|
||||
import { AddDapps, DappPermissions } from '~/modals';
|
||||
import PermissionStore from '~/modals/DappPermissions/store';
|
||||
import { Actionbar, Button, Page } from '~/ui';
|
||||
import { LockedIcon, VisibleIcon } from '~/ui/Icons';
|
||||
|
||||
import DappsStore from './dappsStore';
|
||||
|
||||
import AddDapps from './AddDapps';
|
||||
import Summary from './Summary';
|
||||
|
||||
import styles from './dapps.css';
|
||||
|
||||
@observer
|
||||
export default class Dapps extends Component {
|
||||
class Dapps extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
store = DappsStore.get(this.context.api);
|
||||
permissionStore = new PermissionStore(this.context.api);
|
||||
|
||||
render () {
|
||||
let externalOverlay = null;
|
||||
@ -68,6 +74,7 @@ export default class Dapps extends Component {
|
||||
return (
|
||||
<div>
|
||||
<AddDapps store={ this.store } />
|
||||
<DappPermissions store={ this.permissionStore } />
|
||||
<Actionbar
|
||||
className={ styles.toolbar }
|
||||
title={
|
||||
@ -76,30 +83,31 @@ export default class Dapps extends Component {
|
||||
defaultMessage='Decentralized Applications' />
|
||||
}
|
||||
buttons={ [
|
||||
<FlatButton
|
||||
<Button
|
||||
icon={ <VisibleIcon /> }
|
||||
key='edit'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='dapps.button.edit'
|
||||
defaultMessage='edit' />
|
||||
}
|
||||
key='edit'
|
||||
icon={ <EyeIcon /> }
|
||||
onTouchTap={ this.store.openModal }
|
||||
/>
|
||||
onClick={ this.store.openModal }
|
||||
/>,
|
||||
<Button
|
||||
icon={ <LockedIcon /> }
|
||||
key='permissions'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='dapps.button.permissions'
|
||||
defaultMessage='permissions' />
|
||||
}
|
||||
onClick={ this.openPermissionsModal } />
|
||||
] }
|
||||
/>
|
||||
<Page>
|
||||
<div>
|
||||
{ this.renderList(this.store.visibleLocal) }
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{ this.renderList(this.store.visibleBuiltin) }
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{ this.renderList(this.store.visibleNetwork, externalOverlay) }
|
||||
</div>
|
||||
<div>{ this.renderList(this.store.visibleLocal) }</div>
|
||||
<div>{ this.renderList(this.store.visibleBuiltin) }</div>
|
||||
<div>{ this.renderList(this.store.visibleNetwork, externalOverlay) }</div>
|
||||
</Page>
|
||||
</div>
|
||||
);
|
||||
@ -131,4 +139,27 @@ export default class Dapps extends Component {
|
||||
onClickAcceptExternal = () => {
|
||||
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