Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

refresh certifications automatically #3878

Merged
merged 8 commits into from
Dec 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
"blockies": "0.0.2",
"brace": "0.9.0",
"bytes": "2.4.0",
"debounce": "1.0.0",
"es6-error": "4.0.0",
"es6-promise": "4.0.5",
"ethereumjs-tx": "1.1.4",
Expand Down
2 changes: 1 addition & 1 deletion js/src/modals/Verification/email-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import VerificationStore, {
} from './store';
import { postToServer } from '../../3rdparty/email-verification';

const EMAIL_VERIFICATION = 4; // id in the `BadgeReg.sol` contract
const EMAIL_VERIFICATION = 7; // id in the `BadgeReg.sol` contract

export default class EmailVerificationStore extends VerificationStore {
@observable email = '';
Expand Down
170 changes: 110 additions & 60 deletions js/src/redux/providers/certifications/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,41 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

import { uniq } from 'lodash';
import debounce from 'debounce';

import ABI from '~/contracts/abi/certifier.json';
import Contract from '~/api/contract';
import Contracts from '~/contracts';
import { addCertification, removeCertification } from './actions';

// TODO: move this to a more general place
const updatableFilter = (api, onFilter) => {
let filter = null;

const update = (address, topics) => {
if (filter) {
filter = filter.then((filterId) => {
api.eth.uninstallFilter(filterId);
});
}
filter = (filter || Promise.resolve())
.then(() => api.eth.newFilter({
fromBlock: 0,
toBlock: 'latest',
address,
topics
}))
.then((filterId) => {
onFilter(filterId);
return filterId;
})
.catch((err) => {
console.error('Failed to create certifications filter:', err);
});
};
return update;
};

export default class CertificationsMiddleware {
toMiddleware () {
const api = Contracts.get()._api;
Expand All @@ -29,75 +58,96 @@ export default class CertificationsMiddleware {
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
return (store) => {
const onLogs = (logs) => {
logs = contract.parseEventLogs(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 { id, name, title, icon } = certifier;

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, Revoked.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 { id, name, title, icon } = certifier;
if (log.event === 'Revoked') {
store.dispatch(removeCertification(log.params.who.value, id));
} else {
store.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));
}
let filter = null;

const onFilter = (filterId) => {
filter = filterId;
api.eth.getFilterLogs(filterId)
.then(onLogs)
.catch((err) => {
console.error('Failed to fetch certifier events:', err);
});
})
.catch((err) => {
console.error('Failed to fetch Confirmed events:', err);
});
};
};

return (store) => (next) => (action) => {
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);
});
});
const fetchChanges = debounce(() => {
api.eth.getFilterChanges(filter)
.then(onLogs)
.catch((err) => {
console.error('Failed to fetch new certifier events:', err);
});
}, 10 * 1000, true);
api.subscribe('eth_blockNumber', (err) => {
if (err) return;
fetchChanges();
});

break;
case 'fetchCertifications':
const { address } = action;
const updateFilter = updatableFilter(api, onFilter);
let certifiers = [];
let accounts = []; // these are addresses

if (!accounts.includes(address)) {
accounts = accounts.concat(address);
fetchConfirmedEvents(store.dispatch);
}
const fetchConfirmedEvents = () => {
updateFilter(certifiers.map((c) => c.address), [
[ Confirmed.signature, Revoked.signature ],
accounts
]);
};

return (next) => (action) => {
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();
}
})
.catch((err) => {
console.warn(`Could not fetch certifier ${id}:`, err);
});
});
});

break;
case 'fetchCertifications':
const { address } = action;

if (!accounts.includes(address)) {
accounts = accounts.concat(address);
fetchConfirmedEvents();
}

break;
case 'setVisibleAccounts':
const { addresses } = action;
accounts = uniq(accounts.concat(addresses));
fetchConfirmedEvents(store.dispatch);
break;
case 'setVisibleAccounts':
const { addresses } = action;
accounts = uniq(accounts.concat(addresses));
fetchConfirmedEvents();

break;
default:
next(action);
}
break;
default:
next(action);
}
};
};
}
}
1 change: 1 addition & 0 deletions js/src/ui/Certifications/certifications.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
.certification {
display: inline-block;
position: relative;
margin-top: 1em;
margin-right: .5em;
padding: .3em .6em .2em 2.6em;
border-radius: 1em;
Expand Down