Merge pull request #16 from 67P/feature/ipfs-metadata
Move all metadata to IPFS with proper schemas
This commit was merged in pull request #16.
This commit is contained in:
@@ -1,41 +1,58 @@
|
|||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import Contributor from 'kredits-web/models/contributor';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
const {
|
||||||
|
Component,
|
||||||
|
isPresent,
|
||||||
|
inject: {
|
||||||
|
service
|
||||||
|
},
|
||||||
|
computed
|
||||||
|
} = Ember;
|
||||||
|
|
||||||
id: null,
|
export default Component.extend({
|
||||||
realName: null,
|
|
||||||
address: null,
|
|
||||||
ipfsHash: null,
|
|
||||||
isCore: false,
|
|
||||||
|
|
||||||
|
kredits: service(),
|
||||||
|
|
||||||
|
newContributor: null,
|
||||||
inProgress: false,
|
inProgress: false,
|
||||||
|
|
||||||
isValidId: function() {
|
|
||||||
return Ember.isPresent(this.get('id'));
|
|
||||||
}.property('id'),
|
|
||||||
|
|
||||||
isValidRealName: function() {
|
|
||||||
return Ember.isPresent(this.get('realName'));
|
|
||||||
}.property('realName'),
|
|
||||||
|
|
||||||
isValidAddress: function() {
|
isValidAddress: function() {
|
||||||
return this.get('kredits.web3Instance').isAddress(this.get('address'));
|
return this.get('kredits.web3')
|
||||||
}.property('address'),
|
.isAddress(this.get('newContributor.address'));
|
||||||
|
}.property('kredits.web3', 'newContributor.address'),
|
||||||
|
|
||||||
isValid: function() {
|
isValidName: function() {
|
||||||
return this.get('isValidId') && this.get('isValidRealName') && this.get('isValidAddress');
|
return isPresent(this.get('newContributor.name'));
|
||||||
}.property('isValidAddress', 'isValidId', 'isValidRealName'),
|
}.property('newContributor.name'),
|
||||||
|
|
||||||
|
isValidURL: function() {
|
||||||
|
return isPresent(this.get('newContributor.url'));
|
||||||
|
}.property('newContributor.url'),
|
||||||
|
|
||||||
|
isValidGithubUID: function() {
|
||||||
|
return isPresent(this.get('newContributor.github_uid'));
|
||||||
|
}.property('newContributor.github_uid'),
|
||||||
|
|
||||||
|
isValidGithubUsername: function() {
|
||||||
|
return isPresent(this.get('newContributor.github_username'));
|
||||||
|
}.property('newContributor.github_username'),
|
||||||
|
|
||||||
|
isValidWikiUsername: function() {
|
||||||
|
return isPresent(this.get('newContributor.wiki_username'));
|
||||||
|
}.property('newContributor.wiki_username'),
|
||||||
|
|
||||||
|
isValid: computed.and(
|
||||||
|
'isValidAddress',
|
||||||
|
'isValidName',
|
||||||
|
'isValidGithubUID'
|
||||||
|
),
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
id: null,
|
newContributor: Contributor.create({ kind: 'person' }),
|
||||||
realName: null,
|
|
||||||
address: null,
|
|
||||||
ipfsHash: null,
|
|
||||||
isCore: true,
|
|
||||||
inProgress: false
|
inProgress: false
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -49,12 +66,7 @@ export default Ember.Component.extend({
|
|||||||
if (this.get('isValid')) {
|
if (this.get('isValid')) {
|
||||||
this.set('inProgress', true);
|
this.set('inProgress', true);
|
||||||
|
|
||||||
this.get('kredits').addContributor(
|
this.get('kredits').addContributor(this.get('newContributor')).then(contributor => {
|
||||||
this.get('address'),
|
|
||||||
this.get('realName'),
|
|
||||||
this.get('isCore'),
|
|
||||||
this.get('id')
|
|
||||||
).then(contributor => {
|
|
||||||
this.reset();
|
this.reset();
|
||||||
this.get('contributors').pushObject(contributor);
|
this.get('contributors').pushObject(contributor);
|
||||||
window.scroll(0,0);
|
window.scroll(0,0);
|
||||||
|
|||||||
@@ -1,31 +1,58 @@
|
|||||||
<form {{action "save" on="submit"}}>
|
<form {{action "save" on="submit"}}>
|
||||||
<p>
|
<p>
|
||||||
{{input type="checkbox" name="is-core" id="is-core" checked=isCore}}
|
{{input type="checkbox" name="is-core" id="is-core" checked=newContributor.isCore}}
|
||||||
<label for="is-core" class="checkbox">
|
<label for="is-core" class="checkbox">
|
||||||
Core team member (can add contributors)
|
Core team member (can add contributors)
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
{{input name="id"
|
|
||||||
type="text"
|
|
||||||
placeholder="GitHub UID (123)"
|
|
||||||
value=id
|
|
||||||
class=(if isValidId 'valid' '')}}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{input name="realname"
|
|
||||||
type="text"
|
|
||||||
placeholder="GitHub username"
|
|
||||||
value=realName
|
|
||||||
class=(if isValidRealName 'valid' '')}}
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
{{input name="address"
|
{{input name="address"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
|
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
|
||||||
value=address
|
value=newContributor.address
|
||||||
class=(if isValidAddress 'valid' '')}}
|
class=(if isValidAddress 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<select required onchange={{action (mut newContributor.kind) value="target.value"}}>
|
||||||
|
<option value="person" selected={{eq newContributor.kind "person"}}>Person</option>
|
||||||
|
<option value="organization" selected={{eq newContributor.kind "organization"}}>Organization</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{input name="name"
|
||||||
|
type="text"
|
||||||
|
placeholder="Name"
|
||||||
|
value=newContributor.name
|
||||||
|
class=(if isValidName 'valid' '')}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{input name="url"
|
||||||
|
type="text"
|
||||||
|
placeholder="URL"
|
||||||
|
value=newContributor.url
|
||||||
|
class=(if isValidURL 'valid' '')}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{input name="github_uid"
|
||||||
|
type="text"
|
||||||
|
placeholder="GitHub UID (123)"
|
||||||
|
value=newContributor.github_uid
|
||||||
|
class=(if isValidGithubUID 'valid' '')}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{input name="github_username"
|
||||||
|
type="text"
|
||||||
|
placeholder="GitHub username"
|
||||||
|
value=newContributor.github_username
|
||||||
|
class=(if isValidGithubUsername 'valid' '')}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{input name="wiki_username"
|
||||||
|
type="text"
|
||||||
|
placeholder="Wiki Username"
|
||||||
|
value=newContributor.wiki_username
|
||||||
|
class=(if isValidWikiUsername 'valid' '')}}
|
||||||
|
</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}}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Ember from 'ember';
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
Component,
|
Component,
|
||||||
|
isPresent,
|
||||||
inject: {
|
inject: {
|
||||||
service
|
service
|
||||||
},
|
},
|
||||||
@@ -13,47 +14,52 @@ 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() {
|
||||||
return this.get('kredits.web3Instance').isAddress(this.get('proposal.recipientAddress'));
|
return this.get('kredits.web3').isAddress(this.get('proposal.recipientAddress'));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
isValidAmount: computed('proposal.amount', function() {
|
isValidAmount: computed('proposal.amount', function() {
|
||||||
// TODO
|
return parseInt(this.get('proposal.amount'), 10) > 0;
|
||||||
return true;
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
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.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
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) => {
|
||||||
Ember.Logger.error('Error creating the proposal', error);
|
Ember.Logger.error('[add-proposal] error creating the proposal', error);
|
||||||
alert('Something went wrong.');
|
alert('Something went wrong.');
|
||||||
}).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}}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<tr class="{{if contributor.isCurrentUser 'current-user'}}" {{action "toggleContributorInfo" contributor}}>
|
<tr class="{{if contributor.isCurrentUser 'current-user'}}" {{action "toggleContributorInfo" contributor}}>
|
||||||
<td class="person">
|
<td class="person">
|
||||||
<img class="avatar" src={{contributor.avatarURL}}>
|
<img class="avatar" src={{contributor.avatarURL}}>
|
||||||
{{contributor.github_username}}
|
{{contributor.name}}
|
||||||
</td>
|
</td>
|
||||||
<td class="kredits">
|
<td class="kredits">
|
||||||
<span class="amount">{{contributor.kredits}}</span>
|
<span class="amount">{{contributor.kredits}}</span>
|
||||||
@@ -12,14 +12,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}">
|
<tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}">
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<dl>
|
<ul>
|
||||||
<dt>Ethereum address</dt>
|
<li><a href="https://testnet.etherscan.io/address/{{contributor.address}}">Inspect Ethereum transactions</a></li>
|
||||||
<dd><a href="https://testnet.etherscan.io/address/{{contributor.address}}">{{contributor.address}}</a></dd>
|
|
||||||
{{#if contributor.ipfsHash}}
|
{{#if contributor.ipfsHash}}
|
||||||
<dt>IPFS profile data</dt>
|
<li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li>
|
||||||
<dd><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">{{contributor.ipfsHash}}</a></dd>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</dl>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
{{#each proposals as |proposal|}}
|
{{#each proposals as |proposal|}}
|
||||||
<li>
|
<li data-proposal-id={{proposal.id}} title="({{proposal.kind}}) {{proposal.description}}">
|
||||||
<span class="id">#{{proposal.id}}</span>
|
<span class="category {{proposal.kind}}">♥</span>
|
||||||
Issue{{#if proposal.executed}}d{{/if}}
|
|
||||||
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
|
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
|
||||||
to <span class="recipient" title="{{proposal.recipientAddress}}">{{proposal.recipientName}}</span>
|
for <span class="recipient">{{proposal.recipientName}}</span>
|
||||||
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
|
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
|
||||||
|
|
||||||
{{#unless proposal.executed}}<button {{action "confirm" proposal.id}}>+1</button>{{/unless}}
|
{{#unless proposal.executed}}<button {{action "confirm" proposal.id}}>+1</button>{{/unless}}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default Ember.Controller.extend({
|
|||||||
let proposals = this.get('model.proposals')
|
let proposals = this.get('model.proposals')
|
||||||
.filterBy('executed', false)
|
.filterBy('executed', false)
|
||||||
.map(p => {
|
.map(p => {
|
||||||
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).github_username);
|
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
return proposals;
|
return proposals;
|
||||||
@@ -33,7 +33,7 @@ export default Ember.Controller.extend({
|
|||||||
let proposals = this.get('model.proposals')
|
let proposals = this.get('model.proposals')
|
||||||
.filterBy('executed', true)
|
.filterBy('executed', true)
|
||||||
.map(p => {
|
.map(p => {
|
||||||
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).github_username);
|
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
return proposals;
|
return proposals;
|
||||||
|
|||||||
+98
-10
@@ -1,10 +1,18 @@
|
|||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
const {
|
||||||
|
isPresent,
|
||||||
|
} = Ember;
|
||||||
|
|
||||||
export default Ember.Object.extend({
|
export default Ember.Object.extend({
|
||||||
|
|
||||||
address: null,
|
address: null,
|
||||||
|
name: null,
|
||||||
|
kind: null,
|
||||||
|
url: null,
|
||||||
github_username: null,
|
github_username: null,
|
||||||
github_uid: null,
|
github_uid: null,
|
||||||
|
wiki_username: null,
|
||||||
ipfsHash: null,
|
ipfsHash: null,
|
||||||
kredits: null,
|
kredits: null,
|
||||||
isCore: false,
|
isCore: false,
|
||||||
@@ -14,18 +22,98 @@ export default Ember.Object.extend({
|
|||||||
return `https\:\/\/avatars2.githubusercontent.com/u/${this.get('github_uid')}?v=3&s=128`;
|
return `https\:\/\/avatars2.githubusercontent.com/u/${this.get('github_uid')}?v=3&s=128`;
|
||||||
}.property('github_uid'),
|
}.property('github_uid'),
|
||||||
|
|
||||||
serialize() {
|
|
||||||
return JSON.stringify({
|
/**
|
||||||
profiles: {
|
* Loads the contributor's profile data from IPFS and sets local instance
|
||||||
'github.com': {
|
* properties from it
|
||||||
uid: this.get('github_uid'),
|
*
|
||||||
username: this.get('github_username'),
|
* @method
|
||||||
}
|
* @public
|
||||||
// 'wiki.kosmos.org': {
|
*/
|
||||||
// username: this.get('wiki_username')
|
loadProfile(ipfs) {
|
||||||
// }
|
let promise = new Ember.RSVP.Promise((resolve, reject) => {
|
||||||
|
ipfs.getFile(this.get('ipfsHash')).then(content => {
|
||||||
|
let profileJSON = JSON.parse(content);
|
||||||
|
let profile = Ember.Object.create(profileJSON);
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
name: profile.get('name'),
|
||||||
|
kind: profile.get('kind')
|
||||||
|
});
|
||||||
|
|
||||||
|
let accounts = profile.get('accounts');
|
||||||
|
let github = accounts.findBy('site', 'github.com');
|
||||||
|
let wiki = accounts.findBy('site', 'wiki.kosmos.org');
|
||||||
|
|
||||||
|
if (isPresent(github)) {
|
||||||
|
this.setProperties({
|
||||||
|
github_username: github.username,
|
||||||
|
github_uid: github.uid,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
if (isPresent(wiki)) {
|
||||||
|
this.setProperties({
|
||||||
|
wiki_username: wiki.username
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ember.Logger.debug('[contributor] loaded contributor profile', profile);
|
||||||
|
resolve();
|
||||||
|
}).catch((err) => {
|
||||||
|
Ember.Logger.error('[contributor] error trying to load contributor profile', this.get('ipfsHash'), err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JSON-LD object of the contributor, according to
|
||||||
|
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
toJSON() {
|
||||||
|
let contributor = {
|
||||||
|
"@context": "https://schema.kosmos.org",
|
||||||
|
"@type": "Contributor",
|
||||||
|
"kind": this.get('kind'),
|
||||||
|
"name": this.get('name'),
|
||||||
|
"accounts": []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Ember.isPresent(this.get('url'))) {
|
||||||
|
contributor["url"] = this.get('url');
|
||||||
|
}
|
||||||
|
if (Ember.isPresent(this.get('github_uid'))) {
|
||||||
|
contributor.accounts.push({
|
||||||
|
"site": "github.com",
|
||||||
|
"uid": this.get('github_uid'),
|
||||||
|
"username": this.get('github_username'),
|
||||||
|
"url": `https://github.com/${this.get('github_username')}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (Ember.isPresent(this.get('wiki_username'))) {
|
||||||
|
contributor.accounts.push({
|
||||||
|
"site": "wiki.kosmos.org",
|
||||||
|
"username": this.get('wiki_username'),
|
||||||
|
"url": `https://wiki.kosmos.org/User:${this.get('wiki_username')}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return contributor;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JSON-LD representation of the model as a string
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
serialize() {
|
||||||
|
return JSON.stringify(this.toJSON());
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+87
-1
@@ -1,16 +1,102 @@
|
|||||||
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,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the contribution details from IPFS and sets local instance
|
||||||
|
* properties from it
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
loadContribution(ipfs) {
|
||||||
|
let promise = new Ember.RSVP.Promise((resolve, reject) => {
|
||||||
|
ipfs.getFile(this.get('ipfsHash')).then(content => {
|
||||||
|
let contributionJSON = JSON.parse(content);
|
||||||
|
let contribution = Ember.Object.create(contributionJSON);
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
kind: contribution.get('kind'),
|
||||||
|
description: contribution.get('description'),
|
||||||
|
url: contribution.get('url')
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO load details
|
||||||
|
// let details = profile.get('accounts');
|
||||||
|
|
||||||
|
Ember.Logger.debug('[proposal] loaded contribution details', contributionJSON);
|
||||||
|
resolve();
|
||||||
|
}).catch((err) => {
|
||||||
|
Ember.Logger.error('[proposal] error trying to load contribution details', this.get('ipfsHash'), err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+3
-1
@@ -1,4 +1,5 @@
|
|||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import Contributor from 'kredits-web/models/contributor';
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
|
|
||||||
@@ -10,7 +11,8 @@ export default Ember.Route.extend({
|
|||||||
return Ember.RSVP.hash({
|
return Ember.RSVP.hash({
|
||||||
contributors: kredits.getContributors(),
|
contributors: kredits.getContributors(),
|
||||||
totalSupply: kredits.getValueFromContract('tokenContract', 'totalSupply'),
|
totalSupply: kredits.getValueFromContract('tokenContract', 'totalSupply'),
|
||||||
proposals: kredits.getProposals()
|
proposals: kredits.getProposals(),
|
||||||
|
newContributor: Contributor.create({ kind: 'person' })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ export default Ember.Service.extend({
|
|||||||
getFile(hash) {
|
getFile(hash) {
|
||||||
return this.get('ipfs').cat(hash, { buffer: true }).then(res => {
|
return this.get('ipfs').cat(hash, { buffer: true }).then(res => {
|
||||||
return res.toString();
|
return res.toString();
|
||||||
|
}, err => {
|
||||||
|
Ember.Logger.error('[ipfs] error trying to fetch file', hash, err);
|
||||||
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-19
@@ -4,6 +4,7 @@ import config from 'kredits-web/config/environment';
|
|||||||
import Contributor from 'kredits-web/models/contributor';
|
import Contributor from 'kredits-web/models/contributor';
|
||||||
import Proposal from 'kredits-web/models/proposal';
|
import Proposal from 'kredits-web/models/proposal';
|
||||||
import kreditsContracts from 'npm:kredits-contracts';
|
import kreditsContracts from 'npm:kredits-contracts';
|
||||||
|
import uuid from 'npm:uuid';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
Service,
|
Service,
|
||||||
@@ -101,17 +102,19 @@ 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('person', person);
|
Ember.Logger.debug('[kredits] person', address, person);
|
||||||
|
|
||||||
let contributor = Contributor.create({
|
let contributor = Contributor.create({
|
||||||
address: address,
|
address: address,
|
||||||
github_username: person[1],
|
|
||||||
github_uid: person[0],
|
|
||||||
ipfsHash: person[2],
|
ipfsHash: person[2],
|
||||||
kredits: balance.toNumber(),
|
kredits: balance.toNumber(),
|
||||||
isCurrentUser: this.get('currentUserAccounts').includes(address)
|
isCurrentUser: this.get('currentUserAccounts').includes(address)
|
||||||
});
|
});
|
||||||
Ember.Logger.debug('[kredits] contributor', contributor);
|
|
||||||
resolve(contributor);
|
contributor.loadProfile(this.get('ipfs')).then(
|
||||||
|
() => resolve(contributor),
|
||||||
|
err => reject(err)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).catch(err => reject(err));
|
}).catch(err => reject(err));
|
||||||
@@ -145,8 +148,16 @@ export default Service.extend({
|
|||||||
url : p[6],
|
url : p[6],
|
||||||
ipfsHash : p[7]
|
ipfsHash : p[7]
|
||||||
});
|
});
|
||||||
Ember.Logger.debug('[kredits] proposal', proposal);
|
|
||||||
|
if (proposal.get('ipfsHash')) {
|
||||||
|
proposal.loadContribution(this.get('ipfs')).then(
|
||||||
|
() => resolve(proposal),
|
||||||
|
err => reject(err)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Ember.Logger.warn('[kredits] proposal from blockchain is missing IPFS hash', proposal);
|
||||||
resolve(proposal);
|
resolve(proposal);
|
||||||
|
}
|
||||||
}).catch(err => reject(err));
|
}).catch(err => reject(err));
|
||||||
});
|
});
|
||||||
return promise;
|
return promise;
|
||||||
@@ -175,23 +186,20 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addContributor(address, name, isCore, id) {
|
addContributor(contributor) {
|
||||||
Ember.Logger.debug('[kredits] add contributor', name, address);
|
Ember.Logger.debug('[kredits] add contributor', contributor);
|
||||||
|
|
||||||
let contributor = Contributor.create({
|
contributor.setProperties({
|
||||||
address: address,
|
|
||||||
github_username: name,
|
|
||||||
github_uid: id,
|
|
||||||
kredits: 0,
|
kredits: 0,
|
||||||
isCore: isCore,
|
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
|
||||||
isCurrentUser: this.get('currentUserAccounts').includes(address)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let id = uuid.v4();
|
||||||
|
|
||||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
return new Ember.RSVP.Promise((resolve, reject) => {
|
||||||
this.get('ipfs').storeFile(contributor.serialize()).then(ipfsHash => {
|
this.get('ipfs').storeFile(contributor.serialize()).then(ipfsHash => {
|
||||||
contributor.set('ipfsHash', ipfsHash);
|
contributor.set('ipfsHash', ipfsHash);
|
||||||
Ember.Logger.debug('ADD', address, name, ipfsHash, isCore, id);
|
this.get('kreditsContract').addContributor(contributor.address, contributor.name, contributor.ipfsHash, contributor.isCore, id, (err, data) => {
|
||||||
this.get('kreditsContract').addContributor(address, name, ipfsHash, isCore, id, (err, data) => {
|
|
||||||
if (err) { reject(err); return; }
|
if (err) { reject(err); return; }
|
||||||
Ember.Logger.debug('[kredits] add contributor response', data);
|
Ember.Logger.debug('[kredits] add contributor response', data);
|
||||||
resolve(contributor);
|
resolve(contributor);
|
||||||
@@ -205,16 +213,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() {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
$blue: #68d7fb;
|
||||||
|
$purple: #8f68fb;
|
||||||
|
$pink: #e068fb;
|
||||||
|
$green: #97fb68;
|
||||||
|
$yellow: #fbe468;
|
||||||
|
$red: #fb6868;
|
||||||
|
|
||||||
|
$primaryColor: $blue;
|
||||||
+7
-6
@@ -15,6 +15,7 @@
|
|||||||
@import "neat";
|
@import "neat";
|
||||||
@import "settings/breakpoints";
|
@import "settings/breakpoints";
|
||||||
@import "layout";
|
@import "layout";
|
||||||
|
@import "colors";
|
||||||
|
|
||||||
$font-family-sans: 'Open Sans', sans-serif;
|
$font-family-sans: 'Open Sans', sans-serif;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ body {
|
|||||||
background-repeat: none;
|
background-repeat: none;
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
font-family: $font-family-sans;
|
font-family: $font-family-sans;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,7 @@ h1, h2, h3, h4, h5, input, button {
|
|||||||
section {
|
section {
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 2.8rem;
|
font-size: 2.8rem;
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
@include media($mobile) {
|
@include media($mobile) {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
@@ -54,7 +55,7 @@ section {
|
|||||||
p.stats {
|
p.stats {
|
||||||
padding-top: 3rem;
|
padding-top: 3rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: lightblue;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
span.number {
|
span.number {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -70,14 +71,14 @@ section {
|
|||||||
.actions {
|
.actions {
|
||||||
padding-top: 3rem;
|
padding-top: 3rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@include media($mobile) {
|
@include media($mobile) {
|
||||||
padding-top: 2rem;
|
padding-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,7 @@ button, input[type=submit] {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid rgba(22, 21, 40, 1);
|
border: 1px solid rgba(22, 21, 40, 1);
|
||||||
background-color: rgba(22, 21, 40, 0.6);
|
background-color: rgba(22, 21, 40, 0.6);
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ section#add-contributor, section#add-proposal {
|
|||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
a {
|
a {
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,56 +15,42 @@ table.contributor-list {
|
|||||||
|
|
||||||
&.metadata {
|
&.metadata {
|
||||||
height: 0;
|
height: 0;
|
||||||
|
transition: all 0.2s ease-out;
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 0 1.2rem;
|
padding: 0 1.2rem;
|
||||||
transition: all 0.2s ease-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
&:hover, &:active {
|
&:hover, &:active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dl {
|
ul {
|
||||||
|
list-style: none;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
|
||||||
font-size: 1rem;
|
|
||||||
dt, dd {
|
|
||||||
display: inline-block;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 0;
|
height: 0;
|
||||||
line-height: 1.6rem;
|
|
||||||
transition: all 0.2s ease-out;
|
transition: all 0.2s ease-out;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline;
|
||||||
|
&+li {
|
||||||
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
dt {
|
|
||||||
clear: both;
|
|
||||||
float: left;
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
float: right;
|
|
||||||
width: 70%;
|
|
||||||
text-align: right;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.visible {
|
&.visible {
|
||||||
td {
|
height: auto;
|
||||||
padding: 1rem 1.2rem;
|
ul {
|
||||||
}
|
|
||||||
|
|
||||||
dl {
|
|
||||||
dt, dd {
|
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 0 1.2rem;
|
padding: 0 1.2rem;
|
||||||
line-height: 4.2rem;
|
line-height: 4.2rem;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
margin-top: 12rem;
|
margin-top: 12rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
|
|
||||||
@include media($mobile) {
|
@include media($mobile) {
|
||||||
margin-top: 6rem;
|
margin-top: 6rem;
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
|
|
||||||
#path-comet {
|
#path-comet {
|
||||||
fill: lightblue;
|
fill: $primaryColor;
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
|
|
||||||
animation-name: pulse;
|
animation-name: pulse;
|
||||||
|
|||||||
@@ -14,9 +14,14 @@ ul.proposal-list {
|
|||||||
border-top: 1px solid rgba(255,255,255,0.2);
|
border-top: 1px solid rgba(255,255,255,0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.id {
|
.category {
|
||||||
color: lightblue;
|
color: $blue;
|
||||||
padding-right: 0.2rem;
|
padding-right: 0.2rem;
|
||||||
|
&.community { color: $red; }
|
||||||
|
&.dev { color: $pink; }
|
||||||
|
&.design { color: $yellow; }
|
||||||
|
&.docs { color: $green; }
|
||||||
|
&.ops { color: $purple; }
|
||||||
}
|
}
|
||||||
.amount {
|
.amount {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -31,8 +36,8 @@ ul.proposal-list {
|
|||||||
}
|
}
|
||||||
.votes {
|
.votes {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: lightblue;
|
color: $primaryColor;
|
||||||
padding-left: 1rem;
|
padding-left: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
|||||||
@@ -54,8 +54,8 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{add-contributor kredits=kredits
|
{{add-contributor contributors=model.contributors
|
||||||
contributors=model.contributors
|
newContributor=model.newContributor
|
||||||
contractInteractionEnabled=contractInteractionEnabled}}
|
contractInteractionEnabled=contractInteractionEnabled}}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ module.exports = function(environment) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
browserify: {
|
browserify: {
|
||||||
|
tests: true,
|
||||||
transform: [
|
transform: [
|
||||||
["babelify", {
|
["babelify", {
|
||||||
presets: ["es2015"],
|
presets: ["es2015"],
|
||||||
@@ -38,7 +39,7 @@ module.exports = function(environment) {
|
|||||||
ipfs: {
|
ipfs: {
|
||||||
host: 'ipfs.kosmos.org',
|
host: 'ipfs.kosmos.org',
|
||||||
port: '5444',
|
port: '5444',
|
||||||
protocol: 'http'
|
protocol: 'https'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -3,7 +3,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Contribution dashboard of the Kosmos project",
|
"description": "Contribution dashboard of the Kosmos project",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Kosmos Community",
|
"author": "Kosmos Contributors <mail@kosmos.org>",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Garret Alfert <alfert@wevelop.de>",
|
"Garret Alfert <alfert@wevelop.de>",
|
||||||
"Sebastian Kippe <sebastian@kip.pe>",
|
"Sebastian Kippe <sebastian@kip.pe>",
|
||||||
@@ -55,8 +55,11 @@
|
|||||||
"ember-resolver": "^2.0.3",
|
"ember-resolver": "^2.0.3",
|
||||||
"ember-truth-helpers": "1.3.0",
|
"ember-truth-helpers": "1.3.0",
|
||||||
"ipfs-api": "^12.1.7",
|
"ipfs-api": "^12.1.7",
|
||||||
|
"kosmos-schemas": "~1.1.1",
|
||||||
"kredits-contracts": "^2.4.0",
|
"kredits-contracts": "^2.4.0",
|
||||||
"loader.js": "^4.0.10",
|
"loader.js": "^4.0.10",
|
||||||
|
"tv4": "^1.3.0",
|
||||||
|
"uuid": "^3.0.1",
|
||||||
"web3": "^0.18.2"
|
"web3": "^0.18.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
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:contributor', 'Unit | Model | contributor');
|
moduleFor('model:contributor', 'Unit | Model | contributor');
|
||||||
|
|
||||||
test('avatarURL returns correct URL', function(assert) {
|
test('#avatarURL() returns correct URL', function(assert) {
|
||||||
let model = this.subject();
|
let model = this.subject();
|
||||||
model.set('github_uid', '318');
|
model.set('github_uid', '318');
|
||||||
|
|
||||||
assert.equal(model.get('avatarURL'), 'https://avatars2.githubusercontent.com/u/318?v=3&s=128');
|
assert.equal(model.get('avatarURL'), 'https://avatars2.githubusercontent.com/u/318?v=3&s=128');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('#toJSON() returns a valid JSON-LD representation of the model', function(assert) {
|
||||||
|
let model = this.subject();
|
||||||
|
|
||||||
|
model.setProperties({
|
||||||
|
name: 'Satoshi Nakamoto',
|
||||||
|
kind: 'person',
|
||||||
|
github_uid: 123,
|
||||||
|
github_username: 'therealsatoshi',
|
||||||
|
wiki_username: 'Satoshi',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(tv4.validate(model.toJSON(), schemas['contributor']));
|
||||||
|
});
|
||||||
|
|||||||
@@ -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