Switch to contribution token instead of proposals

No vetos yet, and only for collections (not creation).

closes #20
This commit is contained in:
2019-04-09 12:55:44 +02:00
parent 9c4e431e3b
commit aa28a14d04
11 changed files with 274 additions and 28 deletions
@@ -0,0 +1,20 @@
import Component from '@ember/component';
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.');
// }
// }
//
// }
});
@@ -0,0 +1,16 @@
{{#each contributions as |contribution|}}
<li data-contribution-id={{contribution.id}} class={{if contribution.confirmed "confirmed" "unconfirmed"}}>
<p class="meta">
<span class="category {{contribution.kind}}">♥ ({{contribution.kind}})</span>
<span class="recipient">{{contribution.contributor.name}}:</span>
</p>
<p class="kredits-amount">
<span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
</p>
<p class="description">
<span class="description">{{contribution.description}}</span>
</p>
<p class="voting">
</p>
</li>
{{/each}}
+27 -7
View File
@@ -1,9 +1,11 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { alias, filter, filterBy, sort } from '@ember/object/computed'; import { alias, filter, sort } from '@ember/object/computed';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
export default Controller.extend({ export default Controller.extend({
kredits: service(), kredits: service(),
currentBlock: alias('kredits.currentBlock'),
contributors: alias('kredits.contributors'), contributors: alias('kredits.contributors'),
contributorsWithKredits: filter('contributors', function(contributor) { contributorsWithKredits: filter('contributors', function(contributor) {
@@ -12,14 +14,31 @@ export default Controller.extend({
contributorsSorting: Object.freeze(['balance:desc']), contributorsSorting: Object.freeze(['balance:desc']),
contributorsSorted: sort('contributorsWithKredits', 'contributorsSorting'), contributorsSorted: sort('contributorsWithKredits', 'contributorsSorting'),
proposals: alias('kredits.proposals'), contributions: alias('kredits.contributions'),
proposalsOpen: filterBy('proposals', 'isExecuted', false), contributionsSorting: Object.freeze(['id:desc']),
proposalsClosed: filterBy('proposals', 'isExecuted', true),
proposalsSorting: Object.freeze(['id:desc']), contributionsUnconfirmed: computed('contributions.[]', 'currentBlock', function() {
proposalsClosedSorted: sort('proposalsClosed', 'proposalsSorting'), return this.contributions.filter(contribution => {
proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'), return contribution.confirmedAt < this.currentBlock;
});
}),
contributionsConfirmed: computed('contributions.[]', 'currentBlock', function() {
return this.contributions.filter(contribution => {
return contribution.confirmedAt >= this.currentBlock;
});
}),
contributionsUnconfirmedSorted: sort('contributionsUnconfirmed', 'contributionsSorting'),
contributionsConfirmedSorted: sort('contributionsConfirmed', 'contributionsSorting'),
actions: { actions: {
vetoContribution (/* contributionId */) {
// this.kredits.vote(proposalId).then(transaction => {
// window.confirm('Vote 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); window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
@@ -33,5 +52,6 @@ export default Controller.extend({
return contributor; return contributor;
}); });
} }
} }
}); });
+28
View File
@@ -0,0 +1,28 @@
import EmberObject from '@ember/object';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default EmberObject.extend({
// Contract
id: null,
contributorId: null,
amount: null,
confirmedAt: bignumber('confirmedAtBlock', 'toNumber'),
vetoed: null,
ipfsHash: null,
creatorAccount: null,
// IPFS
kind: null,
description: null,
details: null,
url: null,
ipfsData: '',
init () {
this._super(...arguments);
this.set('details', {});
}
});
+1 -1
View File
@@ -23,7 +23,7 @@ export default Route.extend({
}, },
afterModel() { afterModel() {
return this.kredits.loadContributorsAndProposals() return this.kredits.loadInitialData()
.then(() => { .then(() => {
this.kredits.addContractEventHandlers(); this.kredits.addContractEventHandlers();
}); });
+25 -5
View File
@@ -10,13 +10,16 @@ import { isEmpty } from '@ember/utils';
import config from 'kredits-web/config/environment'; import config from 'kredits-web/config/environment';
import Contributor from 'kredits-web/models/contributor' import Contributor from 'kredits-web/models/contributor'
import Proposal from 'kredits-web/models/proposal' import Proposal from 'kredits-web/models/proposal'
import Contribution from 'kredits-web/models/contribution'
export default Service.extend({ export default Service.extend({
currentBlock: null,
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded. currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
currentUser: null, currentUser: null,
contributors: null, contributors: null,
proposals: null, proposals: null,
contributions: null,
currentUserIsContributor: notEmpty('currentUser'), currentUserIsContributor: notEmpty('currentUser'),
currentUserIsCore: alias('currentUser.isCore'), currentUserIsCore: alias('currentUser.isCore'),
hasAccounts: notEmpty('currentUserAccounts'), hasAccounts: notEmpty('currentUserAccounts'),
@@ -28,6 +31,7 @@ export default Service.extend({
this._super(...arguments); this._super(...arguments);
this.set('contributors', []); this.set('contributors', []);
this.set('proposals', []); this.set('proposals', []);
this.set('contributions', []);
}, },
// this is called in the routes beforeModel(). So it is initialized before everything else // this is called in the routes beforeModel(). So it is initialized before everything else
@@ -83,7 +87,6 @@ export default Service.extend({
setup() { setup() {
return this.getEthProvider().then((providerAndSigner) => { return this.getEthProvider().then((providerAndSigner) => {
let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, { let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, {
addresses: { Kernel: config.kreditsKernelAddress }, addresses: { Kernel: config.kreditsKernelAddress },
apm: config.kreditsApmDomain, apm: config.kreditsApmDomain,
@@ -91,13 +94,16 @@ export default Service.extend({
}); });
return kredits return kredits
.init() .init()
.then((kredits) => { .then(async (kredits) => {
this.set('kredits', kredits); this.set('kredits', kredits);
this.set('currentBlock', await kredits.provider.getBlockNumber());
if (this.currentUserAccounts && this.currentUserAccounts.length > 0) { if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
this.getCurrentUser.then((contributorData) => { this.getCurrentUser.then((contributorData) => {
this.set('currentUser', contributorData); this.set('currentUser', contributorData);
}); });
} }
return kredits; return kredits;
}); });
}); });
@@ -107,11 +113,11 @@ export default Service.extend({
return this.kredits.Token.functions.totalSupply(); return this.kredits.Token.functions.totalSupply();
}), }),
loadContributorsAndProposals() { loadInitialData() {
return this.getContributors() return this.getContributors()
.then(contributors => this.contributors.pushObjects(contributors)) .then(contributors => this.contributors.pushObjects(contributors))
.then(() => this.getProposals()) .then(() => this.getContributions())
.then(proposals => this.proposals.pushObjects(proposals)) .then(contributions => this.contributions.pushObjects(contributions))
}, },
addContributor(attributes) { addContributor(attributes) {
@@ -154,6 +160,16 @@ export default Service.extend({
}); });
}, },
getContributions() {
return this.kredits.Contribution.all()
.then(contributions => {
return contributions.map(contribution => {
contribution.contributor = this.contributors.findBy('id', contribution.contributorId.toString());
return Contribution.create(contribution);
});
});
},
vote(proposalId) { vote(proposalId) {
console.debug('[kredits] vote for', proposalId); console.debug('[kredits] vote for', proposalId);
@@ -184,6 +200,10 @@ 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 // Proposal events
-4
View File
@@ -36,10 +36,6 @@ main section {
@include media-max(small) { @include media-max(small) {
margin-bottom: 5rem; margin-bottom: 5rem;
&#proposals-open, &#proposals-closed {
margin-top: 0;
}
} }
header { header {
+1
View File
@@ -119,3 +119,4 @@ button, input[type=submit] {
@import "components/contributor-list"; @import "components/contributor-list";
@import "components/add-contributor"; @import "components/add-contributor";
@import "components/proposal-list"; @import "components/proposal-list";
@import "components/contribution-list";
@@ -0,0 +1,128 @@
main section {
@include media-max(small) {
&#contributions-unconfirmed, &#contributions-confirmed {
margin-top: 0;
}
}
}
ul.contribution-list {
clear: both;
width: 100%;
list-style: none;
li {
display: grid;
grid-template-columns: auto 5rem;
grid-row-gap: 0.5rem;
padding: 1rem 1.2rem;
background-color: rgba(255,255,255,0.1);
font-size: 1.2rem;
border-bottom: 1px solid rgba(255,255,255,0.2);
&:first-of-type {
border-top: 1px solid rgba(255,255,255,0.2);
}
p {
align-self: center;
margin: 0;
font-size: inherit;
line-height: 2rem;
&.kredits-amount, &.voting {
text-align: right;
}
&.description {
grid-column-start: span 2;
}
&.voting {
grid-column-start: span 2;
}
}
span {
font-size: inherit;
}
.description {
line-height: 1.4em;
font-size: 1rem;
}
.category {
color: $blue;
padding-right: 0.2rem;
&.community { color: $red; }
&.dev { color: $pink; }
&.design { color: $yellow; }
&.docs { color: $green; }
&.ops { color: $purple; }
}
.amount {
font-weight: 500;
}
.symbol {
font-size: 1rem;
font-weight: 500;
padding-left: 0.2rem;
}
.recipient {
font-weight: 500;
}
.votes {
font-size: 1rem;
color: $primary-color;
margin-right: 0.5rem;
}
button {
height: 2rem;
line-height: 2rem;
padding: 0 0.6rem;
}
}
}
@media (min-width: 550px) {
ul.proposal-list {
li {
grid-template-columns: auto 10rem;
grid-row-gap: 0.5rem;
p {
&.kredits-amount, &.voting {
text-align: right;
}
}
&.unconfirmed {
p {
&.kredits-amount, &.voting {
text-align: right;
}
&.description {
grid-column-start: span 1;
}
&.voting {
grid-column-start: span 1;
}
}
}
.description {
font-size: inherit;
}
}
}
}
+8 -8
View File
@@ -20,33 +20,33 @@
<h2>Contributions by type</h2> <h2>Contributions by type</h2>
</header> </header>
<div class="content"> <div class="content">
{{chart-contributions-by-type contributions=proposals}} {{chart-contributions-by-type contributions=contributions}}
</div> </div>
</section> </section>
</div> </div>
<div id="contributions"> <div id="contributions">
{{#if proposalsOpen}} {{#if contributionsUnconfirmed}}
<section id="proposals-open"> <section id="contributions-unconfirmed">
<header> <header>
<h2>Unconfirmed Contributions</h2> <h2>Unconfirmed Contributions</h2>
</header> </header>
<div class="content"> <div class="content">
{{!-- TODO: We need a better naming for kredits.hasAccounts --}} {{!-- TODO: We need a better naming for kredits.hasAccounts --}}
{{proposal-list proposals=proposalsOpenSorted {{contribution-list contributions=contributionsUnconfirmedSorted
confirmProposal=(action "confirmProposal") vetoContribution=(action "vetoContribution")
contractInteractionEnabled=kredits.hasAccounts}} contractInteractionEnabled=kredits.hasAccounts}}
</div> </div>
</section> </section>
{{/if}} {{/if}}
<section id="proposals-closed"> <section id="contributions-confirmed">
<header> <header>
<h2>Confirmed Contributions</h2> <h2>Confirmed Contributions</h2>
</header> </header>
<div class="content"> <div class="content">
{{proposal-list proposals=proposalsClosedSorted {{contribution-list contributions=contributionsConfirmedSorted
confirmProposal=(action "confirmProposal")}} vetoContribution=(action "vetoContribution")}}
</div> </div>
{{!-- TODO: We need a better naming --}} {{!-- TODO: We need a better naming --}}
@@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | contribution-list', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`{{contribution-list}}`);
assert.equal(this.element.textContent.trim(), '');
});
});