Create contribution IPFS objects, attach to proposals
This commit is contained in:
@@ -2,6 +2,7 @@ import Ember from 'ember';
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
Component,
|
Component,
|
||||||
|
isPresent,
|
||||||
inject: {
|
inject: {
|
||||||
service
|
service
|
||||||
},
|
},
|
||||||
@@ -13,6 +14,7 @@ export default Component.extend({
|
|||||||
kredits: service(),
|
kredits: service(),
|
||||||
|
|
||||||
proposal: null,
|
proposal: null,
|
||||||
|
contributors: null,
|
||||||
inProgress: false,
|
inProgress: false,
|
||||||
|
|
||||||
isValidRecipient: computed('proposal.recipientAddress', function() {
|
isValidRecipient: computed('proposal.recipientAddress', function() {
|
||||||
@@ -24,24 +26,31 @@ export default Component.extend({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
isValidUrl: computed('proposal.url', function() {
|
isValidUrl: computed('proposal.url', function() {
|
||||||
// TODO
|
return isPresent(this.get('proposal.url'));
|
||||||
return true;
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
isValidIpfsHash: computed('proposal.ipfsHash', function() {
|
isValidDescription: computed('proposal.description', function() {
|
||||||
// TODO
|
return isPresent(this.get('proposal.description'));
|
||||||
return true;
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
isValid: computed.and('isValidRecipient', 'isValidAmount', 'isValidUrl',
|
isValid: computed.and('isValidRecipient',
|
||||||
'isValidIpfsHash'),
|
'isValidAmount',
|
||||||
|
'isValidDescription'),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save() {
|
save() {
|
||||||
if (this.get('isValid')) {
|
if (! this.get('isValid')) {
|
||||||
|
alert('Invalid data. Please review and try again.');
|
||||||
|
}
|
||||||
this.set('inProgress', true);
|
this.set('inProgress', true);
|
||||||
|
let proposal = this.get('proposal');
|
||||||
|
|
||||||
this.get('kredits').addProposal(this.get('proposal'))
|
// Set the recipient's IPFS profile hash so it can be used in the
|
||||||
|
// contribution object (which is to be stored in IPFS as well)
|
||||||
|
let contributor = this.get('contributors').findBy('address', proposal.get('recipientAddress'));
|
||||||
|
proposal.set('recipientProfile', contributor.get('ipfsHash'));
|
||||||
|
|
||||||
|
this.get('kredits').addProposal(proposal)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.attrs.onSave();
|
this.attrs.onSave();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
@@ -50,9 +59,6 @@ export default Component.extend({
|
|||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.set('inProgress', false);
|
this.set('inProgress', false);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
alert('Invalid data. Please review and try again.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<select required onchange={{action (mut proposal.kind) value="target.value"}}>
|
||||||
|
<option value="community" selected={{eq proposal.kind "community"}}>Community</option>
|
||||||
|
<option value="design" selected={{eq proposal.kind "design"}}>Design</option>
|
||||||
|
<option value="dev" selected={{eq proposal.kind "dev"}}>Development</option>
|
||||||
|
<option value="docs" selected={{eq proposal.kind "docs"}}>Documentation</option>
|
||||||
|
<option value="ops" selected={{eq proposal.kind "ops"}}>IT Operations</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input type="text"
|
{{input type="text"
|
||||||
placeholder="100"
|
placeholder="100"
|
||||||
@@ -15,15 +24,15 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input type="text"
|
{{input type="text"
|
||||||
placeholder="URL"
|
placeholder="Description"
|
||||||
value=proposal.url
|
value=proposal.description
|
||||||
class=(if isValidUrl 'valid' '')}}
|
class=(if isValidDescription 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input type="text"
|
{{input type="text"
|
||||||
placeholder="IPFS Hash"
|
placeholder="URL (optional)"
|
||||||
value=proposal.ipfsHash
|
value=proposal.url
|
||||||
class=(if isValidIpfsHash 'valid' '')}}
|
class=(if isValidUrl 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p class="actions">
|
<p class="actions">
|
||||||
{{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
|
{{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
|
||||||
|
|||||||
+54
-1
@@ -1,16 +1,69 @@
|
|||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
const {
|
||||||
|
isPresent,
|
||||||
|
isEmpty,
|
||||||
|
} = Ember;
|
||||||
|
|
||||||
export default Ember.Object.extend({
|
export default Ember.Object.extend({
|
||||||
|
|
||||||
id: null,
|
id: null,
|
||||||
creatorAddress: null,
|
creatorAddress: null,
|
||||||
recipientAddress: null,
|
recipientAddress: null,
|
||||||
recipientName: null,
|
recipientName: null,
|
||||||
|
recipientProfile: null,
|
||||||
votesCount: null,
|
votesCount: null,
|
||||||
votesNeeded: null,
|
votesNeeded: null,
|
||||||
amount: null,
|
amount: null,
|
||||||
executed: null,
|
executed: null,
|
||||||
|
contribution: null,
|
||||||
|
kind: null,
|
||||||
|
description: null,
|
||||||
url: null,
|
url: null,
|
||||||
ipfsHash: null
|
details: null,
|
||||||
|
ipfsHash: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JSON-LD object of the contribution, according to
|
||||||
|
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
contributionToJSON() {
|
||||||
|
if (isEmpty(this.get('recipientProfile'))) {
|
||||||
|
throw new Error('IPFS hash for recipient profile missing from proposal object');
|
||||||
|
}
|
||||||
|
if (isEmpty(this.get('kind')) || isEmpty(this.get('description'))) {
|
||||||
|
throw new Error('Missing one or more required properties: kind, description');
|
||||||
|
}
|
||||||
|
|
||||||
|
let contribution = {
|
||||||
|
"@context": "https://schema.kosmos.org",
|
||||||
|
"@type": "Contribution",
|
||||||
|
"contributor": {
|
||||||
|
"ipfs": this.get('recipientProfile')
|
||||||
|
},
|
||||||
|
"kind": this.get('kind'),
|
||||||
|
"description": this.get('description'),
|
||||||
|
"details": {}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isPresent(this.get('url'))) {
|
||||||
|
contribution["url"] = this.get('url');
|
||||||
|
}
|
||||||
|
|
||||||
|
return contribution;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JSON-LD representation of the contribution as a string
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
serializeContribution() {
|
||||||
|
return JSON.stringify(this.contributionToJSON());
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export default Route.extend({
|
|||||||
recipientAddress: params.recipient,
|
recipientAddress: params.recipient,
|
||||||
amount: params.amount,
|
amount: params.amount,
|
||||||
url: params.url,
|
url: params.url,
|
||||||
|
kind: params.kind || 'dev',
|
||||||
ipfsHash: params.ipfsHash
|
ipfsHash: params.ipfsHash
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ export default Service.extend({
|
|||||||
this.getValueFromContract('kreditsContract', 'contributorAddresses', i).then(address => {
|
this.getValueFromContract('kreditsContract', 'contributorAddresses', i).then(address => {
|
||||||
this.getValueFromContract('kreditsContract', 'contributors', address).then(person => {
|
this.getValueFromContract('kreditsContract', 'contributors', address).then(person => {
|
||||||
this.getValueFromContract('tokenContract', 'balanceOf', address).then(balance => {
|
this.getValueFromContract('tokenContract', 'balanceOf', address).then(balance => {
|
||||||
|
Ember.Logger.debug('[kredits] person', address, person);
|
||||||
|
|
||||||
let contributor = Contributor.create({
|
let contributor = Contributor.create({
|
||||||
address: address,
|
address: address,
|
||||||
ipfsHash: person[2],
|
ipfsHash: person[2],
|
||||||
@@ -203,16 +205,17 @@ export default Service.extend({
|
|||||||
const {
|
const {
|
||||||
recipientAddress,
|
recipientAddress,
|
||||||
amount,
|
amount,
|
||||||
url,
|
url
|
||||||
ipfsHash
|
} = proposal.getProperties('recipientAddress', 'amount', 'url');
|
||||||
} = proposal.getProperties('recipientAddress', 'amount', 'url', 'ipfsHash');
|
|
||||||
|
|
||||||
|
this.get('ipfs').storeFile(proposal.serializeContribution()).then(ipfsHash => {
|
||||||
this.get('kreditsContract').addProposal(recipientAddress, amount, url, ipfsHash, (err, data) => {
|
this.get('kreditsContract').addProposal(recipientAddress, amount, url, ipfsHash, (err, data) => {
|
||||||
if (err) { reject(err); return; }
|
if (err) { reject(err); return; }
|
||||||
Ember.Logger.debug('[kredits] add proposal response', data);
|
Ember.Logger.debug('[kredits] add proposal response', data);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// logKreditsContract: function() {
|
// logKreditsContract: function() {
|
||||||
|
|||||||
@@ -1,9 +1,49 @@
|
|||||||
import { moduleFor, test } from 'ember-qunit';
|
import { moduleFor, test } from 'ember-qunit';
|
||||||
|
import schemas from 'npm:kosmos-schemas';
|
||||||
|
import tv4 from 'npm:tv4';
|
||||||
|
|
||||||
moduleFor('model:proposal', 'Unit | Model | proposal');
|
moduleFor('model:proposal', 'Unit | Model | proposal');
|
||||||
|
|
||||||
test('it exists', function(assert) {
|
test('#toJSON() requires a recipient profile IPFS hash to be set', function(assert) {
|
||||||
let model = this.subject();
|
let model = this.subject();
|
||||||
// let store = this.store();
|
|
||||||
assert.ok(!!model);
|
model.setProperties({
|
||||||
|
recipientAddress: '0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac'
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
let json = model.contributionToJSON();
|
||||||
|
assert.notOk(json, true);
|
||||||
|
} catch(e) {
|
||||||
|
assert.ok(e.message.match(/IPFS hash .* missing/i));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#toJSON() requires kind and description to be set', function(assert) {
|
||||||
|
let model = this.subject();
|
||||||
|
|
||||||
|
model.setProperties({
|
||||||
|
recipientProfile: 'QmT2A7rY4e7uoKktkcFHQNN7BD1oXdZTgd8wNkr1u9nNVE'
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
let json = model.contributionToJSON();
|
||||||
|
assert.notOk(json, true);
|
||||||
|
} catch(e) {
|
||||||
|
assert.ok(e.message.match(/Missing .* kind.*description/i));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#toJSON() returns a valid JSON-LD representation of the model', function(assert) {
|
||||||
|
let model = this.subject();
|
||||||
|
|
||||||
|
model.setProperties({
|
||||||
|
recipientAddress: '0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac',
|
||||||
|
kind: 'design',
|
||||||
|
description: 'New logo design',
|
||||||
|
url: 'http://opensourcedesign.org',
|
||||||
|
recipientProfile: 'QmT2A7rY4e7uoKktkcFHQNN7BD1oXdZTgd8wNkr1u9nNVE'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(tv4.validate(model.contributionToJSON(), schemas['contribution']));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user