diff --git a/app/components/proposal-list/template.hbs b/app/components/proposal-list/template.hbs index f346830..87bdbae 100644 --- a/app/components/proposal-list/template.hbs +++ b/app/components/proposal-list/template.hbs @@ -2,9 +2,11 @@
  • {{proposal.amount}}₭S - for {{proposal.recipientName}} + for {{proposal.contributor.name}} ({{proposal.votesCount}}/{{proposal.votesNeeded}} votes) - {{#unless proposal.executed}}{{/unless}} + {{#unless proposal.isExecuted}} + + {{/unless}}
  • {{/each}} diff --git a/app/controllers/index.js b/app/controllers/index.js index e502daf..fce8ada 100644 --- a/app/controllers/index.js +++ b/app/controllers/index.js @@ -1,51 +1,10 @@ import Ember from 'ember'; -import Proposal from 'kredits-web/models/proposal'; +import Controller from 'ember-controller'; +import computed, { alias, filter, filterBy, sort } from 'ember-computed'; +import injectService from 'ember-service/inject'; -const { - computed, - inject: { - service - } -} = Ember; - -export default Ember.Controller.extend({ - - kredits: service(), - - contractInteractionEnabled: computed.alias('kredits.hasAccounts'), - - proposalsOpen: function() { - let proposals = this.get('model.proposals') - .filterBy('executed', false) - .map(p => { - p.set('recipientName', this.get('model.contributors').findBy('id', p.get('recipientId')).name); - return p; - }); - return proposals; - }.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'), - - proposalsClosed: function() { - let proposals = this.get('model.proposals') - .filterBy('executed', true) - .map(p => { - p.set('recipientName', this.get('model.contributors').findBy('id', p.get('recipientId')).name); - return p; - }); - return proposals; - }.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'), - - proposalsSorting: ['id:desc'], - proposalsClosedSorted: Ember.computed.sort('proposalsClosed', 'proposalsSorting'), - proposalsOpenSorted: Ember.computed.sort('proposalsOpen', 'proposalsSorting'), - - contributorsWithKredits: function() { - return this.get('model.contributors').filter(c => { - return c.get('balance') !== 0; - }); - }.property('model.contributors.@each.balance'), - - contributorsSorting: ['balance:desc'], - contributorsSorted: Ember.computed.sort('contributorsWithKredits', 'contributorsSorting'), +export default Controller.extend({ + kredits: injectService(), init() { this._super(...arguments); @@ -58,60 +17,76 @@ export default Ember.Controller.extend({ }); }, - _handleProposalCreated(proposalId, creatorAddress, recipientAddress, amount) { - if (Ember.isPresent(this.get('model.proposals') - .findBy('id', proposalId.toNumber()))) { + contributors: alias('model.contributors'), + contributorsWithKredits: filter('contributors', function(contributor) { + return contributor.get('balance') !== 0; + }), + contributorsSorting: ['balance:desc'], + contributorsSorted: sort('contributorsWithKredits', 'contributorsSorting'), + + proposals: computed('model.proposals.[]', 'contributors.[]', function() { + return this.get('model.proposals') + .map((proposal) => { + let contributor = this.get('contributors') + .findBy('id', proposal.get('recipientId')); + + proposal.set('contributor', contributor); + + return proposal; + }); + }), + proposalsOpen: filterBy('proposals', 'isExecuted', false), + proposalsClosed: filterBy('proposals', 'isExecuted', true), + proposalsSorting: ['id:desc'], + proposalsClosedSorted: sort('proposalsClosed', 'proposalsSorting'), + proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'), + + _handleProposalCreated(proposalId) { + // TODO: check if proposalId is already a string + let proposal = this.get('proposals') + .findBy('id', proposalId.toString()); + if (proposal) { Ember.Logger.debug('[index] proposal exists, not adding from event'); - return false; + return; } - let proposal = Proposal.create({ - id: proposalId.toNumber(), - creatorAddress: creatorAddress, - recipientAddress: recipientAddress, - recipientName: null, - votesCount: 0, - votesNeeded: 2, - amount: amount.toNumber(), - executed: false - }); - - this.get('model.proposals').pushObject(proposal); + proposal = this.get('kredits').getProposalById(proposalId); + this.get('proposals').pushObject(proposal); }, _handleProposalExecuted(proposalId, recipientId, amount) { - if (this.get('model.proposals') - .findBy('id', recipientId.toNumber()) - .get('executed')) { + // TODO: check if proposalId is already a string + let proposal = this.get('proposals') + .findBy('id', proposalId.toString()); + + if (proposal.get('isExecuted')) { Ember.Logger.debug('[index] proposal already executed, not adding from event'); - return false; + return; } - this.get('model.proposals') - .findBy('id', recipientId.toNumber()) - .setProperties({ - 'executed': true, - 'votesCount': 2 // TODO use real count - }); + proposal.setProperties({ + 'executed': true, + }); - this.get('model.contributors') - .findBy('id', recipientId) + this.get('contributors') + .findBy('id', recipientId.toString()) .incrementProperty('balance', amount.toNumber()); }, _handleProposalVoted(proposalId, voter, totalVotes) { - this.get('model.proposals') - .findBy('id', proposalId.toNumber()) + this.get('proposals') + .findBy('id', proposalId.toString()) .setProperties({ 'votesCount': totalVotes }); }, - _handleTransfer(data) { - this.get('model.contributors') - .findBy('address', data.args.from) - .incrementProperty('balance', - data.args.value.toNumber()); - this.get('model.contributors') - .findBy('address', data.args.to) - .incrementProperty('balance', data.args.value.toNumber()); + _handleTransfer(from, to, value) { + value = value.toNumber(); + this.get('contributors') + .findBy('address', from) + .decrementProperty('balance', value); + this.get('contributors') + .findBy('address', to) + .incrementProperty('balance', value); }, @@ -125,7 +100,7 @@ export default Ember.Controller.extend({ save(contributor) { return this.get('kredits').addContributor(contributor) .then((contributor) => { - this.get('model.contributors').pushObject(contributor); + this.get('contributors').pushObject(contributor); return contributor; }); } diff --git a/app/models/proposal.js b/app/models/proposal.js index 4b49981..c52d162 100644 --- a/app/models/proposal.js +++ b/app/models/proposal.js @@ -1,4 +1,5 @@ import EmberObject from 'ember-object'; +import { alias } from 'ember-computed'; export default EmberObject.extend({ // Contract @@ -11,6 +12,9 @@ export default EmberObject.extend({ executed: null, ipfsHash: null, + // Shortcuts + isExecuted: alias('executed'), + // IPFS kind: null, description: null, @@ -18,10 +22,6 @@ export default EmberObject.extend({ url: null, ipfsData: '', - // Deprecated - recipientAddress: null, - recipientName: null, - recipientProfile: null, - - // TODO: add contributor relation + // Relationships + contributor: null, }); diff --git a/app/services/kredits.js b/app/services/kredits.js index bd218d8..e0a8334 100644 --- a/app/services/kredits.js +++ b/app/services/kredits.js @@ -109,24 +109,22 @@ export default Service.extend({ }); }, - // TODO: Should be part of the service - buildContributor(attributes) { - debug('[kredits] buildContributor', attributes); + buildModel(name, attributes) { + debug('[kredits] build', name, attributes); + let model = getOwner(this).lookup(`model:${name}`); - let contributor = getOwner(this).lookup('model:contributor'); - contributor.setProperties(attributes); - return contributor; - }, + // coerce id to string + if (attributes.id) { + attributes.id = attributes.id.toString(); + } - buildProposal(attributes) { - debug('[kredits] buildProposal', attributes); - - let proposal = getOwner(this).lookup('model:proposal'); - proposal.setProperties(attributes); - return proposal; + model.setProperties(attributes); + return model; }, getContributorById(id) { + id = ethers.utils.bigNumberify(id); + return this.get('contributorsContract') .then((contract) => contract.getContributorById(id)) .then(this.reassembleIpfsHash) @@ -135,7 +133,7 @@ export default Service.extend({ let isCurrentUser = this.get('currentUserAccounts').includes(address); return { - id: id.toString(), + id: id, address, balance: balance.toNumber(), ipfsHash, @@ -148,7 +146,7 @@ export default Service.extend({ return this.fetchAndMergeIpfsData(data, ContributorSerializer); }) .then((attributes) => { - return this.buildContributor(attributes); + return this.buildModel('contributor', attributes); }); }, @@ -203,6 +201,8 @@ export default Service.extend({ }, getProposalById(id) { + id = ethers.utils.bigNumberify(id); + return this.get('kreditsContract') .then((contract) => contract.proposals(id)) .then(this.reassembleIpfsHash) @@ -217,9 +217,9 @@ export default Service.extend({ ipfsHash, }) => { return { - id: id.toString(), + id: id, creatorAddress, - recipientId: recipientId.toNumber(), + recipientId: recipientId.toString(), votesCount: votesCount.toNumber(), votesNeeded: votesNeeded.toNumber(), amount: amount.toNumber(), @@ -232,7 +232,7 @@ export default Service.extend({ return this.fetchAndMergeIpfsData(data, ContributionSerializer); }) .then((attributes) => { - return this.buildProposal(attributes); + return this.buildModel('proposal', attributes); }); }, @@ -296,7 +296,7 @@ export default Service.extend({ }) .then((data) => { debug('[kredits] add contributor response', data); - return this.buildContributor(attributes); + return this.buildModel('contributor', attributes); }); }, @@ -333,7 +333,7 @@ export default Service.extend({ }) .then((data) => { debug('[kredits] add proposal response', data); - return this.buildProposal(attributes); + return this.buildModel('proposal', attributes); }); }, diff --git a/app/templates/index.hbs b/app/templates/index.hbs index 5dfcad5..cf22e2a 100644 --- a/app/templates/index.hbs +++ b/app/templates/index.hbs @@ -30,11 +30,13 @@

    Open Proposals

    + {{!-- TODO: We need a better naming for kredits.hasAccounts --}} {{proposal-list proposals=proposalsOpenSorted confirmAction="confirmProposal" - contractInteractionEnabled=contractInteractionEnabled}} + contractInteractionEnabled=kredits.hasAccounts}} - {{#if contractInteractionEnabled}} + {{!-- TODO: We need a better naming --}} + {{#if kredits.hasAccounts}}

    {{#link-to 'proposals.new'}}Create new proposal{{/link-to}}

    @@ -51,7 +53,8 @@ {{proposal-list proposals=proposalsClosedSorted confirmAction="confirmProposal"}} - {{#if contractInteractionEnabled}} + {{!-- TODO: We need a better naming --}} + {{#if kredits.hasAccounts}}

    {{#link-to 'proposals.new'}}Create new proposal{{/link-to}}

    @@ -65,11 +68,10 @@
    - {{!-- - TODO: - only show the form if currentUser.isCore else show: + {{#if currentUser.isCore}} + {{add-contributor contributors=model.contributors save=(action 'save')}} + {{else}} Only core team members can add new contributors. Please ask someone to set you up. - --}} - {{add-contributor contributors=model.contributors save=(action 'save')}} + {{/if}}
    diff --git a/tests/unit/lib/kredits/serializers/contribution-test.js b/tests/unit/lib/kredits/serializers/contribution-test.js index 5069a13..7d0b02a 100644 --- a/tests/unit/lib/kredits/serializers/contribution-test.js +++ b/tests/unit/lib/kredits/serializers/contribution-test.js @@ -7,7 +7,6 @@ module('Serializers contribution'); test('#serialize returns a valid JSON-LD representation', function(assert) { let serialized = ContributionSerializer.serialize({ - recipientAddress: '0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac', kind: 'design', description: 'New logo design', url: 'http://opensourcedesign.org',