diff --git a/.template-lintrc.js b/.template-lintrc.js
index b45e96f..02bee12 100644
--- a/.template-lintrc.js
+++ b/.template-lintrc.js
@@ -1,5 +1,9 @@
'use strict';
module.exports = {
- extends: 'recommended'
+ extends: 'recommended',
+
+ rules: {
+ 'simple-unless': false
+ }
};
diff --git a/app/components/contribution-list/component.js b/app/components/contribution-list/component.js
index c490f86..f85da1d 100644
--- a/app/components/contribution-list/component.js
+++ b/app/components/contribution-list/component.js
@@ -5,16 +5,16 @@ export default Component.extend({
tagName: 'ul',
classNames: ['contribution-list'],
- // actions: {
- //
- // veto (contributionId) {
- // if (this.contractInteractionEnabled) {
- // this.vetoContribution(contributionId);
- // } else {
- // window.alert('Only members can veto contributions. Please ask someone to set you up.');
- // }
- // }
- //
- // }
+ actions: {
+
+ veto (contributionId) {
+ if (this.contractInteractionEnabled) {
+ this.vetoContribution(contributionId);
+ } else {
+ window.alert('Only members can veto contributions. Please ask someone to set you up.');
+ }
+ }
+
+ }
});
diff --git a/app/components/contribution-list/template.hbs b/app/components/contribution-list/template.hbs
index d53ad52..29a5760 100644
--- a/app/components/contribution-list/template.hbs
+++ b/app/components/contribution-list/template.hbs
@@ -1,5 +1,5 @@
{{#each contributions as |contribution|}}
-
+
{{user-avatar contributor=contribution.contributor}}
({{contribution.kind}})
@@ -14,5 +14,12 @@
{{contribution.amount}}₭S
+ {{#unless contribution.vetoed}}
+ {{#unless (is-confirmed-contribution contribution)}}
+
+
+
+ {{/unless}}
+ {{/unless}}
{{/each}}
\ No newline at end of file
diff --git a/app/controllers/index.js b/app/controllers/index.js
index 7b2cc77..31009ad 100644
--- a/app/controllers/index.js
+++ b/app/controllers/index.js
@@ -35,15 +35,15 @@ export default Controller.extend({
actions: {
- vetoContribution (/* contributionId */) {
- // this.kredits.vote(proposalId).then(transaction => {
- // window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
- // });
+ vetoContribution (contributionId) {
+ this.kredits.veto(contributionId).then(transaction => {
+ console.debug('[controllers:index] Veto submitted to Ethereum blockhain: '+transaction.hash);
+ });
},
confirmProposal (proposalId) {
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);
});
},
diff --git a/app/helpers/contribution-status.js b/app/helpers/contribution-status.js
new file mode 100644
index 0000000..79e4bbb
--- /dev/null
+++ b/app/helpers/contribution-status.js
@@ -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'
+ }
+ }
+
+});
diff --git a/app/helpers/is-confirmed-contribution.js b/app/helpers/is-confirmed-contribution.js
new file mode 100644
index 0000000..0f63b76
--- /dev/null
+++ b/app/helpers/is-confirmed-contribution.js
@@ -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);
+ }
+
+});
diff --git a/app/services/kredits.js b/app/services/kredits.js
index ca78947..e66aeee 100644
--- a/app/services/kredits.js
+++ b/app/services/kredits.js
@@ -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');
ethProvider = new ethers.providers.Web3Provider(web3Provider);
+ // const network = await ethProvider.getNetwork();
ethProvider.listAccounts().then(accounts => {
context.set('currentUserAccounts', accounts);
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner();
@@ -188,12 +189,22 @@ export default Service.extend({
console.debug('[kredits] vote for', proposalId);
return this.kredits.Proposal.functions.vote(proposalId)
- .then((data) => {
+ .then(data => {
console.debug('[kredits] vote response', 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() {
if (isEmpty(this.currentUserAccounts)) {
return RSVP.resolve();
@@ -214,23 +225,30 @@ export default Service.extend({
return this.proposals.findBy('id', proposalId.toString());
},
- findContributionById(contributionId) {
- return this.contributions.findBy('id', contributionId.toString());
- },
-
// Contract events
addContractEventHandlers() {
- // Proposal events
+ this.kredits.Contribution
+ .on('ContributionVetoed', this.handleContributionVetoed.bind(this))
+
this.kredits.Proposal
.on('ProposalCreated', this.handleProposalCreated.bind(this))
.on('ProposalVoted', this.handleProposalVoted.bind(this))
.on('ProposalExecuted', this.handleProposalExecuted.bind(this));
- // Token events
this.kredits.Token
.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) {
let proposal = this.findProposalById(proposalId);
diff --git a/app/styles/_buttons.scss b/app/styles/_buttons.scss
new file mode 100644
index 0000000..8a6956a
--- /dev/null
+++ b/app/styles/_buttons.scss
@@ -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);
+ }
+ }
+}
diff --git a/app/styles/app.scss b/app/styles/app.scss
index 84ec881..69555ef 100644
--- a/app/styles/app.scss
+++ b/app/styles/app.scss
@@ -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) {
section {
h2 {
@@ -114,6 +98,7 @@ button, input[type=submit] {
}
}
+@import "buttons";
@import "components/topbar";
@import "components/loading-spinner";
@import "components/contributor-list";
diff --git a/app/styles/components/_contribution-list.scss b/app/styles/components/_contribution-list.scss
index c865532..71367b9 100644
--- a/app/styles/components/_contribution-list.scss
+++ b/app/styles/components/_contribution-list.scss
@@ -13,7 +13,7 @@ ul.contribution-list {
li {
display: grid;
- grid-template-columns: auto 5rem;
+ grid-template-columns: auto 5rem 5rem;
grid-row-gap: 0.5rem;
padding: 1rem 1.2rem;
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);
}
+ &.confirmed {
+ grid-template-columns: auto 5rem;
+ }
+
+ &.vetoed {
+ grid-template-columns: auto 5rem;
+ text-decoration: line-through;
+ opacity: 0.6;
+ }
+
p {
align-self: center;
margin: 0;
@@ -33,14 +43,6 @@ ul.contribution-list {
&.kredits-amount, &.voting {
text-align: right;
}
-
- &.description {
- grid-column-start: span 2;
- }
-
- &.voting {
- grid-column-start: span 2;
- }
}
span {
@@ -92,12 +94,6 @@ ul.contribution-list {
color: $primary-color;
margin-right: 0.5rem;
}
-
- button {
- height: 2rem;
- line-height: 2rem;
- padding: 0 0.6rem;
- }
}
}
diff --git a/app/styles/components/_proposal-list.scss b/app/styles/components/_proposal-list.scss
index 2599a0b..2527ad2 100644
--- a/app/styles/components/_proposal-list.scss
+++ b/app/styles/components/_proposal-list.scss
@@ -73,12 +73,6 @@ ul.proposal-list {
color: $primary-color;
margin-right: 0.5rem;
}
-
- button {
- height: 2rem;
- line-height: 2rem;
- padding: 0 0.6rem;
- }
}
}
diff --git a/tests/unit/helpers/contribution-status.js b/tests/unit/helpers/contribution-status.js
new file mode 100644
index 0000000..785b56c
--- /dev/null
+++ b/tests/unit/helpers/contribution-status.js
@@ -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');
+ });
+});
diff --git a/tests/unit/helpers/is-confirmed-contribution.js b/tests/unit/helpers/is-confirmed-contribution.js
new file mode 100644
index 0000000..3db7534
--- /dev/null
+++ b/tests/unit/helpers/is-confirmed-contribution.js
@@ -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');
+ });
+});