Gracefully handle pending changes for contributions #159

Merged
raucao merged 6 commits from feature/pending_changes into master 2019-10-18 15:31:42 +00:00
9 changed files with 46 additions and 12 deletions
@@ -45,7 +45,10 @@
{{#unless contribution.vetoed}} {{#unless contribution.vetoed}}
{{#unless (is-confirmed-contribution contribution)}} {{#unless (is-confirmed-contribution contribution)}}
<p class="voting"> <p class="voting">
<button {{action "veto" contribution.id}} class="small danger">veto</button> {{input type="button" class="button small danger"
click=(action "veto" contribution.id)
disabled=contribution.hasPendingChanges
value="veto"}}
</p> </p>
{{/unless}} {{/unless}}
{{/unless}} {{/unless}}
@@ -14,7 +14,7 @@ export default Component.extend({
userHasEthereumWallet: computed(function() { userHasEthereumWallet: computed(function() {
return isPresent(window.ethereum); return isPresent(window.ethereum);
}).volatile(), }),
showConnectButton: computed('userHasEthereumWallet', showConnectButton: computed('userHasEthereumWallet',
'kredits.hasAccounts', function() { 'kredits.hasAccounts', function() {
@@ -7,6 +7,6 @@ export default Controller.extend({
ipfsGatewayUrl: computed(function() { ipfsGatewayUrl: computed(function() {
return config.ipfs.gatewayUrl; return config.ipfs.gatewayUrl;
}).volatile() })
}); });
3
@@ -12,6 +12,6 @@ export default Controller.extend({
ipfsGatewayUrl: computed(function() { ipfsGatewayUrl: computed(function() {
return config.ipfs.gatewayUrl; return config.ipfs.gatewayUrl;
}).volatile() })
}); });
+15 -3
View File
@@ -11,13 +11,23 @@ export default Helper.extend({
compute([contribution]) { compute([contribution]) {
this.setupRecompute(contribution); this.setupRecompute(contribution);
let status = [];
if (contribution.vetoed) { if (contribution.vetoed) {
return 'vetoed'; status.push('vetoed');
} else if (contribution.confirmedAt > this.currentBlock) { } else if (contribution.confirmedAt > this.currentBlock) {
return 'unconfirmed'; status.push('unconfirmed');
} else { } else {
return 'confirmed' status.push('confirmed');
} }
if (contribution.hasPendingChanges) {
status.push('pending');
}
status.push('pending');
return status.join(' ');
}, },
destroy () { destroy () {
@@ -31,11 +41,13 @@ export default Helper.extend({
contribution.addObserver('vetoed' , this, this.triggerRecompute); contribution.addObserver('vetoed' , this, this.triggerRecompute);
contribution.addObserver('confirmedAt' , this, this.triggerRecompute); contribution.addObserver('confirmedAt' , this, this.triggerRecompute);
contribution.addObserver('currentBlock' , this, this.triggerRecompute); contribution.addObserver('currentBlock' , this, this.triggerRecompute);
contribution.addObserver('hasPendingChanges' , this, this.triggerRecompute);
this.teardown = () => { this.teardown = () => {
contribution.removeObserver('vetoed', this, this.triggerRecompute); contribution.removeObserver('vetoed', this, this.triggerRecompute);
contribution.removeObserver('confirmedAt', this, this.triggerRecompute); contribution.removeObserver('confirmedAt', this, this.triggerRecompute);
contribution.removeObserver('currentBlock', this, this.triggerRecompute); contribution.removeObserver('currentBlock', this, this.triggerRecompute);
contribution.removeObserver('hadPendingChanges', this, this.triggerRecompute);
}; };
}, },
+7 -1
View File
@@ -1,5 +1,5 @@
import EmberObject, { computed } from '@ember/object'; import EmberObject, { computed } from '@ember/object';
import { isEmpty } from '@ember/utils'; import { isEmpty, isPresent } from '@ember/utils';
import bignumber from 'kredits-web/utils/cps/bignumber'; import bignumber from 'kredits-web/utils/cps/bignumber';
import moment from 'moment'; import moment from 'moment';
@@ -24,6 +24,8 @@ export default EmberObject.extend({
time: null, time: null,
ipfsData: '', ipfsData: '',
pendingTx: null,
init () { init () {
this._super(...arguments); this._super(...arguments);
if (isEmpty(this.details)) this.set('details', {}); if (isEmpty(this.details)) this.set('details', {});
@@ -35,6 +37,10 @@ export default EmberObject.extend({
jsDate: computed('iso8601Date', function() { jsDate: computed('iso8601Date', function() {
return moment(this.iso8601Date).toDate(); return moment(this.iso8601Date).toDate();
}),
hasPendingChanges: computed('pendingTx', function() {
return isPresent(this.pendingTx);
}) })
}); });
+6 -2
View File
@@ -210,8 +210,8 @@ export default Service.extend({
getContributors () { getContributors () {
return this.kredits.Contributor.all() return this.kredits.Contributor.all()
.then((contributors) => { .then(contributors => {
return contributors.map((contributor) => { return contributors.map(contributor => {
return Contributor.create(contributor); return Contributor.create(contributor);
}); });
}); });
@@ -225,6 +225,7 @@ export default Service.extend({
console.debug('[kredits] add contribution response', data); console.debug('[kredits] add contribution response', data);
attributes.contributor = this.contributors.findBy('id', attributes.contributorId); attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
const contribution = Contribution.create(attributes); const contribution = Contribution.create(attributes);
contribution.set('pendingTx', data);
contribution.set('confirmedAtBlock', data.blockNumber + 40320); contribution.set('confirmedAtBlock', data.blockNumber + 40320);
this.contributions.pushObject(contribution); this.contributions.pushObject(contribution);
return contribution; return contribution;
@@ -278,10 +279,12 @@ export default Service.extend({
veto (contributionId) { veto (contributionId) {
console.debug('[kredits] veto against', contributionId); console.debug('[kredits] veto against', contributionId);
const contribution = this.contributions.findBy('id', contributionId);
return this.kredits.Contribution.functions.veto(contributionId, { gasLimit: 300000 }) return this.kredits.Contribution.functions.veto(contributionId, { gasLimit: 300000 })
.then(data => { .then(data => {
console.debug('[kredits] veto response', data); console.debug('[kredits] veto response', data);
contribution.set('pendingTx', data);
return data; return data;
}); });
}, },
@@ -367,6 +370,7 @@ export default Service.extend({
if (contribution) { if (contribution) {
contribution.set('vetoed', true); contribution.set('vetoed', true);
contribution.set('pendingTx', null);
} }
}, },
+2 -2
View File
@@ -32,7 +32,7 @@ button, input[type=submit], .button {
padding: 0.2rem 0.8rem; padding: 0.2rem 0.8rem;
} }
&.danger { &.danger:not(:disabled) {
color: $red; color: $red;
background-color: rgba(40, 21, 21, 0.6); background-color: rgba(40, 21, 21, 0.6);
border-color: rgba(40, 21, 21, 1); border-color: rgba(40, 21, 21, 1);
@@ -45,7 +45,7 @@ button, input[type=submit], .button {
} }
} }
&.green { &.green:not(:disabled) {
color: $green; color: $green;
background-color: rgba(21, 40, 21, 0.6); background-color: rgba(21, 40, 21, 0.6);
border-color: rgba(21, 40, 21, 1); border-color: rgba(21, 40, 21, 1);
+9
View File
@@ -24,4 +24,13 @@ module('Unit | Model | contribution', function(hooks) {
assert.ok(model.jsDate instanceof Date); assert.ok(model.jsDate instanceof Date);
assert.equal(model.jsDate.toISOString(), '2019-09-10T09:33:00.141Z'); assert.equal(model.jsDate.toISOString(), '2019-09-10T09:33:00.141Z');
}); });
test('hasPendingChanges', function(assert) {
const model = Contribution.create({});
assert.equal(model.hasPendingChanges, false);
model.set('pendingTx', { hash: 'abcdef123456' });
assert.equal(model.hasPendingChanges, true);
});
}); });