Feature: Batch voting #62
@@ -1,20 +1,30 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
|
import { inject } from '@ember/service';
|
||||||
|
import { empty } from '@ember/object/computed';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
kredits: inject(),
|
||||||
|
|
||||||
tagName: 'ul',
|
tagName: 'ul',
|
||||||
classNames: ['proposal-list'],
|
classNames: ['proposal-list'],
|
||||||
|
|
||||||
|
selectedProposals: [],
|
||||||
|
submitButtonDisabled: empty('selectedProposals'),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
confirm() {
|
||||||
confirm(proposalId) {
|
this.confirmProposals(this.selectedProposals)
|
||||||
if (this.contractInteractionEnabled) {
|
.then(() => {
|
||||||
this.confirmProposal(proposalId);
|
this.selectedProposals = [];
|
||||||
} else {
|
});
|
||||||
window.alert('Only members can vote on proposals. Please ask someone to set you up.');
|
},
|
||||||
}
|
|
||||||
}
|
toggleSelect(proposalId, selected) {
|
||||||
|
if (selected) {
|
||||||
}
|
this.selectedProposals.removeObject(proposalId);
|
||||||
|
} else {
|
||||||
|
this.selectedProposals.addObject(proposalId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<span class="category {{proposal.kind}}">♥</span>
|
||||||
|
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
|
||||||
|
for <span class="recipient">{{proposal.contributor.name}}</span>
|
||||||
|
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
|
||||||
|
|
||||||
|
{{#if canBeVoted}}
|
||||||
|
<button {{action toggleSelect proposal.id selected}} class="{{if selected 'selected' ''}}">+1</button>
|
||||||
|
{{/if}}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
{{#each proposals as |proposal|}}
|
{{#each proposals as |proposal|}}
|
||||||
<li data-proposal-id={{proposal.id}} title="({{proposal.kind}}) {{proposal.description}}">
|
{{proposal-list/item
|
||||||
<span class="category {{proposal.kind}}">♥</span>
|
proposal=proposal
|
||||||
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
|
selectedProposals=selectedProposals
|
||||||
for <span class="recipient">{{proposal.contributor.name}}</span>
|
toggleSelect=(action 'toggleSelect')
|
||||||
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
|
}}
|
||||||
|
|
||||||
{{#unless proposal.isExecuted}}
|
|
||||||
<button {{action "confirm" proposal.id}}>+1</button>
|
|
||||||
{{/unless}}
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if confirmProposals}}
|
||||||
|
<center>
|
||||||
|
<button disabled={{submitButtonDisabled}} {{action 'confirm'}}>
|
||||||
|
Submit selected
|
||||||
|
</button>
|
||||||
|
</center>
|
||||||
|
{{/if}}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { alias, filter, filterBy, sort } from '@ember/object/computed';
|
import { alias, filter, filterBy, sort } from '@ember/object/computed';
|
||||||
import { inject as injectService } from '@ember/service';
|
import { inject as injectService } from '@ember/service';
|
||||||
|
import RSVP from 'rsvp';
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
kredits: injectService(),
|
kredits: injectService(),
|
||||||
@@ -20,10 +21,17 @@ export default Controller.extend({
|
|||||||
proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'),
|
proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
confirmProposal(proposalId) {
|
confirmProposals(proposalIds) {
|
||||||
this.kredits.vote(proposalId).then(transaction => {
|
if (this.kredits.currentUser.isCore) {
|
||||||
|
return this.kredits.batchVote(proposalIds)
|
||||||
|
.then((transaction) => {
|
||||||
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
|
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) {
|
save(contributor) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import RSVP from 'rsvp';
|
|||||||
|
|
||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import { alias, notEmpty } from '@ember/object/computed';
|
import { notEmpty } from '@ember/object/computed';
|
||||||
import { isEmpty } from '@ember/utils';
|
import { isEmpty } from '@ember/utils';
|
||||||
|
|
||||||
import config from 'kredits-web/config/environment';
|
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.
|
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
|
||||||
currentUser: null,
|
currentUser: null,
|
||||||
currentUserIsContributor: notEmpty('currentUser'),
|
|
||||||
currentUserIsCore: alias('currentUser.isCore'),
|
|
||||||
hasAccounts: notEmpty('currentUserAccounts'),
|
hasAccounts: notEmpty('currentUserAccounts'),
|
||||||
accountNeedsUnlock: computed('currentUserAccounts', function() {
|
accountNeedsUnlock: computed('currentUserAccounts', function() {
|
||||||
return this.currentUserAccounts && isEmpty(this.currentUserAccounts);
|
return this.currentUserAccounts && isEmpty(this.currentUserAccounts);
|
||||||
@@ -68,7 +66,7 @@ export default Service.extend({
|
|||||||
|
|
||||||
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', Contributor.create(contributorData));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return kredits;
|
return kredits;
|
||||||
@@ -130,10 +128,11 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
vote(proposalId) {
|
batchVote(proposalIds) {
|
||||||
console.debug('[kredits] vote for', proposalId);
|
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) => {
|
.then((data) => {
|
||||||
console.debug('[kredits] vote response', data);
|
console.debug('[kredits] vote response', data);
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -46,7 +46,22 @@ ul.proposal-list {
|
|||||||
height: 2rem;
|
height: 2rem;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
padding: 0 0.6rem;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<section id="user-account">
|
<section id="user-account">
|
||||||
{{#if kredits.currentUser }}
|
{{#if kredits.currentUser }}
|
||||||
{{ kredits.currentUser.name }}
|
{{ kredits.currentUser.name }}
|
||||||
{{#if kredits.currentUserIsCore }}
|
{{#if kredits.currentUser.isCore }}
|
||||||
(core)
|
(core)
|
||||||
{{/if}}
|
{{/if}}
|
||||||
Account: {{kredits.currentUser.account}}
|
Account: {{kredits.currentUser.account}}
|
||||||
@@ -32,8 +32,7 @@
|
|||||||
<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
|
{{proposal-list proposals=proposalsOpenSorted
|
||||||
confirmProposal=(action "confirmProposal")
|
confirmProposals=(action 'confirmProposals')}}
|
||||||
contractInteractionEnabled=kredits.hasAccounts}}
|
|
||||||
|
|
||||||
{{!-- TODO: We need a better naming --}}
|
{{!-- TODO: We need a better naming --}}
|
||||||
{{#if kredits.hasAccounts}}
|
{{#if kredits.hasAccounts}}
|
||||||
@@ -50,8 +49,7 @@
|
|||||||
<h2>Closed Proposals</h2>
|
<h2>Closed Proposals</h2>
|
||||||
</header>
|
</header>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{proposal-list proposals=proposalsClosedSorted
|
{{proposal-list proposals=proposalsClosedSorted}}
|
||||||
confirmProposal=(action "confirmProposal")}}
|
|
||||||
|
|
||||||
{{!-- TODO: We need a better naming --}}
|
{{!-- TODO: We need a better naming --}}
|
||||||
{{#if kredits.hasAccounts}}
|
{{#if kredits.hasAccounts}}
|
||||||
|
|||||||
Reference in New Issue
Block a user