Switch to contribution token instead of proposals
No vetos yet, and only for collections (not creation). closes #20
This commit is contained in:
@@ -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}}
|
||||
@@ -1,9 +1,11 @@
|
||||
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 { computed } from '@ember/object';
|
||||
|
||||
export default Controller.extend({
|
||||
kredits: service(),
|
||||
currentBlock: alias('kredits.currentBlock'),
|
||||
|
||||
contributors: alias('kredits.contributors'),
|
||||
contributorsWithKredits: filter('contributors', function(contributor) {
|
||||
@@ -12,26 +14,44 @@ export default Controller.extend({
|
||||
contributorsSorting: Object.freeze(['balance:desc']),
|
||||
contributorsSorted: sort('contributorsWithKredits', 'contributorsSorting'),
|
||||
|
||||
proposals: alias('kredits.proposals'),
|
||||
proposalsOpen: filterBy('proposals', 'isExecuted', false),
|
||||
proposalsClosed: filterBy('proposals', 'isExecuted', true),
|
||||
proposalsSorting: Object.freeze(['id:desc']),
|
||||
proposalsClosedSorted: sort('proposalsClosed', 'proposalsSorting'),
|
||||
proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'),
|
||||
contributions: alias('kredits.contributions'),
|
||||
contributionsSorting: Object.freeze(['id:desc']),
|
||||
|
||||
contributionsUnconfirmed: computed('contributions.[]', 'currentBlock', function() {
|
||||
return this.contributions.filter(contribution => {
|
||||
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: {
|
||||
confirmProposal(proposalId) {
|
||||
|
||||
vetoContribution (/* contributionId */) {
|
||||
// this.kredits.vote(proposalId).then(transaction => {
|
||||
// window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
|
||||
// });
|
||||
},
|
||||
|
||||
confirmProposal (proposalId) {
|
||||
this.kredits.vote(proposalId).then(transaction => {
|
||||
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
|
||||
});
|
||||
},
|
||||
|
||||
save(contributor) {
|
||||
save (contributor) {
|
||||
return this.kredits.addContributor(contributor)
|
||||
.then((contributor) => {
|
||||
this.contributors.pushObject(contributor);
|
||||
return contributor;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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', {});
|
||||
}
|
||||
|
||||
});
|
||||
@@ -23,7 +23,7 @@ export default Route.extend({
|
||||
},
|
||||
|
||||
afterModel() {
|
||||
return this.kredits.loadContributorsAndProposals()
|
||||
return this.kredits.loadInitialData()
|
||||
.then(() => {
|
||||
this.kredits.addContractEventHandlers();
|
||||
});
|
||||
|
||||
+25
-5
@@ -10,13 +10,16 @@ import { isEmpty } from '@ember/utils';
|
||||
import config from 'kredits-web/config/environment';
|
||||
import Contributor from 'kredits-web/models/contributor'
|
||||
import Proposal from 'kredits-web/models/proposal'
|
||||
import Contribution from 'kredits-web/models/contribution'
|
||||
|
||||
export default Service.extend({
|
||||
|
||||
currentBlock: null,
|
||||
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
|
||||
currentUser: null,
|
||||
contributors: null,
|
||||
proposals: null,
|
||||
contributions: null,
|
||||
currentUserIsContributor: notEmpty('currentUser'),
|
||||
currentUserIsCore: alias('currentUser.isCore'),
|
||||
hasAccounts: notEmpty('currentUserAccounts'),
|
||||
@@ -28,6 +31,7 @@ export default Service.extend({
|
||||
this._super(...arguments);
|
||||
this.set('contributors', []);
|
||||
this.set('proposals', []);
|
||||
this.set('contributions', []);
|
||||
},
|
||||
|
||||
// this is called in the routes beforeModel(). So it is initialized before everything else
|
||||
@@ -83,7 +87,6 @@ export default Service.extend({
|
||||
|
||||
setup() {
|
||||
return this.getEthProvider().then((providerAndSigner) => {
|
||||
|
||||
let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, {
|
||||
addresses: { Kernel: config.kreditsKernelAddress },
|
||||
apm: config.kreditsApmDomain,
|
||||
@@ -91,13 +94,16 @@ export default Service.extend({
|
||||
});
|
||||
return kredits
|
||||
.init()
|
||||
.then((kredits) => {
|
||||
.then(async (kredits) => {
|
||||
this.set('kredits', kredits);
|
||||
this.set('currentBlock', await kredits.provider.getBlockNumber());
|
||||
|
||||
if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
|
||||
this.getCurrentUser.then((contributorData) => {
|
||||
this.set('currentUser', contributorData);
|
||||
});
|
||||
}
|
||||
|
||||
return kredits;
|
||||
});
|
||||
});
|
||||
@@ -107,11 +113,11 @@ export default Service.extend({
|
||||
return this.kredits.Token.functions.totalSupply();
|
||||
}),
|
||||
|
||||
loadContributorsAndProposals() {
|
||||
loadInitialData() {
|
||||
return this.getContributors()
|
||||
.then(contributors => this.contributors.pushObjects(contributors))
|
||||
.then(() => this.getProposals())
|
||||
.then(proposals => this.proposals.pushObjects(proposals))
|
||||
.then(() => this.getContributions())
|
||||
.then(contributions => this.contributions.pushObjects(contributions))
|
||||
},
|
||||
|
||||
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) {
|
||||
console.debug('[kredits] vote for', proposalId);
|
||||
|
||||
@@ -184,6 +200,10 @@ 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
|
||||
|
||||
@@ -36,10 +36,6 @@ main section {
|
||||
|
||||
@include media-max(small) {
|
||||
margin-bottom: 5rem;
|
||||
|
||||
&#proposals-open, &#proposals-closed {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
|
||||
@@ -119,3 +119,4 @@ button, input[type=submit] {
|
||||
@import "components/contributor-list";
|
||||
@import "components/add-contributor";
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,33 +20,33 @@
|
||||
<h2>Contributions by type</h2>
|
||||
</header>
|
||||
<div class="content">
|
||||
{{chart-contributions-by-type contributions=proposals}}
|
||||
{{chart-contributions-by-type contributions=contributions}}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="contributions">
|
||||
{{#if proposalsOpen}}
|
||||
<section id="proposals-open">
|
||||
{{#if contributionsUnconfirmed}}
|
||||
<section id="contributions-unconfirmed">
|
||||
<header>
|
||||
<h2>Unconfirmed Contributions</h2>
|
||||
</header>
|
||||
<div class="content">
|
||||
{{!-- TODO: We need a better naming for kredits.hasAccounts --}}
|
||||
{{proposal-list proposals=proposalsOpenSorted
|
||||
confirmProposal=(action "confirmProposal")
|
||||
contractInteractionEnabled=kredits.hasAccounts}}
|
||||
{{contribution-list contributions=contributionsUnconfirmedSorted
|
||||
vetoContribution=(action "vetoContribution")
|
||||
contractInteractionEnabled=kredits.hasAccounts}}
|
||||
</div>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
<section id="proposals-closed">
|
||||
<section id="contributions-confirmed">
|
||||
<header>
|
||||
<h2>Confirmed Contributions</h2>
|
||||
</header>
|
||||
<div class="content">
|
||||
{{proposal-list proposals=proposalsClosedSorted
|
||||
confirmProposal=(action "confirmProposal")}}
|
||||
{{contribution-list contributions=contributionsConfirmedSorted
|
||||
vetoContribution=(action "vetoContribution")}}
|
||||
</div>
|
||||
|
||||
{{!-- 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(), '');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user