diff --git a/app/components/proposal-list/component.js b/app/components/proposal-list/component.js index 272e28e..1ce555d 100644 --- a/app/components/proposal-list/component.js +++ b/app/components/proposal-list/component.js @@ -1,20 +1,30 @@ import Component from '@ember/component'; +import { inject } from '@ember/service'; +import { empty } from '@ember/object/computed'; export default Component.extend({ + kredits: inject(), tagName: 'ul', classNames: ['proposal-list'], + selectedProposals: [], + submitButtonDisabled: empty('selectedProposals'), + actions: { + confirm() { + this.confirmProposals(this.selectedProposals) + .then(() => { + this.selectedProposals = []; + }); + }, - confirm(proposalId) { - if (this.contractInteractionEnabled) { - this.confirmProposal(proposalId); + toggleSelect(proposalId, selected) { + if (selected) { + this.selectedProposals.removeObject(proposalId); } else { - window.alert('Only members can vote on proposals. Please ask someone to set you up.'); + this.selectedProposals.addObject(proposalId); } - } - + }, } - }); diff --git a/app/components/proposal-list/item/component.js b/app/components/proposal-list/item/component.js new file mode 100644 index 0000000..66c000e --- /dev/null +++ b/app/components/proposal-list/item/component.js @@ -0,0 +1,29 @@ +import Component from '@ember/component'; +import tag from 'ember-awesome-macros/tag'; +import { computed } from '@ember/object'; +import { reads } from '@ember/object/computed'; +import { inject } from '@ember/service'; + +export default Component.extend({ + kredits: inject(), + + tagName: 'li', + classNames: ['proposal-list-item'], + + attributeBindings: ['data-proposal-id', 'title'], + + title: tag`(${'proposal.kind'}) ${'proposal.description'}`, + 'data-proposal-id': reads('proposal.id'), + + canBeVoted: computed('voterIds.[]', 'kredits.currentUser', function() { + let { isExecuted, voterIds } = this.proposal; + let { currentUser } = this.kredits; + voterIds = voterIds.map((id) => id.toString()); + + return currentUser.isCore && !isExecuted && !voterIds.includes(currentUser.id); + }), + + selected: computed('selectedProposals.[]', function() { + return this.selectedProposals.includes(this.proposal.id); + }) +}); diff --git a/app/components/proposal-list/item/template.hbs b/app/components/proposal-list/item/template.hbs new file mode 100644 index 0000000..75ab52e --- /dev/null +++ b/app/components/proposal-list/item/template.hbs @@ -0,0 +1,8 @@ + +{{proposal.amount}}₭S +for {{proposal.contributor.name}} +({{proposal.votesCount}}/{{proposal.votesNeeded}} votes) + +{{#if canBeVoted}} + +{{/if}} diff --git a/app/components/proposal-list/template.hbs b/app/components/proposal-list/template.hbs index 87bdbae..e12313a 100644 --- a/app/components/proposal-list/template.hbs +++ b/app/components/proposal-list/template.hbs @@ -1,12 +1,15 @@ {{#each proposals as |proposal|}} -
  • - - {{proposal.amount}}₭S - for {{proposal.contributor.name}} - ({{proposal.votesCount}}/{{proposal.votesNeeded}} votes) - - {{#unless proposal.isExecuted}} - - {{/unless}} -
  • + {{proposal-list/item + proposal=proposal + selectedProposals=selectedProposals + toggleSelect=(action 'toggleSelect') + }} {{/each}} + +{{#if confirmProposals}} +
    + +
    +{{/if}} diff --git a/app/controllers/index.js b/app/controllers/index.js index 10c8920..868b76d 100644 --- a/app/controllers/index.js +++ b/app/controllers/index.js @@ -1,6 +1,7 @@ import Controller from '@ember/controller'; import { alias, filter, filterBy, sort } from '@ember/object/computed'; import { inject as injectService } from '@ember/service'; +import RSVP from 'rsvp'; export default Controller.extend({ kredits: injectService(), @@ -20,10 +21,17 @@ export default Controller.extend({ proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'), actions: { - confirmProposal(proposalId) { - this.kredits.vote(proposalId).then(transaction => { - window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash); - }); + confirmProposals(proposalIds) { + if (this.kredits.currentUser.isCore) { + return this.kredits.batchVote(proposalIds) + .then((transaction) => { + window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash); + return transaction; + }); + } else { + window.alert('Only members can vote on proposals. Please ask someone to set you up.'); + return RSVP.reject(); + } }, save(contributor) { diff --git a/app/services/kredits.js b/app/services/kredits.js index ca1e74a..80a56ba 100644 --- a/app/services/kredits.js +++ b/app/services/kredits.js @@ -4,7 +4,7 @@ import RSVP from 'rsvp'; import Service from '@ember/service'; import { computed } from '@ember/object'; -import { alias, notEmpty } from '@ember/object/computed'; +import { notEmpty } from '@ember/object/computed'; import { isEmpty } from '@ember/utils'; import config from 'kredits-web/config/environment'; @@ -16,8 +16,6 @@ export default Service.extend({ currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded. currentUser: null, - currentUserIsContributor: notEmpty('currentUser'), - currentUserIsCore: alias('currentUser.isCore'), hasAccounts: notEmpty('currentUserAccounts'), accountNeedsUnlock: computed('currentUserAccounts', function() { return this.currentUserAccounts && isEmpty(this.currentUserAccounts); @@ -68,7 +66,7 @@ export default Service.extend({ if (this.currentUserAccounts && this.currentUserAccounts.length > 0) { this.getCurrentUser.then((contributorData) => { - this.set('currentUser', contributorData); + this.set('currentUser', Contributor.create(contributorData)); }); } return kredits; @@ -130,10 +128,11 @@ export default Service.extend({ }); }, - vote(proposalId) { - console.debug('[kredits] vote for', proposalId); + batchVote(proposalIds) { + proposalIds = proposalIds.map((id) => parseInt(id)) + console.debug('[kredits] vote for', proposalIds); - return this.kredits.Operator.functions.vote(proposalId) + return this.kredits.Operator.functions.batchVote(proposalIds) .then((data) => { console.debug('[kredits] vote response', data); return data; diff --git a/app/styles/components/_proposal-list.scss b/app/styles/components/_proposal-list.scss index a35f17c..f86dd4a 100644 --- a/app/styles/components/_proposal-list.scss +++ b/app/styles/components/_proposal-list.scss @@ -46,7 +46,22 @@ ul.proposal-list { height: 2rem; line-height: 2rem; padding: 0 0.6rem; + + &.selected { + background-color: rgba(224,104,251, 0.6); + } } } + button { + margin-top: 1.1rem; + height: 2rem; + line-height: 2rem; + padding: 0 0.6rem; + + &:disabled { + cursor: not-allowed; + color: rgba(204,204,204, 0.6); + } + } } diff --git a/app/templates/index.hbs b/app/templates/index.hbs index 99ccd78..bf72f44 100644 --- a/app/templates/index.hbs +++ b/app/templates/index.hbs @@ -2,7 +2,7 @@
    {{#if kredits.currentUser }} {{ kredits.currentUser.name }} - {{#if kredits.currentUserIsCore }} + {{#if kredits.currentUser.isCore }} (core) {{/if}} Account: {{kredits.currentUser.account}} @@ -32,8 +32,7 @@
    {{!-- TODO: We need a better naming for kredits.hasAccounts --}} {{proposal-list proposals=proposalsOpenSorted - confirmProposal=(action "confirmProposal") - contractInteractionEnabled=kredits.hasAccounts}} + confirmProposals=(action 'confirmProposals')}} {{!-- TODO: We need a better naming --}} {{#if kredits.hasAccounts}} @@ -50,8 +49,7 @@

    Closed Proposals

    - {{proposal-list proposals=proposalsClosedSorted - confirmProposal=(action "confirmProposal")}} + {{proposal-list proposals=proposalsClosedSorted}} {{!-- TODO: We need a better naming --}} {{#if kredits.hasAccounts}}