Refactor contributor model #30
@@ -1,81 +1,67 @@
|
|||||||
import Ember from 'ember';
|
import Component from 'ember-component';
|
||||||
import Contributor from 'kredits-web/models/contributor';
|
import computed, { and } from 'ember-computed';
|
||||||
|
import injectService from 'ember-service/inject';
|
||||||
|
import isPresent from 'kredits-web/utils/cps/is-present';
|
||||||
|
|
||||||
const {
|
|
||||||
Component,
|
|
||||||
isPresent,
|
|
||||||
inject: {
|
|
||||||
service
|
|
||||||
},
|
|
||||||
computed
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
kredits: injectService(),
|
||||||
|
|
||||||
kredits: service(),
|
// Default attributes used by reset
|
||||||
|
attributes: {
|
||||||
|
address: null,
|
||||||
|
name: null,
|
||||||
|
kind: 'person',
|
||||||
|
url: null,
|
||||||
|
github_username: null,
|
||||||
|
github_uid: null,
|
||||||
|
wiki_username: null,
|
||||||
|
isCore: false,
|
||||||
|
},
|
||||||
|
|
||||||
newContributor: null,
|
didInsertElement() {
|
||||||
inProgress: false,
|
this._super(...arguments);
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
isValidAddress: function() {
|
isValidAddress: computed('kredits.ethProvider', 'address', function() {
|
||||||
// TODO: add proper address validation
|
// TODO: add proper address validation
|
||||||
return this.get('newContributor.address') !== '';
|
return this.get('address') !== '';
|
||||||
}.property('kredits.ethProvider', 'newContributor.address'),
|
}),
|
||||||
|
isValidName: isPresent('name'),
|
||||||
isValidName: function() {
|
isValidURL: isPresent('url'),
|
||||||
return isPresent(this.get('newContributor.name'));
|
isValidGithubUID: isPresent('github_uid'),
|
||||||
}.property('newContributor.name'),
|
isValidGithubUsername: isPresent('github_username'),
|
||||||
|
isValidWikiUsername: isPresent('wiki_username'),
|
||||||
isValidURL: function() {
|
isValid: and(
|
||||||
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',
|
'isValidAddress',
|
||||||
'isValidName',
|
'isValidName',
|
||||||
'isValidGithubUID'
|
'isValidGithubUID'
|
||||||
),
|
),
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this.setProperties({
|
this.setProperties(this.get('attributes'));
|
||||||
newContributor: Contributor.create({ kind: 'person' }),
|
|
||||||
inProgress: false
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
submit() {
|
||||||
save() {
|
if (!this.get('isValid')) {
|
||||||
if (!this.get('contractInteractionEnabled')) {
|
alert('Invalid data. Please review and try again.');
|
||||||
alert('Only core team members can add new contributors. Please ask someone to set you up.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.get('isValid')) {
|
let attributes = Object.keys(this.get('attributes'));
|
||||||
this.set('inProgress', true);
|
let contributor = this.getProperties(attributes);
|
||||||
|
let saved = this.save(contributor);
|
||||||
|
|
||||||
this.get('kredits').addContributor(this.get('newContributor')).then(contributor => {
|
// The promise handles inProgress
|
||||||
this.reset();
|
this.set('inProgress', saved);
|
||||||
this.get('contributors').pushObject(contributor);
|
|
||||||
window.scroll(0,0);
|
saved.then(() => {
|
||||||
});
|
this.reset();
|
||||||
} else {
|
window.scroll(0,0);
|
||||||
alert('Invalid data. Please review and try again.');
|
window.alert('Contributor added.');
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<form {{action "save" on="submit"}}>
|
<form {{action "submit" on="submit"}}>
|
||||||
<p>
|
<p>
|
||||||
{{input type="checkbox" name="is-core" id="is-core" checked=newContributor.isCore}}
|
{{input name="is-core"
|
||||||
|
type="checkbox"
|
||||||
|
id="is-core"
|
||||||
|
checked=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>
|
||||||
@@ -9,51 +12,53 @@
|
|||||||
{{input name="address"
|
{{input name="address"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
|
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
|
||||||
value=newContributor.address
|
value=address
|
||||||
class=(if isValidAddress 'valid' '')}}
|
class=(if isValidAddress 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<select required onchange={{action (mut newContributor.kind) value="target.value"}}>
|
<select required onchange={{action (mut kind) value="target.value"}}>
|
||||||
<option value="person" selected={{eq newContributor.kind "person"}}>Person</option>
|
<option value="person" selected={{eq kind "person"}}>Person</option>
|
||||||
<option value="organization" selected={{eq newContributor.kind "organization"}}>Organization</option>
|
<option value="organization" selected={{eq kind "organization"}}>Organization</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="name"
|
{{input name="name"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
value=newContributor.name
|
value=name
|
||||||
class=(if isValidName 'valid' '')}}
|
class=(if isValidName 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="url"
|
{{input name="url"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="URL"
|
placeholder="URL"
|
||||||
value=newContributor.url
|
value=url
|
||||||
class=(if isValidURL 'valid' '')}}
|
class=(if isValidURL 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="github_uid"
|
{{input name="github_uid"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="GitHub UID (123)"
|
placeholder="GitHub UID (123)"
|
||||||
value=newContributor.github_uid
|
value=github_uid
|
||||||
class=(if isValidGithubUID 'valid' '')}}
|
class=(if isValidGithubUID 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="github_username"
|
{{input name="github_username"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="GitHub username"
|
placeholder="GitHub username"
|
||||||
value=newContributor.github_username
|
value=github_username
|
||||||
class=(if isValidGithubUsername 'valid' '')}}
|
class=(if isValidGithubUsername 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="wiki_username"
|
{{input name="wiki_username"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Wiki Username"
|
placeholder="Wiki Username"
|
||||||
value=newContributor.wiki_username
|
value=wiki_username
|
||||||
class=(if isValidWikiUsername 'valid' '')}}
|
class=(if isValidWikiUsername 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p class="actions">
|
<p class="actions">
|
||||||
{{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
|
{{input type="submit"
|
||||||
|
disabled=(is-pending inProgress)
|
||||||
|
value=(if (is-pending inProgress) 'Processing' 'Save')}}
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -116,13 +116,18 @@ export default Ember.Controller.extend({
|
|||||||
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
||||||
confirmProposal(proposalId) {
|
confirmProposal(proposalId) {
|
||||||
this.get('kredits').vote(proposalId).then(transaction => {
|
this.get('kredits').vote(proposalId).then(transaction => {
|
||||||
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
|
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
save(contributor) {
|
||||||
|
return this.get('kredits').addContributor(contributor)
|
||||||
|
.then((contributor) => {
|
||||||
|
this.get('model.contributors').pushObject(contributor);
|
||||||
|
return contributor;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import ContributorSerializer from './serializers/contributor';
|
||||||
|
|
||||||
|
export {
|
||||||
|
ContributorSerializer
|
||||||
|
};
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* Handle serialization for JSON-LD object of the contributor, according to
|
||||||
|
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export default class Contributor {
|
||||||
|
/**
|
||||||
|
* Deserialize JSON to object
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
static deserialize(serialized) {
|
||||||
|
let {
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
url,
|
||||||
|
accounts,
|
||||||
|
} = JSON.parse(serialized);
|
||||||
|
|
||||||
|
let github_username, github_uid, wiki_username;
|
||||||
|
let github = accounts.find((a) => a.site === 'github.com');
|
||||||
|
let wiki = accounts.find((a) => a.site === 'wiki.kosmos.org');
|
||||||
|
|
||||||
|
if (github) {
|
||||||
|
({ username: github_username, uid: github_uid} = github);
|
||||||
|
}
|
||||||
|
if (wiki) {
|
||||||
|
({ username: wiki_username } = wiki);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
url,
|
||||||
|
github_uid,
|
||||||
|
github_username,
|
||||||
|
wiki_username,
|
||||||
|
ipfsData: serialized,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize object to JSON
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
static serialize(deserialized) {
|
||||||
|
let {
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
url,
|
||||||
|
github_uid,
|
||||||
|
github_username,
|
||||||
|
wiki_username,
|
||||||
|
} = deserialized;
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
"@context": "https://schema.kosmos.org",
|
||||||
|
"@type": "Contributor",
|
||||||
|
kind,
|
||||||
|
name,
|
||||||
|
"accounts": []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
data["url"] = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (github_uid) {
|
||||||
|
data.accounts.push({
|
||||||
|
"site": "github.com",
|
||||||
|
"uid": github_uid,
|
||||||
|
"username": github_username,
|
||||||
|
"url": `https://github.com/${github_username}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wiki_username) {
|
||||||
|
data.accounts.push({
|
||||||
|
"site": "wiki.kosmos.org",
|
||||||
|
"username": wiki_username,
|
||||||
|
"url": `https://wiki.kosmos.org/User:${wiki_username}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write it pretty to ipfs
|
||||||
|
return JSON.stringify(data, null, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-105
@@ -1,14 +1,7 @@
|
|||||||
import Ember from 'ember';
|
|
||||||
import computed from 'ember-computed';
|
import computed from 'ember-computed';
|
||||||
import injectService from 'ember-service/inject';
|
import EmberObject from 'ember-object';
|
||||||
|
|
||||||
const {
|
|
||||||
isPresent,
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Ember.Object.extend({
|
|
||||||
ipfs: injectService(),
|
|
||||||
|
|
||||||
|
export default EmberObject.extend({
|
||||||
id: null,
|
id: null,
|
||||||
address: null,
|
address: null,
|
||||||
name: null,
|
name: null,
|
||||||
@@ -18,7 +11,7 @@ export default Ember.Object.extend({
|
|||||||
github_uid: null,
|
github_uid: null,
|
||||||
wiki_username: null,
|
wiki_username: null,
|
||||||
profileHash: null,
|
profileHash: null,
|
||||||
balance: null,
|
balance: 0,
|
||||||
isCore: false,
|
isCore: false,
|
||||||
isCurrentUser: false,
|
isCurrentUser: false,
|
||||||
|
|
||||||
@@ -28,99 +21,4 @@ export default Ember.Object.extend({
|
|||||||
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`;
|
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the contributor's profile data from IPFS and sets local instance
|
|
||||||
* properties from it
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
loadProfile() {
|
|
||||||
let profileHash = this.get('profileHash');
|
|
||||||
if (!profileHash) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.get('ipfs')
|
|
||||||
.getFile(profileHash)
|
|
||||||
.then((content) => {
|
|
||||||
let profile = Ember.Object.create(JSON.parse(content));
|
|
||||||
this.set('ipfsData', JSON.stringify(profile, null, 2));
|
|
||||||
|
|
||||||
Ember.Logger.debug('[contributor] loaded contributor profile', profile);
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
Ember.Logger.error('[contributor] error trying to load contributor profile', profileHash, err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+105
-48
@@ -14,12 +14,14 @@ import Proposal from 'kredits-web/models/proposal';
|
|||||||
|
|
||||||
import abis from 'contracts/abis';
|
import abis from 'contracts/abis';
|
||||||
import addresses from 'contracts/addresses';
|
import addresses from 'contracts/addresses';
|
||||||
|
import { ContributorSerializer } from 'kredits-web/lib/kredits';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getOwner,
|
getOwner,
|
||||||
Logger: {
|
Logger: {
|
||||||
debug,
|
debug,
|
||||||
warn
|
warn,
|
||||||
|
error
|
||||||
}
|
}
|
||||||
} = Ember;
|
} = Ember;
|
||||||
|
|
||||||
@@ -103,37 +105,55 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getContributorData(id) {
|
// TODO: Should be part of the service
|
||||||
|
buildContributor(attributes) {
|
||||||
|
debug('[kredits] buildContributor', attributes);
|
||||||
|
|
||||||
|
let contributor = getOwner(this).lookup('model:contributor');
|
||||||
|
contributor.setProperties(attributes);
|
||||||
|
return contributor;
|
||||||
|
},
|
||||||
|
|
||||||
|
getContributorById(id) {
|
||||||
return this.get('contributorsContract')
|
return this.get('contributorsContract')
|
||||||
.then((contract) => contract.contributors(id))
|
.then((contract) => contract.contributors(id))
|
||||||
.then((data) => {
|
// Set basic data
|
||||||
debug('[kredits] contributor', data);
|
.then(({
|
||||||
|
account: address,
|
||||||
|
hashFunction,
|
||||||
|
hashSize,
|
||||||
|
isCore,
|
||||||
|
profileHash: digest,
|
||||||
|
}) => {
|
||||||
|
|
||||||
let [ address, digest, hashFunction, size, isCore ] = data;
|
|
||||||
let isCurrentUser = this.get('currentUserAccounts').includes(address);
|
let isCurrentUser = this.get('currentUserAccounts').includes(address);
|
||||||
let profileHash = this.getMultihashFromBytes32({
|
let profileHash = this.getMultihashFromBytes32({
|
||||||
digest,
|
digest,
|
||||||
hashFunction: hashFunction,
|
hashFunction,
|
||||||
size: size
|
size: hashSize
|
||||||
});
|
});
|
||||||
return this.get('tokenContract')
|
|
||||||
.then((contract) => contract.balanceOf(address))
|
|
||||||
.then((balance) => {
|
|
||||||
balance = balance.toNumber();
|
|
||||||
|
|
||||||
let contributor = getOwner(this).lookup('model:contributor');
|
return {
|
||||||
contributor.setProperties({
|
id,
|
||||||
id,
|
address,
|
||||||
address,
|
isCore,
|
||||||
profileHash,
|
isCurrentUser,
|
||||||
isCore,
|
profileHash,
|
||||||
isCurrentUser,
|
};
|
||||||
balance
|
})
|
||||||
});
|
// Add the balance
|
||||||
// Load data from IPFS
|
.then((data) => {
|
||||||
contributor.loadProfile();
|
return this.get('tokenContract')
|
||||||
return contributor;
|
.then((contract) => contract.balanceOf(data.address))
|
||||||
|
.then((balance) => {
|
||||||
|
data.balance = balance.toNumber();
|
||||||
|
return data;
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
// Fetch IPFS data if available
|
||||||
|
.then(this.loadContributorProfile.bind(this))
|
||||||
|
.then((attributes) => {
|
||||||
|
return this.buildContributor(attributes);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -145,13 +165,43 @@ export default Service.extend({
|
|||||||
let contributors = [];
|
let contributors = [];
|
||||||
|
|
||||||
for(var id = 1; id <= contributorsCount.toNumber(); id++) {
|
for(var id = 1; id <= contributorsCount.toNumber(); id++) {
|
||||||
contributors.push(this.getContributorData(id));
|
contributors.push(this.getContributorById(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
return RSVP.all(contributors);
|
return RSVP.all(contributors);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the contributor's profile data from IPFS and returns the attributes
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
loadContributorProfile(data) {
|
||||||
|
let profileHash = data.profileHash;
|
||||||
|
|
||||||
|
if (!profileHash) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.get('ipfs')
|
||||||
|
.getFile(profileHash)
|
||||||
|
.then(ContributorSerializer.deserialize)
|
||||||
|
.then((attributes) => {
|
||||||
|
debug('[kredits] loaded contributor profile', attributes);
|
||||||
|
return Object.assign({}, data, attributes);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
error(
|
||||||
|
'[kredits] error trying to load contributor profile',
|
||||||
|
profileHash,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getProposalData(i) {
|
getProposalData(i) {
|
||||||
return this.get('kreditsContract')
|
return this.get('kreditsContract')
|
||||||
.then((contract) => contract.proposals(i))
|
.then((contract) => contract.proposals(i))
|
||||||
@@ -234,36 +284,42 @@ export default Service.extend({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
addContributor(contributor) {
|
|
||||||
debug('[kredits] add contributor', contributor);
|
// TODO: extract common logic to module
|
||||||
|
addContributor(attributes) {
|
||||||
|
debug('[kredits] add contributor', attributes);
|
||||||
|
|
||||||
|
let json = ContributorSerializer.serialize(attributes);
|
||||||
|
|
||||||
return this.get('ipfs')
|
return this.get('ipfs')
|
||||||
.storeFile(contributor.serialize())
|
.storeFile(json)
|
||||||
.then(profileHash => {
|
// Set profileHash
|
||||||
contributor.setProperties({
|
.then((profileHash) => {
|
||||||
profileHash: profileHash,
|
attributes.profileHash = profileHash;
|
||||||
balance: 0,
|
return attributes;
|
||||||
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
|
})
|
||||||
});
|
.then((attributes) => {
|
||||||
|
|
||||||
let {
|
|
||||||
digest, hashFunction, size
|
|
||||||
} = this.getBytes32FromMultihash(profileHash);
|
|
||||||
|
|
||||||
return this.get('kreditsContract')
|
return this.get('kreditsContract')
|
||||||
.then((contract) => {
|
.then((contract) => {
|
||||||
return contract.addContributor(
|
let { address, isCore, profileHash } = attributes;
|
||||||
contributor.address,
|
let {
|
||||||
|
digest, hashFunction, size
|
||||||
|
} = this.getBytes32FromMultihash(profileHash);
|
||||||
|
|
||||||
|
let contributor = [
|
||||||
|
address,
|
||||||
digest,
|
digest,
|
||||||
hashFunction,
|
hashFunction,
|
||||||
size,
|
size,
|
||||||
contributor.isCore
|
isCore,
|
||||||
);
|
];
|
||||||
})
|
debug('[kredits] addContributor', ...contributor);
|
||||||
.then((data) => {
|
return contract.addContributor(...contributor);
|
||||||
debug('[kredits] add contributor response', data);
|
|
||||||
return contributor;
|
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
debug('[kredits] add contributor response', data);
|
||||||
|
return this.buildContributor(attributes);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -301,11 +357,12 @@ export default Service.extend({
|
|||||||
.then((contract) => {
|
.then((contract) => {
|
||||||
return contract.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
|
return contract.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
|
||||||
.then((id) => {
|
.then((id) => {
|
||||||
|
id = id.toNumber();
|
||||||
// check if the user is a contributor or not
|
// check if the user is a contributor or not
|
||||||
if( id.toNumber() === 0) {
|
if (id === 0) {
|
||||||
return RSVP.resolve();
|
return RSVP.resolve();
|
||||||
} else {
|
} else {
|
||||||
return this.getContributorData(id.toNumber());
|
return this.getContributorById(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,8 +65,11 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{add-contributor contributors=model.contributors
|
{{!--
|
||||||
newContributor=model.newContributor
|
TODO:
|
||||||
contractInteractionEnabled=contractInteractionEnabled}}
|
only show the form if currentUser.isCore else show:
|
||||||
|
Only core team members can add new contributors. Please ask someone to set you up.
|
||||||
|
--}}
|
||||||
|
{{add-contributor contributors=model.contributors save=(action 'save')}}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import computed from 'ember-computed';
|
||||||
|
import { isPresent } from 'ember-utils';
|
||||||
|
|
||||||
|
export default function(key) {
|
||||||
|
return computed(key, function() {
|
||||||
|
return isPresent(this.get(key));
|
||||||
|
});
|
||||||
|
}
|
||||||
Generated
+17
-19
@@ -3144,6 +3144,11 @@
|
|||||||
"safe-buffer": "5.1.1"
|
"safe-buffer": "5.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
||||||
@@ -3163,13 +3168,6 @@
|
|||||||
"safe-buffer": "5.1.1",
|
"safe-buffer": "5.1.1",
|
||||||
"string_decoder": "1.0.3",
|
"string_decoder": "1.0.3",
|
||||||
"util-deprecate": "1.0.2"
|
"util-deprecate": "1.0.2"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"core-util-is": {
|
|
||||||
"version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
@@ -3462,6 +3460,11 @@
|
|||||||
"source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz"
|
"source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"convert-source-map": {
|
||||||
|
"version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-8iaBspZWViJD+h8epOgGyXGWH2wSDLzQ8w9qDs/L7W67IfnEeXKu/Q1cPeWpIrRJc9Mvzbn6hKxnL3goD5ncwQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
|
"version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
|
||||||
"integrity": "sha512-NyXjqu1IwcqH6nv5vmMtaG3iw7kdV3g6MwlUBZkc3Vn5b5AMIWYKfptvzipoyFfhlfOgBQ9zoTxQMravF1QTnw==",
|
"integrity": "sha512-NyXjqu1IwcqH6nv5vmMtaG3iw7kdV3g6MwlUBZkc3Vn5b5AMIWYKfptvzipoyFfhlfOgBQ9zoTxQMravF1QTnw==",
|
||||||
@@ -5668,11 +5671,6 @@
|
|||||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"convert-source-map": {
|
|
||||||
"version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-8iaBspZWViJD+h8epOgGyXGWH2wSDLzQ8w9qDs/L7W67IfnEeXKu/Q1cPeWpIrRJc9Mvzbn6hKxnL3goD5ncwQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"copy-dereference": {
|
"copy-dereference": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/copy-dereference/-/copy-dereference-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/copy-dereference/-/copy-dereference-1.0.0.tgz",
|
||||||
@@ -22277,7 +22275,7 @@
|
|||||||
"ember-cli-babel": {
|
"ember-cli-babel": {
|
||||||
"version": "6.12.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz",
|
||||||
"integrity": "sha512-LMwZ3Xf3Q3jQUXaJtLLJsbbhRZRNv/iea64lZ8OgqZp1fh66CSXfmqV3L9QSuYQKPDNqFiu2v6IpOT08C6GU6w==",
|
"integrity": "sha1-Otzb4SeNofzQuQOPE2DLSsXUQUw=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"amd-name-resolver": "0.0.7",
|
"amd-name-resolver": "0.0.7",
|
||||||
@@ -23378,7 +23376,7 @@
|
|||||||
"ember-weakmap": {
|
"ember-weakmap": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ember-weakmap/-/ember-weakmap-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ember-weakmap/-/ember-weakmap-3.1.1.tgz",
|
||||||
"integrity": "sha512-rfW3A1m3NFsHd/NuHyBkssU0Qf0zGcJmASGfhjZc7fIQeBZvSLIFYZTej+W+YBLPtED9h/SVW63DHTRY5PUR4Q==",
|
"integrity": "sha1-KubgCAtbgM8NEI93Utxp6pYD29c=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"browserslist": "2.11.3",
|
"browserslist": "2.11.3",
|
||||||
@@ -23389,7 +23387,7 @@
|
|||||||
"debug": {
|
"debug": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
@@ -23398,7 +23396,7 @@
|
|||||||
"ember-cli-babel": {
|
"ember-cli-babel": {
|
||||||
"version": "6.12.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz",
|
||||||
"integrity": "sha512-LMwZ3Xf3Q3jQUXaJtLLJsbbhRZRNv/iea64lZ8OgqZp1fh66CSXfmqV3L9QSuYQKPDNqFiu2v6IpOT08C6GU6w==",
|
"integrity": "sha1-Otzb4SeNofzQuQOPE2DLSsXUQUw=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"amd-name-resolver": "0.0.7",
|
"amd-name-resolver": "0.0.7",
|
||||||
@@ -25695,7 +25693,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"kredits-contracts": {
|
"kredits-contracts": {
|
||||||
"version": "github:67P/truffle-kredits#4d2ba2a4bf1bb3ceb1ae9c768abfc73a9cf5ab00",
|
"version": "github:67P/truffle-kredits#bdd99d58cf601d9fadd81c7f0cbd61e57cf552b5",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lcid": {
|
"lcid": {
|
||||||
@@ -28245,7 +28243,7 @@
|
|||||||
},
|
},
|
||||||
"slash": {
|
"slash": {
|
||||||
"version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
|
"version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
|
||||||
"integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==",
|
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"snapdragon": {
|
"snapdragon": {
|
||||||
@@ -29020,7 +29018,7 @@
|
|||||||
},
|
},
|
||||||
"trim-right": {
|
"trim-right": {
|
||||||
"version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
"version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
||||||
"integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==",
|
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"true-case-path": {
|
"true-case-path": {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
"ember-load-initializers": "^0.5.1",
|
"ember-load-initializers": "^0.5.1",
|
||||||
"ember-macro-helpers": "0.17.0",
|
"ember-macro-helpers": "0.17.0",
|
||||||
"ember-parachute": "0.1.0",
|
"ember-parachute": "0.1.0",
|
||||||
|
"ember-promise-helpers": "1.0.6",
|
||||||
"ember-resolver": "^2.0.3",
|
"ember-resolver": "^2.0.3",
|
||||||
"ember-truth-helpers": "1.3.0",
|
"ember-truth-helpers": "1.3.0",
|
||||||
"ethers": "^3.0.8",
|
"ethers": "^3.0.8",
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { module, test } from 'ember-qunit';
|
||||||
|
import schemas from 'npm:kosmos-schemas';
|
||||||
|
import tv4 from 'npm:tv4';
|
||||||
|
import { ContributorSerializer } from 'kredits-web/lib/kredits';
|
||||||
|
|
||||||
|
module('Serializers contributor');
|
||||||
|
|
||||||
|
test('#serialize returns a valid JSON-LD representation', function(assert) {
|
||||||
|
let serialized = ContributorSerializer.serialize({
|
||||||
|
name: 'Satoshi Nakamoto',
|
||||||
|
kind: 'person',
|
||||||
|
github_uid: 123,
|
||||||
|
github_username: 'therealsatoshi',
|
||||||
|
wiki_username: 'Satoshi',
|
||||||
|
});
|
||||||
|
|
||||||
|
let valid = tv4.validate(JSON.parse(serialized), schemas['contributor']);
|
||||||
|
assert.ok(valid);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#deserialize returns a valid object representation', function(assert) {
|
||||||
|
let json = JSON.stringify({
|
||||||
|
"@context": "https://schema.kosmos.org",
|
||||||
|
"@type": "Contributor",
|
||||||
|
"kind": "person",
|
||||||
|
"name": "Satoshi Nakamoto",
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"site": "github.com",
|
||||||
|
"uid": 123,
|
||||||
|
"username": "therealsatoshi",
|
||||||
|
"url": "https://github.com/therealsatoshi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"site": "wiki.kosmos.org",
|
||||||
|
"username": "Satoshi",
|
||||||
|
"url": "https://wiki.kosmos.org/User:Satoshi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
let deserialized = ContributorSerializer.deserialize(json);
|
||||||
|
|
||||||
|
let expected = {
|
||||||
|
name: 'Satoshi Nakamoto',
|
||||||
|
kind: 'person',
|
||||||
|
github_uid: 123,
|
||||||
|
github_username: 'therealsatoshi',
|
||||||
|
wiki_username: 'Satoshi',
|
||||||
|
url: undefined,
|
||||||
|
ipfsData: json,
|
||||||
|
};
|
||||||
|
assert.deepEqual(expected, deserialized);
|
||||||
|
});
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
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');
|
||||||
|
|
||||||
@@ -10,17 +8,3 @@ test('#avatarURL() returns correct URL', function(assert) {
|
|||||||
|
|
||||||
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']));
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user