From 2b34d76b8ca02347e0bb8f2851c42c7805853d70 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 8 Dec 2016 12:54:13 +0100 Subject: [PATCH 01/11] pull out fetchMeta --- js/src/contracts/badgereg.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/js/src/contracts/badgereg.js b/js/src/contracts/badgereg.js index 45760b277..02d08e516 100644 --- a/js/src/contracts/badgereg.js +++ b/js/src/contracts/badgereg.js @@ -38,16 +38,9 @@ export default class BadgeReg { .then((badgeReg) => { return badgeReg.instance.fromName.call({}, [name]) .then(([ id, address ]) => { - return Promise.all([ - badgeReg.instance.meta.call({}, [id, 'TITLE']), - badgeReg.instance.meta.call({}, [id, 'IMG']) - ]) - .then(([ title, img ]) => { - title = bytesToHex(title); - title = title === ZERO ? null : hex2Ascii(title); - if (bytesToHex(img) === ZERO) img = null; - - const data = { address, name, title, icon: img }; + return this.fetchMeta(id) + .then(({ title, icon }) => { + const data = { address, name, title, icon }; this.certifiers[name] = data; return data; }); @@ -55,6 +48,22 @@ export default class BadgeReg { }); } + fetchMeta (id) { + return this._registry.getContract('badgereg') + .then((badgeReg) => { + return Promise.all([ + badgeReg.instance.meta.call({}, [id, 'TITLE']), + badgeReg.instance.meta.call({}, [id, 'IMG']) + ]); + }) + .then(([ title, icon ]) => { + title = bytesToHex(title); + title = title === ZERO ? null : hex2Ascii(title); + if (bytesToHex(icon) === ZERO) icon = null; + return { title, icon }; + }); + } + checkIfCertified (certifier, address) { if (!this.contracts[certifier]) { this.contracts[certifier] = this._api.newContract(ABI, certifier); From b32b636697dac347b2d9de259d6c97ccd6acbd46 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 8 Dec 2016 13:07:27 +0100 Subject: [PATCH 02/11] fetch certifiers by id --- js/src/contracts/badgereg.js | 44 ++++++++++++------- .../providers/certifications/middleware.js | 29 +++++++++--- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/js/src/contracts/badgereg.js b/js/src/contracts/badgereg.js index 02d08e516..45f7df315 100644 --- a/js/src/contracts/badgereg.js +++ b/js/src/contracts/badgereg.js @@ -18,7 +18,8 @@ import { bytesToHex, hex2Ascii } from '~/api/util/format'; import ABI from './abi/certifier.json'; -const ZERO = '0x0000000000000000000000000000000000000000000000000000000000000000'; +const ZERO20 = '0x0000000000000000000000000000000000000000'; +const ZERO32 = '0x0000000000000000000000000000000000000000000000000000000000000000'; export default class BadgeReg { constructor (api, registry) { @@ -26,25 +27,36 @@ export default class BadgeReg { this._registry = registry; registry.getContract('badgereg'); - this.certifiers = {}; // by name + this.certifiers = []; // by id this.contracts = {}; // by name } - fetchCertifier (name) { - if (this.certifiers[name]) { - return Promise.resolve(this.certifiers[name]); + nrOfCertifiers () { + return this._registry.getContract('badgereg') + .then((badgeReg) => { + return badgeReg.instance.badgeCount.call({}, []) + .then((count) => count.valueOf()); + }); + } + + fetchCertifier (id) { + if (this.certifiers[id]) { + return Promise.resolve(this.certifiers[id]); } return this._registry.getContract('badgereg') .then((badgeReg) => { - return badgeReg.instance.fromName.call({}, [name]) - .then(([ id, address ]) => { - return this.fetchMeta(id) - .then(({ title, icon }) => { - const data = { address, name, title, icon }; - this.certifiers[name] = data; - return data; - }); - }); + return badgeReg.instance.badge.call({}, [ id ]); + }) + .then(([ address, name ]) => { + if (address === ZERO20) throw new Error(`Certifier ${id} does not exist.`); + name = bytesToHex(name); + name = name === ZERO32 ? null : hex2Ascii(name); + return this.fetchMeta(id) + .then(({ title, icon }) => { + const data = { address, name, title, icon }; + this.certifiers[id] = data; + return data; + }); }); } @@ -58,8 +70,8 @@ export default class BadgeReg { }) .then(([ title, icon ]) => { title = bytesToHex(title); - title = title === ZERO ? null : hex2Ascii(title); - if (bytesToHex(icon) === ZERO) icon = null; + title = title === ZERO32 ? null : hex2Ascii(title); + if (bytesToHex(icon) === ZERO32) icon = null; return { title, icon }; }); } diff --git a/js/src/redux/providers/certifications/middleware.js b/js/src/redux/providers/certifications/middleware.js index a5406051f..6c443cea5 100644 --- a/js/src/redux/providers/certifications/middleware.js +++ b/js/src/redux/providers/certifications/middleware.js @@ -17,20 +17,39 @@ import Contracts from '~/contracts'; import { addCertification } from './actions'; -const knownCertifiers = [ 'smsverification' ]; +const knownCertifiers = [ + 0 // sms verification +]; export default class CertificationsMiddleware { toMiddleware () { return (store) => (next) => (action) => { - if (action.type !== 'fetchCertifications') { + if (action.type === 'fetchCertifiers') { + badgeReg.nrOfCertifiers().then((count) => { + new Array(+count).fill(null).forEach((_, id) => { + badgeReg.fetchCertifier(id) + .then((cert) => { + const { address, name, title, icon } = cert; + store.dispatch(addCertifier(address, name, title, icon)); + }) + .catch((err) => { + if (err) { + console.error(`Failed to fetch certifier ${id}:`, err); + } + }); + }); + }); + } + + else if (action.type !== 'fetchCertifications') { return next(action); } const { address } = action; const badgeReg = Contracts.get().badgeReg; - knownCertifiers.forEach((name) => { - badgeReg.fetchCertifier(name) + knownCertifiers.forEach((id) => { + badgeReg.fetchCertifier(id) .then((cert) => { return badgeReg.checkIfCertified(cert.address, address) .then((isCertified) => { @@ -42,7 +61,7 @@ export default class CertificationsMiddleware { }) .catch((err) => { if (err) { - console.error(`Failed to check if ${address} certified by ${name}:`, err); + console.error(`Failed to check if ${address} certified by ${id}:`, err); } }); }); From 409c4adfbfd2a396f46534d5098a7af738e85607 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 8 Dec 2016 22:05:16 +0100 Subject: [PATCH 03/11] fetch certifiers from BadgeReg --- .../redux/providers/certifications/actions.js | 4 + .../providers/certifications/middleware.js | 84 +++++++++++-------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/js/src/redux/providers/certifications/actions.js b/js/src/redux/providers/certifications/actions.js index c84f7db55..a8cc43f03 100644 --- a/js/src/redux/providers/certifications/actions.js +++ b/js/src/redux/providers/certifications/actions.js @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +export const fetchCertifiers = () => ({ + type: 'fetchCertifiers' +}); + export const fetchCertifications = (address) => ({ type: 'fetchCertifications', address }); diff --git a/js/src/redux/providers/certifications/middleware.js b/js/src/redux/providers/certifications/middleware.js index 6c443cea5..715a5cb59 100644 --- a/js/src/redux/providers/certifications/middleware.js +++ b/js/src/redux/providers/certifications/middleware.js @@ -14,57 +14,73 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { uniq } from 'lodash'; + +import ABI from '~/contracts/abi/certifier.json'; +import Contract from '~/api/contract'; import Contracts from '~/contracts'; import { addCertification } from './actions'; -const knownCertifiers = [ - 0 // sms verification -]; - export default class CertificationsMiddleware { toMiddleware () { + const api = Contracts.get()._api; + const badgeReg = Contracts.get().badgeReg; + const contract = new Contract(api, ABI); + const Confirmed = contract.events.find((e) => e.name === 'Confirmed'); + + let certifiers = []; + let accounts = []; // these are addresses + + const fetchConfirmedEvents = (dispatch) => { + if (certifiers.length === 0 || accounts.length === 0) return; + api.eth.getLogs({ + fromBlock: 0, + toBlock: 'latest', + address: certifiers.map((c) => c.address), + topics: [ Confirmed.signature, accounts ] + }) + .then((logs) => contract.parseEventLogs(logs)) + .then((logs) => { + logs.forEach((log) => { + const certifier = certifiers.find((c) => c.address === log.address); + if (!certifier) throw new Error(`Could not find certifier at ${log.address}.`); + const { name, title, icon } = certifier; + dispatch(addCertification(log.params.who.value, name, title, icon)); + }); + }) + .catch((err) => { + console.error('Failed to fetch Confirmed events:', err); + }); + }; + return (store) => (next) => (action) => { if (action.type === 'fetchCertifiers') { badgeReg.nrOfCertifiers().then((count) => { new Array(+count).fill(null).forEach((_, id) => { badgeReg.fetchCertifier(id) .then((cert) => { - const { address, name, title, icon } = cert; - store.dispatch(addCertifier(address, name, title, icon)); + if (!certifiers.some((c) => c.address === cert.address)) { + certifiers = certifiers.concat(cert); + fetchConfirmedEvents(store.dispatch); + } }) .catch((err) => { - if (err) { - console.error(`Failed to fetch certifier ${id}:`, err); - } + console.warn(`Could not fetch certifier ${id}:`, err); }); }); }); - } + } else if (action.type === 'fetchCertifications') { + const { address } = action; - else if (action.type !== 'fetchCertifications') { - return next(action); - } - - const { address } = action; - const badgeReg = Contracts.get().badgeReg; - - knownCertifiers.forEach((id) => { - badgeReg.fetchCertifier(id) - .then((cert) => { - return badgeReg.checkIfCertified(cert.address, address) - .then((isCertified) => { - if (isCertified) { - const { name, title, icon } = cert; - store.dispatch(addCertification(address, name, title, icon)); - } - }); - }) - .catch((err) => { - if (err) { - console.error(`Failed to check if ${address} certified by ${id}:`, err); - } - }); - }); + if (!accounts.includes(address)) { + accounts = accounts.concat(address); + fetchConfirmedEvents(store.dispatch); + } + } else if (action.type === 'setVisibleAccounts') { + const { addresses } = action; + accounts = uniq(accounts.concat(addresses)); + fetchConfirmedEvents(store.dispatch); + } else return next(action); }; } } From e1c5796a5c43643aab7075ffe2be984a2f4a6d3a Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 8 Dec 2016 22:58:24 +0100 Subject: [PATCH 04/11] fetch certifications in account view --- js/src/ui/Certifications/certifications.js | 16 ++-------------- js/src/views/Account/account.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/js/src/ui/Certifications/certifications.js b/js/src/ui/Certifications/certifications.js index 871b14e9c..b034deda4 100644 --- a/js/src/ui/Certifications/certifications.js +++ b/js/src/ui/Certifications/certifications.js @@ -19,7 +19,6 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { hashToImageUrl } from '~/redux/providers/imagesReducer'; -import { fetchCertifications } from '~/redux/providers/certifications/actions'; import defaultIcon from '../../../assets/images/certifications/unknown.svg'; @@ -29,14 +28,7 @@ class Certifications extends Component { static propTypes = { account: PropTypes.string.isRequired, certifications: PropTypes.array.isRequired, - dappsUrl: PropTypes.string.isRequired, - - fetchCertifications: PropTypes.func.isRequired - } - - componentWillMount () { - const { account, fetchCertifications } = this.props; - fetchCertifications(account); + dappsUrl: PropTypes.string.isRequired } render () { @@ -77,11 +69,7 @@ function mapStateToProps (_, initProps) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({ fetchCertifications }, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(Certifications); diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js index 98b0a5e97..ec9551d6b 100644 --- a/js/src/views/Account/account.js +++ b/js/src/views/Account/account.js @@ -31,6 +31,7 @@ import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png'; import Header from './Header'; import Transactions from './Transactions'; import { setVisibleAccounts } from '~/redux/providers/personalActions'; +import { fetchCertifiers, fetchCertifications } from '~/redux/providers/certifications/actions'; import VerificationStore from '~/modals/SMSVerification/store'; @@ -43,6 +44,8 @@ class Account extends Component { static propTypes = { setVisibleAccounts: PropTypes.func.isRequired, + fetchCertifiers: PropTypes.func.isRequired, + fetchCertifications: PropTypes.func.isRequired, images: PropTypes.object.isRequired, params: PropTypes.object, @@ -62,6 +65,7 @@ class Account extends Component { } componentDidMount () { + this.props.fetchCertifiers(); this.setVisibleAccounts(); } @@ -88,9 +92,10 @@ class Account extends Component { } setVisibleAccounts (props = this.props) { - const { params, setVisibleAccounts } = props; + const { params, setVisibleAccounts, fetchCertifications } = props; const addresses = [ params.address ]; setVisibleAccounts(addresses); + fetchCertifications(params.address); } render () { @@ -344,7 +349,9 @@ function mapStateToProps (state) { function mapDispatchToProps (dispatch) { return bindActionCreators({ - setVisibleAccounts + setVisibleAccounts, + fetchCertifiers, + fetchCertifications }, dispatch); } From 5862f2a9ebd2d72f2d639802de1c2d7b96f5b3b4 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 9 Dec 2016 00:30:05 +0100 Subject: [PATCH 05/11] Certifications: read dappsUrl from state --- js/src/ui/Certifications/certifications.js | 6 +++--- js/src/views/Account/Header/header.js | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/js/src/ui/Certifications/certifications.js b/js/src/ui/Certifications/certifications.js index b034deda4..47cf36942 100644 --- a/js/src/ui/Certifications/certifications.js +++ b/js/src/ui/Certifications/certifications.js @@ -27,8 +27,7 @@ import styles from './certifications.css'; class Certifications extends Component { static propTypes = { account: PropTypes.string.isRequired, - certifications: PropTypes.array.isRequired, - dappsUrl: PropTypes.string.isRequired + certifications: PropTypes.array.isRequired } render () { @@ -65,7 +64,8 @@ function mapStateToProps (_, initProps) { return (state) => { const certifications = state.certifications[account] || []; - return { certifications }; + const dappsUrl = state.api.dappsUrl; + return { certifications, dappsUrl }; }; } diff --git a/js/src/views/Account/Header/header.js b/js/src/views/Account/Header/header.js index b126951b2..c77c4987d 100644 --- a/js/src/views/Account/Header/header.js +++ b/js/src/views/Account/Header/header.js @@ -78,7 +78,6 @@ export default class Header extends Component { balance={ balance } /> { children } From a84cd9143f014c9bba9a5b2bc6c9790bf9d9f27b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 9 Dec 2016 00:34:28 +0100 Subject: [PATCH 06/11] show certifications in accounts list --- js/src/views/Accounts/List/list.js | 40 ++++++++++++++++++++++-- js/src/views/Accounts/Summary/summary.js | 17 +++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/js/src/views/Accounts/List/list.js b/js/src/views/Accounts/List/list.js index 4d54b640f..8c29d17f0 100644 --- a/js/src/views/Accounts/List/list.js +++ b/js/src/views/Accounts/List/list.js @@ -15,13 +15,16 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import { Container } from '~/ui'; +import { fetchCertifiers, fetchCertifications } from '~/redux/providers/certifications/actions'; import Summary from '../Summary'; import styles from './list.css'; -export default class List extends Component { +class List extends Component { static propTypes = { accounts: PropTypes.object, walletsOwners: PropTypes.object, @@ -31,7 +34,11 @@ export default class List extends Component { empty: PropTypes.bool, order: PropTypes.string, orderFallback: PropTypes.string, - handleAddSearchToken: PropTypes.func + certifications: PropTypes.object.isRequired, + + handleAddSearchToken: PropTypes.func, + fetchCertifiers: PropTypes.func.isRequired, + fetchCertifications: PropTypes.func.isRequired }; render () { @@ -42,6 +49,14 @@ export default class List extends Component { ); } + componentWillMount () { + const { fetchCertifiers, accounts, fetchCertifications } = this.props; + fetchCertifiers(); + for (let address in accounts) { + fetchCertifications(address); + } + } + renderAccounts () { const { accounts, balances, link, empty, handleAddSearchToken, walletsOwners } = this.props; @@ -72,7 +87,9 @@ export default class List extends Component { account={ account } balance={ balance } owners={ owners } - handleAddSearchToken={ handleAddSearchToken } /> + handleAddSearchToken={ handleAddSearchToken } + showCertifications + /> ); }); @@ -207,3 +224,20 @@ export default class List extends Component { }); } } + +function mapStateToProps (state) { + const { certifications } = state; + return { certifications }; +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({ + fetchCertifiers, + fetchCertifications + }, dispatch); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(List); diff --git a/js/src/views/Accounts/Summary/summary.js b/js/src/views/Accounts/Summary/summary.js index 764f24edf..0894e8699 100644 --- a/js/src/views/Accounts/Summary/summary.js +++ b/js/src/views/Accounts/Summary/summary.js @@ -20,6 +20,7 @@ import { isEqual } from 'lodash'; import ReactTooltip from 'react-tooltip'; import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '~/ui'; +import Certifications from '~/ui/Certifications'; import { nullableProptype } from '~/util/proptypes'; import styles from '../accounts.css'; @@ -35,12 +36,14 @@ export default class Summary extends Component { link: PropTypes.string, name: PropTypes.string, noLink: PropTypes.bool, + showCertifications: PropTypes.bool, handleAddSearchToken: PropTypes.func, owners: nullableProptype(PropTypes.array) }; static defaultProps = { - noLink: false + noLink: false, + showCertifications: false }; shouldComponentUpdate (nextProps) { @@ -107,6 +110,7 @@ export default class Summary extends Component { { this.renderOwners() } { this.renderBalance() } + { this.renderCertifications() } ); } @@ -172,4 +176,15 @@ export default class Summary extends Component { ); } + + renderCertifications () { + const { showCertifications, account } = this.props; + if (!showCertifications) { + return null; + } + + return ( + + ); + } } From e53629089230f23899f73840e8b218f5d4062c8d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 9 Dec 2016 00:38:44 +0100 Subject: [PATCH 07/11] fix linting issues --- js/src/ui/Certifications/certifications.js | 4 ++-- js/src/views/Account/Header/header.js | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/js/src/ui/Certifications/certifications.js b/js/src/ui/Certifications/certifications.js index 47cf36942..36635ff58 100644 --- a/js/src/ui/Certifications/certifications.js +++ b/js/src/ui/Certifications/certifications.js @@ -16,7 +16,6 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import { hashToImageUrl } from '~/redux/providers/imagesReducer'; @@ -27,7 +26,8 @@ import styles from './certifications.css'; class Certifications extends Component { static propTypes = { account: PropTypes.string.isRequired, - certifications: PropTypes.array.isRequired + certifications: PropTypes.array.isRequired, + dappsUrl: PropTypes.string.isRequired } render () { diff --git a/js/src/views/Account/Header/header.js b/js/src/views/Account/Header/header.js index c77c4987d..c2b9151e7 100644 --- a/js/src/views/Account/Header/header.js +++ b/js/src/views/Account/Header/header.js @@ -23,10 +23,6 @@ import Certifications from '~/ui/Certifications'; import styles from './header.css'; export default class Header extends Component { - static contextTypes = { - api: PropTypes.object - }; - static propTypes = { account: PropTypes.object, balance: PropTypes.object, @@ -40,7 +36,6 @@ export default class Header extends Component { }; render () { - const { api } = this.context; const { account, balance, className, children } = this.props; const { address, meta, uuid } = account; From fd88421e88d71153e7917e4958264d62ea969e7a Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 9 Dec 2016 13:35:10 +0100 Subject: [PATCH 08/11] store id with certifiers --- js/src/contracts/badgereg.js | 2 +- js/src/redux/providers/certifications/actions.js | 8 ++++++-- js/src/redux/providers/certifications/middleware.js | 6 +++--- js/src/redux/providers/certifications/reducer.js | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/js/src/contracts/badgereg.js b/js/src/contracts/badgereg.js index 45f7df315..629add3a4 100644 --- a/js/src/contracts/badgereg.js +++ b/js/src/contracts/badgereg.js @@ -53,7 +53,7 @@ export default class BadgeReg { name = name === ZERO32 ? null : hex2Ascii(name); return this.fetchMeta(id) .then(({ title, icon }) => { - const data = { address, name, title, icon }; + const data = { address, id, name, title, icon }; this.certifiers[id] = data; return data; }); diff --git a/js/src/redux/providers/certifications/actions.js b/js/src/redux/providers/certifications/actions.js index a8cc43f03..d0c2d56f4 100644 --- a/js/src/redux/providers/certifications/actions.js +++ b/js/src/redux/providers/certifications/actions.js @@ -22,6 +22,10 @@ export const fetchCertifications = (address) => ({ type: 'fetchCertifications', address }); -export const addCertification = (address, name, title, icon) => ({ - type: 'addCertification', address, name, title, icon +export const addCertification = (address, id, name, title, icon) => ({ + type: 'addCertification', address, id, name, title, icon +}); + +export const removeCertification = (address, id, name, title, icon) => ({ + type: 'removeCertification', address, id, name, title, icon }); diff --git a/js/src/redux/providers/certifications/middleware.js b/js/src/redux/providers/certifications/middleware.js index 715a5cb59..d90045270 100644 --- a/js/src/redux/providers/certifications/middleware.js +++ b/js/src/redux/providers/certifications/middleware.js @@ -44,8 +44,8 @@ export default class CertificationsMiddleware { logs.forEach((log) => { const certifier = certifiers.find((c) => c.address === log.address); if (!certifier) throw new Error(`Could not find certifier at ${log.address}.`); - const { name, title, icon } = certifier; - dispatch(addCertification(log.params.who.value, name, title, icon)); + const { id, name, title, icon } = certifier; + dispatch(addCertification(log.params.who.value, id, name, title, icon)); }); }) .catch((err) => { @@ -59,7 +59,7 @@ export default class CertificationsMiddleware { new Array(+count).fill(null).forEach((_, id) => { badgeReg.fetchCertifier(id) .then((cert) => { - if (!certifiers.some((c) => c.address === cert.address)) { + if (!certifiers.some((c) => c.id === cert.id)) { certifiers = certifiers.concat(cert); fetchConfirmedEvents(store.dispatch); } diff --git a/js/src/redux/providers/certifications/reducer.js b/js/src/redux/providers/certifications/reducer.js index 87b0dfd33..df726be82 100644 --- a/js/src/redux/providers/certifications/reducer.js +++ b/js/src/redux/providers/certifications/reducer.js @@ -21,13 +21,13 @@ export default (state = initialState, action) => { return state; } - const { address, name, icon, title } = action; + const { address, id, name, icon, title } = action; const certifications = state[address] || []; - if (certifications.some((c) => c.name === name)) { + if (certifications.some((c) => c.id === id)) { return state; } - const newCertifications = certifications.concat({ name, icon, title }); + const newCertifications = certifications.concat({ id, name, icon, title }); return { ...state, [address]: newCertifications }; }; From 6d20592f761ac7b948dfa917e719d9805e472a3c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 9 Dec 2016 14:06:02 +0100 Subject: [PATCH 09/11] remove certification on Revoke --- .../redux/providers/certifications/actions.js | 4 ++-- .../providers/certifications/middleware.js | 11 ++++++--- .../redux/providers/certifications/reducer.js | 24 ++++++++++++------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/js/src/redux/providers/certifications/actions.js b/js/src/redux/providers/certifications/actions.js index d0c2d56f4..3638896b4 100644 --- a/js/src/redux/providers/certifications/actions.js +++ b/js/src/redux/providers/certifications/actions.js @@ -26,6 +26,6 @@ export const addCertification = (address, id, name, title, icon) => ({ type: 'addCertification', address, id, name, title, icon }); -export const removeCertification = (address, id, name, title, icon) => ({ - type: 'removeCertification', address, id, name, title, icon +export const removeCertification = (address, id) => ({ + type: 'removeCertification', address, id }); diff --git a/js/src/redux/providers/certifications/middleware.js b/js/src/redux/providers/certifications/middleware.js index d90045270..fe3d6b8df 100644 --- a/js/src/redux/providers/certifications/middleware.js +++ b/js/src/redux/providers/certifications/middleware.js @@ -19,7 +19,7 @@ import { uniq } from 'lodash'; import ABI from '~/contracts/abi/certifier.json'; import Contract from '~/api/contract'; import Contracts from '~/contracts'; -import { addCertification } from './actions'; +import { addCertification, removeCertification } from './actions'; export default class CertificationsMiddleware { toMiddleware () { @@ -27,6 +27,7 @@ export default class CertificationsMiddleware { const badgeReg = Contracts.get().badgeReg; const contract = new Contract(api, ABI); const Confirmed = contract.events.find((e) => e.name === 'Confirmed'); + const Revoked = contract.events.find((e) => e.name === 'Revoked'); let certifiers = []; let accounts = []; // these are addresses @@ -37,7 +38,7 @@ export default class CertificationsMiddleware { fromBlock: 0, toBlock: 'latest', address: certifiers.map((c) => c.address), - topics: [ Confirmed.signature, accounts ] + topics: [ [ Confirmed.signature, Revoked.signature ], accounts ] }) .then((logs) => contract.parseEventLogs(logs)) .then((logs) => { @@ -45,7 +46,11 @@ export default class CertificationsMiddleware { const certifier = certifiers.find((c) => c.address === log.address); if (!certifier) throw new Error(`Could not find certifier at ${log.address}.`); const { id, name, title, icon } = certifier; - dispatch(addCertification(log.params.who.value, id, name, title, icon)); + if (log.event === 'Revoked') { + dispatch(removeCertification(log.params.who.value, id)); + } else { + dispatch(addCertification(log.params.who.value, id, name, title, icon)); + } }); }) .catch((err) => { diff --git a/js/src/redux/providers/certifications/reducer.js b/js/src/redux/providers/certifications/reducer.js index df726be82..4014ca241 100644 --- a/js/src/redux/providers/certifications/reducer.js +++ b/js/src/redux/providers/certifications/reducer.js @@ -17,17 +17,25 @@ const initialState = {}; export default (state = initialState, action) => { - if (action.type !== 'addCertification') { - return state; + if (action.type === 'addCertification') { + const { address, id, name, icon, title } = action; + const certifications = state[address] || []; + + if (certifications.some((c) => c.id === id)) { + return state; + } + const newCertifications = certifications.concat({ id, name, icon, title }); + + return { ...state, [address]: newCertifications }; } - const { address, id, name, icon, title } = action; - const certifications = state[address] || []; + if (action.type === 'removeCertification') { + const { address, id } = action; + const certifications = state[address] || []; - if (certifications.some((c) => c.id === id)) { - return state; + const newCertifications = certifications.filter((c) => c.id !== id); + return { ...state, [address]: newCertifications }; } - const newCertifications = certifications.concat({ id, name, icon, title }); - return { ...state, [address]: newCertifications }; + return state; }; From f59f7c57730c1280f499e4c6dbe46a4c3d5e221c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 15 Dec 2016 13:48:24 +0100 Subject: [PATCH 10/11] address style grumbles :lipstick: --- js/src/contracts/badgereg.js | 11 +++- .../providers/certifications/middleware.js | 62 +++++++++++-------- .../redux/providers/certifications/reducer.js | 4 +- js/src/ui/Certifications/certifications.js | 1 + 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/js/src/contracts/badgereg.js b/js/src/contracts/badgereg.js index 10943c43e..8075f456e 100644 --- a/js/src/contracts/badgereg.js +++ b/js/src/contracts/badgereg.js @@ -31,7 +31,7 @@ export default class BadgeReg { this.contracts = {}; // by name } - nrOfCertifiers () { + certifierCount () { return this._registry.getContract('badgereg') .then((badgeReg) => { return badgeReg.instance.badgeCount.call({}, []) @@ -48,9 +48,14 @@ export default class BadgeReg { return badgeReg.instance.badge.call({}, [ id ]); }) .then(([ address, name ]) => { - if (address === ZERO20) throw new Error(`Certifier ${id} does not exist.`); + if (address === ZERO20) { + throw new Error(`Certifier ${id} does not exist.`); + } + name = bytesToHex(name); - name = name === ZERO32 ? null : hex2Ascii(name); + name = name === ZERO32 + ? null + : hex2Ascii(name); return this.fetchMeta(id) .then(({ title, icon }) => { const data = { address, id, name, title, icon }; diff --git a/js/src/redux/providers/certifications/middleware.js b/js/src/redux/providers/certifications/middleware.js index 6cbfffa72..6e4b898d0 100644 --- a/js/src/redux/providers/certifications/middleware.js +++ b/js/src/redux/providers/certifications/middleware.js @@ -44,8 +44,11 @@ export default class CertificationsMiddleware { .then((logs) => { logs.forEach((log) => { const certifier = certifiers.find((c) => c.address === log.address); - if (!certifier) throw new Error(`Could not find certifier at ${log.address}.`); + if (!certifier) { + throw new Error(`Could not find certifier at ${log.address}.`); + } const { id, name, title, icon } = certifier; + if (log.event === 'Revoked') { dispatch(removeCertification(log.params.who.value, id)); } else { @@ -59,33 +62,42 @@ export default class CertificationsMiddleware { }; return (store) => (next) => (action) => { - if (action.type === 'fetchCertifiers') { - badgeReg.nrOfCertifiers().then((count) => { - new Array(+count).fill(null).forEach((_, id) => { - badgeReg.fetchCertifier(id) - .then((cert) => { - if (!certifiers.some((c) => c.id === cert.id)) { - certifiers = certifiers.concat(cert); - fetchConfirmedEvents(store.dispatch); - } - }) - .catch((err) => { - console.warn(`Could not fetch certifier ${id}:`, err); - }); + switch (action.type) { + case 'fetchCertifiers': + badgeReg.certifierCount().then((count) => { + new Array(+count).fill(null).forEach((_, id) => { + badgeReg.fetchCertifier(id) + .then((cert) => { + if (!certifiers.some((c) => c.id === cert.id)) { + certifiers = certifiers.concat(cert); + fetchConfirmedEvents(store.dispatch); + } + }) + .catch((err) => { + console.warn(`Could not fetch certifier ${id}:`, err); + }); + }); }); - }); - } else if (action.type === 'fetchCertifications') { - const { address } = action; - if (!accounts.includes(address)) { - accounts = accounts.concat(address); + break; + case 'fetchCertifications': + const { address } = action; + + if (!accounts.includes(address)) { + accounts = accounts.concat(address); + fetchConfirmedEvents(store.dispatch); + } + + break; + case 'setVisibleAccounts': + const { addresses } = action; + accounts = uniq(accounts.concat(addresses)); fetchConfirmedEvents(store.dispatch); - } - } else if (action.type === 'setVisibleAccounts') { - const { addresses } = action; - accounts = uniq(accounts.concat(addresses)); - fetchConfirmedEvents(store.dispatch); - } else return next(action); + + break; + default: + next(action); + } }; } } diff --git a/js/src/redux/providers/certifications/reducer.js b/js/src/redux/providers/certifications/reducer.js index d96ed1d65..bb2681cf6 100644 --- a/js/src/redux/providers/certifications/reducer.js +++ b/js/src/redux/providers/certifications/reducer.js @@ -24,8 +24,10 @@ export default (state = initialState, action) => { if (certifications.some((c) => c.id === id)) { return state; } - const newCertifications = certifications.concat({ id, name, icon, title }); + const newCertifications = certifications.concat({ + id, name, icon, title + }); return { ...state, [address]: newCertifications }; } diff --git a/js/src/ui/Certifications/certifications.js b/js/src/ui/Certifications/certifications.js index 6c9b2fe2c..bafd06f35 100644 --- a/js/src/ui/Certifications/certifications.js +++ b/js/src/ui/Certifications/certifications.js @@ -65,6 +65,7 @@ function mapStateToProps (_, initProps) { return (state) => { const certifications = state.certifications[account] || []; const dappsUrl = state.api.dappsUrl; + return { certifications, dappsUrl }; }; } From 4c42dedcad11969a58cf381cc0328da7db770817 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 15 Dec 2016 13:51:44 +0100 Subject: [PATCH 11/11] sort props :lipstick: --- js/src/views/Accounts/List/list.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/js/src/views/Accounts/List/list.js b/js/src/views/Accounts/List/list.js index 44a8ff888..d5bdd9662 100644 --- a/js/src/views/Accounts/List/list.js +++ b/js/src/views/Accounts/List/list.js @@ -27,18 +27,18 @@ import styles from './list.css'; class List extends Component { static propTypes = { accounts: PropTypes.object, - walletsOwners: PropTypes.object, balances: PropTypes.object, - link: PropTypes.string, - search: PropTypes.array, + certifications: PropTypes.object.isRequired, empty: PropTypes.bool, + link: PropTypes.string, order: PropTypes.string, orderFallback: PropTypes.string, - certifications: PropTypes.object.isRequired, + search: PropTypes.array, + walletsOwners: PropTypes.object, - handleAddSearchToken: PropTypes.func, fetchCertifiers: PropTypes.func.isRequired, - fetchCertifications: PropTypes.func.isRequired + fetchCertifications: PropTypes.func.isRequired, + handleAddSearchToken: PropTypes.func }; render () { @@ -50,7 +50,7 @@ class List extends Component { } componentWillMount () { - const { fetchCertifiers, accounts, fetchCertifications } = this.props; + const { accounts, fetchCertifiers, fetchCertifications } = this.props; fetchCertifiers(); for (let address in accounts) { fetchCertifications(address); @@ -58,7 +58,7 @@ class List extends Component { } renderAccounts () { - const { accounts, balances, link, empty, handleAddSearchToken, walletsOwners } = this.props; + const { accounts, balances, empty, link, walletsOwners, handleAddSearchToken } = this.props; if (empty) { return (