Merge pull request #113 from 67P/feature/98-veto_unconfirmed_contributions

Veto contributions
This commit was merged in pull request #113.
This commit is contained in:
2019-05-01 19:43:28 +01:00
committed by GitHub
13 changed files with 174 additions and 63 deletions
+5 -1
View File
@@ -1,5 +1,9 @@
'use strict'; 'use strict';
module.exports = { module.exports = {
extends: 'recommended' extends: 'recommended',
rules: {
'simple-unless': false
}
}; };
+11 -11
View File
@@ -5,16 +5,16 @@ export default Component.extend({
tagName: 'ul', tagName: 'ul',
classNames: ['contribution-list'], classNames: ['contribution-list'],
// actions: { actions: {
//
// veto (contributionId) { veto (contributionId) {
// if (this.contractInteractionEnabled) { if (this.contractInteractionEnabled) {
// this.vetoContribution(contributionId); this.vetoContribution(contributionId);
// } else { } else {
// window.alert('Only members can veto contributions. Please ask someone to set you up.'); window.alert('Only members can veto contributions. Please ask someone to set you up.');
// } }
// } }
//
// } }
}); });
@@ -1,5 +1,5 @@
{{#each contributions as |contribution|}} {{#each contributions as |contribution|}}
<li data-contribution-id={{contribution.id}} class={{if contribution.confirmed "confirmed" "unconfirmed"}}> <li data-contribution-id={{contribution.id}} class="{{contribution-status contribution}} {{if contribution.vetoed "vetoed"}}">
<p class="meta"> <p class="meta">
<span class="recipient">{{user-avatar contributor=contribution.contributor}}</span> <span class="recipient">{{user-avatar contributor=contribution.contributor}}</span>
<span class="category {{contribution.kind}}">({{contribution.kind}})</span> <span class="category {{contribution.kind}}">({{contribution.kind}})</span>
@@ -14,5 +14,12 @@
<p class="kredits-amount"> <p class="kredits-amount">
<span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span> <span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
</p> </p>
{{#unless contribution.vetoed}}
{{#unless (is-confirmed-contribution contribution)}}
<p class="voting">
<button {{action "veto" contribution.id}} class="small danger">veto</button>
</p>
{{/unless}}
{{/unless}}
</li> </li>
{{/each}} {{/each}}
+5 -5
View File
@@ -35,15 +35,15 @@ export default Controller.extend({
actions: { actions: {
vetoContribution (/* contributionId */) { vetoContribution (contributionId) {
// this.kredits.vote(proposalId).then(transaction => { this.kredits.veto(contributionId).then(transaction => {
// window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash); console.debug('[controllers:index] Veto submitted to Ethereum blockhain: '+transaction.hash);
// }); });
}, },
confirmProposal (proposalId) { confirmProposal (proposalId) {
this.kredits.vote(proposalId).then(transaction => { this.kredits.vote(proposalId).then(transaction => {
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash); console.debug('[controllers:index] Vote submitted to Ethereum blockhain: '+transaction.hash);
}); });
}, },
+20
View File
@@ -0,0 +1,20 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
export default Helper.extend({
kredits: service(),
currentBlock: alias('kredits.currentBlock'),
compute([contribution]) {
if (contribution.vetoed) {
return 'vetoed';
} else if (contribution.confirmedAt > this.currentBlock) {
return 'unconfirmed';
} else {
return 'confirmed'
}
}
});
+15
View File
@@ -0,0 +1,15 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
export default Helper.extend({
kredits: service(),
currentBlock: alias('kredits.currentBlock'),
compute([contribution]) {
return !contribution.vetoed &&
(contribution.confirmedAt <= this.currentBlock);
}
});
+26 -8
View File
@@ -52,9 +52,10 @@ export default Service.extend({
}); });
} }
function instantiateWithAccount (web3Provider, context) { async function instantiateWithAccount (web3Provider, context) {
console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask'); console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
ethProvider = new ethers.providers.Web3Provider(web3Provider); ethProvider = new ethers.providers.Web3Provider(web3Provider);
// const network = await ethProvider.getNetwork();
ethProvider.listAccounts().then(accounts => { ethProvider.listAccounts().then(accounts => {
context.set('currentUserAccounts', accounts); context.set('currentUserAccounts', accounts);
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner(); const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner();
@@ -188,12 +189,22 @@ export default Service.extend({
console.debug('[kredits] vote for', proposalId); console.debug('[kredits] vote for', proposalId);
return this.kredits.Proposal.functions.vote(proposalId) return this.kredits.Proposal.functions.vote(proposalId)
.then((data) => { .then(data => {
console.debug('[kredits] vote response', data); console.debug('[kredits] vote response', data);
return data; return data;
}); });
}, },
veto(contributionId) {
console.debug('[kredits] veto against', contributionId);
return this.kredits.Contribution.functions.veto(contributionId, { gasLimit: 300000 })
.then(data => {
console.debug('[kredits] veto response', data);
return data;
});
},
getCurrentUser: computed('kredits.provider', function() { getCurrentUser: computed('kredits.provider', function() {
if (isEmpty(this.currentUserAccounts)) { if (isEmpty(this.currentUserAccounts)) {
return RSVP.resolve(); return RSVP.resolve();
@@ -214,23 +225,30 @@ export default Service.extend({
return this.proposals.findBy('id', proposalId.toString()); return this.proposals.findBy('id', proposalId.toString());
}, },
findContributionById(contributionId) {
return this.contributions.findBy('id', contributionId.toString());
},
// Contract events // Contract events
addContractEventHandlers() { addContractEventHandlers() {
// Proposal events this.kredits.Contribution
.on('ContributionVetoed', this.handleContributionVetoed.bind(this))
this.kredits.Proposal this.kredits.Proposal
.on('ProposalCreated', this.handleProposalCreated.bind(this)) .on('ProposalCreated', this.handleProposalCreated.bind(this))
.on('ProposalVoted', this.handleProposalVoted.bind(this)) .on('ProposalVoted', this.handleProposalVoted.bind(this))
.on('ProposalExecuted', this.handleProposalExecuted.bind(this)); .on('ProposalExecuted', this.handleProposalExecuted.bind(this));
// Token events
this.kredits.Token this.kredits.Token
.on('Transfer', this.handleTransfer.bind(this)); .on('Transfer', this.handleTransfer.bind(this));
}, },
handleContributionVetoed(contributionId) {
console.debug('[kredits] ContributionVetoed event received for ', contributionId);
const contribution = this.contributions.findBy('id', contributionId);
console.debug('[kredits] contribution', contribution);
if (contribution) {
contribution.set('vetoed', true);
}
},
handleProposalCreated(proposalId) { handleProposalCreated(proposalId) {
let proposal = this.findProposalById(proposalId); let proposal = this.findProposalById(proposalId);
+30
View File
@@ -0,0 +1,30 @@
button, input[type=submit] {
display: inline-block;
padding: 0.6rem 2rem;
background-color: rgba(22, 21, 40, 0.6);
border: 1px solid rgba(22, 21, 40, 1);
border-radius: 3px;
color: $primary-color;
font-weight: 500;
text-transform: uppercase;
cursor: pointer;
letter-spacing: 0.1em;
&:hover {
background-color: rgba(22, 21, 40, 0.8);
}
&.small {
font-size: 0.8rem;
padding: 0.2rem 0.8rem;
}
&.danger {
color: $red;
background-color: rgba(40, 21, 21, 0.6);
&:hover {
background-color: rgba(40, 21, 21, 0.8);
}
}
}
+1 -16
View File
@@ -90,22 +90,6 @@ section {
} }
} }
button, input[type=submit] {
display: inline-block;
border: 1px solid rgba(22, 21, 40, 1);
background-color: rgba(22, 21, 40, 0.6);
color: $primary-color;
border-radius: 3px;
font-weight: 500;
text-transform: uppercase;
cursor: pointer;
letter-spacing: 0.1em;
&:hover {
background-color: rgba(22, 21, 40, 0.8);
}
}
@media (min-width: 550px) { @media (min-width: 550px) {
section { section {
h2 { h2 {
@@ -114,6 +98,7 @@ button, input[type=submit] {
} }
} }
@import "buttons";
@import "components/topbar"; @import "components/topbar";
@import "components/loading-spinner"; @import "components/loading-spinner";
@import "components/contributor-list"; @import "components/contributor-list";
+11 -15
View File
@@ -13,7 +13,7 @@ ul.contribution-list {
li { li {
display: grid; display: grid;
grid-template-columns: auto 5rem; grid-template-columns: auto 5rem 5rem;
grid-row-gap: 0.5rem; grid-row-gap: 0.5rem;
padding: 1rem 1.2rem; padding: 1rem 1.2rem;
background-color: rgba(255,255,255,0.1); background-color: rgba(255,255,255,0.1);
@@ -24,6 +24,16 @@ ul.contribution-list {
border-top: 1px solid rgba(255,255,255,0.2); border-top: 1px solid rgba(255,255,255,0.2);
} }
&.confirmed {
grid-template-columns: auto 5rem;
}
&.vetoed {
grid-template-columns: auto 5rem;
text-decoration: line-through;
opacity: 0.6;
}
p { p {
align-self: center; align-self: center;
margin: 0; margin: 0;
@@ -33,14 +43,6 @@ ul.contribution-list {
&.kredits-amount, &.voting { &.kredits-amount, &.voting {
text-align: right; text-align: right;
} }
&.description {
grid-column-start: span 2;
}
&.voting {
grid-column-start: span 2;
}
} }
span { span {
@@ -92,12 +94,6 @@ ul.contribution-list {
color: $primary-color; color: $primary-color;
margin-right: 0.5rem; margin-right: 0.5rem;
} }
button {
height: 2rem;
line-height: 2rem;
padding: 0 0.6rem;
}
} }
} }
@@ -73,12 +73,6 @@ ul.proposal-list {
color: $primary-color; color: $primary-color;
margin-right: 0.5rem; margin-right: 0.5rem;
} }
button {
height: 2rem;
line-height: 2rem;
padding: 0 0.6rem;
}
} }
} }
+21
View File
@@ -0,0 +1,21 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Helper | contribution-status', function (hooks) {
setupTest(hooks);
test('returns the appropriate status', function (assert) {
const contributionStatus = this.owner.factoryFor('helper:contribution-status').create();
const kredits = this.owner.lookup('service:kredits');
kredits.set('currentBlock', 23000);
const contributionUnconfirmed = { confirmedAt: 23001, vetoed: false };
const contributionConfirmed = { confirmedAt: 21000, vetoed: false };
const contributionVetoed = { confirmedAt: 23001, vetoed: true };
assert.eq(contributionStatus.compute([contributionUnconfirmed]), 'unconfirmed');
assert.eq(contributionStatus.compute([contributionConfirmed]), 'confirmed');
assert.eq(contributionStatus.compute([contributionVetoed]), 'vetoed');
});
});
@@ -0,0 +1,21 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Helper | contribution-status', function (hooks) {
setupTest(hooks);
test('returns the appropriate status', function (assert) {
const contributionStatus = this.owner.factoryFor('helper:contribution-status').create();
const kredits = this.owner.lookup('service:kredits');
kredits.set('currentBlock', 23000);
const contributionUnconfirmed = { confirmedAt: 23001, vetoed: false };
const contributionConfirmed = { confirmedAt: 21000, vetoed: false };
const contributionVetoed = { confirmedAt: 23001, vetoed: true };
assert.notOk(contributionStatus.compute([contributionUnconfirmed]), 'unconfirmed');
assert.notOk(contributionStatus.compute([contributionVetoed]), 'vetoed');
assert.ok(contributionStatus.compute([contributionConfirmed]), 'confirmed');
});
});