Compare commits

..

3 Commits

Author SHA1 Message Date
basti 970b390509 Show contribution history from events 2017-08-14 17:33:38 +02:00
basti b8f4040def Get contributions from minted events (WIP) 2017-08-14 17:15:11 +02:00
basti 8fbecd928b Update deps 2017-08-14 17:14:17 +02:00
133 changed files with 2025 additions and 40572 deletions
+4
View File
@@ -0,0 +1,4 @@
{
"directory": "bower_components",
"analytics": false
}
+1 -1
View File
@@ -5,5 +5,5 @@
Setting `disableAnalytics` to true will prevent any data from being sent. Setting `disableAnalytics` to true will prevent any data from being sent.
*/ */
"disableAnalytics": true "disableAnalytics": false
} }
-21
View File
@@ -1,21 +0,0 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
/release/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try
-47
View File
@@ -1,47 +0,0 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module'
},
globals: {
console: true
},
plugins: [
'ember'
],
extends: [
'eslint:recommended',
'plugin:ember/recommended'
],
env: {
browser: true
},
rules: {
'ember/avoid-leaking-state-in-ember-objects': 'warn',
'no-console': 'off'
},
overrides: [
// node files
{
files: [
'.eslintrc.js',
'.template-lintrc.js',
'ember-cli-build.js',
'testem.js',
'blueprints/*/index.js',
'config/**/*.js',
'lib/*/index.js',
'server/**/*.js'
],
parserOptions: {
sourceType: 'script',
ecmaVersion: 2015
},
env: {
browser: false,
node: true
}
}
]
};
+7 -16
View File
@@ -1,26 +1,17 @@
# See https://help.github.com/ignore-files/ for more about ignoring files. # See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output # compiled output
/dist/ /dist
/tmp/ /tmp
# dependencies # dependencies
/bower_components/ /node_modules
/node_modules/ /bower_components
# misc # misc
/.env*
/.pnp*
/.sass-cache /.sass-cache
/connect.lock /connect.lock
/coverage/ /coverage/*
/libpeerconnection.log /libpeerconnection.log
/npm-debug.log* npm-debug.log*
/testem.log testem.log
/yarn-error.log
.tm_properties
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try
+32
View File
@@ -0,0 +1,32 @@
{
"predef": [
"document",
"window",
"-Promise"
],
"browser": true,
"boss": true,
"curly": true,
"debug": false,
"devel": true,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esversion": 6,
"unused": true
}
-9
View File
@@ -1,9 +0,0 @@
'use strict';
module.exports = {
extends: 'recommended',
rules: {
'simple-unless': false
}
};
+10 -17
View File
@@ -1,32 +1,25 @@
--- ---
language: node_js language: node_js
node_js: node_js:
- "10" - "6"
sudo: false sudo: false
dist: xenial
notifications:
email:
on_success: never
on_failure: always
addons:
chrome: stable
cache: cache:
directories: directories:
- $HOME/.npm - $HOME/.npm
- $HOME/.cache # includes bowers cache
env:
global:
# See https://git.io/vdao3 for details.
- JOBS=1
before_install: before_install:
- npm config set spin false - npm config set spin false
- npm install -g bower
- bower --version
- npm install phantomjs-prebuilt
- node_modules/phantomjs-prebuilt/bin/phantomjs --version
install:
- npm install
- bower install
script: script:
- npm run lint:hbs
- npm run lint:js
- npm test - npm test
+19 -58
View File
@@ -10,24 +10,22 @@ A short introduction of this app could easily go here.
You will need the following things properly installed on your computer. You will need the following things properly installed on your computer.
* [Git](https://git-scm.com/) * [Git](https://git-scm.com/)
* [Node.js](https://nodejs.org/) (with npm) * [Node.js](https://nodejs.org/) (with NPM)
* [Bower](https://bower.io/)
* [Ember CLI](https://ember-cli.com/) * [Ember CLI](https://ember-cli.com/)
* [Google Chrome](https://google.com/chrome/) * [PhantomJS](http://phantomjs.org/)
## Installation ## Installation
* `git clone git@github.com:67P/kredits-web.git` this repository * `git clone <repository-url>` this repository
* `cd kredits-web` * `cd kredits-web`
* `npm install` * `npm install`
* `bower install`
## Running / Development ## Running / Development
* `ember serve`
* `ember serve` - by default kredits-web connects to the Ethreum Kovan network
* Visit your app at [http://localhost:4200](http://localhost:4200). * Visit your app at [http://localhost:4200](http://localhost:4200).
* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
See [working with locally deployed contracts](https://github.com/67P/kredits-web#working-with-locally-deployed-contracts) for details on how to develop with locally deployed contracts.
### Code Generators ### Code Generators
@@ -38,12 +36,6 @@ Make use of the many generators for code, try `ember help generate` for more det
* `ember test` * `ember test`
* `ember test --server` * `ember test --server`
### Linting
* `npm run lint:hbs`
* `npm run lint:js`
* `npm run lint:js -- --fix`
### Building ### Building
* `ember build` (development) * `ember build` (development)
@@ -51,66 +43,35 @@ Make use of the many generators for code, try `ember help generate` for more det
### Deploying ### Deploying
_(You need collaborator permissions on the 5apps Deploy project.)_ Specify what it takes to deploy your app.
`npm run deploy`
## Working with locally deployed contracts ## Working with locally deployed contracts
The smart contracts and their JavaScript wrapper library are developed in the For development you might want to run your own development chain with your own development contracts.
[kredits-contracts](https://github.com/67P/kredits-contracts) repo/package. The kredits-contracts helper scripts are available in kredits-web. See [kredits-contracts](https://github.com/67P/kredits-contracts) for more information.
You can run `kredits-web` on your machine, against a local, simulated Ethereum Start your parity node with a KreditsChain:
network, provided e.g. by [ganache](http://truffleframework.com/ganache/).
[kredits-contracts](https://github.com/67P/kredits-contracts) holds all the tools npm run kredits-parity
to start and set up such a simulated network, as well as to deploy smart
contracts to it.
These are the basic steps to get up and running: Deploy the contracts to your local chain:
#### 1. IPFS npm run kredits-deploy
Run a local IPFS deamon in offline mode. Run kredits-web with your localy deployed contracts:
* Make sure CORS headers are configured. See [IPFS](#ipfs) for more info. KREDITS_CONTRACT_ADDR=`kredits-inspect -c Kredits -r -w address` \
* `ipfs daemon --offline` TOKEN_CONTRACT_ADDR=`kredits-inspect -c Token -r -w address` \
npm start
#### 2. kredits-contracts You need to have `./node_modules/.bin` in your path for this to work. Otherwise simply copy the contract addresses and set the environment variables manually.
Get your local Ethereum development node running. (See [kredits-contracts README](https://github.com/67P/kredits-contracts)
for details.
* Clone [kredits-contracts](https://github.com/67P/kredits-contracts)
* `npm install`
* `npm run devchain` - runs a local development chain
* `npm run bootstrap` - bootstrap runs deploys all the contracts
* `npm link` - make the `kredits-contracts` module linkable as `kredits-contracts` on your machine (currently broken :( )
#### 3. kredits-web
With IPFS and Ethereum/ganache running, you can now start this Ember app.
* `npm link kredits-contracts` - link the local `kredits-contracts` package
* `npm run start:local` - WEB3_PROVIDER_URL=http://localhost:7545 must be set for local settings
If you restart the devchain your network ID changes. So kredits-contracts does not find the correct DAO Kernel address.
If that is the case the DAO address must be provided as `KREDITS_KERNEL_ADDRESS` environment variable.
* `KREDITS_KERNEL_ADDRESS=<0xYOURADDRESS> npm run start:local` - find your address with the `npm run dao:address` command in the kredits-contracts repo.
#### IPFS
Install IPFS with your favorite package manager and run
ipfs init (on initial installation)
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["localhost:4200"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'
## Further Reading / Useful Links ## Further Reading / Useful Links
* [ember.js](https://emberjs.com/) * [ember.js](http://emberjs.com/)
* [ember-cli](https://ember-cli.com/) * [ember-cli](https://ember-cli.com/)
* Development Browser Extensions * Development Browser Extensions
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
+6 -2
View File
@@ -1,9 +1,13 @@
import Application from '@ember/application'; import Ember from 'ember';
import Resolver from './resolver'; import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers'; import loadInitializers from 'ember-load-initializers';
import config from './config/environment'; import config from './config/environment';
const App = Application.extend({ let App;
Ember.MODEL_FACTORY_INJECTIONS = true;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix, modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix, podModulePrefix: config.podModulePrefix,
Resolver Resolver
@@ -1,70 +0,0 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { and, notEmpty } from '@ember/object/computed';
export default Component.extend({
attributes: null,
contributors: Object.freeze([]),
isValidContributor: notEmpty('contributorId'),
isValidKind: notEmpty('kind'),
isValidAmount: computed('amount', function() {
return parseInt(this.amount, 10) > 0;
}),
isValidDescription: notEmpty('description'),
isValidUrl: notEmpty('url'),
isValid: and('isValidContributor',
'isValidKind',
'isValidAmount',
'isValidDescription'),
init () {
this._super(...arguments);
this.set('defaultDate', new Date());
// Default attributes used by reset
this.set('attributes', {
contributorId: null,
kind: null,
date: [new Date()],
amount: null,
description: null,
url: null,
});
this.reset();
},
reset () {
this.setProperties(this.attributes);
},
actions: {
submit () {
if (!this.isValid) {
alert('Invalid data. Please review and try again.');
return;
}
const attributes = this.getProperties(Object.keys(this.attributes));
const [ date/* , time */ ] = attributes.date[0].toISOString().split('T');
attributes.date = date;
this.set('inProgress', true);
this.save(attributes)
.then((/*contribution*/) => {
this.reset();
}, err => {
console.warn(err);
window.alert('Something went wrong. Check the browser console for details.');
})
.finally(() => this.set('inProgress', false));
}
}
});
@@ -1,58 +0,0 @@
<form {{action "submit" on="submit"}}>
<p>
<select required onchange={{action (mut contributorId) value="target.value"}}>
<option value="" selected disabled hidden>Contributor</option>
{{#each contributors as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.name}}</option>
{{/each}}
</select>
</p>
<p>
<select required onchange={{action (mut kind) value="target.value"}}>
<option value="" selected disabled hidden>Kind</option>
<option value="community" selected={{eq kind "community"}}>Community</option>
<option value="design" selected={{eq kind "design"}}>Design</option>
<option value="dev" selected={{eq kind "dev"}}>Development</option>
<option value="docs" selected={{eq kind "docs"}}>Documentation</option>
<option value="ops" selected={{eq kind "ops"}}>IT Operations</option>
<option value="special" selected={{eq kind "special"}}>Special</option>
</select>
</p>
<p>
{{ember-flatpickr
allowInput=false
altFormat="F j, Y"
altInput=true
altInputClass="date-alt"
date=date
dateFormat="Y-m-d"
defaultDate=defaultDate
maxDate=defaultDate
onChange=(action (mut date))
}}
</p>
<p>
{{input type="text"
placeholder="500"
value=amount
class=(if isValidAmount "valid" "")}}
</p>
<p>
{{input type="text"
placeholder="Description"
value=description
class=(if isValidDescription "valid" "")}}
</p>
<p>
{{input type="text"
placeholder="URL (optional)"
value=url
class=(if isValidUrl "valid" "")}}
</p>
<p class="actions">
{{input type="submit"
disabled=inProgress
value=(if inProgress "Processing" "Save")}}
</p>
</form>
+57 -54
View File
@@ -1,76 +1,79 @@
import Component from '@ember/component'; import Ember from 'ember';
import { and, notEmpty } from '@ember/object/computed'; import Contributor from 'kredits-web/models/contributor';
import { inject as service } from '@ember/service';
import { isPresent } from '@ember/utils'; const {
Component,
isPresent,
inject: {
service
},
computed
} = Ember;
export default Component.extend({ export default Component.extend({
kredits: service(), kredits: service(),
attributes: null, newContributor: null,
inProgress: false,
// TODO: add proper address validation isValidAddress: function() {
isValidAccount: notEmpty('account'), return this.get('kredits.web3')
isValidName: notEmpty('name'), .isAddress(this.get('newContributor.address'));
isValidURL: notEmpty('url'), }.property('kredits.web3', 'newContributor.address'),
isValidGithubUID: notEmpty('github_uid'),
isValidGithubUsername: notEmpty('github_username'), isValidName: function() {
isValidGiteaUsername: notEmpty('gitea_username'), return isPresent(this.get('newContributor.name'));
isValidWikiUsername: notEmpty('wiki_username'), }.property('newContributor.name'),
isValid: and(
'isValidAccount', 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', 'isValidName',
'isValidGithubUID' 'isValidGithubUID'
), ),
inProgress: false,
init () {
this._super(...arguments);
this.setDefaultAttributes();
this.reset();
},
setDefaultAttributes () {
if (isPresent(this.attributes)) { return; }
this.set('attributes', {
account: null,
name: null,
kind: 'person',
url: null,
github_username: null,
github_uid: null,
gitea_username: null,
wiki_username: null
});
},
reset: function() { reset: function() {
this.setProperties(this.attributes); this.setProperties({
newContributor: Contributor.create({ kind: 'person' }),
inProgress: false
});
}, },
actions: { actions: {
submit () { save() {
if (!this.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;
} }
const attributes = Object.keys(this.attributes); if (this.get('isValid')) {
const contributor = this.getProperties(attributes); this.set('inProgress', true);
this.set('inProgress', true); this.get('kredits').addContributor(this.get('newContributor')).then(contributor => {
this.reset();
this.save(contributor).then(() => { this.get('contributors').pushObject(contributor);
this.reset(); window.scroll(0,0);
}).catch(err => { });
console.warn(err); } else {
window.alert('Something went wrong. Please check the browser console.'); alert('Invalid data. Please review and try again.');
}).finally(() => { }
this.set('inProgress', false);
});
} }
} }
+41 -32
View File
@@ -1,50 +1,59 @@
<form {{action "submit" on="submit"}}> <form {{action "save" on="submit"}}>
<p> <p>
<label for="c-account">Ethereum account</label> {{input type="checkbox" name="is-core" id="is-core" checked=newContributor.isCore}}
{{input name="account" id="c-account" <label for="is-core" class="checkbox">
type="text" value=account Core team member (can add contributors)
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" </label>
class=(if isValidAccount "valid" "")}}
</p> </p>
<p> <p>
<label for="c-kind">Kind</label> {{input name="address"
<select required onchange={{action (mut kind) value="target.value"}} id="c-kind"> type="text"
<option value="person" selected={{eq kind "person"}}>Person</option> placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
<option value="organization" selected={{eq kind "organization"}}>Organization</option> value=newContributor.address
class=(if isValidAddress 'valid' '')}}
</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> </select>
</p> </p>
<p> <p>
<label for="c-name">Name</label> {{input name="name"
{{input name="name" type="text" value=name placeholder="Zero Cool" type="text"
class=(if isValidName "valid" "") id="c-name"}} placeholder="Name"
value=newContributor.name
class=(if isValidName 'valid' '')}}
</p> </p>
<p> <p>
<label for="c-url">URL</label> {{input name="url"
{{input name="url" type="text" value=url placeholder="http://zerocool.bit" type="text"
class=(if isValidURL "valid" "") id="c-url"}} placeholder="URL"
value=newContributor.url
class=(if isValidURL 'valid' '')}}
</p> </p>
<p> <p>
<label for="c-github-uid">GitHub UID</label> {{input name="github_uid"
{{input name="github_uid" type="text" value=github_uid placeholder="2342" type="text"
class=(if isValidGithubUID "valid" "") id="c-github-uid"}} placeholder="GitHub UID (123)"
value=newContributor.github_uid
class=(if isValidGithubUID 'valid' '')}}
</p> </p>
<p> <p>
<label for="c-github-username">GitHub username</label> {{input name="github_username"
{{input name="github_username" type="text" value=github_username placeholder="zerocool" type="text"
class=(if isValidGithubUsername "valid" "") id="c-github-username"}} placeholder="GitHub username"
value=newContributor.github_username
class=(if isValidGithubUsername 'valid' '')}}
</p> </p>
<p> <p>
<label for="c-gitea-username">Gitea username</label> {{input name="wiki_username"
{{input name="gitea_username" type="text" value=gitea_username placeholder="zerocool" type="text"
class=(if isValidGiteaUsername "valid" "") id="c-gitea-username"}} placeholder="Wiki Username"
</p> value=newContributor.wiki_username
<p> class=(if isValidWikiUsername 'valid' '')}}
<label for="c-wiki-username">Wiki username</label>
{{input name="wiki_username" type="text" value=wiki_username placeholder="ZeroCool"
class=(if isValidWikiUsername "valid" "") id="c-wiki-username"}}
</p> </p>
<p class="actions"> <p class="actions">
{{input type="submit" disabled=inProgress {{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
value=(if inProgress "Processing" "Save")}}
</p> </p>
</form> </form>
+47 -45
View File
@@ -1,63 +1,65 @@
import Component from '@ember/component'; import Ember from 'ember';
import { computed } from '@ember/object';
import { and, notEmpty } from '@ember/object/computed'; const {
Component,
isPresent,
inject: {
service
},
computed
} = Ember;
export default Component.extend({ export default Component.extend({
attributes: null, kredits: service(),
contributors: Object.freeze([]),
isValidContributor: notEmpty('contributorId'), proposal: null,
isValidAmount: computed('amount', function() { contributors: null,
return parseInt(this.amount, 10) > 0; inProgress: false,
isValidRecipient: computed('proposal.recipientAddress', function() {
return this.get('kredits.web3').isAddress(this.get('proposal.recipientAddress'));
}), }),
isValidDescription: notEmpty('description'),
isValidUrl: notEmpty('url'),
isValid: and('isValidContributor',
'isValidAmount',
'isValidDescription'),
init () { isValidAmount: computed('proposal.amount', function() {
this._super(...arguments); return parseInt(this.get('proposal.amount'), 10) > 0;
}),
// Default attributes used by reset isValidUrl: computed('proposal.url', function() {
this.set('attributes', { return isPresent(this.get('proposal.url'));
contributorId: null, }),
kind: 'community',
amount: null,
description: null,
url: null,
});
},
didInsertElement() { isValidDescription: computed('proposal.description', function() {
this._super(...arguments); return isPresent(this.get('proposal.description'));
this.reset(); }),
},
reset: function() { isValid: computed.and('isValidRecipient',
this.setProperties(this.attributes); 'isValidAmount',
}, 'isValidDescription'),
actions: { actions: {
submit() { save() {
if (!this.isValid) { if (! this.get('isValid')) {
alert('Invalid data. Please review and try again.'); alert('Invalid data. Please review and try again.');
return; return false;
} }
this.set('inProgress', true);
let proposal = this.get('proposal');
let attributes = Object.keys(this.attributes); // Set the recipient's IPFS profile hash so it can be used in the
let proposal = this.getProperties(attributes); // contribution object (which is to be stored in IPFS as well)
let saved = this.save(proposal); let contributor = this.get('contributors').findBy('address', proposal.get('recipientAddress'));
proposal.set('recipientProfile', contributor.get('ipfsHash'));
// The promise handles inProgress this.get('kredits').addProposal(proposal)
this.set('inProgress', saved); .then(() => {
this.attrs.onSave();
saved.then(() => { }).catch((error) => {
this.reset(); Ember.Logger.error('[add-proposal] error creating the proposal', error);
window.scroll(0,0); alert('Something went wrong.');
window.alert('Proposal added.'); }).finally(() => {
}); this.set('inProgress', false);
});
} }
} }
+17 -19
View File
@@ -1,44 +1,42 @@
<form {{action "submit" on="submit"}}> <form {{action "save" on="submit"}}>
<p> <p>
<select required onchange={{action (mut contributorId) value="target.value"}}> <select required onchange={{action (mut proposal.recipientAddress) value="target.value"}}>
<option value="" selected disabled hidden>Contributor</option> <option value="" selected disabled hidden>Contributor</option>
{{#each contributors as |contributor|}} {{#each contributors as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.github_username}}</option> <option value={{contributor.address}} selected={{eq proposal.recipientAddress contributor.address}}>{{contributor.github_username}}</option>
{{/each}} {{/each}}
</select> </select>
</p> </p>
<p> <p>
<select required onchange={{action (mut kind) value="target.value"}}> <select required onchange={{action (mut proposal.kind) value="target.value"}}>
<option value="community" selected={{eq kind "community"}}>Community</option> <option value="community" selected={{eq proposal.kind "community"}}>Community</option>
<option value="design" selected={{eq kind "design"}}>Design</option> <option value="design" selected={{eq proposal.kind "design"}}>Design</option>
<option value="dev" selected={{eq kind "dev"}}>Development</option> <option value="dev" selected={{eq proposal.kind "dev"}}>Development</option>
<option value="docs" selected={{eq kind "docs"}}>Documentation</option> <option value="docs" selected={{eq proposal.kind "docs"}}>Documentation</option>
<option value="ops" selected={{eq kind "ops"}}>IT Operations</option> <option value="ops" selected={{eq proposal.kind "ops"}}>IT Operations</option>
</select> </select>
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="100" placeholder="100"
value=amount value=proposal.amount
class=(if isValidAmount "valid" "")}} class=(if isValidAmount 'valid' '')}}
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="Description" placeholder="Description"
value=description value=proposal.description
class=(if isValidDescription "valid" "")}} class=(if isValidDescription 'valid' '')}}
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="URL (optional)" placeholder="URL (optional)"
value=url value=proposal.url
class=(if isValidUrl "valid" "")}} class=(if isValidUrl 'valid' '')}}
</p> </p>
<p class="actions"> <p class="actions">
{{input type="submit" {{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
disabled=(is-pending inProgress) {{#link-to 'index'}}Back{{/link-to}}
value=(if (is-pending inProgress) "Processing" "Save")}}
{{#link-to "index"}}Back{{/link-to}}
</p> </p>
</form> </form>
@@ -1,64 +0,0 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
let categoryColors = {
community: "#fb6868",
design: "#fbe468",
dev: "#e068fb",
docs: "#97fb68",
ops: "#8f68fb",
}
export default Component.extend({
contributions: null,
chartOptions: Object.freeze({
legend: {
display: false
}
}),
chartData: computed('contributions', function() {
let kredits = this.contributions
.filterBy('vetoed', false)
.map(c => {
return { kind: c.kind, amount: c.amount }
})
.reduce(function (kinds, c) {
if (c.kind in kinds) {
kinds[c.kind] = kinds[c.kind] + c.amount
} else {
kinds[c.kind] = c.amount;
}
return kinds;
}, {});
return {
datasets: [{
data: [
kredits['community'],
kredits['design'],
kredits['dev'],
kredits['ops'],
kredits['docs'],
],
borderColor: [
categoryColors.community,
categoryColors.design,
categoryColors.dev,
categoryColors.ops,
categoryColors.docs,
],
borderWidth: 1
}],
labels: [
'Community',
'Design',
'Development',
'Operations & Infrastructure',
'Documentation'
],
}
})
});
@@ -1,7 +0,0 @@
<div class="chart">
{{ember-chart type="doughnut"
data=chartData
options=chartOptions
width=200
height=200}}
</div>
+10 -58
View File
@@ -1,64 +1,16 @@
import Component from '@ember/component'; import Ember from 'ember';
import { computed } from '@ember/object';
import { sort } from '@ember/object/computed';
import { isPresent } from '@ember/utils';
import { inject as service } from '@ember/service';
export default Component.extend({ export default Ember.Component.extend({
tagName: 'div', tagName: 'ul',
classNames: ['contributions'], classNames: ['contribution-list'],
showQuickFilter: false, contributionsWithContributors: function() {
hideSmallContributions: false, return this.get('contributions').map((c) => {
contributorId: null, c.set('contributor', this.get('contributors')
contributionKind: null, .findBy('address', c.get('recipientAddress')));
return c;
kredits: service(),
contributorsSorting: Object.freeze(['name:asc']),
contributors: sort('kredits.contributors', 'contributorsSorting'),
contributorsActive: computed('contributors.[]', 'contributions', function() {
let activeIds = this.contributions.mapBy('contributorId')
.map(id => id.toString())
.uniq();
return this.contributors.filter(c => {
return activeIds.includes(c.id.toString());
}); });
}), }.property('contributions.@each')
contributionKinds: computed('contributions.[]', function() {
return this.contributions.mapBy('kind').uniq();
}),
contributionsFiltered: computed('contributions.[]', 'hideSmallContributions', 'contributorId', 'contributionKind', function() {
return this.contributions.filter(c => {
let included = true;
if (this.hideSmallContributions &&
c.amount <= 500) { included = false; }
if (isPresent(this.contributorId) &&
c.contributorId.toString() !== this.contributorId.toString()) { included = false; }
if (isPresent(this.contributionKind) &&
c.kind !== this.contributionKind) { included = false; }
return included;
});
}),
actions: {
veto (contributionId) {
if (this.contractInteractionEnabled) {
this.vetoContribution(contributionId);
} else {
window.alert('Only members can veto contributions. Please ask someone to set you up.');
}
}
}
}); });
+7 -58
View File
@@ -1,58 +1,7 @@
{{#if showQuickFilter}} {{#each contributionsWithContributors as |contribution|}}
<div class="quick-filter"> <li title="({{contribution.kind}}) {{contribution.description}}">
<p> <span class="category {{contribution.kind}}">♥</span>
<label class="filter-contributor"> <span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
Contributor: for <span class="recipient">{{contribution.contributor.name}}</span>
<select onchange={{action (mut contributorId) value="target.value"}}> </li>
<option value="" selected>all</option> {{/each}}
{{#each contributorsActive as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.name}}</option>
{{/each}}
</select>
</label>
<label class="filter-contribution-kind">
Kind:
<select onchange={{action (mut contributionKind) value="target.value"}}>
<option value="" selected>all</option>
{{#each contributionKinds as |kind|}}
<option value={{kind}} selected={{eq contributionKind kind}}>{{capitalize-string kind}}</option>
{{/each}}
</select>
</label>
<label class="filter-contribution-size">
{{input type="checkbox" checked=hideSmallContributions}}
Hide small contributions
</label>
</p>
</div>
{{/if}}
<ul class="contribution-list">
{{#each contributionsFiltered as |contribution|}}
<li data-contribution-id={{contribution.id}} class="{{contribution-status contribution}} {{if contribution.vetoed "vetoed"}}">
<p class="meta">
<span class="recipient">{{user-avatar contributor=contribution.contributor}}</span>
<span class="category {{contribution.kind}}">({{contribution.kind}})</span>
<span class="title">
{{#if contribution.url}}
<a href={{contribution.url}} target="_blank" rel="noopener">{{contribution.description}}</a>
{{else}}
{{contribution.description}}
{{/if}}
</span>
</p>
<p class="kredits-amount">
<span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
</p>
{{#unless contribution.vetoed}}
{{#unless (is-confirmed-contribution contribution)}}
<p class="voting">
<button {{action "veto" contribution.id}} class="small danger">veto</button>
</p>
{{/unless}}
{{/unless}}
</li>
{{/each}}
</ul>
+4 -4
View File
@@ -1,6 +1,6 @@
import Component from '@ember/component'; import Ember from 'ember';
export default Component.extend({ export default Ember.Component.extend({
tagName: 'table', tagName: 'table',
classNames: 'contributor-list', classNames: 'contributor-list',
@@ -9,10 +9,10 @@ export default Component.extend({
actions: { actions: {
toggleContributorInfo(contributor) { toggleContributorInfo(contributor) {
if (contributor.showMetadata) { if (contributor.get('showMetadata')) {
contributor.set('showMetadata', false); contributor.set('showMetadata', false);
} else { } else {
this.contributorList.setEach('showMetadata', false); this.get('contributors').setEach('showMetadata', false);
contributor.set('showMetadata', true); contributor.set('showMetadata', true);
} }
} }
+9 -24
View File
@@ -1,38 +1,23 @@
<tbody> <tbody>
{{#each contributorList as |c|}} {{#each contributors as |contributor|}}
<tr role="button" class={{if (is-current-user c.contributor) "current-user"}} {{action "toggleContributorInfo" c}}> <tr class="{{if contributor.isCurrentUser 'current-user'}}" {{action "toggleContributorInfo" contributor}}>
<td class="person"> <td class="person">
{{user-avatar contributor=c.contributor}} {{c.contributor.name}} <img class="avatar" src={{contributor.avatarURL}}>
{{contributor.name}}
</td> </td>
<td class="kredits"> <td class="kredits">
<span class="amount"> <span class="amount">{{contributor.kredits}}</span>
{{#if showUnconfirmedKredits}}
{{c.amountTotal}}
{{else}}
{{c.amountConfirmed}}
{{/if}}
</span>
<span class="symbol">₭S</span> <span class="symbol">₭S</span>
</td> </td>
</tr> </tr>
<tr class="metadata {{if (is-current-user c.contributor) "current-user"}} {{if c.showMetadata "visible"}}"> <tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}">
<td colspan="2"> <td colspan="2">
<ul> <ul>
<li> <li><a href="https://testnet.etherscan.io/address/{{contributor.address}}">Inspect Ethereum transactions</a></li>
<a href="https://rinkeby.etherscan.io/address/{{c.contributor.account}}" target="_blank" rel="noopener">Inspect Ethereum transactions</a> {{#if contributor.ipfsHash}}
</li> <li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li>
{{#if c.contributor.ipfsHash}}
<li>
<a href="https://ipfs.io/ipfs/{{c.contributor.ipfsHash}}" target="_blank" rel="noopener">Inspect IPFS profile</a>
</li>
{{/if}} {{/if}}
<li>
{{link-to "Edit profile" "contributors.edit" c.contributor.id}}
</li>
</ul> </ul>
{{#if c.showMetadata}}
<pre>{{c.contributor.ipfsData}}</pre>
{{/if}}
</td> </td>
</tr> </tr>
{{/each}} {{/each}}
+2 -2
View File
@@ -1,6 +1,6 @@
import Component from '@ember/component'; import Ember from 'ember';
export default Component.extend({ export default Ember.Component.extend({
classNames: ['loading-spinner'] classNames: ['loading-spinner']
+1 -1
View File
@@ -10,5 +10,5 @@
</svg> </svg>
</p> </p>
<p> <p>
Loading data... Loading data from Ethereum...
</p> </p>
+4 -4
View File
@@ -1,6 +1,6 @@
import Component from '@ember/component'; import Ember from 'ember';
export default Component.extend({ export default Ember.Component.extend({
tagName: 'ul', tagName: 'ul',
classNames: ['proposal-list'], classNames: ['proposal-list'],
@@ -8,8 +8,8 @@ export default Component.extend({
actions: { actions: {
confirm(proposalId) { confirm(proposalId) {
if (this.contractInteractionEnabled) { if (this.get('contractInteractionEnabled')) {
this.confirmProposal(proposalId); this.sendAction('confirmAction', proposalId);
} else { } else {
window.alert('Only members can vote on proposals. Please ask someone to set you up.'); window.alert('Only members can vote on proposals. Please ask someone to set you up.');
} }
+7 -17
View File
@@ -1,20 +1,10 @@
{{#each proposals as |proposal|}} {{#each proposals as |proposal|}}
<li data-proposal-id={{proposal.id}} class={{if proposal.isExecuted "confirmed" "unconfirmed"}}> <li data-proposal-id={{proposal.id}} title="({{proposal.kind}}) {{proposal.description}}">
<p class="meta"> <span class="category {{proposal.kind}}">♥</span>
<span class="category {{proposal.kind}}">♥ ({{proposal.kind}})</span> <span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
<span class="recipient">{{proposal.contributor.name}}:</span> for <span class="recipient">{{proposal.recipientName}}</span>
</p> <span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
<p class="kredits-amount">
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span> {{#unless proposal.executed}}<button {{action "confirm" proposal.id}}>+1</button>{{/unless}}
</p>
<p class="description">
<span class="description">{{proposal.description}}</span>
</p>
<p class="voting">
{{#unless proposal.isExecuted}}
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
<button {{action "confirm" proposal.id}}>+1</button>
{{/unless}}
</p>
</li> </li>
{{/each}} {{/each}}
-11
View File
@@ -1,11 +0,0 @@
import Component from '@ember/component';
import { alias } from '@ember/object/computed';
export default Component.extend({
contributor: null,
tagName: 'img',
classNames: ['avatar'],
attributeBindings: ['src', 'title'],
src: alias('contributor.avatarURL'),
title: alias('contributor.name')
});
+2 -4
View File
@@ -1,6 +1,4 @@
import Controller from '@ember/controller'; import Ember from 'ember';
import { inject as service } from '@ember/service';
export default Controller.extend({ export default Ember.Controller.extend({
kredits: service(),
}); });
-30
View File
@@ -1,30 +0,0 @@
import Controller from '@ember/controller';
import { alias, filterBy, sort } from '@ember/object/computed';
import { inject as service } from '@ember/service';
export default Controller.extend({
kredits: service(),
contributors: alias('kredits.contributors'),
minedContributors: filterBy('contributors', 'id'),
contributorsSorting: Object.freeze(['name:asc']),
sortedContributors: sort('minedContributors', 'contributorsSorting'),
actions: {
save (contribution) {
const contributor = this.contributors.findBy('id', contribution.contributorId);
contribution.contributorIpfsHash = contributor.ipfsHash;
return this.kredits.addContribution(contribution)
.then(contribution => {
this.transitionToRoute('index');
return contribution;
});
}
}
});
-18
View File
@@ -1,18 +0,0 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
kredits: service(),
actions: {
save (attributes) {
return this.kredits
.updateContributor(this.model.id, attributes)
.then(() => this.transitionToRoute('index'))
}
}
});
-18
View File
@@ -1,18 +0,0 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
kredits: service(),
actions: {
save (contributor) {
return this.kredits
.addContributor(contributor)
.then(() => this.transitionToRoute('index'))
}
}
});
+132 -38
View File
@@ -1,54 +1,148 @@
import Controller from '@ember/controller'; import Ember from 'ember';
import { computed } from '@ember/object'; import Proposal from 'kredits-web/models/proposal';
import { alias, not, sort } from '@ember/object/computed';
import { inject as service } from '@ember/service'; const {
computed,
inject: {
service
}
} = Ember;
export default Ember.Controller.extend({
export default Controller.extend({
kredits: service(), kredits: service(),
currentBlock: alias('kredits.currentBlock'),
contributions: alias('kredits.contributions'), contractInteractionEnabled: computed.alias('kredits.web3Provided'),
contributionsConfirmed: alias('kredits.contributionsConfirmed'),
contributionsUnconfirmed: alias('kredits.contributionsUnconfirmed'),
contributionsSorting: Object.freeze(['date:desc', 'time:desc', 'id:desc']), findContributorByAddress(address) {
contributionsUnconfirmedSorted: sort('contributionsUnconfirmed', 'contributionsSorting'), return this.get('model.contributors')
contributionsConfirmedSorted: sort('contributionsConfirmed', 'contributionsSorting'), .findBy('address', address);
},
kreditsByContributor: alias('kredits.kreditsByContributor'), proposalsOpen: function() {
let proposals = this.get('model.proposals')
.filterBy('executed', false)
.map(p => {
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
return p;
});
return proposals;
}.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'),
kreditsToplistSorting: computed('showUnconfirmedKredits', function(){ proposalsClosed: function() {
return this.showUnconfirmedKredits ? ['amountTotal:desc'] : ['amountConfirmed:desc']; let proposals = this.get('model.proposals')
}), .filterBy('executed', true)
kreditsToplist: sort('kreditsByContributor', 'kreditsToplistSorting'), .map(p => {
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
return p;
});
return proposals;
}.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'),
showUnconfirmedKredits: true, proposalsSorting: ['id:desc'],
hideUnconfirmedKredits: not('showUnconfirmedKredits'), proposalsClosedSorted: Ember.computed.sort('proposalsClosed', 'proposalsSorting'),
proposalsOpenSorted: Ember.computed.sort('proposalsOpen', 'proposalsSorting'),
contributorsWithKredits: function() {
return this.get('model.contributors').filter(c => {
return c.get('kredits') !== 0;
});
}.property('model.contributors.@each.kredits'),
contributorsSorting: ['kredits:desc'],
contributorsSorted: Ember.computed.sort('contributorsWithKredits', 'contributorsSorting'),
watchContractEvents: function() {
let events = this.get('kredits.kreditsContract')
.allEvents(/* [additionalFilterObject], */);
events.watch((error, data) => {
Ember.Logger.debug('[index] Received contract event', data);
switch (data.event) {
case 'ProposalCreated':
this._handleProposalCreated(data);
break;
case 'ProposalExecuted':
this._handleProposalExecuted(data);
break;
case 'ProposalVoted':
this._handleProposalVoted(data);
break;
case 'Transfer':
this._handleTransfer(data);
break;
}
});
}.on('init'),
_handleProposalCreated(data) {
if (Ember.isPresent(this.get('model.proposals')
.findBy('id', data.args.id.toNumber()))) {
Ember.Logger.debug('[index] proposal exists, not adding from event');
return false;
}
let proposal = Proposal.create({
id: data.args.id.toNumber(),
creatorAddress: data.args.creator,
recipientAddress: data.args.recipient,
recipientName: null,
votesCount: 0,
votesNeeded: 2,
amount: data.args.amount.toNumber(),
executed: false,
url: data.args.url,
ipfsHash: data.args.ipfsHash
});
this.get('model.proposals').pushObject(proposal);
},
_handleProposalExecuted(data) {
if (this.get('model.proposals')
.findBy('id', data.args.id.toNumber())
.get('executed')) {
Ember.Logger.debug('[index] proposal already executed, not adding from event');
return false;
}
this.get('model.proposals')
.findBy('id', data.args.id.toNumber())
.setProperties({
'executed': true,
'votesCount': 2 // TODO use real count
});
this.get('model.contributors')
.findBy('address', data.args.recipient)
.incrementProperty('kredits', data.args.amount.toNumber());
},
_handleProposalVoted(data) {
this.get('model.proposals')
.findBy('id', data.args.id.toNumber())
.incrementProperty('votesCount', 1);
},
_handleTransfer(data) {
this.get('model.contributors')
.findBy('address', data.args.from)
.incrementProperty('kredits', - data.args.value.toNumber());
this.get('model.contributors')
.findBy('address', data.args.to)
.incrementProperty('kredits', data.args.value.toNumber());
},
showQuickFilterUnconfirmed: false,
showQuickFilterConfirmed: false,
actions: { actions: {
vetoContribution (contributionId) { confirmProposal(proposalId) {
this.kredits.veto(contributionId).then(transaction => { this.get('kredits').vote(proposalId).then(transactionId => {
console.debug('[controllers:index] Veto submitted to Ethereum blockhain: '+transaction.hash); window.confirm('Vote submitted to Ethereum blockhain: '+transactionId);
}); });
},
confirmProposal (proposalId) {
this.kredits.vote(proposalId).then(transaction => {
console.debug('[controllers:index] Vote submitted to Ethereum blockhain: '+transaction.hash);
});
},
toggleQuickFilterUnconfirmed () {
this.toggleProperty('showQuickFilterUnconfirmed');
},
toggleQuickFilterConfirmed () {
this.toggleProperty('showQuickFilterConfirmed');
} }
} }
}); });
+26 -21
View File
@@ -1,24 +1,29 @@
import Controller from '@ember/controller'; import Ember from 'ember';
import { alias, filterBy } from '@ember/object/computed'; import QueryParams from 'ember-parachute';
import { inject as service } from '@ember/service';
export default Controller.extend({ export const queryParams = new QueryParams({
kredits: service(), recipient: {
defaultValue: ''
contributors: alias('kredits.contributors'), },
minedContributors: filterBy('contributors', 'id'), amount: {
defaultValue: ''
actions: { },
save(proposal) { url: {
// contributorIpfsHash is needed for the proposal ipfs data. I'm not happy to do this here but I think to load all the contributors in addProposal again is a bit too much. I hope we can refactor it later. defaultValue: ''
let contributor = this.contributors.findBy('id', proposal.contributorId); },
proposal.contributorIpfsHash = contributor.get('ipfsHash'); ipfsHash: {
defaultValue: ''
return this.kredits.addProposal(proposal)
.then((proposal) => {
this.transitionToRoute('index');
return proposal;
});
}
} }
}); });
export default Ember.Controller.extend(queryParams.Mixin, {
contributors: null,
actions: {
onSave() {
this.transitionToRoute('index');
}
}
});
-7
View File
@@ -1,7 +0,0 @@
import Helper from '@ember/component/helper';
export default Helper.extend({
compute([string]) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
});
-20
View File
@@ -1,20 +0,0 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
export default Helper.extend({
kredits: service(),
currentBlock: alias('kredits.currentBlock'),
compute([contribution]) {
if (contribution.vetoed) {
return 'vetoed';
} else if (contribution.confirmedAt > this.currentBlock) {
return 'unconfirmed';
} else {
return 'confirmed'
}
}
});
-15
View File
@@ -1,15 +0,0 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
export default Helper.extend({
kredits: service(),
currentBlock: alias('kredits.currentBlock'),
compute([contribution]) {
return !contribution.vetoed &&
(contribution.confirmedAt <= this.currentBlock);
}
});
-16
View File
@@ -1,16 +0,0 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
import { isPresent } from '@ember/utils';
export default Helper.extend({
kredits: service(),
currentUser: alias('kredits.currentUser'),
compute([contributor]) {
return isPresent(contributor) && isPresent(this.currentUser) &&
contributor.account === this.currentUser.account;
}
});
+2 -2
View File
@@ -9,8 +9,8 @@
{{content-for "head"}} {{content-for "head"}}
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css"> <link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/kredits-web.css"> <link rel="stylesheet" href="{{rootURL}}assets/kredits-web.css">
{{content-for "head-footer"}} {{content-for "head-footer"}}
+20
View File
@@ -0,0 +1,20 @@
/**
* This pauses the app's boot process until the window load event is fired.
* It allows the user provided Web3 instance (e.g. Metamask, Mist Browser)
* to be inserted before we try to use it.
*/
export default {
name: 'defer-loading',
initialize: function(application) {
// Load event already fired, so web3 should be loaded if available
if (window.windowLoadComplete) { return true; }
// Pause app loading
application.deferReadiness();
window.addEventListener('load', function() {
// Continue app loading
application.advanceReadiness();
});
}
};
+42 -17
View File
@@ -1,28 +1,53 @@
import EmberObject from '@ember/object'; import Ember from 'ember';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default EmberObject.extend({ export default Ember.Object.extend({
// Contract // blockNumber: null,
id: null, // blockHash: null,
// transactionHash: null,
recipientAddress: null,
contributorId: null, contributorId: null,
amount: null,
confirmedAt: bignumber('confirmedAtBlock', 'toNumber'),
vetoed: null,
ipfsHash: null, ipfsHash: null,
amount: null,
creatorAccount: null, contributor: null,
contribution: null,
// IPFS
kind: null, kind: null,
description: null, description: null,
details: null,
url: null, url: null,
ipfsData: '', details: null,
init () { /**
this._super(...arguments); * Loads the contribution details from IPFS and sets local instance
this.set('details', {}); * properties from it
} *
* @method
* @public
*/
loadDetails(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;
},
}); });
+109 -21
View File
@@ -1,31 +1,119 @@
import { computed } from '@ember/object'; import Ember from 'ember';
import EmberObject from '@ember/object';
import bignumber from 'kredits-web/utils/cps/bignumber';
import kreditsValue from 'kredits-web/utils/cps/kredits';
export default EmberObject.extend({ const {
// Contract isPresent,
id: bignumber('idRaw', 'toString'), } = Ember;
account: null,
balance: kreditsValue('balanceRaw'),
totalKreditsEarned: bignumber('totalKreditsEarnedRaw', 'toNumber'),
contributionsCount: bignumber('contributionsCountRaw', 'toNumber'),
isCore: false,
ipfsHash: null,
// IPFS export default Ember.Object.extend({
kind: null,
address: null,
name: null, name: null,
kind: null,
url: null, url: null,
github_username: null, github_username: null,
github_uid: null, github_uid: null,
wiki_username: null, wiki_username: null,
ipfsData: '', ipfsHash: null,
kredits: null,
isCore: false,
isCurrentUser: false,
avatarURL: computed('github_uid', function() { avatarURL: function() {
let github_uid = this.github_uid; return `https\:\/\/avatars2.githubusercontent.com/u/${this.get('github_uid')}?v=3&s=128`;
if (github_uid) { }.property('github_uid'),
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(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());
}
}); });
+92 -22
View File
@@ -1,32 +1,102 @@
import EmberObject from '@ember/object'; import Ember from 'ember';
import { alias } from '@ember/object/computed';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default EmberObject.extend({ const {
isPresent,
isEmpty,
} = Ember;
// Contract export default Ember.Object.extend({
id: bignumber('idRaw', 'toString'),
creatorAccount: null, id: null,
contributorId: bignumber('contributorIdRaw', 'toString'), creatorAddress: null,
amount: bignumber('amountRaw', 'toNumber'), recipientAddress: null,
votesCount: bignumber('votesCountRaw', 'toNumber'), recipientName: null,
votesNeeded: bignumber('votesNeededRaw', 'toNumber'), recipientProfile: null,
votesCount: null,
votesNeeded: null,
amount: null,
executed: null, executed: null,
ipfsHash: null, contribution: null,
// Shortcuts
isExecuted: alias('executed'),
// IPFS
kind: null, kind: null,
description: null, description: null,
details: null,
url: null, url: null,
ipfsData: '', details: null,
ipfsHash: null,
init () { /**
this._super(...arguments); * Loads the contribution details from IPFS and sets local instance
this.set('details', {}); * 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 -9
View File
@@ -1,22 +1,16 @@
import EmberRouter from '@ember/routing/router'; import Ember from 'ember';
import config from './config/environment'; import config from './config/environment';
const Router = EmberRouter.extend({ const Router = Ember.Router.extend({
location: config.locationType, location: config.locationType,
rootURL: config.rootURL rootURL: config.rootURL
}); });
Router.map(function() { Router.map(function() {
this.route('spinner');
this.route('proposals', function() { this.route('proposals', function() {
this.route('new'); this.route('new');
}); });
this.route('contributions', function() {
this.route('new');
});
this.route('contributors', function() {
this.route('new');
this.route('edit', { path: ':id/edit' });
});
}); });
export default Router; export default Router;
+2 -29
View File
@@ -1,31 +1,4 @@
import { inject as service } from '@ember/service'; import Ember from 'ember';
import Route from '@ember/routing/route';
export default Route.extend({ export default Ember.Route.extend({
kredits: service(),
beforeModel(transition) {
const kredits = this.kredits;
return kredits.setup().then(() => {
kredits.get('kredits').preflightChecks().catch((error) => {
console.error('Kredits preflight check failed!');
console.error(error);
});
if (kredits.get('accountNeedsUnlock')) {
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
transition.retry();
}
}
}).catch((error) => {
console.log('Error initializing Kredits', error);
});
},
afterModel() {
return this.kredits.loadInitialData()
.then(() => {
this.kredits.addContractEventHandlers();
});
}
}); });
-29
View File
@@ -1,29 +0,0 @@
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import { alias } from '@ember/object/computed';
export default Route.extend({
kredits: service(),
contributors: alias('kredits.contributors'),
model(params) {
return this.kredits.contributors.findBy('id', params.id);
},
setupController (controller, model) {
this._super(controller, model);
controller.set('attributes', {
account: model.account,
name: model.name,
kind: model.kind,
url: model.url,
github_username: model.github_username,
github_uid: model.github_uid,
gitea_username: model.gitea_username,
wiki_username: model.wiki_username
});
}
});
+34
View File
@@ -0,0 +1,34 @@
import Ember from 'ember';
import Contributor from 'kredits-web/models/contributor';
export default Ember.Route.extend({
kredits: Ember.inject.service(),
beforeModel(transition) {
const kredits = this.get('kredits');
if (kredits.get('web3') && kredits.get('web3Provided')) {
kredits.get('web3').eth.getAccounts((error, accounts) => {
if (error || accounts.length === 0) {
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
transition.retry();
}
}
});
}
},
model() {
let kredits = this.get('kredits');
return Ember.RSVP.hash({
contributors: kredits.getContributors(),
totalSupply: kredits.getValueFromContract('tokenContract', 'totalSupply'),
proposals: kredits.getProposals(),
contributions: kredits.getContributions(),
newContributor: Contributor.create({ kind: 'person' })
});
}
});
+37
View File
@@ -0,0 +1,37 @@
import Ember from 'ember';
import Proposal from 'kredits-web/models/proposal';
const {
Route,
RSVP,
inject: {
service
}
} = Ember;
export default Route.extend({
kredits: service(),
model(params) {
const proposal = Proposal.create({
recipientAddress: params.recipient,
amount: params.amount,
url: params.url,
kind: params.kind || 'dev',
ipfsHash: params.ipfsHash
});
return RSVP.hash({
proposal,
contributors: this.get('kredits').getContributors()
});
},
setupController(controller, model) {
this._super(controller, model.proposal);
controller.set('contributors', model.contributors);
}
});
+4
View File
@@ -0,0 +1,4 @@
import Ember from 'ember';
export default Ember.Route.extend({
});
+35
View File
@@ -0,0 +1,35 @@
import Ember from 'ember';
import ipfsAPI from 'npm:ipfs-api';
import config from 'kredits-web/config/environment';
export default Ember.Service.extend({
ipfsInstance: null,
ipfs: function() {
if (this.get('ipfsInstance')) {
return this.get('ipfsInstance');
}
let ipfs = ipfsAPI(config.ipfs);
this.set('ipfsInstance', ipfs);
return ipfs;
}.property('ipfsInstance'),
storeFile(content) {
let ipfs = this.get('ipfs');
return ipfs.add(new ipfs.Buffer(content)).then(res => {
Ember.Logger.debug('[ipfs] stored content in IPFS', content, res[0].hash);
return res[0].hash;
});
},
getFile(hash) {
return this.get('ipfs').cat(hash, { buffer: true }).then(res => {
return res.toString();
}, err => {
Ember.Logger.error('[ipfs] error trying to fetch file', hash, err);
throw err;
});
}
});
+236 -352
View File
@@ -1,400 +1,284 @@
import ethers from 'npm:ethers'; import Ember from 'ember';
import Kredits from 'npm:kredits-contracts'; import Web3 from 'npm:web3';
import RSVP from 'rsvp';
import Service from '@ember/service';
import EmberObject from '@ember/object';
import { computed } from '@ember/object';
import { alias, notEmpty } from '@ember/object/computed';
import { isEmpty } from '@ember/utils';
import groupBy from 'kredits-web/utils/group-by';
import formatKredits from 'kredits-web/utils/format-kredits';
import config from 'kredits-web/config/environment'; 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 Contribution from 'kredits-web/models/contribution' import Contribution from 'kredits-web/models/contribution';
import kreditsContracts from 'npm:kredits-contracts';
import uuid from 'npm:uuid';
const {
Service,
isPresent,
inject: {
service
}
} = Ember;
export default Service.extend({ export default Service.extend({
currentBlock: null, ipfs: service(),
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
currentUser: null,
contributors: null,
contributions: null,
proposals: null,
currentUserIsContributor: notEmpty('currentUser'), web3Instance: null,
currentUserIsCore: alias('currentUser.isCore'), web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.)
hasAccounts: notEmpty('currentUserAccounts'),
accountNeedsUnlock: computed('currentUserAccounts', function() { web3: function() {
return this.currentUserAccounts && isEmpty(this.currentUserAccounts); if (this.get('web3Instance')) {
}), return this.get('web3Instance');
}
contributionsUnconfirmed: computed('contributions.[]', 'currentBlock', function() { let web3Instance;
return this.contributions.filter(contribution => {
return contribution.confirmedAt > this.currentBlock;
});
}),
contributionsConfirmed: computed('contributions.[]', 'currentBlock', function() { if (typeof window.web3 !== 'undefined') {
return this.contributions Ember.Logger.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
.filterBy('vetoed', false) web3Instance = window.web3;
.filter(contribution => { this.set('web3Provided', true);
return contribution.confirmedAt <= this.currentBlock; } else {
}); Ember.Logger.debug('[kredits] Creating new instance from npm module class');
}), let providerUrl = localStorage.getItem('config:web3ProviderUrl') || config.web3ProviderUrl;
let provider = new Web3.providers.HttpProvider(providerUrl);
web3Instance = new Web3(provider);
}
kreditsByContributor: computed('contributionsUnconfirmed.@each.vetoed', 'contributors.[]', function() { this.set('web3Instance', web3Instance);
const contributionsUnconfirmed = this.contributionsUnconfirmed.filterBy('vetoed', false); window.web3 = web3Instance;
const contributionsGrouped = groupBy(contributionsUnconfirmed, 'contributorId');
const contributorsWithUnconfirmed = contributionsGrouped.map(c => c.value.toString());
const contributorsWithOnlyConfirmed = this.contributors.reject(c => contributorsWithUnconfirmed.includes(c.id))
const kreditsByContributor = contributionsGrouped.map(c => { return web3Instance;
const amountUnconfirmed = c.items.mapBy('amount').reduce((a, b) => a + b); }.property('web3Instance'),
const contributor = this.contributors.findBy('id', c.value.toString());
return EmberObject.create({ currentUserAccounts: function() {
contributor: contributor, return (this.get('web3Provided') && this.get('web3').eth.accounts) || [];
amountUnconfirmed: amountUnconfirmed, }.property('web3Provided', 'web3'),
amountConfirmed: contributor.totalKreditsEarned,
amountTotal: contributor.totalKreditsEarned + amountUnconfirmed
})
});
contributorsWithOnlyConfirmed.forEach(c => { initializeKreditsContract() {
kreditsByContributor.push(EmberObject.create({ let contract = null;
contributor: c,
amountUnconfirmed: 0,
amountConfirmed: c.totalKreditsEarned,
amountTotal: c.totalKreditsEarned
}));
})
return kreditsByContributor; if (isPresent(config.contractMetadata)) {
}), if (localStorage.getItem('config:networkId')) {
config.contractMetadata['networkId'] = localStorage.getItem('config:networkId');
}
contract = kreditsContracts(this.get('web3'), config.contractMetadata)['Kredits'];
} else {
contract = kreditsContracts(this.get('web3'))['Kredits'];
}
init () { return contract;
this._super(...arguments);
this.set('contributors', []);
this.set('proposals', []);
this.set('contributions', []);
}, },
// This is called in the application route's beforeModel(). So it is kreditsContract: function() {
// initialized before everything else, and we can rely on the ethProvider and if (this.get('kreditsContractInstance')) {
// the potential currentUserAccounts to be available return this.get('kreditsContractInstance');
getEthProvider () { }
let ethProvider;
return new RSVP.Promise(async (resolve) => { let contract = this.initializeKreditsContract();
function instantiateWithoutAccount () {
console.debug('[kredits] Creating new instance from npm module class');
console.debug(`[kredits] providerURL: ${config.web3ProviderUrl}`);
ethProvider = new ethers.providers.JsonRpcProvider(config.web3ProviderUrl);
resolve({
ethProvider: ethProvider,
ethSigner: null
});
}
async function instantiateWithAccount (web3Provider, context) { this.set('kreditsContractInstance', contract);
console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask'); // window.Kredits = contract;
ethProvider = new ethers.providers.Web3Provider(web3Provider); return contract;
// const network = await ethProvider.getNetwork(); }.property('kreditsContractInstance', 'web3'),
ethProvider.listAccounts().then(accounts => {
context.set('currentUserAccounts', accounts); tokenContract: function() {
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner(); if (this.get('tokenContractInstance')) {
resolve({ return this.get('tokenContractInstance');
ethProvider, }
ethSigner
let contract = kreditsContracts(this.get('web3'), config.contractMetadata)['Token'];
this.set('tokenContractInstance', contract);
window.Token = contract;
return contract;
}.property('tokenContractInstance', 'web3'),
getValueFromContract(contract, contractMethod, ...args) {
Ember.Logger.debug('[kredits] read from contract', contract);
return new Ember.RSVP.Promise((resolve, reject) => {
this.get(contract)[contractMethod](...args, (err, data) => {
if (err) { reject(err); return; }
resolve(data);
});
});
},
getContributorData(i) {
let promise = new Ember.RSVP.Promise((resolve, reject) => {
this.getValueFromContract('kreditsContract', 'contributorAddresses', i).then(address => {
this.getValueFromContract('kreditsContract', 'contributors', address).then(person => {
this.getValueFromContract('tokenContract', 'balanceOf', address).then(balance => {
Ember.Logger.debug('[kredits] person', address, person);
let contributor = Contributor.create({
address: address,
ipfsHash: person[2],
kredits: balance.toNumber(),
isCurrentUser: this.get('currentUserAccounts').includes(address)
});
contributor.loadProfile(this.get('ipfs')).then(
() => resolve(contributor),
err => reject(err)
);
}); });
}); });
}).catch(err => reject(err));
});
return promise;
},
getContributors() {
return this.getValueFromContract('kreditsContract', 'contributorsCount').then(contributorsCount => {
let contributors = [];
for(var i = 0; i < contributorsCount.toNumber(); i++) {
contributors.push(this.getContributorData(i));
} }
if (window.ethereum) { return Ember.RSVP.all(contributors);
try {
// Request account access if needed
await window.ethereum.enable();
// Acccounts now exposed
instantiateWithAccount(window.ethereum, this);
} catch (error) {
instantiateWithoutAccount();
}
}
// Legacy dapp browsers...
else if (window.web3) {
instantiateWithAccount(window.web3.currentProvider, this);
}
// Non-dapp browsers...
else {
instantiateWithoutAccount();
}
}); });
}, },
setup () { getProposalData(i) {
return this.getEthProvider().then((providerAndSigner) => { let promise = new Ember.RSVP.Promise((resolve, reject) => {
let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, { this.getValueFromContract('kreditsContract', 'proposals', i).then(p => {
addresses: { Kernel: config.kreditsKernelAddress }, let proposal = Proposal.create({
apm: config.kreditsApmDomain, id : i,
ipfsConfig: config.ipfs creatorAddress : p[0],
}); recipientAddress : p[1],
return kredits votesCount : p[2].toNumber(),
.init() votesNeeded : p[3].toNumber(),
.then(async (kredits) => { amount : p[4].toNumber(),
this.set('kredits', kredits); executed : p[5],
this.set('currentBlock', await kredits.provider.getBlockNumber()); url : p[6],
ipfsHash : p[7]
if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
this.getCurrentUser.then((contributorData) => {
this.set('currentUser', contributorData);
});
}
return kredits;
}); });
});
},
totalSupply: computed(function() { if (proposal.get('ipfsHash')) {
return this.kredits.Token.functions.totalSupply().then(total => { proposal.loadContribution(this.get('ipfs')).then(
return formatKredits(total); () => resolve(proposal),
}) err => reject(err)
}), );
totalKreditsEarned: computed(function() {
return this.kredits.Contribution.functions.totalKreditsEarned(true)
.then(total => total.toNumber());
}),
loadInitialData () {
return this.getContributors()
.then(contributors => this.contributors.pushObjects(contributors))
.then(() => this.getContributions())
.then(contributions => this.contributions.pushObjects(contributions))
},
addContributor (attributes) {
if (attributes.github_uid) {
const uidInt = parseInt(attributes.github_uid);
attributes.github_uid = uidInt;
}
console.debug('[kredits] add contributor', attributes);
return this.kredits.Contributor.add(attributes, { gasLimit: 350000 })
.then(data => {
console.debug('[kredits] add contributor response', data);
});
},
updateContributor (id, attributes) {
if (attributes.github_uid) {
const uidInt = parseInt(attributes.github_uid);
attributes.github_uid = uidInt;
}
console.debug('[kredits] update contributor', attributes);
return this.kredits.Contributor.updateProfile(id, attributes, { gasLimit: 350000 })
.then(data => {
console.debug('[kredits] updateProfile response', data);
});
},
getContributors () {
return this.kredits.Contributor.all()
.then((contributors) => {
return contributors.map((contributor) => {
return Contributor.create(contributor);
});
});
},
addContribution (attributes) {
console.debug('[kredits] add contribution', attributes);
return this.kredits.Contribution.addContribution(attributes, { gasLimit: 300000 })
.then(data => {
console.debug('[kredits] add contribution response', data);
attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
const contribution = Contribution.create(attributes);
// TODO receive from wrapper
contribution.set('confirmedAtBlock', data.blockNumber + 40320);
this.contributions.pushObject(contribution);
return contribution;
});
},
addProposal (attributes) {
console.debug('[kredits] add proposal', attributes);
return this.kredits.Proposal.addProposal(attributes)
.then((data) => {
console.debug('[kredits] add proposal response', data);
attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
return Proposal.create(attributes);
});
},
getProposals () {
return this.kredits.Proposal.all()
.then((proposals) => {
return proposals.map((proposal) => {
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
return Proposal.create(proposal);
});
});
},
getContributions () {
return this.kredits.Contribution.all({page: {size: 200}})
.then(contributions => {
return contributions.map(contribution => {
contribution.contributor = this.contributors.findBy('id', contribution.contributorId.toString());
return Contribution.create(contribution);
});
});
},
vote (proposalId) {
console.debug('[kredits] vote for', proposalId);
return this.kredits.Proposal.functions.vote(proposalId)
.then(data => {
console.debug('[kredits] vote response', data);
return data;
});
},
veto (contributionId) {
console.debug('[kredits] veto against', contributionId);
return this.kredits.Contribution.functions.veto(contributionId, { gasLimit: 300000 })
.then(data => {
console.debug('[kredits] veto response', data);
return data;
});
},
getCurrentUser: computed('kredits.provider', function() {
if (isEmpty(this.currentUserAccounts)) {
return RSVP.resolve();
}
return this.kredits.Contributor
.functions.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
.then((id) => {
// check if the user is a contributor or not
if (id === 0) {
return RSVP.resolve();
} else { } else {
return this.kredits.Contributor.getById(id); Ember.Logger.warn('[kredits] proposal from blockchain is missing IPFS hash', proposal);
resolve(proposal);
} }
}).catch(err => reject(err));
});
return promise;
},
getProposals() {
return this.getValueFromContract('kreditsContract', 'proposalsCount').then(proposalsCount => {
let proposals = [];
for(var i = 0; i < proposalsCount.toNumber(); i++) {
proposals.push(this.getProposalData(i));
}
return Ember.RSVP.all(proposals);
});
},
vote(proposalId) {
Ember.Logger.debug('[kredits] vote for', proposalId);
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('kreditsContract').vote(proposalId, (err, data) => {
if (err) { reject(err); return; }
Ember.Logger.debug('[kredits] vote response', data);
resolve(data);
}); });
}), });
findProposalById(proposalId) {
return this.proposals.findBy('id', proposalId.toString());
}, },
// Contract events addContributor(contributor) {
addContractEventHandlers () { Ember.Logger.debug('[kredits] add contributor', contributor);
this.kredits.Contributor
.on('ContributorProfileUpdated', this.handleContributorChange.bind(this))
.on('ContributorAccountUpdated', this.handleContributorChange.bind(this))
.on('ContributorAdded', this.handleContributorChange.bind(this))
this.kredits.Contribution contributor.setProperties({
.on('ContributionVetoed', this.handleContributionVetoed.bind(this)) kredits: 0,
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
});
this.kredits.Proposal let id = uuid.v4();
.on('ProposalCreated', this.handleProposalCreated.bind(this))
.on('ProposalVoted', this.handleProposalVoted.bind(this))
.on('ProposalExecuted', this.handleProposalExecuted.bind(this));
this.kredits.Token return new Ember.RSVP.Promise((resolve, reject) => {
.on('Transfer', this.handleTransfer.bind(this)); this.get('ipfs').storeFile(contributor.serialize()).then(ipfsHash => {
}, contributor.set('ipfsHash', ipfsHash);
this.get('kreditsContract').addContributor(contributor.address, contributor.name, contributor.ipfsHash, contributor.isCore, id, (err, data) => {
async handleContributorChange (contributorId, ...args) { if (err) { reject(err); return; }
console.debug('[kredits] Contributor add/update event received for ID', contributorId); Ember.Logger.debug('[kredits] add contributor response', data);
console.debug('[kredits] Event data:', args); resolve(contributor);
const contributorData = await this.kredits.Contributor.getById(contributorId); });
const newContributor = Contributor.create(contributorData);
const oldContributor = this.contributors.findBy('id', contributorId.toString());
if (oldContributor) {
console.debug('[kredits] old contributor', oldContributor);
this.contributors.removeObject(oldContributor);
}
console.debug('[kredits] new contributor', newContributor);
this.contributors.pushObject(newContributor);
},
handleContributionVetoed (contributionId) {
console.debug('[kredits] ContributionVetoed event received for ', contributionId);
const contribution = this.contributions.findBy('id', contributionId);
console.debug('[kredits] contribution', contribution);
if (contribution) {
contribution.set('vetoed', true);
}
},
handleProposalCreated (proposalId) {
let proposal = this.findProposalById(proposalId);
if (proposal) {
console.debug('[events] proposal exists, not adding from event');
return;
}
this.kredits.Proposal.getById(proposalId)
.then((proposal) => {
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
this.proposals.pushObject(Proposal.create(proposal));
}); });
});
}, },
// TODO: We may want to reload that proposal to show the voter as voted addProposal(proposal) {
handleProposalVoted (proposalId, voterId, totalVotes) { return new Ember.RSVP.Promise((resolve, reject) => {
let proposal = this.findProposalById(proposalId); const {
recipientAddress,
amount,
url
} = proposal.getProperties('recipientAddress', 'amount', 'url');
if (proposal) { this.get('ipfs').storeFile(proposal.serializeContribution()).then(ipfsHash => {
proposal.set('votesCount', totalVotes); this.get('kreditsContract').addProposal(recipientAddress, amount, url, ipfsHash, (err, data) => {
} if (err) { reject(err); return; }
Ember.Logger.debug('[kredits] add proposal response', data);
resolve();
});
});
});
}, },
handleProposalExecuted (proposalId, contributorId, amount) { getTokenMintedEvents(fromBlock=0, toBlock='latest') {
let proposal = this.findProposalById(proposalId); return new Ember.RSVP.Promise((resolve, reject) => {
const minted = this.get('tokenContract')
.Minted({}, {fromBlock: fromBlock, toBlock: toBlock});
if (proposal.get('isExecuted')) { minted.get((err, res) => {
console.debug('[events] proposal already executed, not adding from event'); if (err) {
return; Ember.Logger.error('[kredits] Error reading Token#Minted events', res);
} reject(err);
return;
proposal.set('executed', true); }
Ember.Logger.warn('[kredits] Token#Minted events', res);
this.contributors resolve(res);
.findBy('id', contributorId.toString()) });
.incrementProperty('balance', amount); });
}, },
handleTransfer (from, to, value) { getContributions() {
value = value.toNumber(); // this.getTokenMintedEvents().then(events => {
let contributions = [];
this.contributors config.fixtures.mintedEvents.forEach(mintedEvent => {
.findBy('address', from) contributions.push(this.getContributionData(mintedEvent));
.decrementProperty('balance', value); });
this.contributors return Ember.RSVP.all(contributions);
.findBy('address', to) // });
.incrementProperty('balance', value);
}, },
getContributionData(mintedEvent) {
return new Ember.RSVP.Promise((resolve, reject) => {
let contribution = Contribution.create({
recipientAddress: mintedEvent.args.recipientAddress,
contributorId: mintedEvent.args.contributorId,
amount: mintedEvent.args.amount,
ipfsHash: mintedEvent.args.reference
});
if (contribution.get('ipfsHash')) {
contribution.loadDetails(this.get('ipfs')).then(
() => resolve(contribution),
err => reject(err)
);
} else {
Ember.Logger.warn('[kredits] contribution from Minted event is missing IPFS hash', contribution);
resolve(contribution);
}
});
},
// logKreditsContract: function() {
// Ember.Logger.debug('[kredits] kreditsContract', this.get('kreditsContract'));
// }.on('init')
}); });
-56
View File
@@ -1,56 +0,0 @@
button, input[type=submit], .button {
display: inline-block;
padding: 0.6rem 2rem;
background-color: rgba(22, 21, 40, 0.6);
border: 1px solid rgba(22, 21, 40, 1);
border-radius: 3px;
color: $primary-color;
font-weight: 500;
text-decoration: none;
text-transform: uppercase;
cursor: pointer;
letter-spacing: 0.1em;
&+button, &+input[type=submit], &+.button {
margin-left: 0.5rem;
}
&:hover {
background-color: rgba(22, 21, 40, 0.8);
}
&:active, &.active {
border-color: $primary-color;
}
&.small {
font-size: 0.8rem;
padding: 0.2rem 0.8rem;
}
&.danger {
color: $red;
background-color: rgba(40, 21, 21, 0.6);
border-color: rgba(40, 21, 21, 1);
&:hover {
background-color: rgba(40, 21, 21, 0.8);
}
&:active, &.active {
border-color: $red;
}
}
&.green {
color: $green;
background-color: rgba(21, 40, 21, 0.6);
border-color: rgba(21, 40, 21, 1);
&:hover {
background-color: rgba(21, 40, 21, 0.8);
}
&:active, &.active {
border-color: $green;
}
}
}
+1 -8
View File
@@ -5,11 +5,4 @@ $green: #97fb68;
$yellow: #fbe468; $yellow: #fbe468;
$red: #fb6868; $red: #fb6868;
$primary-color: $blue; $primaryColor: $blue;
$body-text-color: #fff;
#topbar {
background-color: rgba(255, 0, 255, .2);
}
+18 -69
View File
@@ -1,82 +1,31 @@
main { section {
padding: 1rem 2rem; @include outer-container;
margin-bottom: 8rem;
@include media-max(small) {
padding: 1rem;
}
&#index {
width: 100%;
display: grid;
grid-row-gap: 2rem;
grid-template-areas:
"stats"
"contributions";
}
&.center-column {
display: flex;
flex-direction: column;
align-items: center;
section {
width: 600px;
max-width: 100%;
header {
text-align: center;
}
}
}
}
@media (min-width: 550px) {
main {
&#index {
grid-column-gap: 4rem;
grid-row-gap: 2rem;
grid-template-columns: 2fr 4fr;
grid-template-areas:
"stats contributions";
}
}
}
main section {
margin-bottom: 5rem;
&:first-of-type { &:first-of-type {
margin-top: 2rem; margin-top: 6rem;
} }
@include media-max(small) { @include media($mobile) {
margin-bottom: 5rem; margin-bottom: 5rem;
&:first-of-type {
margin-top: 2rem;
}
} }
header { header {
margin-bottom: 3rem; @include span-columns(12);
padding-bottom: 3rem;
text-align: center;
// background-color: purple;
&.with-nav { @include media($mobile) {
display: grid; @include span-columns(10);
grid-template-columns: auto 5rem; @include shift(1);
grid-template-areas: padding-bottom: 2rem;
"title" "actions";
} }
h2 { div.content {
display: inline-block; @include span-columns(12);
// padding-left: 1.2rem;
}
nav {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
@include media-max(small) {
margin-bottom: 2rem;
} }
} }
} }
-33
View File
@@ -1,33 +0,0 @@
$breakpoints-max: (
small: 600px,
medium: 960px,
large: 1280px
);
$breakpoints-min: (
small: 601px,
medium: 961px,
large: 1281px
);
@mixin media-max($screen-size) {
@if map-has-key($breakpoints-max, $screen-size) {
@media (max-width: map-get($breakpoints-max, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}
@mixin media-min($screen-size) {
@if map-has-key($breakpoints-min, $screen-size) {
@media (min-width: map-get($breakpoints-min, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}
+36 -34
View File
@@ -2,7 +2,6 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
font-size: 14px;
} }
*:hover, *:active, *:focus { *:hover, *:active, *:focus {
@@ -11,19 +10,23 @@
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
@import "mediaqueries"; @import "bourbon";
@import "settings/neat";
@import "neat";
@import "settings/breakpoints";
@import "layout"; @import "layout";
@import "colors"; @import "colors";
$font-family-sans: 'Open Sans', sans-serif; $font-family-sans: 'Open Sans', sans-serif;
body { body {
font-family: $font-family-sans; background-image: url('/img/bg.jpg');
font-weight: 300;
background-image: linear-gradient(to bottom, rgba(22, 21, 40, .4), rgba(0, 0, 0, .75)), url('/img/bg.jpg');
background-repeat: none; background-repeat: none;
background-attachment: fixed; background-attachment: fixed;
color: $body-text-color; font-family: $font-family-sans;
font-size: 14px;
font-weight: 300;
color: #fff;
} }
@media screen and (min-width: 900px) { @media screen and (min-width: 900px) {
@@ -40,32 +43,26 @@ h1, h2, h3, h4, h5, input, button {
section { section {
h2 { h2 {
font-size: 1.4rem; font-size: 2.8rem;
color: $primary-color; color: $primaryColor;
@include media($mobile) {
font-size: 2rem;
}
} }
&#people { &#contributors {
.content { .content {
p.stats { p.stats {
margin-bottom: 1rem; padding-top: 3rem;
font-size: 1rem; font-size: 1rem;
color: white; color: white;
text-align: center; text-align: center;
span.number { span.number {
font-weight: 600; font-weight: 600;
} }
} @include media($mobile) {
} padding-top: 2rem;
} }
&#contributions-by-type {
.chart {
width: 50%;
margin-left: auto;
margin-right: auto;
@include media-max(small) {
width: 75%;
} }
} }
} }
@@ -74,32 +71,37 @@ section {
.actions { .actions {
padding-top: 3rem; padding-top: 3rem;
font-size: 1rem; font-size: 1rem;
color: $primary-color; color: $primaryColor;
text-align: center; text-align: center;
@include media-max(small) { @include media($mobile) {
padding-top: 2rem; padding-top: 2rem;
} }
a { a {
color: $primary-color; color: $primaryColor;
} }
} }
} }
} }
@media (min-width: 550px) { button, input[type=submit] {
section { display: inline-block;
h2 { border: 1px solid rgba(22, 21, 40, 1);
font-size: 2rem; background-color: rgba(22, 21, 40, 0.6);
} color: $primaryColor;
border-radius: 3px;
font-weight: 500;
text-transform: uppercase;
cursor: pointer;
letter-spacing: 0.1em;
&:hover {
background-color: rgba(22, 21, 40, 0.8);
} }
} }
@import "buttons";
@import "forms";
@import "components/topbar";
@import "components/loading-spinner"; @import "components/loading-spinner";
@import "components/contributor-list"; @import "components/contributor-list";
@import "components/add-contributor";
@import "components/proposal-list"; @import "components/proposal-list";
@import "components/contribution-list"; @import "components/contribution-list";
@import "components/user-avatar";
@@ -1,28 +1,20 @@
section#add-contributor, section#add-contributor, section#add-proposal {
section#add-contribution,
section#add-proposal {
form { form {
p { p {
margin-bottom: 1.5rem; margin-bottom: 1rem;
&.actions { &.actions {
padding-top: 1.5rem; padding-top: 1rem;
text-align: center; text-align: center;
a { a {
color: $primary-color; color: $primaryColor;
margin-left: 1rem; margin-left: 1rem;
} }
} }
} }
label {
display: block;
margin-bottom: 0.5rem;
opacity: 0.7;
}
input[type=text], select { input[type=text], select {
width: 100%; width: 100%;
padding: 1rem; padding: 1rem;
@@ -31,22 +23,17 @@ section#add-proposal {
background-color: rgba(22, 21, 40, 0.3); background-color: rgba(22, 21, 40, 0.3);
color: #fff; color: #fff;
font-size: 1.2rem; font-size: 1.2rem;
transition: border-color 0.1s linear;
&:focus, &.valid { &:focus, &.valid {
background-color: rgba(22, 21, 40, 0.6); background-color: rgba(22, 21, 40, 0.6);
} }
&:focus { @include placeholder {
border-color: $blue;
}
&::placeholder {
color: rgba(238, 238, 238, 0.5); color: rgba(238, 238, 238, 0.5);
} }
} }
select { select {
-moz-appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none;
appearance: none; appearance: none;
border-radius: 0; border-radius: 0;
background-color: rgba(22, 21, 40, 0.6); background-color: rgba(22, 21, 40, 0.6);
+6 -119
View File
@@ -1,88 +1,19 @@
main section {
@include media-max(small) {
&#contributions-unconfirmed, &#contributions-confirmed {
margin-top: 0;
}
}
}
.quick-filter {
font-size: 1.2rem;
margin-bottom: 2rem;
p {
font-size: inherit;
padding: 0.2rem 0 0;
}
label {
font-size: inherit;
&+ label {
margin-left: 3.6rem;
}
}
}
ul.contribution-list { ul.contribution-list {
clear: both; clear: both;
width: 100%; width: 100%;
list-style: none; list-style: none;
li { li {
display: grid; // display: block;
grid-template-columns: auto 5rem 5rem; padding: 0 1.2rem;
grid-row-gap: 0.5rem; line-height: 4.2rem;
padding: 0.8rem 1.2rem;
background-color: rgba(255,255,255,0.1); background-color: rgba(255,255,255,0.1);
font-size: 1.2rem; font-size: 1.4rem;
border-bottom: 1px solid rgba(255,255,255,0.2); border-bottom: 1px solid rgba(255,255,255,0.2);
&:first-of-type { &:first-of-type {
border-top: 1px solid rgba(255,255,255,0.2); border-top: 1px solid rgba(255,255,255,0.2);
} }
&.confirmed {
grid-template-columns: auto 5rem;
}
&.vetoed {
grid-template-columns: auto 5rem;
text-decoration: line-through;
opacity: 0.6;
}
p {
align-self: center;
margin: 0;
font-size: inherit;
line-height: 2rem;
&.kredits-amount, &.voting {
text-align: right;
}
}
span {
font-size: inherit;
&.title {
a {
font-size: inherit;
text-decoration: none;
color: $body-text-color;
}
a:hover, a:active {
color: $primary-color;
}
}
}
.description {
line-height: 1.4em;
font-size: 1rem;
}
.category { .category {
color: $blue; color: $blue;
padding-right: 0.2rem; padding-right: 0.2rem;
@@ -92,61 +23,17 @@ ul.contribution-list {
&.docs { color: $green; } &.docs { color: $green; }
&.ops { color: $purple; } &.ops { color: $purple; }
} }
.amount { .amount {
font-weight: 500; font-weight: 500;
} }
.symbol { .symbol {
font-size: 0.8rem; font-size: 1rem;
font-weight: 500;
padding-left: 0.2rem; padding-left: 0.2rem;
} }
.recipient { .recipient {
font-weight: 500; font-weight: 500;
} }
.votes {
font-size: 1rem;
color: $primary-color;
margin-right: 0.5rem;
}
} }
} }
@media (min-width: 550px) {
ul.proposal-list {
li {
grid-template-columns: auto 10rem;
grid-row-gap: 0.5rem;
p {
&.kredits-amount, &.voting {
text-align: right;
}
}
&.unconfirmed {
p {
&.kredits-amount, &.voting {
text-align: right;
}
&.description {
grid-column-start: span 1;
}
&.voting {
grid-column-start: span 1;
}
}
}
.description {
font-size: inherit;
}
}
}
}
+11 -19
View File
@@ -1,7 +1,6 @@
table.contributor-list { table.contributor-list {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
margin-bottom: 1.5rem;
tr { tr {
border-bottom: 1px solid rgba(255,255,255,0.2); border-bottom: 1px solid rgba(255,255,255,0.2);
@@ -16,18 +15,14 @@ table.contributor-list {
&.metadata { &.metadata {
height: 0; height: 0;
visibility: hidden; transition: all 0.2s ease-out;
&:not(.visible) {
border-bottom: none;
}
td { td {
padding: 0 1.2rem; padding: 0 1.2rem;
} }
a { a {
color: $primary-color; color: $primaryColor;
&:hover, &:active { &:hover, &:active {
color: #fff; color: #fff;
} }
@@ -38,6 +33,7 @@ table.contributor-list {
display: block; display: block;
overflow: hidden; overflow: hidden;
height: 0; height: 0;
transition: all 0.2s ease-out;
li { li {
display: inline; display: inline;
@@ -49,7 +45,6 @@ table.contributor-list {
&.visible { &.visible {
height: auto; height: auto;
visibility: visible;
ul { ul {
height: auto; height: auto;
} }
@@ -58,33 +53,30 @@ table.contributor-list {
td { td {
padding: 0 1.2rem; padding: 0 1.2rem;
line-height: 3.6rem; line-height: 4.2rem;
&.person { &.person {
text-align: left; text-align: left;
font-size: 1.2rem; font-size: 1.4rem;
img.avatar { img.avatar {
width: 2rem;
height: 2rem;
vertical-align: middle;
margin-right: 0.2rem; margin-right: 0.2rem;
border-radius: 1rem;
} }
} }
&.kredits { &.kredits {
text-align: right; text-align: right;
.amount { .amount {
font-size: 1.2rem; font-size: 1.4rem;
font-weight: 500;
} }
.symbol { .symbol {
font-size: 0.8rem; font-size: 1rem;
padding-left: 0.2rem; padding-left: 0.2rem;
} }
} }
pre {
line-height: 1rem;
padding-bottom: 1rem;
}
} }
} }
} }
+3 -3
View File
@@ -11,9 +11,9 @@
margin-top: 12rem; margin-top: 12rem;
text-align: center; text-align: center;
font-size: 1.4rem; font-size: 1.4rem;
color: $primary-color; color: $primaryColor;
@include media-max(small) { @include media($mobile) {
margin-top: 6rem; margin-top: 6rem;
} }
@@ -23,7 +23,7 @@
margin-bottom: 2rem; margin-bottom: 2rem;
#path-comet { #path-comet {
fill: $primary-color; fill: $primaryColor;
opacity: 0.1; opacity: 0.1;
animation-name: pulse; animation-name: pulse;
+14 -76
View File
@@ -4,46 +4,16 @@ ul.proposal-list {
list-style: none; list-style: none;
li { li {
display: grid; // display: block;
grid-template-columns: auto 5rem; padding: 0 1.2rem;
grid-row-gap: 0.5rem; line-height: 4.2rem;
padding: 1rem 1.2rem;
background-color: rgba(255,255,255,0.1); background-color: rgba(255,255,255,0.1);
font-size: 1.2rem; font-size: 1.4rem;
border-bottom: 1px solid rgba(255,255,255,0.2); border-bottom: 1px solid rgba(255,255,255,0.2);
&:first-of-type { &:first-of-type {
border-top: 1px solid rgba(255,255,255,0.2); border-top: 1px solid rgba(255,255,255,0.2);
} }
p {
align-self: center;
margin: 0;
font-size: inherit;
line-height: 2rem;
&.kredits-amount, &.voting {
text-align: right;
}
&.description {
grid-column-start: span 2;
}
&.voting {
grid-column-start: span 2;
}
}
span {
font-size: inherit;
}
.description {
line-height: 1.4em;
font-size: 1rem;
}
.category { .category {
color: $blue; color: $blue;
padding-right: 0.2rem; padding-right: 0.2rem;
@@ -53,62 +23,30 @@ ul.proposal-list {
&.docs { color: $green; } &.docs { color: $green; }
&.ops { color: $purple; } &.ops { color: $purple; }
} }
.amount { .amount {
font-weight: 500; font-weight: 500;
} }
.symbol { .symbol {
font-size: 1rem; font-size: 1rem;
font-weight: 500; font-weight: 500;
padding-left: 0.2rem; padding-left: 0.2rem;
} }
.recipient { .recipient {
font-weight: 500; font-weight: 500;
} }
.votes { .votes {
font-size: 1rem; font-size: 1rem;
color: $primary-color; color: $primaryColor;
margin-right: 0.5rem; padding-left: 0.5rem;
}
button {
float: right;
margin-top: 1.1rem;
height: 2rem;
line-height: 2rem;
padding: 0 0.6rem;
} }
} }
} }
@media (min-width: 550px) {
ul.proposal-list {
li {
grid-template-columns: auto 10rem;
grid-row-gap: 0.5rem;
p {
&.kredits-amount, &.voting {
text-align: right;
}
}
&.unconfirmed {
p {
&.kredits-amount, &.voting {
text-align: right;
}
&.description {
grid-column-start: span 1;
}
&.voting {
grid-column-start: span 1;
}
}
}
.description {
font-size: inherit;
}
}
}
}
-22
View File
@@ -1,22 +0,0 @@
#topbar {
padding: 0 1rem;
height: 3rem;
line-height: 3rem;
background-color: rgba(0,0,0,.3);
@include media-min(medium) {
padding: 0 2rem;
height: 4rem;
line-height: 4rem;
}
h1 {
display: inline-block;
text-transform: uppercase;
}
section#user-account {
display: inline-block;
float: right;
}
}
-7
View File
@@ -1,7 +0,0 @@
img.avatar {
width: 2rem;
height: 2rem;
vertical-align: middle;
margin-right: 0.2rem;
border-radius: 1rem;
}
+1
View File
@@ -0,0 +1 @@
$mobile: new-breakpoint(max-width 600px);
+5
View File
@@ -0,0 +1,5 @@
// $visual-grid: true;
// $visual-grid-color: red;
// $visual-grid-index: front;
// $visual-grid-opacity: 0.5;
$max-width: em(500, 12);
+3 -16
View File
@@ -1,16 +1,3 @@
<header id="topbar"> <main>
<h1>Kosmos Kredits</h1> {{outlet}}
</main>
<section id="user-account">
{{#if kredits.hasAccounts }}
{{#if kredits.currentUser}}
{{kredits.currentUser.name}}
{{#if kredits.currentUserIsCore}}(core){{/if}}
{{/if}}
{{else}}
Anonymous
{{/if}}
</section>
</header>
{{outlet}}
-13
View File
@@ -1,13 +0,0 @@
<main class="center-column">
<section id="add-contribution">
<header>
<h2>Submit a contribution</h2>
</header>
<div class="content">
{{add-contribution contributors=sortedContributors save=(action "save")}}
</div>
</section>
</main>
-13
View File
@@ -1,13 +0,0 @@
<main class="center-column">
<section id="add-contributor">
<header>
<h2>Update contributor profile</h2>
</header>
<div class="content">
{{add-contributor attributes=attributes save=(action "save")}}
</div>
</section>
</main>
-13
View File
@@ -1,13 +0,0 @@
<main class="center-column">
<section id="add-contributor">
<header>
<h2>Add contributor profile</h2>
</header>
<div class="content">
{{add-contributor save=(action "save")}}
</div>
</section>
</main>
+48 -68
View File
@@ -1,75 +1,55 @@
<main id="index"> <section id="contributors">
<header>
<h2>Kosmos Contributors</h2>
</header>
<div id="stats"> <div class="content">
<section id="people"> {{contributor-list contributors=contributorsSorted}}
<header class="with-nav">
<h2>Contributors</h2>
{{#if kredits.hasAccounts}}
<nav>
{{link-to "add" "contributors.new" title="Add contributor profile" class="button small green"}}
</nav>
{{/if}}
</header>
<div class="content">
{{contributor-list contributorList=kreditsToplist
showUnconfirmedKredits=showUnconfirmedKredits}}
<p class="stats"> <p class="stats">
<span class="number">{{await kredits.totalKreditsEarned}}</span> kredits confirmed and issued to <span class="number">{{model.totalSupply}}</span> kredits issued and distributed among
<span class="number">{{contributorsWithKredits.length}}</span> contributors <span class="number">{{contributorsWithKredits.length}}</span> contributors.
</p> </p>
<p class="stats">
{{input type="checkbox" id="hide-unnconfirmed-kredits" checked=showUnconfirmedKredits}}
<label for="hide-unnconfirmed-kredits">Show unconfirmed kredits in toplist</label>
</p>
</div>
</section>
<section id="contributions-by-type">
<header>
<h2>Contributions by type</h2>
</header>
<div class="content">
{{chart-contributions-by-type contributions=contributions}}
</div>
</section>
</div> </div>
</section>
<div id="contributions"> {{#if proposalsOpen}}
{{#if contributionsUnconfirmed}} <section id="proposals-open">
<section id="contributions-unconfirmed"> <header>
<header class="with-nav"> <h2>Open Proposals</h2>
<h2>Latest Contributions</h2> </header>
{{#if kredits.hasAccounts}} <div class="content">
<nav> {{proposal-list proposals=proposalsOpenSorted
<button {{action "toggleQuickFilterUnconfirmed"}} class="small {{if showQuickFilterUnconfirmed "active"}}">filter</button> confirmAction="confirmProposal"
{{link-to "add" "contributions.new" title="Submit a contribution" class="button small green"}} contractInteractionEnabled=contractInteractionEnabled}}
</nav>
{{/if}}
</header>
<div class="content">
{{!-- TODO: We need a better naming for kredits.hasAccounts --}}
{{contribution-list contributions=contributionsUnconfirmedSorted
vetoContribution=(action "vetoContribution")
contractInteractionEnabled=kredits.hasAccounts
showQuickFilter=showQuickFilterUnconfirmed}}
</div>
</section>
{{/if}}
<section id="contributions-confirmed"> {{#if contractInteractionEnabled}}
<header class="with-nav"> <p class="actions">
<h2>Confirmed Contributions</h2> {{#link-to 'proposals.new'}}Create new proposal{{/link-to}}
<nav> </p>
<button {{action "toggleQuickFilterConfirmed"}} class="small {{if showQuickFilterConfirmed "active"}}">filter</button> {{/if}}
</nav> </div>
</header> </section>
<div class="content"> {{/if}}
{{contribution-list contributions=contributionsConfirmedSorted
vetoContribution=(action "vetoContribution") <section id="contributions">
showQuickFilter=showQuickFilterConfirmed}} <header>
</div> <h2>Contributions</h2>
</section> </header>
<div class="content">
{{contribution-list contributions=model.contributions
contributors=model.contributors}}
</div> </div>
</section>
</main> <section id="add-contributor">
<header>
<h2>Add Contributor</h2>
</header>
<div class="content">
{{add-contributor contributors=model.contributors
newContributor=model.newContributor
contractInteractionEnabled=contractInteractionEnabled}}
</div>
</section>
+1 -1
View File
@@ -4,7 +4,7 @@
</header> </header>
<div class="content"> <div class="content">
{{add-proposal contributors=minedContributors save=(action "save")}} {{add-proposal proposal=model contributors=contributors onSave=(action 'onSave')}}
</div> </div>
</section> </section>
+1
View File
@@ -0,0 +1 @@
{{loading-spinner}}
-20
View File
@@ -1,20 +0,0 @@
import { computed } from '@ember/object';
import ethers from 'npm:ethers';
export default function(dependentKey, converterMethod) {
return computed(dependentKey, {
get () {
let value = this.get(dependentKey);
if (value && ethers.utils.BigNumber.isBigNumber(value)) {
return value[converterMethod]();
} else {
return value;
}
},
set (key, value) {
const bnValue = ethers.utils.bigNumberify(value);
this.set(dependentKey, bnValue);
return bnValue[converterMethod]();
}
});
}
-17
View File
@@ -1,17 +0,0 @@
import { computed } from '@ember/object';
import ethers from 'npm:ethers';
import formatKredits from 'kredits-web/utils/format-kredits';
export default function(dependentKey, options = {}) {
return computed(dependentKey, {
get () {
const value = this.get(dependentKey);
return formatKredits(value, options);
},
set (key, value) {
const bnValue = ethers.utils.bigNumberify(value);
this.set(dependentKey, bnValue);
return formatKredits(bnValue, options);
}
});
}
-9
View File
@@ -1,9 +0,0 @@
import ethers from 'npm:ethers';
export default function(value, options = {}) {
let etherValue = ethers.utils.formatEther(value);
if (!options.decimals) {
etherValue = parseInt(etherValue).toString();
}
return etherValue;
}
-27
View File
@@ -1,27 +0,0 @@
//
// Code from https://github.com/HeroicEric/ember-group-by (MIT licensed)
//
import { A } from '@ember/array';
import { get } from '@ember/object';
import { isPresent } from '@ember/utils';
export default function groupBy (collection, property) {
let groups = A();
let items = collection;
if (items) {
items.forEach(function(item) {
let value = get(item, property);
let group = groups.findBy('value', value);
if (isPresent(group)) {
get(group, 'items').push(item);
} else {
group = { property: property, value: value, items: [item] };
groups.push(group);
}
});
}
return groups;
}
+9
View File
@@ -0,0 +1,9 @@
{
"name": "kredits-web",
"dependencies": {
"ember": "~2.10.0",
"ember-cli-shims": "0.1.3",
"bourbon": "^4.2.7",
"neat": "^1.8.0"
}
}
+20 -22
View File
@@ -1,11 +1,10 @@
/* jshint node: true */ /* jshint node: true */
'use strict'; const fs = require('fs');
module.exports = function(environment) { module.exports = function(environment) {
var ENV = {
let ENV = {
modulePrefix: 'kredits-web', modulePrefix: 'kredits-web',
environment, environment: environment,
rootURL: '/', rootURL: '/',
locationType: 'auto', locationType: 'auto',
@@ -35,29 +34,41 @@ module.exports = function(environment) {
] ]
}, },
web3ProviderUrl: 'https://rinkeby.infura.io/v3/d4f788b7a6584f7db2fc3c268d4d09e9', contractMetadata: {},
web3ProviderUrl: 'https://parity.kosmos.org:8545',
ipfs: { ipfs: {
host: 'ipfs.kosmos.org', host: 'ipfs.kosmos.org',
port: '5444', port: '5444',
protocol: 'https', protocol: 'https'
gatewayUrl: 'https://ipfs.kosmos.org/ipfs'
} }
}; };
if (process.env.KREDITS_CONTRACT_ADDR) {
ENV.contractMetadata['Kredits'] = { address: process.env.KREDITS_CONTRACT_ADDR };
}
if (process.env.TOKEN_CONTRACT_ADDR) {
ENV.contractMetadata['Token'] = { address: process.env.TOKEN_CONTRACT_ADDR };
}
if (process.env.WEB3_PROVIDER_URL) {
ENV.web3ProviderUrl = process.env.WEB3_PROVIDER_URL;
}
ENV.contractMetadata['networkId'] = "17";
if (environment === 'development') { if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true; // ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true; // ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true; // ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true; // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true; // ENV.APP.LOG_VIEW_LOOKUPS = true;
ENV.web3ProviderUrl = 'https://rinkeby.infura.io/v3/d4f788b7a6584f7db2fc3c268d4d09e9';
ENV.ipfs = { ENV.ipfs = {
host: 'localhost', host: 'localhost',
port: '5001', port: '5001',
protocol: 'http' protocol: 'http'
}; };
const mintedEvents = JSON.parse(fs.readFileSync('config/fixtures-token-minted.json'));
ENV.fixtures = { mintedEvents: mintedEvents };
} }
if (environment === 'test') { if (environment === 'test') {
@@ -69,22 +80,9 @@ module.exports = function(environment) {
ENV.APP.LOG_VIEW_LOOKUPS = false; ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing'; ENV.APP.rootElement = '#ember-testing';
ENV.APP.autoboot = false;
} }
if (environment === 'production') { if (environment === 'production') {
// here you can enable a production-specific feature
ENV.kreditsApmDomain = process.env.KREDITS_APM_DOMAIN || 'open.aragonpm.eth';
}
if (process.env.WEB3_PROVIDER_URL) {
ENV.web3ProviderUrl = process.env.WEB3_PROVIDER_URL;
}
if (process.env.KREDITS_KERNEL_ADDRESS) {
ENV.kreditsKernelAddress = process.env.KREDITS_KERNEL_ADDRESS;
}
if (process.env.KREDITS_APM_DOMAIN) {
ENV.kreditsApmDomain = process.env.KREDITS_APM_DOMAIN;
} }
return ENV; return ENV;
+291
View File
@@ -0,0 +1,291 @@
[
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x70ba4415fc788d9481b5c2e557a55ce698de15b02eb5fba514a0b33027796c5a",
"blockNumber": 46,
"logIndex": 0,
"transactionHash": "0x3d4baea1f8a63f5b80491d5e67f2a833a4a6db7427a0c07ca181a852cd29dedb",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac",
"contributorId": 1,
"amount": "150",
"reference": "QmWj7XC6sSu1D7zmcH1tHFV9711P96m1z4qFP8yALZMWem"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x0112ca39f27dbc17ec7478336a9ba63ede0930cad0ead68b6fd441269950d6cc",
"blockNumber": 47,
"logIndex": 0,
"transactionHash": "0x8d1277270c51afdb9b6345015de176260412d7a649dfb00307dc8e7c513a250e",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac",
"contributorId": 2,
"amount": "150",
"reference": "QmSfgMxbBHNouW9vwD8DHkfdbMjAuMpv1fpBB6uPCazRCA"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x98fc834495394b8b26520fcd503508b953f7977a89df49a2a2d6711f2aa4d0c6",
"blockNumber": 48,
"logIndex": 0,
"transactionHash": "0x8f21b1df6ea33520c437bf9252a611603f52dca397ba93a3e2680da2c31d28e1",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xf18e631ea191ae4ebe70046fcb01a43655441c6d",
"contributorId": 1,
"amount": "1000",
"reference": "QmNbmsh3Rozksv6a5cTQM5mapQNpksU4Zcy9zMLcWsJoUf"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xf34ce94de4496441cb3972525f4010370b6957bbc09deba15501a169d063fa59",
"blockNumber": 50,
"logIndex": 0,
"transactionHash": "0x328ffbe2b34ba80c861445768b6069bc43eb511e196e7c22042a1f714aad78fa",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac",
"contributorId": 3,
"amount": "150",
"reference": "QmYE2ipsfbk9fwLJQ5wBrc9WKtneHjEGfV2RfngsxdKe1g"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xf6f8c1686497c7dc0ae4af2c87d7547b75bdffb960dce52a62bf74c1976980fb",
"blockNumber": 51,
"logIndex": 0,
"transactionHash": "0x694796b4b1bd82cadea6471eb2f718323ca85fed00ddb9b8a73524dec31141e6",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac",
"contributorId": 2,
"amount": "50",
"reference": "QmZZVbZN6hQTA5ZxrUywehdC2cCHkD6wvkVbVWJP21UmgR"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x771045eecdd57959e9cb9ac613df988eeee8b800e8f9edebfdf01a8f1a4f3c5d",
"blockNumber": 52,
"logIndex": 0,
"transactionHash": "0xaf9f073f80b8b492a7e822cfd38e5d13474a99aaef9be0c3bc0ffaf8c653a5f4",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xf18e631ea191ae4ebe70046fcb01a43655441c6d",
"contributorId": 1,
"amount": "50",
"reference": "QmbrszXGexzCZaWer6nFxjj4EXp5y4WT3WYPrbeNooQMyy"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x86ee58b0e36f3d199b0d241f3efb868a7276fdf084cff48ef045bdfe235c0506",
"blockNumber": 53,
"logIndex": 0,
"transactionHash": "0x9c7076b2fa49988fe706639376413bc79e4c5549c0145f00d7f27af6088c7128",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xf18e631ea191ae4ebe70046fcb01a43655441c6d",
"contributorId": 1,
"amount": "150",
"reference": "QmV5wSKLRBdXXbRuxWneQ1tDZP75Dpjv3adQHGye8CemEb"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xc2b3f860e0b8d4037529eca9b7d539332b3cc08741165378ac8368c22c6a18b3",
"blockNumber": 54,
"logIndex": 0,
"transactionHash": "0xfd2f5439e265e8899be5e2e18a9df56f4706e618d5459aa8f206aec091f7c0d9",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0x805bf817be0e53bf618eb41ae0775ac314f989a1",
"contributorId": 2,
"amount": "50",
"reference": "QmNp6ggpaR3yNuEWZbCttnUBjshQonFWE766iyVLAKfq7M"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x860667b8f67ae22ac97bd67d9ccb245eb98312eb5cb22303d1a8a383defbb5fc",
"blockNumber": 60,
"logIndex": 0,
"transactionHash": "0xbe153f64a31a1669ef1bffa16b0aeb1657b1b9fe779c38f6455d05fe3d96fd5b",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0x805bf817be0e53bf618eb41ae0775ac314f989a1",
"contributorId": 1,
"amount": "50",
"reference": "QmY2eBgBtFir4edYbw91KQNidMNHnCCem2LCJpTJ56Q87U"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x2c5da053f98cb5462a8aa4fa6bb2a5e7bb324baa404fbbd8ea25fdaaaac1b0db",
"blockNumber": 65,
"logIndex": 0,
"transactionHash": "0x1df5a2819a6312d170c323b517b03a7186a789845ecfe896d37de6fa482b924c",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0x805bf817be0e53bf618eb41ae0775ac314f989a1",
"contributorId": 3,
"amount": "50",
"reference": "QmeVbzGiXmPz4qgi5PfktZi2Esb4EVPQZLRnWnUPVatJgF"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x0e478ae9cf6e09e2ca8c4413c874f39decfb9d51c976467ef14d99efa79c9bcb",
"blockNumber": 75,
"logIndex": 0,
"transactionHash": "0xf16ace4f72c599c3b738bdaa4518f59a47dcdc304a8f24308c5d121ae0ae5b97",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0x0536806df512d6cdde913cf95c9886f65b1d3462",
"contributorId": 1,
"amount": "100",
"reference": "QmS3cywqGhHh6QBLnyBWXPTas2UnHEW8ZV8zZy6pVw95Zx"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0x31e91da869e9b87949e75ee14879005b172df96267f9ffd76d5024a46fe48a3c",
"blockNumber": 76,
"logIndex": 0,
"transactionHash": "0x735eb48c81f6868b886d3afede6b031323e78b221f3e2653ad2186c3e4e1abe9",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xd4a64570b12da659ee4bbd41c3509b7b1f9c51ac",
"contributorId": 2,
"amount": "500",
"reference": "QmP5RPzjgRdtq9Uj6tXxgMfA1GkY9Fh5ghXpDYBtc9YwMp"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xd41a4c3caae9e52632eba047d038e430b50920138e3a625b50a0a887e719a487",
"blockNumber": 77,
"logIndex": 0,
"transactionHash": "0x075beb007d18b38344599fe5b4dc315633aebedf08201ab9537f7d407811b0a2",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0x805bf817be0e53bf618eb41ae0775ac314f989a1",
"contributorId": 2,
"amount": "1000",
"reference": "QmTxwugByKfyPX2czyVDGhXK3NLZkA3uh2DbNnaAMVrVJB"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xcb3863eb681873a2c5954e07e085d4004a2824fcd17364b647e2fff1bea3ad4b",
"blockNumber": 78,
"logIndex": 0,
"transactionHash": "0xdf680c7a0bc60ef02619def582f68230bdd0fd8a945c9006841212ebcd6b80db",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xf18e631ea191ae4ebe70046fcb01a43655441c6d",
"contributorId": 3,
"amount": "300",
"reference": "QmYXyGSK6RyXQHNPHAADjpJ4P3mrz5Lf1bNnrp2yRaDxNo"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xe322d5292506ef1c4c915e875a4e8c1882d154fd9b711522809045bbb884e11d",
"blockNumber": 89,
"logIndex": 0,
"transactionHash": "0x5d90bf8c74b18b602e8b04fbca8678033589f6527b1ced08c0ee7d6c96f71949",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xb2d955733e6a470533f68f72d0af442070f24f55",
"contributorId": 1,
"amount": "300",
"reference": "QmZdUuq4NMz2S5bD3m9h9MNNsbTVeFVHzQss4uS1ZqPLsw"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xbb84cfc9c3298b045d4ae70afa9cc966db578b00e897f3d6988b08deeca0e3d9",
"blockNumber": 120,
"logIndex": 0,
"transactionHash": "0xf91a038b17cfd9b5eac8f4474d5e4d9a5034dead51fc2fffc9e88afdefd9129c",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0xf18e631ea191ae4ebe70046fcb01a43655441c6d",
"contributorId": 1,
"amount": "500",
"reference": "QmdQWivxwjuEHEYVu3ezZCzEWps6wEvQ8ePUHbQ7htSN8V"
}
},
{
"address": "0x63770f81fbe0d2b4159b6752eda99ee7c44ca5e4",
"blockHash": "0xc85a520ffc16b5f84d5f5bd29478d0871a045356e598c72be9a44ae3e6826bb5",
"blockNumber": 121,
"logIndex": 0,
"transactionHash": "0x59a2e3e1f81b8113b3b71eba26f28f75ba18d60b6dda6a4a216d566d7d68a31d",
"transactionIndex": 0,
"transactionLogIndex": "0x0",
"type": "mined",
"event": "Minted",
"args": {
"recipientAddress": "0x805bf817be0e53bf618eb41ae0775ac314f989a1",
"contributorId": 1,
"amount": "50",
"reference": "QmfDG3vCAgQsqkxkb6jCnLqw11wjw3uUpiZRbRBLZ9GpYi"
}
}
]
-3
View File
@@ -1,3 +0,0 @@
{
"jquery-integration": true
}
-18
View File
@@ -1,18 +0,0 @@
'use strict';
const browsers = [
'last 1 Chrome versions',
'last 1 Firefox versions',
'last 1 Safari versions'
];
const isCI = !!process.env.CI;
const isProduction = process.env.EMBER_ENV === 'production';
if (isCI || isProduction) {
browsers.push('ie 11');
}
module.exports = {
browsers
};
+10 -12
View File
@@ -1,17 +1,15 @@
'use strict'; /*jshint node:true*/
/* global require, module */
const EmberApp = require('ember-cli/lib/broccoli/ember-app'); var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) { module.exports = function(defaults) {
let app = new EmberApp(defaults, { var app = new EmberApp(defaults, {
// sassOptions: { // Add options here
// includePaths: [ sassOptions: {
// 'node_modules/bourbon/app/assets/stylesheets' includePaths: [
// ] 'bower_components/bourbon/app/assets/stylesheets',
// }, 'bower_components/neat/app/assets/stylesheets'
sourcemaps: { ]
enabled: true,
extensions: ['js']
} }
}); });
-4
View File
@@ -1,4 +0,0 @@
{
"node": true,
"browser": false
}
-20211
View File
File diff suppressed because it is too large Load Diff
+49 -50
View File
@@ -1,70 +1,69 @@
{ {
"name": "kredits-web", "name": "kredits-web",
"version": "1.4.0", "version": "1.0.0",
"private": true,
"description": "Contribution dashboard of the Kosmos project", "description": "Contribution dashboard of the Kosmos project",
"repository": "https://github.com/67P/kredits-web",
"license": "MIT", "license": "MIT",
"author": "Kosmos Contributors <mail@kosmos.org>", "author": "Kosmos Contributors <mail@kosmos.org>",
"contributors": [
"Garret Alfert <alfert@wevelop.de>",
"Sebastian Kippe <sebastian@kip.pe>",
"Michael Bumann <hello@michaelbumann.com>"
],
"directories": { "directories": {
"doc": "doc", "doc": "doc",
"test": "tests" "test": "tests"
}, },
"repository": "https://github.com/67P/kredits-web",
"scripts": { "scripts": {
"lint:hbs": "ember-template-lint .", "start": "ember server",
"lint:js": "eslint .", "start-local": "WEB3_PROVIDER_URL=http://localhost:8545 KREDITS_PROVIDER_URL=http://localhost:8545 KREDITS_CONTRACT_ADDR=`kredits-inspect -c Kredits -r -w address` TOKEN_CONTRACT_ADDR=`kredits-inspect -c Token -r -w address` npm start",
"test": "ember test", "test": "ember test",
"start": "KREDITS_APM_DOMAIN=open.aragonpm.eth ember serve", "postinstall": "bower install",
"start:local": "WEB3_PROVIDER_URL=http://localhost:7545 ember s",
"build": "ember build", "build": "ember build",
"build-prod": "rm -rf release/* && ember build -prod --output-path release", "build-prod": "ember build --environment production",
"preversion": "npm test", "update-version-file": "bash scripts/update-version-file.sh",
"version": "npm run build-prod && git add release/", "deploy": "npm run build-prod && npm run update-version-file && bash scripts/deploy.sh",
"postversion": "git push origin master", "kredits-console": "kredits-console",
"deploy": "git push 5apps master" "kredits-parity": "kredits-parity",
"kredits-deploy": "kredits-deploy",
"kredits-inspect": "kredits-inspect",
"kredits-seed": "kredits-seed"
}, },
"devDependencies": { "devDependencies": {
"@ember/jquery": "^0.5.2",
"@ember/optional-features": "^0.6.3",
"babel-preset-es2015": "^6.22.0", "babel-preset-es2015": "^6.22.0",
"babelify": "^7.3.0", "babelify": "^7.3.0",
"broccoli-asset-rev": "^2.7.0", "broccoli-asset-rev": "^2.4.5",
"ember-ajax": "^4.0.1", "ember-ajax": "^2.4.1",
"ember-awesome-macros": "0.41.0",
"ember-browserify": "^1.1.13", "ember-browserify": "^1.1.13",
"ember-cli": "~3.8.1", "ember-cli": "2.10.0",
"ember-cli-app-version": "^3.2.0", "ember-cli-app-version": "^2.0.0",
"ember-cli-babel": "^7.1.2", "ember-cli-babel": "^5.1.7",
"ember-cli-chart": "^3.4.0", "ember-cli-dependency-checker": "^1.3.0",
"ember-cli-dependency-checker": "^3.1.0", "ember-cli-htmlbars": "^1.0.10",
"ember-cli-eslint": "^4.2.3", "ember-cli-htmlbars-inline-precompile": "^0.3.3",
"ember-cli-htmlbars": "^3.0.0", "ember-cli-inject-live-reload": "^1.4.1",
"ember-cli-htmlbars-inline-precompile": "^1.0.3", "ember-cli-jshint": "^2.0.1",
"ember-cli-inject-live-reload": "^1.8.2", "ember-cli-qunit": "^3.0.1",
"ember-cli-sass": "^7.2.0", "ember-cli-release": "^0.2.9",
"ember-cli-sri": "^2.1.1", "ember-cli-sass": "^7.0.0",
"ember-cli-template-lint": "^1.0.0-beta.1", "ember-cli-sri": "^2.1.0",
"ember-cli-uglify": "^2.1.0", "ember-cli-test-loader": "^1.1.0",
"ember-cli-update": "^0.21.2", "ember-cli-uglify": "^1.2.0",
"ember-export-application-global": "^2.0.0", "ember-export-application-global": "^1.0.5",
"ember-flatpickr": "^2.14.0", "ember-load-initializers": "^0.5.1",
"ember-load-initializers": "^1.1.0", "ember-parachute": "0.1.0",
"ember-macro-helpers": "0.17.0", "ember-resolver": "^2.0.3",
"ember-maybe-import-regenerator": "^0.1.6", "ember-truth-helpers": "1.3.0",
"ember-promise-helpers": "1.0.6", "ipfs-api": "^12.1.7",
"ember-qunit": "^3.4.1", "kosmos-schemas": "~1.1.2",
"ember-resolver": "^5.0.1", "kredits-contracts": "^2.4.0",
"ember-source": "~3.8.0", "loader.js": "^4.0.10",
"ember-truth-helpers": "github:jmurphyau/ember-truth-helpers#31a14373a31f1f82c77537720549b47a95c28e5f", "tv4": "^1.3.0",
"eslint-plugin-ember": "^5.2.0", "uuid": "^3.0.1",
"ethers": "^4.0.27", "web3": "^0.18.2"
"kosmos-schemas": "^2.0.0",
"kredits-contracts": "^5.4.0",
"loader.js": "^4.7.0",
"qunit-dom": "^0.8.0",
"tv4": "^1.3.0"
}, },
"engines": { "engines": {
"node": "8.* || >= 10.*" "node": ">= 0.12.0"
} },
"private": true
} }
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
<!-- Most restrictive policy: -->
<site-control permitted-cross-domain-policies="none"/>
<!-- Least restrictive policy: -->
<!--
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-ports="*" secure="false"/>
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
-->
</cross-domain-policy>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,197 +0,0 @@
"use strict"
define("kredits-web/app",["exports","kredits-web/resolver","ember-load-initializers","kredits-web/config/environment"],function(e,t,n,r){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var i=Ember.Application.extend({modulePrefix:r.default.modulePrefix,podModulePrefix:r.default.podModulePrefix,Resolver:t.default});(0,n.default)(i,r.default.modulePrefix)
var o=i
e.default=o}),define("kredits-web/components/add-contribution/component",["exports"],function(e){function t(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=[],r=!0,i=!1,o=void 0
try{for(var s,u=e[Symbol.iterator]();!(r=(s=u.next()).done)&&(n.push(s.value),!t||n.length!==t);r=!0);}catch(a){i=!0,o=a}finally{try{r||null==u.return||u.return()}finally{if(i)throw o}}return n}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Component.extend({attributes:null,contributors:Object.freeze([]),isValidContributor:Ember.computed.notEmpty("contributorId"),isValidKind:Ember.computed.notEmpty("kind"),isValidAmount:Ember.computed("amount",function(){return parseInt(this.amount,10)>0}),isValidDescription:Ember.computed.notEmpty("description"),isValidUrl:Ember.computed.notEmpty("url"),isValid:Ember.computed.and("isValidContributor","isValidKind","isValidAmount","isValidDescription"),init:function(){this._super.apply(this,arguments),this.set("defaultDate",new Date),this.set("attributes",{contributorId:null,kind:null,date:[new Date],amount:null,description:null,url:null}),this.reset()},reset:function(){this.setProperties(this.attributes)},actions:{submit:function(){var e=this
if(this.isValid){var n=this.getProperties(Object.keys(this.attributes)),r=t(n.date[0].toISOString().split("T"),1)[0]
n.date=r,this.set("inProgress",!0),this.save(n).then(function(){e.reset()},function(e){console.warn(e),window.alert("Something went wrong. Check the browser console for details.")}).finally(function(){return e.set("inProgress",!1)})}else alert("Invalid data. Please review and try again.")}}})
e.default=n}),define("kredits-web/components/add-contribution/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"U4IJj1aB",block:'{"symbols":["contributor"],"statements":[[7,"form"],[9],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"select"],[11,"required",""],[12,"onchange",[27,"action",[[22,0,[]],[27,"mut",[[23,["contributorId"]]],null]],[["value"],["target.value"]]]],[9],[0,"\\n "],[7,"option"],[11,"value",""],[11,"selected",""],[11,"disabled",""],[11,"hidden",""],[9],[0,"Contributor"],[10],[0,"\\n"],[4,"each",[[23,["contributors"]]],null,{"statements":[[0," "],[7,"option"],[12,"value",[22,1,["id"]]],[12,"selected",[27,"eq",[[23,["contributorId"]],[22,1,["id"]]],null]],[9],[1,[22,1,["name"]],false],[10],[0,"\\n"]],"parameters":[1]},null],[0," "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"select"],[11,"required",""],[12,"onchange",[27,"action",[[22,0,[]],[27,"mut",[[23,["kind"]]],null]],[["value"],["target.value"]]]],[9],[0,"\\n "],[7,"option"],[11,"value",""],[11,"selected",""],[11,"disabled",""],[11,"hidden",""],[9],[0,"Kind"],[10],[0,"\\n "],[7,"option"],[11,"value","community"],[12,"selected",[27,"eq",[[23,["kind"]],"community"],null]],[9],[0,"Community"],[10],[0,"\\n "],[7,"option"],[11,"value","design"],[12,"selected",[27,"eq",[[23,["kind"]],"design"],null]],[9],[0,"Design"],[10],[0,"\\n "],[7,"option"],[11,"value","dev"],[12,"selected",[27,"eq",[[23,["kind"]],"dev"],null]],[9],[0,"Development"],[10],[0,"\\n "],[7,"option"],[11,"value","docs"],[12,"selected",[27,"eq",[[23,["kind"]],"docs"],null]],[9],[0,"Documentation"],[10],[0,"\\n "],[7,"option"],[11,"value","ops"],[12,"selected",[27,"eq",[[23,["kind"]],"ops"],null]],[9],[0,"IT Operations"],[10],[0,"\\n "],[7,"option"],[11,"value","special"],[12,"selected",[27,"eq",[[23,["kind"]],"special"],null]],[9],[0,"Special"],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[1,[27,"ember-flatpickr",null,[["allowInput","altFormat","altInput","altInputClass","date","dateFormat","defaultDate","maxDate","onChange"],[false,"F j, Y",true,"date-alt",[23,["date"]],"Y-m-d",[23,["defaultDate"]],[23,["defaultDate"]],[27,"action",[[22,0,[]],[27,"mut",[[23,["date"]]],null]],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[1,[27,"input",null,[["type","placeholder","value","class"],["text","500",[23,["amount"]],[27,"if",[[23,["isValidAmount"]],"valid",""],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[1,[27,"input",null,[["type","placeholder","value","class"],["text","Description",[23,["description"]],[27,"if",[[23,["isValidDescription"]],"valid",""],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[1,[27,"input",null,[["type","placeholder","value","class"],["text","URL (optional)",[23,["url"]],[27,"if",[[23,["isValidUrl"]],"valid",""],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","actions"],[9],[0,"\\n "],[1,[27,"input",null,[["type","disabled","value"],["submit",[23,["inProgress"]],[27,"if",[[23,["inProgress"]],"Processing","Save"],null]]]],false],[0,"\\n "],[10],[0,"\\n"],[3,"action",[[22,0,[]],"submit"],[["on"],["submit"]]],[10],[0,"\\n\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/components/add-contribution/template.hbs"}})
e.default=t}),define("kredits-web/components/add-contributor/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Component.extend({kredits:Ember.inject.service(),attributes:null,isValidAccount:Ember.computed.notEmpty("account"),isValidName:Ember.computed.notEmpty("name"),isValidURL:Ember.computed.notEmpty("url"),isValidGithubUID:Ember.computed.notEmpty("github_uid"),isValidGithubUsername:Ember.computed.notEmpty("github_username"),isValidGiteaUsername:Ember.computed.notEmpty("gitea_username"),isValidWikiUsername:Ember.computed.notEmpty("wiki_username"),isValid:Ember.computed.and("isValidAccount","isValidName","isValidGithubUID"),inProgress:!1,init:function(){this._super.apply(this,arguments),this.setDefaultAttributes(),this.reset()},setDefaultAttributes:function(){Ember.isPresent(this.attributes)||this.set("attributes",{account:null,name:null,kind:"person",url:null,github_username:null,github_uid:null,gitea_username:null,wiki_username:null})},reset:function(){this.setProperties(this.attributes)},actions:{submit:function(){var e=this
if(this.isValid){var t=Object.keys(this.attributes),n=this.getProperties(t)
this.set("inProgress",!0),this.save(n).then(function(){e.reset()}).catch(function(e){console.warn(e),window.alert("Something went wrong. Please check the browser console.")}).finally(function(){e.set("inProgress",!1)})}else alert("Invalid data. Please review and try again.")}}})
e.default=t}),define("kredits-web/components/add-contributor/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"ub+zr3DC",block:'{"symbols":[],"statements":[[7,"form"],[9],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-account"],[9],[0,"Ethereum account"],[10],[0,"\\n "],[1,[27,"input",null,[["name","id","type","value","placeholder","class"],["account","c-account","text",[23,["account"]],"0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4",[27,"if",[[23,["isValidAccount"]],"valid",""],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-kind"],[9],[0,"Kind"],[10],[0,"\\n "],[7,"select"],[11,"required",""],[12,"onchange",[27,"action",[[22,0,[]],[27,"mut",[[23,["kind"]]],null]],[["value"],["target.value"]]]],[11,"id","c-kind"],[9],[0,"\\n "],[7,"option"],[11,"value","person"],[12,"selected",[27,"eq",[[23,["kind"]],"person"],null]],[9],[0,"Person"],[10],[0,"\\n "],[7,"option"],[11,"value","organization"],[12,"selected",[27,"eq",[[23,["kind"]],"organization"],null]],[9],[0,"Organization"],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-name"],[9],[0,"Name"],[10],[0,"\\n "],[1,[27,"input",null,[["name","type","value","placeholder","class","id"],["name","text",[23,["name"]],"Zero Cool",[27,"if",[[23,["isValidName"]],"valid",""],null],"c-name"]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-url"],[9],[0,"URL"],[10],[0,"\\n "],[1,[27,"input",null,[["name","type","value","placeholder","class","id"],["url","text",[23,["url"]],"http://zerocool.bit",[27,"if",[[23,["isValidURL"]],"valid",""],null],"c-url"]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-github-uid"],[9],[0,"GitHub UID"],[10],[0,"\\n "],[1,[27,"input",null,[["name","type","value","placeholder","class","id"],["github_uid","text",[23,["github_uid"]],"2342",[27,"if",[[23,["isValidGithubUID"]],"valid",""],null],"c-github-uid"]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-github-username"],[9],[0,"GitHub username"],[10],[0,"\\n "],[1,[27,"input",null,[["name","type","value","placeholder","class","id"],["github_username","text",[23,["github_username"]],"zerocool",[27,"if",[[23,["isValidGithubUsername"]],"valid",""],null],"c-github-username"]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-gitea-username"],[9],[0,"Gitea username"],[10],[0,"\\n "],[1,[27,"input",null,[["name","type","value","placeholder","class","id"],["gitea_username","text",[23,["gitea_username"]],"zerocool",[27,"if",[[23,["isValidGiteaUsername"]],"valid",""],null],"c-gitea-username"]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"for","c-wiki-username"],[9],[0,"Wiki username"],[10],[0,"\\n "],[1,[27,"input",null,[["name","type","value","placeholder","class","id"],["wiki_username","text",[23,["wiki_username"]],"ZeroCool",[27,"if",[[23,["isValidWikiUsername"]],"valid",""],null],"c-wiki-username"]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","actions"],[9],[0,"\\n "],[1,[27,"input",null,[["type","disabled","value"],["submit",[23,["inProgress"]],[27,"if",[[23,["inProgress"]],"Processing","Save"],null]]]],false],[0,"\\n "],[10],[0,"\\n"],[3,"action",[[22,0,[]],"submit"],[["on"],["submit"]]],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/components/add-contributor/template.hbs"}})
e.default=t}),define("kredits-web/components/add-proposal/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Component.extend({attributes:null,contributors:Object.freeze([]),isValidContributor:Ember.computed.notEmpty("contributorId"),isValidAmount:Ember.computed("amount",function(){return parseInt(this.amount,10)>0}),isValidDescription:Ember.computed.notEmpty("description"),isValidUrl:Ember.computed.notEmpty("url"),isValid:Ember.computed.and("isValidContributor","isValidAmount","isValidDescription"),init:function(){this._super.apply(this,arguments),this.set("attributes",{contributorId:null,kind:"community",amount:null,description:null,url:null})},didInsertElement:function(){this._super.apply(this,arguments),this.reset()},reset:function(){this.setProperties(this.attributes)},actions:{submit:function(){var e=this
if(this.isValid){var t=Object.keys(this.attributes),n=this.getProperties(t),r=this.save(n)
this.set("inProgress",r),r.then(function(){e.reset(),window.scroll(0,0),window.alert("Proposal added.")})}else alert("Invalid data. Please review and try again.")}}})
e.default=t}),define("kredits-web/components/add-proposal/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"3DOgBCvD",block:'{"symbols":["contributor"],"statements":[[7,"form"],[9],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"select"],[11,"required",""],[12,"onchange",[27,"action",[[22,0,[]],[27,"mut",[[23,["contributorId"]]],null]],[["value"],["target.value"]]]],[9],[0,"\\n "],[7,"option"],[11,"value",""],[11,"selected",""],[11,"disabled",""],[11,"hidden",""],[9],[0,"Contributor"],[10],[0,"\\n"],[4,"each",[[23,["contributors"]]],null,{"statements":[[0," "],[7,"option"],[12,"value",[22,1,["id"]]],[12,"selected",[27,"eq",[[23,["contributorId"]],[22,1,["id"]]],null]],[9],[1,[22,1,["github_username"]],false],[10],[0,"\\n"]],"parameters":[1]},null],[0," "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"select"],[11,"required",""],[12,"onchange",[27,"action",[[22,0,[]],[27,"mut",[[23,["kind"]]],null]],[["value"],["target.value"]]]],[9],[0,"\\n "],[7,"option"],[11,"value","community"],[12,"selected",[27,"eq",[[23,["kind"]],"community"],null]],[9],[0,"Community"],[10],[0,"\\n "],[7,"option"],[11,"value","design"],[12,"selected",[27,"eq",[[23,["kind"]],"design"],null]],[9],[0,"Design"],[10],[0,"\\n "],[7,"option"],[11,"value","dev"],[12,"selected",[27,"eq",[[23,["kind"]],"dev"],null]],[9],[0,"Development"],[10],[0,"\\n "],[7,"option"],[11,"value","docs"],[12,"selected",[27,"eq",[[23,["kind"]],"docs"],null]],[9],[0,"Documentation"],[10],[0,"\\n "],[7,"option"],[11,"value","ops"],[12,"selected",[27,"eq",[[23,["kind"]],"ops"],null]],[9],[0,"IT Operations"],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[1,[27,"input",null,[["type","placeholder","value","class"],["text","100",[23,["amount"]],[27,"if",[[23,["isValidAmount"]],"valid",""],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[1,[27,"input",null,[["type","placeholder","value","class"],["text","Description",[23,["description"]],[27,"if",[[23,["isValidDescription"]],"valid",""],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[1,[27,"input",null,[["type","placeholder","value","class"],["text","URL (optional)",[23,["url"]],[27,"if",[[23,["isValidUrl"]],"valid",""],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","actions"],[9],[0,"\\n "],[1,[27,"input",null,[["type","disabled","value"],["submit",[27,"is-pending",[[23,["inProgress"]]],null],[27,"if",[[27,"is-pending",[[23,["inProgress"]]],null],"Processing","Save"],null]]]],false],[0,"\\n "],[4,"link-to",["index"],null,{"statements":[[0,"Back"]],"parameters":[]},null],[0,"\\n "],[10],[0,"\\n"],[3,"action",[[22,0,[]],"submit"],[["on"],["submit"]]],[10],[0,"\\n\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/components/add-proposal/template.hbs"}})
e.default=t}),define("kredits-web/components/chart-contributions-by-type/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t="#fb6868",n="#fbe468",r="#e068fb",i="#97fb68",o="#8f68fb",s=Ember.Component.extend({contributions:null,chartOptions:Object.freeze({legend:{display:!1}}),chartData:Ember.computed("contributions",function(){var e=this.contributions.filterBy("vetoed",!1).map(function(e){return{kind:e.kind,amount:e.amount}}).reduce(function(e,t){return t.kind in e?e[t.kind]=e[t.kind]+t.amount:e[t.kind]=t.amount,e},{})
return{datasets:[{data:[e.community,e.design,e.dev,e.ops,e.docs],borderColor:[t,n,r,o,i],borderWidth:1}],labels:["Community","Design","Development","Operations & Infrastructure","Documentation"]}})})
e.default=s}),define("kredits-web/components/chart-contributions-by-type/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"knNRD8x3",block:'{"symbols":[],"statements":[[7,"div"],[11,"class","chart"],[9],[0,"\\n "],[1,[27,"ember-chart",null,[["type","data","options","width","height"],["doughnut",[23,["chartData"]],[23,["chartOptions"]],200,200]]],false],[0,"\\n"],[10]],"hasEval":false}',meta:{moduleName:"kredits-web/components/chart-contributions-by-type/template.hbs"}})
e.default=t}),define("kredits-web/components/contribution-list/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Component.extend({tagName:"div",classNames:["contributions"],showQuickFilter:!1,hideSmallContributions:!1,contributorId:null,contributionKind:null,kredits:Ember.inject.service(),contributorsSorting:Object.freeze(["name:asc"]),contributors:Ember.computed.sort("kredits.contributors","contributorsSorting"),contributorsActive:Ember.computed("contributors.[]","contributions",function(){var e=this.contributions.mapBy("contributorId").map(function(e){return e.toString()}).uniq()
return this.contributors.filter(function(t){return e.includes(t.id.toString())})}),contributionKinds:Ember.computed("contributions.[]",function(){return this.contributions.mapBy("kind").uniq()}),contributionsFiltered:Ember.computed("contributions.[]","hideSmallContributions","contributorId","contributionKind",function(){var e=this
return this.contributions.filter(function(t){var n=!0
return e.hideSmallContributions&&t.amount<=500&&(n=!1),Ember.isPresent(e.contributorId)&&t.contributorId.toString()!==e.contributorId.toString()&&(n=!1),Ember.isPresent(e.contributionKind)&&t.kind!==e.contributionKind&&(n=!1),n})}),actions:{veto:function(e){this.contractInteractionEnabled?this.vetoContribution(e):window.alert("Only members can veto contributions. Please ask someone to set you up.")}}})
e.default=t}),define("kredits-web/components/contribution-list/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"nesENUZ5",block:'{"symbols":["contribution","kind","contributor"],"statements":[[4,"if",[[23,["showQuickFilter"]]],null,{"statements":[[0," "],[7,"div"],[11,"class","quick-filter"],[9],[0,"\\n "],[7,"p"],[9],[0,"\\n "],[7,"label"],[11,"class","filter-contributor"],[9],[0,"\\n Contributor:\\n "],[7,"select"],[12,"onchange",[27,"action",[[22,0,[]],[27,"mut",[[23,["contributorId"]]],null]],[["value"],["target.value"]]]],[9],[0,"\\n "],[7,"option"],[11,"value",""],[11,"selected",""],[9],[0,"all"],[10],[0,"\\n"],[4,"each",[[23,["contributorsActive"]]],null,{"statements":[[0," "],[7,"option"],[12,"value",[22,3,["id"]]],[12,"selected",[27,"eq",[[23,["contributorId"]],[22,3,["id"]]],null]],[9],[1,[22,3,["name"]],false],[10],[0,"\\n"]],"parameters":[3]},null],[0," "],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"label"],[11,"class","filter-contribution-kind"],[9],[0,"\\n Kind:\\n "],[7,"select"],[12,"onchange",[27,"action",[[22,0,[]],[27,"mut",[[23,["contributionKind"]]],null]],[["value"],["target.value"]]]],[9],[0,"\\n "],[7,"option"],[11,"value",""],[11,"selected",""],[9],[0,"all"],[10],[0,"\\n"],[4,"each",[[23,["contributionKinds"]]],null,{"statements":[[0," "],[7,"option"],[12,"value",[22,2,[]]],[12,"selected",[27,"eq",[[23,["contributionKind"]],[22,2,[]]],null]],[9],[1,[27,"capitalize-string",[[22,2,[]]],null],false],[10],[0,"\\n"]],"parameters":[2]},null],[0," "],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"label"],[11,"class","filter-contribution-size"],[9],[0,"\\n "],[1,[27,"input",null,[["type","checked"],["checkbox",[23,["hideSmallContributions"]]]]],false],[0,"\\n Hide small contributions\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n"]],"parameters":[]},null],[0,"\\n"],[7,"ul"],[11,"class","contribution-list"],[9],[0,"\\n"],[4,"each",[[23,["contributionsFiltered"]]],null,{"statements":[[0," "],[7,"li"],[12,"data-contribution-id",[22,1,["id"]]],[12,"class",[28,[[27,"contribution-status",[[22,1,[]]],null]," ",[27,"if",[[22,1,["vetoed"]],"vetoed"],null]]]],[9],[0,"\\n "],[7,"p"],[11,"class","meta"],[9],[0,"\\n "],[7,"span"],[11,"class","recipient"],[9],[1,[27,"user-avatar",null,[["contributor"],[[22,1,["contributor"]]]]],false],[10],[0,"\\n "],[7,"span"],[12,"class",[28,["category ",[22,1,["kind"]]]]],[9],[0,"("],[1,[22,1,["kind"]],false],[0,")"],[10],[0,"\\n "],[7,"span"],[11,"class","title"],[9],[0,"\\n"],[4,"if",[[22,1,["url"]]],null,{"statements":[[0," "],[7,"a"],[12,"href",[22,1,["url"]]],[11,"target","_blank"],[11,"rel","noopener"],[9],[1,[22,1,["description"]],false],[10],[0,"\\n"]],"parameters":[]},{"statements":[[0," "],[1,[22,1,["description"]],false],[0,"\\n"]],"parameters":[]}],[0," "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","kredits-amount"],[9],[0,"\\n "],[7,"span"],[11,"class","amount"],[9],[1,[22,1,["amount"]],false],[10],[7,"span"],[11,"class","symbol"],[9],[0,"₭S"],[10],[0,"\\n "],[10],[0,"\\n"],[4,"unless",[[22,1,["vetoed"]]],null,{"statements":[[4,"unless",[[27,"is-confirmed-contribution",[[22,1,[]]],null]],null,{"statements":[[0," "],[7,"p"],[11,"class","voting"],[9],[0,"\\n "],[7,"button"],[11,"class","small danger"],[9],[0,"veto"],[3,"action",[[22,0,[]],"veto",[22,1,["id"]]]],[10],[0,"\\n "],[10],[0,"\\n"]],"parameters":[]},null]],"parameters":[]},null],[0," "],[10],[0,"\\n"]],"parameters":[1]},null],[10]],"hasEval":false}',meta:{moduleName:"kredits-web/components/contribution-list/template.hbs"}})
e.default=t}),define("kredits-web/components/contributor-list/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Component.extend({tagName:"table",classNames:"contributor-list",selectedContributor:null,actions:{toggleContributorInfo:function(e){e.showMetadata?e.set("showMetadata",!1):(this.contributorList.setEach("showMetadata",!1),e.set("showMetadata",!0))}}})
e.default=t}),define("kredits-web/components/contributor-list/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"1on37xaL",block:'{"symbols":["c"],"statements":[[7,"tbody"],[9],[0,"\\n"],[4,"each",[[23,["contributorList"]]],null,{"statements":[[0," "],[7,"tr"],[11,"role","button"],[12,"class",[27,"if",[[27,"is-current-user",[[22,1,["contributor"]]],null],"current-user"],null]],[9],[0,"\\n "],[7,"td"],[11,"class","person"],[9],[0,"\\n "],[1,[27,"user-avatar",null,[["contributor"],[[22,1,["contributor"]]]]],false],[0," "],[1,[22,1,["contributor","name"]],false],[0,"\\n "],[10],[0,"\\n "],[7,"td"],[11,"class","kredits"],[9],[0,"\\n "],[7,"span"],[11,"class","amount"],[9],[0,"\\n"],[4,"if",[[23,["showUnconfirmedKredits"]]],null,{"statements":[[0," "],[1,[22,1,["amountTotal"]],false],[0,"\\n"]],"parameters":[]},{"statements":[[0," "],[1,[22,1,["amountConfirmed"]],false],[0,"\\n"]],"parameters":[]}],[0," "],[10],[0,"\\n "],[7,"span"],[11,"class","symbol"],[9],[0,"₭S"],[10],[0,"\\n "],[10],[0,"\\n "],[3,"action",[[22,0,[]],"toggleContributorInfo",[22,1,[]]]],[10],[0,"\\n "],[7,"tr"],[12,"class",[28,["metadata ",[27,"if",[[27,"is-current-user",[[22,1,["contributor"]]],null],"current-user"],null]," ",[27,"if",[[22,1,["showMetadata"]],"visible"],null]]]],[9],[0,"\\n "],[7,"td"],[11,"colspan","2"],[9],[0,"\\n "],[7,"ul"],[9],[0,"\\n "],[7,"li"],[9],[0,"\\n "],[7,"a"],[12,"href",[28,["https://rinkeby.etherscan.io/address/",[22,1,["contributor","account"]]]]],[11,"target","_blank"],[11,"rel","noopener"],[9],[0,"Inspect Ethereum transactions"],[10],[0,"\\n "],[10],[0,"\\n"],[4,"if",[[22,1,["contributor","ipfsHash"]]],null,{"statements":[[0," "],[7,"li"],[9],[0,"\\n "],[7,"a"],[12,"href",[28,["https://ipfs.io/ipfs/",[22,1,["contributor","ipfsHash"]]]]],[11,"target","_blank"],[11,"rel","noopener"],[9],[0,"Inspect IPFS profile"],[10],[0,"\\n "],[10],[0,"\\n"]],"parameters":[]},null],[0," "],[7,"li"],[9],[0,"\\n "],[4,"link-to",["contributors.edit",[22,1,["contributor","id"]]],null,{"statements":[[0,"Edit profile"]],"parameters":[]},null],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n"],[4,"if",[[22,1,["showMetadata"]]],null,{"statements":[[0," "],[7,"pre"],[9],[1,[22,1,["contributor","ipfsData"]],false],[10],[0,""]],"parameters":[]},null],[0," "],[10],[0,"\\n "],[10],[0,"\\n"]],"parameters":[1]},null],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/components/contributor-list/template.hbs"}})
e.default=t}),define("kredits-web/components/ember-chart",["exports","ember-cli-chart/components/ember-chart"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=t.default
e.default=n}),define("kredits-web/components/ember-flatpickr",["exports","ember-flatpickr/components/ember-flatpickr"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}})}),define("kredits-web/components/loading-spinner/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Component.extend({classNames:["loading-spinner"]})
e.default=t}),define("kredits-web/components/loading-spinner/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"b6SWTOaj",block:'{"symbols":[],"statements":[[7,"p"],[9],[0,"\\n "],[7,"svg"],[11,"xmlns","http://www.w3.org/2000/svg","http://www.w3.org/2000/xmlns/"],[11,"xmlns:xlink","http://www.w3.org/1999/xlink","http://www.w3.org/2000/xmlns/"],[11,"version","1.1"],[11,"x","0px"],[11,"y","0px"],[11,"viewBox","0 0 132 100"],[11,"enable-background","new 0 0 100 100"],[11,"xml:space","preserve","http://www.w3.org/XML/1998/namespace"],[9],[0,"\\n "],[7,"path"],[11,"id","path-comet"],[11,"d","M79.062,24.173L79.062,24.173l-0.021-0.011c-0.068-0.03-0.137-0.062-0.204-0.093L42.392,7.337\\n c0,0,3.065,14.104,4.458,18.019l0,0c0,0-41.754-12.481-46.85-13.67c1.67,6.52,30.607,62.492,30.607,62.492\\n c5.848,11.873,19.394,18.485,33.522,18.485c19.811,0,35.87-16.059,35.87-35.869C100,42.313,91.418,29.837,79.062,24.173z"],[9],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n"],[10],[0,"\\n"],[7,"p"],[9],[0,"\\n Loading data...\\n"],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/components/loading-spinner/template.hbs"}})
e.default=t}),define("kredits-web/components/proposal-list/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Component.extend({tagName:"ul",classNames:["proposal-list"],actions:{confirm:function(e){this.contractInteractionEnabled?this.confirmProposal(e):window.alert("Only members can vote on proposals. Please ask someone to set you up.")}}})
e.default=t}),define("kredits-web/components/proposal-list/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"pZHMSOZc",block:'{"symbols":["proposal"],"statements":[[4,"each",[[23,["proposals"]]],null,{"statements":[[0," "],[7,"li"],[12,"data-proposal-id",[22,1,["id"]]],[12,"class",[27,"if",[[22,1,["isExecuted"]],"confirmed","unconfirmed"],null]],[9],[0,"\\n "],[7,"p"],[11,"class","meta"],[9],[0,"\\n "],[7,"span"],[12,"class",[28,["category ",[22,1,["kind"]]]]],[9],[0,"♥ ("],[1,[22,1,["kind"]],false],[0,")"],[10],[0,"\\n "],[7,"span"],[11,"class","recipient"],[9],[1,[22,1,["contributor","name"]],false],[0,":"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","kredits-amount"],[9],[0,"\\n "],[7,"span"],[11,"class","amount"],[9],[1,[22,1,["amount"]],false],[10],[7,"span"],[11,"class","symbol"],[9],[0,"₭S"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","description"],[9],[0,"\\n "],[7,"span"],[11,"class","description"],[9],[1,[22,1,["description"]],false],[10],[0,"\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","voting"],[9],[0,"\\n"],[4,"unless",[[22,1,["isExecuted"]]],null,{"statements":[[0," "],[7,"span"],[11,"class","votes"],[9],[0,"("],[1,[22,1,["votesCount"]],false],[0,"/"],[1,[22,1,["votesNeeded"]],false],[0," votes)"],[10],[0,"\\n "],[7,"button"],[9],[0,"+1"],[3,"action",[[22,0,[]],"confirm",[22,1,["id"]]]],[10],[0,"\\n"]],"parameters":[]},null],[0," "],[10],[0,"\\n "],[10],[0,"\\n"]],"parameters":[1]},null]],"hasEval":false}',meta:{moduleName:"kredits-web/components/proposal-list/template.hbs"}})
e.default=t}),define("kredits-web/components/user-avatar/component",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Component.extend({contributor:null,tagName:"img",classNames:["avatar"],attributeBindings:["src","title"],src:Ember.computed.alias("contributor.avatarURL"),title:Ember.computed.alias("contributor.name")})
e.default=t}),define("kredits-web/components/user-avatar/template",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"rnnAjEgP",block:'{"symbols":[],"statements":[],"hasEval":false}',meta:{moduleName:"kredits-web/components/user-avatar/template.hbs"}})
e.default=t}),define("kredits-web/controllers/application",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({kredits:Ember.inject.service()})
e.default=t}),define("kredits-web/controllers/contributions/new",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({kredits:Ember.inject.service(),contributors:Ember.computed.alias("kredits.contributors"),minedContributors:Ember.computed.filterBy("contributors","id"),contributorsSorting:Object.freeze(["name:asc"]),sortedContributors:Ember.computed.sort("minedContributors","contributorsSorting"),actions:{save:function(e){var t=this,n=this.contributors.findBy("id",e.contributorId)
return e.contributorIpfsHash=n.ipfsHash,this.kredits.addContribution(e).then(function(e){return t.transitionToRoute("index"),e})}}})
e.default=t}),define("kredits-web/controllers/contributors/edit",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({kredits:Ember.inject.service(),actions:{save:function(e){var t=this
return this.kredits.updateContributor(this.model.id,e).then(function(){return t.transitionToRoute("index")})}}})
e.default=t}),define("kredits-web/controllers/contributors/new",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({kredits:Ember.inject.service(),actions:{save:function(e){var t=this
return this.kredits.addContributor(e).then(function(){return t.transitionToRoute("index")})}}})
e.default=t}),define("kredits-web/controllers/index",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({kredits:Ember.inject.service(),currentBlock:Ember.computed.alias("kredits.currentBlock"),contributions:Ember.computed.alias("kredits.contributions"),contributionsConfirmed:Ember.computed.alias("kredits.contributionsConfirmed"),contributionsUnconfirmed:Ember.computed.alias("kredits.contributionsUnconfirmed"),contributionsSorting:Object.freeze(["date:desc","time:desc","id:desc"]),contributionsUnconfirmedSorted:Ember.computed.sort("contributionsUnconfirmed","contributionsSorting"),contributionsConfirmedSorted:Ember.computed.sort("contributionsConfirmed","contributionsSorting"),kreditsByContributor:Ember.computed.alias("kredits.kreditsByContributor"),kreditsToplistSorting:Ember.computed("showUnconfirmedKredits",function(){return this.showUnconfirmedKredits?["amountTotal:desc"]:["amountConfirmed:desc"]}),kreditsToplist:Ember.computed.sort("kreditsByContributor","kreditsToplistSorting"),showUnconfirmedKredits:!0,hideUnconfirmedKredits:Ember.computed.not("showUnconfirmedKredits"),showQuickFilterUnconfirmed:!1,showQuickFilterConfirmed:!1,actions:{vetoContribution:function(e){this.kredits.veto(e).then(function(e){console.debug("[controllers:index] Veto submitted to Ethereum blockhain: "+e.hash)})},confirmProposal:function(e){this.kredits.vote(e).then(function(e){console.debug("[controllers:index] Vote submitted to Ethereum blockhain: "+e.hash)})},toggleQuickFilterUnconfirmed:function(){this.toggleProperty("showQuickFilterUnconfirmed")},toggleQuickFilterConfirmed:function(){this.toggleProperty("showQuickFilterConfirmed")}}})
e.default=t}),define("kredits-web/controllers/proposals/new",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({kredits:Ember.inject.service(),contributors:Ember.computed.alias("kredits.contributors"),minedContributors:Ember.computed.filterBy("contributors","id"),actions:{save:function(e){var t=this,n=this.contributors.findBy("id",e.contributorId)
return e.contributorIpfsHash=n.get("ipfsHash"),this.kredits.addProposal(e).then(function(e){return t.transitionToRoute("index"),e})}}})
e.default=t}),define("kredits-web/helpers/and",["exports","ember-truth-helpers/helpers/and"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"and",{enumerable:!0,get:function(){return t.and}})}),define("kredits-web/helpers/app-version",["exports","kredits-web/config/environment","ember-cli-app-version/utils/regexp"],function(e,t,n){function r(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=t.default.APP.version,o=r.versionOnly||r.hideSha,s=r.shaOnly||r.hideVersion,u=null
return o&&(r.showExtended&&(u=i.match(n.versionExtendedRegExp)),u||(u=i.match(n.versionRegExp))),s&&(u=i.match(n.shaRegExp)),u?u[0]:i}Object.defineProperty(e,"__esModule",{value:!0}),e.appVersion=r,e.default=void 0
var i=Ember.Helper.helper(r)
e.default=i}),define("kredits-web/helpers/await",["exports","ember-promise-helpers/helpers/await"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}})})
define("kredits-web/helpers/capitalize-string",["exports"],function(e){function t(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=[],r=!0,i=!1,o=void 0
try{for(var s,u=e[Symbol.iterator]();!(r=(s=u.next()).done)&&(n.push(s.value),!t||n.length!==t);r=!0);}catch(a){i=!0,o=a}finally{try{r||null==u.return||u.return()}finally{if(i)throw o}}return n}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Helper.extend({compute:function(e){var n=t(e,1)[0]
return n.charAt(0).toUpperCase()+n.slice(1)}})
e.default=n}),define("kredits-web/helpers/contribution-status",["exports"],function(e){function t(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=[],r=!0,i=!1,o=void 0
try{for(var s,u=e[Symbol.iterator]();!(r=(s=u.next()).done)&&(n.push(s.value),!t||n.length!==t);r=!0);}catch(a){i=!0,o=a}finally{try{r||null==u.return||u.return()}finally{if(i)throw o}}return n}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Helper.extend({kredits:Ember.inject.service(),currentBlock:Ember.computed.alias("kredits.currentBlock"),compute:function(e){var n=t(e,1)[0]
return n.vetoed?"vetoed":n.confirmedAt>this.currentBlock?"unconfirmed":"confirmed"}})
e.default=n}),define("kredits-web/helpers/eq",["exports","ember-truth-helpers/helpers/equal"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"equal",{enumerable:!0,get:function(){return t.equal}})}),define("kredits-web/helpers/gt",["exports","ember-truth-helpers/helpers/gt"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"gt",{enumerable:!0,get:function(){return t.gt}})}),define("kredits-web/helpers/gte",["exports","ember-truth-helpers/helpers/gte"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"gte",{enumerable:!0,get:function(){return t.gte}})}),define("kredits-web/helpers/is-array",["exports","ember-truth-helpers/helpers/is-array"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"isArray",{enumerable:!0,get:function(){return t.isArray}})}),define("kredits-web/helpers/is-confirmed-contribution",["exports"],function(e){function t(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=[],r=!0,i=!1,o=void 0
try{for(var s,u=e[Symbol.iterator]();!(r=(s=u.next()).done)&&(n.push(s.value),!t||n.length!==t);r=!0);}catch(a){i=!0,o=a}finally{try{r||null==u.return||u.return()}finally{if(i)throw o}}return n}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Helper.extend({kredits:Ember.inject.service(),currentBlock:Ember.computed.alias("kredits.currentBlock"),compute:function(e){var n=t(e,1)[0]
return!n.vetoed&&n.confirmedAt<=this.currentBlock}})
e.default=n}),define("kredits-web/helpers/is-current-user",["exports"],function(e){function t(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=[],r=!0,i=!1,o=void 0
try{for(var s,u=e[Symbol.iterator]();!(r=(s=u.next()).done)&&(n.push(s.value),!t||n.length!==t);r=!0);}catch(a){i=!0,o=a}finally{try{r||null==u.return||u.return()}finally{if(i)throw o}}return n}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Helper.extend({kredits:Ember.inject.service(),currentUser:Ember.computed.alias("kredits.currentUser"),compute:function(e){var n=t(e,1)[0]
return Ember.isPresent(n)&&Ember.isPresent(this.currentUser)&&n.account===this.currentUser.account}})
e.default=n}),define("kredits-web/helpers/is-equal",["exports","ember-truth-helpers/helpers/is-equal"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"isEqual",{enumerable:!0,get:function(){return t.isEqual}})}),define("kredits-web/helpers/is-fulfilled",["exports","ember-promise-helpers/helpers/is-fulfilled"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"isFulfilled",{enumerable:!0,get:function(){return t.isFulfilled}})}),define("kredits-web/helpers/is-pending",["exports","ember-promise-helpers/helpers/is-pending"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"isPending",{enumerable:!0,get:function(){return t.isPending}})}),define("kredits-web/helpers/is-rejected",["exports","ember-promise-helpers/helpers/is-rejected"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"isRejected",{enumerable:!0,get:function(){return t.isRejected}})}),define("kredits-web/helpers/lt",["exports","ember-truth-helpers/helpers/lt"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"lt",{enumerable:!0,get:function(){return t.lt}})}),define("kredits-web/helpers/lte",["exports","ember-truth-helpers/helpers/lte"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"lte",{enumerable:!0,get:function(){return t.lte}})}),define("kredits-web/helpers/not-eq",["exports","ember-truth-helpers/helpers/not-equal"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"notEq",{enumerable:!0,get:function(){return t.notEq}})}),define("kredits-web/helpers/not",["exports","ember-truth-helpers/helpers/not"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"not",{enumerable:!0,get:function(){return t.not}})}),define("kredits-web/helpers/or",["exports","ember-truth-helpers/helpers/or"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"or",{enumerable:!0,get:function(){return t.or}})}),define("kredits-web/helpers/promise-all",["exports","ember-promise-helpers/helpers/promise-all"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"promiseAll",{enumerable:!0,get:function(){return t.promiseAll}})}),define("kredits-web/helpers/promise-hash",["exports","ember-promise-helpers/helpers/promise-hash"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"promiseHash",{enumerable:!0,get:function(){return t.promiseHash}})}),define("kredits-web/helpers/promise-rejected-reason",["exports","ember-promise-helpers/helpers/promise-rejected-reason"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}})}),define("kredits-web/helpers/xor",["exports","ember-truth-helpers/helpers/xor"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}}),Object.defineProperty(e,"xor",{enumerable:!0,get:function(){return t.xor}})}),define("kredits-web/initializers/app-version",["exports","ember-cli-app-version/initializer-factory","kredits-web/config/environment"],function(e,t,n){var r,i
Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0,n.default.APP&&(r=n.default.APP.name,i=n.default.APP.version)
var o={name:"App Version",initialize:(0,t.default)(r,i)}
e.default=o}),define("kredits-web/initializers/container-debug-adapter",["exports","ember-resolver/resolvers/classic/container-debug-adapter"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n={name:"container-debug-adapter",initialize:function(){var e=arguments[1]||arguments[0]
e.register("container-debug-adapter:main",t.default),e.inject("container-debug-adapter:main","namespace","application:main")}}
e.default=n}),define("kredits-web/initializers/export-application-global",["exports","kredits-web/config/environment"],function(e,t){function n(){var e=arguments[1]||arguments[0]
if(!1!==t.default.exportApplicationGlobal){var n
if("undefined"!=typeof window)n=window
else if("undefined"!=typeof global)n=global
else{if("undefined"==typeof self)return
n=self}var r,i=t.default.exportApplicationGlobal
r="string"==typeof i?i:Ember.String.classify(t.default.modulePrefix),n[r]||(n[r]=e,e.reopen({willDestroy:function(){this._super.apply(this,arguments),delete n[r]}}))}}Object.defineProperty(e,"__esModule",{value:!0}),e.initialize=n,e.default=void 0
var r={name:"export-application-global",initialize:n}
e.default=r}),define("kredits-web/models/contribution",["exports","kredits-web/utils/cps/bignumber"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Object.extend({id:null,contributorId:null,amount:null,confirmedAt:(0,t.default)("confirmedAtBlock","toNumber"),vetoed:null,ipfsHash:null,creatorAccount:null,kind:null,description:null,details:null,url:null,ipfsData:"",init:function(){this._super.apply(this,arguments),this.set("details",{})}})
e.default=n}),define("kredits-web/models/contributor",["exports","kredits-web/utils/cps/bignumber","kredits-web/utils/cps/kredits"],function(e,t,n){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var r=Ember.Object.extend({id:(0,t.default)("idRaw","toString"),account:null,balance:(0,n.default)("balanceRaw"),totalKreditsEarned:(0,t.default)("totalKreditsEarnedRaw","toNumber"),contributionsCount:(0,t.default)("contributionsCountRaw","toNumber"),isCore:!1,ipfsHash:null,kind:null,name:null,url:null,github_username:null,github_uid:null,wiki_username:null,ipfsData:"",avatarURL:Ember.computed("github_uid",function(){var e=this.github_uid
if(e)return"https://avatars2.githubusercontent.com/u/".concat(e,"?v=3&s=128")})})
e.default=r}),define("kredits-web/models/proposal",["exports","kredits-web/utils/cps/bignumber"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Object.extend({id:(0,t.default)("idRaw","toString"),creatorAccount:null,contributorId:(0,t.default)("contributorIdRaw","toString"),amount:(0,t.default)("amountRaw","toNumber"),votesCount:(0,t.default)("votesCountRaw","toNumber"),votesNeeded:(0,t.default)("votesNeededRaw","toNumber"),executed:null,ipfsHash:null,isExecuted:Ember.computed.alias("executed"),kind:null,description:null,details:null,url:null,ipfsData:"",init:function(){this._super.apply(this,arguments),this.set("details",{})}})
e.default=n}),define("kredits-web/resolver",["exports","ember-resolver"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=t.default
e.default=n}),define("kredits-web/router",["exports","kredits-web/config/environment"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var n=Ember.Router.extend({location:t.default.locationType,rootURL:t.default.rootURL})
n.map(function(){this.route("proposals",function(){this.route("new")}),this.route("contributions",function(){this.route("new")}),this.route("contributors",function(){this.route("new"),this.route("edit",{path:":id/edit"})})})
var r=n
e.default=r}),define("kredits-web/routes/application",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Route.extend({kredits:Ember.inject.service(),beforeModel:function(e){var t=this.kredits
return t.setup().then(function(){t.get("kredits").preflightChecks().catch(function(e){console.error("Kredits preflight check failed!"),console.error(e)}),t.get("accountNeedsUnlock")&&confirm("It looks like you have an Ethereum wallet available. Please unlock your account.")&&e.retry()}).catch(function(e){console.log("Error initializing Kredits",e)})},afterModel:function(){var e=this
return this.kredits.loadInitialData().then(function(){e.kredits.addContractEventHandlers()})}})
e.default=t})
define("kredits-web/routes/contributors/edit",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Route.extend({kredits:Ember.inject.service(),contributors:Ember.computed.alias("kredits.contributors"),model:function(e){return this.kredits.contributors.findBy("id",e.id)},setupController:function(e,t){this._super(e,t),e.set("attributes",{account:t.account,name:t.name,kind:t.kind,url:t.url,github_username:t.github_username,github_uid:t.github_uid,gitea_username:t.gitea_username,wiki_username:t.wiki_username})}})
e.default=t}),define("kredits-web/services/ajax",["exports","ember-ajax/services/ajax"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return t.default}})}),define("kredits-web/services/kredits",["exports","npm:ethers","npm:kredits-contracts","kredits-web/utils/group-by","kredits-web/utils/format-kredits","kredits-web/config/environment","kredits-web/models/contributor","kredits-web/models/proposal","kredits-web/models/contribution"],function(e,t,n,r,i,o,s,u,a){function l(e,t,n,r,i,o,s){try{var u=e[o](s),a=u.value}catch(l){return void n(l)}u.done?t(a):Promise.resolve(a).then(r,i)}function d(e){return function(){var t=this,n=arguments
return new Promise(function(r,i){var o=e.apply(t,n)
function s(e){l(o,r,i,s,u,"next",e)}function u(e){l(o,r,i,s,u,"throw",e)}s(void 0)})}}Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var c=Ember.Service.extend({currentBlock:null,currentUserAccounts:null,currentUser:null,contributors:null,contributions:null,proposals:null,currentUserIsContributor:Ember.computed.notEmpty("currentUser"),currentUserIsCore:Ember.computed.alias("currentUser.isCore"),hasAccounts:Ember.computed.notEmpty("currentUserAccounts"),accountNeedsUnlock:Ember.computed("currentUserAccounts",function(){return this.currentUserAccounts&&Ember.isEmpty(this.currentUserAccounts)}),contributionsUnconfirmed:Ember.computed("contributions.[]","currentBlock",function(){var e=this
return this.contributions.filter(function(t){return t.confirmedAt>e.currentBlock})}),contributionsConfirmed:Ember.computed("contributions.[]","currentBlock",function(){var e=this
return this.contributions.filterBy("vetoed",!1).filter(function(t){return t.confirmedAt<=e.currentBlock})}),kreditsByContributor:Ember.computed("contributionsUnconfirmed.@each.vetoed","contributors.[]",function(){var e=this,t=this.contributionsUnconfirmed.filterBy("vetoed",!1),n=(0,r.default)(t,"contributorId"),i=n.map(function(e){return e.value.toString()}),o=this.contributors.reject(function(e){return i.includes(e.id)}),s=n.map(function(t){var n=t.items.mapBy("amount").reduce(function(e,t){return e+t}),r=e.contributors.findBy("id",t.value.toString())
return Ember.Object.create({contributor:r,amountUnconfirmed:n,amountConfirmed:r.totalKreditsEarned,amountTotal:r.totalKreditsEarned+n})})
return o.forEach(function(e){s.push(Ember.Object.create({contributor:e,amountUnconfirmed:0,amountConfirmed:e.totalKreditsEarned,amountTotal:e.totalKreditsEarned}))}),s}),init:function(){this._super.apply(this,arguments),this.set("contributors",[]),this.set("proposals",[]),this.set("contributions",[])},getEthProvider:function(){var e,n=this
return new Ember.RSVP.Promise(function(){var r=d(regeneratorRuntime.mark(function r(i){var s,u,a
return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:if(a=function(){return(a=d(regeneratorRuntime.mark(function n(r,o){return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:console.debug("[kredits] Using user-provided instance, e.g. from Mist browser or Metamask"),(e=new t.default.providers.Web3Provider(r)).listAccounts().then(function(t){o.set("currentUserAccounts",t)
var n=0===t.length?null:e.getSigner()
i({ethProvider:e,ethSigner:n})})
case 3:case"end":return n.stop()}},n)}))).apply(this,arguments)},u=function(e,t){return a.apply(this,arguments)},s=function(){console.debug("[kredits] Creating new instance from npm module class"),console.debug("[kredits] providerURL: ".concat(o.default.web3ProviderUrl)),e=new t.default.providers.JsonRpcProvider(o.default.web3ProviderUrl),i({ethProvider:e,ethSigner:null})},!window.ethereum){r.next=15
break}return r.prev=4,r.next=7,window.ethereum.enable()
case 7:u(window.ethereum,n),r.next=13
break
case 10:r.prev=10,r.t0=r.catch(4),s()
case 13:r.next=16
break
case 15:window.web3?u(window.web3.currentProvider,n):s()
case 16:case"end":return r.stop()}},r,null,[[4,10]])}))
return function(e){return r.apply(this,arguments)}}())},setup:function(){var e=this
return this.getEthProvider().then(function(t){return new n.default(t.ethProvider,t.ethSigner,{addresses:{Kernel:o.default.kreditsKernelAddress},apm:o.default.kreditsApmDomain,ipfsConfig:o.default.ipfs}).init().then(function(){var t=d(regeneratorRuntime.mark(function t(n){return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return e.set("kredits",n),t.t0=e,t.next=4,n.provider.getBlockNumber()
case 4:return t.t1=t.sent,t.t0.set.call(t.t0,"currentBlock",t.t1),e.currentUserAccounts&&e.currentUserAccounts.length>0&&e.getCurrentUser.then(function(t){e.set("currentUser",t)}),t.abrupt("return",n)
case 8:case"end":return t.stop()}},t)}))
return function(e){return t.apply(this,arguments)}}())})},totalSupply:Ember.computed(function(){return this.kredits.Token.functions.totalSupply().then(function(e){return(0,i.default)(e)})}),totalKreditsEarned:Ember.computed(function(){return this.kredits.Contribution.functions.totalKreditsEarned(!0).then(function(e){return e.toNumber()})}),loadInitialData:function(){var e=this
return this.getContributors().then(function(t){return e.contributors.pushObjects(t)}).then(function(){return e.getContributions()}).then(function(t){return e.contributions.pushObjects(t)})},addContributor:function(e){if(e.github_uid){var t=parseInt(e.github_uid)
e.github_uid=t}return console.debug("[kredits] add contributor",e),this.kredits.Contributor.add(e,{gasLimit:35e4}).then(function(e){console.debug("[kredits] add contributor response",e)})},updateContributor:function(e,t){if(t.github_uid){var n=parseInt(t.github_uid)
t.github_uid=n}return console.debug("[kredits] update contributor",t),this.kredits.Contributor.updateProfile(e,t,{gasLimit:35e4}).then(function(e){console.debug("[kredits] updateProfile response",e)})},getContributors:function(){return this.kredits.Contributor.all().then(function(e){return e.map(function(e){return s.default.create(e)})})},addContribution:function(e){var t=this
return console.debug("[kredits] add contribution",e),this.kredits.Contribution.addContribution(e,{gasLimit:3e5}).then(function(n){console.debug("[kredits] add contribution response",n),e.contributor=t.contributors.findBy("id",e.contributorId)
var r=a.default.create(e)
return r.set("confirmedAtBlock",n.blockNumber+40320),t.contributions.pushObject(r),r})},addProposal:function(e){var t=this
return console.debug("[kredits] add proposal",e),this.kredits.Proposal.addProposal(e).then(function(n){return console.debug("[kredits] add proposal response",n),e.contributor=t.contributors.findBy("id",e.contributorId),u.default.create(e)})},getProposals:function(){var e=this
return this.kredits.Proposal.all().then(function(t){return t.map(function(t){return t.contributor=e.contributors.findBy("id",t.contributorId.toString()),u.default.create(t)})})},getContributions:function(){var e=this
return this.kredits.Contribution.all({page:{size:200}}).then(function(t){return t.map(function(t){return t.contributor=e.contributors.findBy("id",t.contributorId.toString()),a.default.create(t)})})},vote:function(e){return console.debug("[kredits] vote for",e),this.kredits.Proposal.functions.vote(e).then(function(e){return console.debug("[kredits] vote response",e),e})},veto:function(e){return console.debug("[kredits] veto against",e),this.kredits.Contribution.functions.veto(e,{gasLimit:3e5}).then(function(e){return console.debug("[kredits] veto response",e),e})},getCurrentUser:Ember.computed("kredits.provider",function(){var e=this
return Ember.isEmpty(this.currentUserAccounts)?Ember.RSVP.resolve():this.kredits.Contributor.functions.getContributorIdByAddress(this.get("currentUserAccounts.firstObject")).then(function(t){return 0===t?Ember.RSVP.resolve():e.kredits.Contributor.getById(t)})}),findProposalById:function(e){return this.proposals.findBy("id",e.toString())},addContractEventHandlers:function(){this.kredits.Contributor.on("ContributorProfileUpdated",this.handleContributorChange.bind(this)).on("ContributorAccountUpdated",this.handleContributorChange.bind(this)).on("ContributorAdded",this.handleContributorChange.bind(this)),this.kredits.Contribution.on("ContributionVetoed",this.handleContributionVetoed.bind(this)),this.kredits.Proposal.on("ProposalCreated",this.handleProposalCreated.bind(this)).on("ProposalVoted",this.handleProposalVoted.bind(this)).on("ProposalExecuted",this.handleProposalExecuted.bind(this)),this.kredits.Token.on("Transfer",this.handleTransfer.bind(this))},handleContributorChange:function(){var e=d(regeneratorRuntime.mark(function e(t){var n,r,i,o,u,a,l=arguments
return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:for(console.debug("[kredits] Contributor add/update event received for ID",t),n=l.length,r=new Array(n>1?n-1:0),i=1;i<n;i++)r[i-1]=l[i]
return console.debug("[kredits] Event data:",r),e.next=5,this.kredits.Contributor.getById(t)
case 5:o=e.sent,u=s.default.create(o),(a=this.contributors.findBy("id",t.toString()))&&(console.debug("[kredits] old contributor",a),this.contributors.removeObject(a)),console.debug("[kredits] new contributor",u),this.contributors.pushObject(u)
case 11:case"end":return e.stop()}},e,this)}))
return function(t){return e.apply(this,arguments)}}(),handleContributionVetoed:function(e){console.debug("[kredits] ContributionVetoed event received for ",e)
var t=this.contributions.findBy("id",e)
console.debug("[kredits] contribution",t),t&&t.set("vetoed",!0)},handleProposalCreated:function(e){var t=this
this.findProposalById(e)?console.debug("[events] proposal exists, not adding from event"):this.kredits.Proposal.getById(e).then(function(e){e.contributor=t.contributors.findBy("id",e.contributorId.toString()),t.proposals.pushObject(u.default.create(e))})},handleProposalVoted:function(e,t,n){var r=this.findProposalById(e)
r&&r.set("votesCount",n)},handleProposalExecuted:function(e,t,n){var r=this.findProposalById(e)
r.get("isExecuted")?console.debug("[events] proposal already executed, not adding from event"):(r.set("executed",!0),this.contributors.findBy("id",t.toString()).incrementProperty("balance",n))},handleTransfer:function(e,t,n){n=n.toNumber(),this.contributors.findBy("address",e).decrementProperty("balance",n),this.contributors.findBy("address",t).incrementProperty("balance",n)}})
e.default=c}),define("kredits-web/templates/application-loading",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"/K85jZFR",block:'{"symbols":[],"statements":[[1,[21,"loading-spinner"],false],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/templates/application-loading.hbs"}})
e.default=t}),define("kredits-web/templates/application",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"M6vaFJ5T",block:'{"symbols":[],"statements":[[7,"header"],[11,"id","topbar"],[9],[0,"\\n "],[7,"h1"],[9],[0,"Kosmos Kredits"],[10],[0,"\\n\\n "],[7,"section"],[11,"id","user-account"],[9],[0,"\\n"],[4,"if",[[23,["kredits","hasAccounts"]]],null,{"statements":[[4,"if",[[23,["kredits","currentUser"]]],null,{"statements":[[0," "],[1,[23,["kredits","currentUser","name"]],false],[0,"\\n "],[4,"if",[[23,["kredits","currentUserIsCore"]]],null,{"statements":[[0,"(core)"]],"parameters":[]},null],[0,"\\n"]],"parameters":[]},null]],"parameters":[]},{"statements":[[0," Anonymous\\n"]],"parameters":[]}],[0," "],[10],[0,"\\n"],[10],[0,"\\n\\n"],[1,[21,"outlet"],false],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/templates/application.hbs"}})
e.default=t}),define("kredits-web/templates/contributions/new",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"YPbtYII6",block:'{"symbols":[],"statements":[[7,"main"],[11,"class","center-column"],[9],[0,"\\n\\n "],[7,"section"],[11,"id","add-contribution"],[9],[0,"\\n "],[7,"header"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Submit a contribution"],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n "],[1,[27,"add-contribution",null,[["contributors","save"],[[23,["sortedContributors"]],[27,"action",[[22,0,[]],"save"],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n\\n"],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/templates/contributions/new.hbs"}})
e.default=t}),define("kredits-web/templates/contributors/edit",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"LZQyoYt+",block:'{"symbols":[],"statements":[[7,"main"],[11,"class","center-column"],[9],[0,"\\n\\n "],[7,"section"],[11,"id","add-contributor"],[9],[0,"\\n "],[7,"header"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Update contributor profile"],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n "],[1,[27,"add-contributor",null,[["attributes","save"],[[23,["attributes"]],[27,"action",[[22,0,[]],"save"],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n\\n"],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/templates/contributors/edit.hbs"}})
e.default=t}),define("kredits-web/templates/contributors/new",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"IoVi6fNU",block:'{"symbols":[],"statements":[[7,"main"],[11,"class","center-column"],[9],[0,"\\n\\n "],[7,"section"],[11,"id","add-contributor"],[9],[0,"\\n "],[7,"header"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Add contributor profile"],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n "],[1,[27,"add-contributor",null,[["save"],[[27,"action",[[22,0,[]],"save"],null]]]],false],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n\\n"],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/templates/contributors/new.hbs"}})
e.default=t}),define("kredits-web/templates/index",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"GRxyQS7g",block:'{"symbols":[],"statements":[[7,"main"],[11,"id","index"],[9],[0,"\\n\\n "],[7,"div"],[11,"id","stats"],[9],[0,"\\n "],[7,"section"],[11,"id","people"],[9],[0,"\\n "],[7,"header"],[11,"class","with-nav"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Contributors"],[10],[0,"\\n"],[4,"if",[[23,["kredits","hasAccounts"]]],null,{"statements":[[0," "],[7,"nav"],[9],[0,"\\n "],[4,"link-to",["contributors.new"],[["title","class"],["Add contributor profile","button small green"]],{"statements":[[0,"add"]],"parameters":[]},null],[0,"\\n "],[10],[0,"\\n"]],"parameters":[]},null],[0," "],[10],[0,"\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n "],[1,[27,"contributor-list",null,[["contributorList","showUnconfirmedKredits"],[[23,["kreditsToplist"]],[23,["showUnconfirmedKredits"]]]]],false],[0,"\\n\\n "],[7,"p"],[11,"class","stats"],[9],[0,"\\n "],[7,"span"],[11,"class","number"],[9],[1,[27,"await",[[23,["kredits","totalKreditsEarned"]]],null],false],[10],[0," kredits confirmed and issued to\\n "],[7,"span"],[11,"class","number"],[9],[1,[23,["contributorsWithKredits","length"]],false],[10],[0," contributors\\n "],[10],[0,"\\n "],[7,"p"],[11,"class","stats"],[9],[0,"\\n "],[1,[27,"input",null,[["type","id","checked"],["checkbox","hide-unnconfirmed-kredits",[23,["showUnconfirmedKredits"]]]]],false],[0,"\\n "],[7,"label"],[11,"for","hide-unnconfirmed-kredits"],[9],[0,"Show unconfirmed kredits in toplist"],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"section"],[11,"id","contributions-by-type"],[9],[0,"\\n "],[7,"header"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Contributions by type"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n "],[1,[27,"chart-contributions-by-type",null,[["contributions"],[[23,["contributions"]]]]],false],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"div"],[11,"id","contributions"],[9],[0,"\\n"],[4,"if",[[23,["contributionsUnconfirmed"]]],null,{"statements":[[0," "],[7,"section"],[11,"id","contributions-unconfirmed"],[9],[0,"\\n "],[7,"header"],[11,"class","with-nav"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Latest Contributions"],[10],[0,"\\n"],[4,"if",[[23,["kredits","hasAccounts"]]],null,{"statements":[[0," "],[7,"nav"],[9],[0,"\\n "],[7,"button"],[12,"class",[28,["small ",[27,"if",[[23,["showQuickFilterUnconfirmed"]],"active"],null]]]],[9],[0,"filter"],[3,"action",[[22,0,[]],"toggleQuickFilterUnconfirmed"]],[10],[0,"\\n "],[4,"link-to",["contributions.new"],[["title","class"],["Submit a contribution","button small green"]],{"statements":[[0,"add"]],"parameters":[]},null],[0,"\\n "],[10],[0,"\\n"]],"parameters":[]},null],[0," "],[10],[0,"\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n"],[0," "],[1,[27,"contribution-list",null,[["contributions","vetoContribution","contractInteractionEnabled","showQuickFilter"],[[23,["contributionsUnconfirmedSorted"]],[27,"action",[[22,0,[]],"vetoContribution"],null],[23,["kredits","hasAccounts"]],[23,["showQuickFilterUnconfirmed"]]]]],false],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n"]],"parameters":[]},null],[0,"\\n "],[7,"section"],[11,"id","contributions-confirmed"],[9],[0,"\\n "],[7,"header"],[11,"class","with-nav"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Confirmed Contributions"],[10],[0,"\\n "],[7,"nav"],[9],[0,"\\n "],[7,"button"],[12,"class",[28,["small ",[27,"if",[[23,["showQuickFilterConfirmed"]],"active"],null]]]],[9],[0,"filter"],[3,"action",[[22,0,[]],"toggleQuickFilterConfirmed"]],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n "],[1,[27,"contribution-list",null,[["contributions","vetoContribution","showQuickFilter"],[[23,["contributionsConfirmedSorted"]],[27,"action",[[22,0,[]],"vetoContribution"],null],[23,["showQuickFilterConfirmed"]]]]],false],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n\\n"],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/templates/index.hbs"}})
e.default=t}),define("kredits-web/templates/proposals/new",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.HTMLBars.template({id:"vauAEy3a",block:'{"symbols":[],"statements":[[7,"section"],[11,"id","add-proposal"],[9],[0,"\\n "],[7,"header"],[9],[0,"\\n "],[7,"h2"],[9],[0,"Add Proposal"],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"div"],[11,"class","content"],[9],[0,"\\n "],[1,[27,"add-proposal",null,[["contributors","save"],[[23,["minedContributors"]],[27,"action",[[22,0,[]],"save"],null]]]],false],[0,"\\n "],[10],[0,"\\n"],[10],[0,"\\n\\n"]],"hasEval":false}',meta:{moduleName:"kredits-web/templates/proposals/new.hbs"}})
e.default=t}),define("kredits-web/utils/cps/bignumber",["exports","npm:ethers"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(e,n){return Ember.computed(e,{get:function(){var r=this.get(e)
return r&&t.default.utils.BigNumber.isBigNumber(r)?r[n]():r},set:function(r,i){var o=t.default.utils.bigNumberify(i)
return this.set(e,o),o[n]()}})}}),define("kredits-web/utils/cps/kredits",["exports","npm:ethers","kredits-web/utils/format-kredits"],function(e,t,n){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}
return Ember.computed(e,{get:function(){var t=this.get(e)
return(0,n.default)(t,r)},set:function(i,o){var s=t.default.utils.bigNumberify(o)
return this.set(e,s),(0,n.default)(s,r)}})}}),define("kredits-web/utils/format-kredits",["exports","npm:ethers"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.default.utils.formatEther(e)
n.decimals||(r=parseInt(r).toString())
return r}}),define("kredits-web/utils/group-by",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(e,t){var n=Ember.A(),r=e
r&&r.forEach(function(e){var r=Ember.get(e,t),i=n.findBy("value",r)
Ember.isPresent(i)?Ember.get(i,"items").push(e):(i={property:t,value:r,items:[e]},n.push(i))})
return n}}),define("kredits-web/config/environment",[],function(){try{var e="kredits-web/config/environment",t=document.querySelector('meta[name="'+e+'"]').getAttribute("content"),n={default:JSON.parse(decodeURIComponent(t))}
return Object.defineProperty(n,"__esModule",{value:!0}),n}catch(r){throw new Error('Could not read config from meta tag with name "'+e+'".')}}),runningTests||require("kredits-web/app").default.create({name:"kredits-web",version:"1.4.0+b2bbbfd0"})
//# sourceMappingURL=kredits-web-45753e2af7c6c6726dbf6abe755a6e40.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 KiB

-9
View File
@@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1" x="0px" y="0px" viewBox="0 0 100 100"
enable-background="new 0 0 100 100" xml:space="preserve">
<path id="path-comet"
d="M79.062,24.173L79.062,24.173l-0.021-0.011c-0.068-0.03-0.137-0.062-0.204-0.093L42.392,7.337
c0,0,3.065,14.104,4.458,18.019l0,0c0,0-41.754-12.481-46.85-13.67c1.67,6.52,30.607,62.492,30.607,62.492
c5.848,11.873,19.394,18.485,33.522,18.485c19.811,0,35.87-16.059,35.87-35.869C100,42.313,91.418,29.837,79.062,24.173z">
</path>
</svg>

Before

Width:  |  Height:  |  Size: 590 B

-32
View File
@@ -1,32 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Kosmos Kredits</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="kredits-web/config/environment" content="%7B%22modulePrefix%22%3A%22kredits-web%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%7D%2C%22APP%22%3A%7B%22name%22%3A%22kredits-web%22%2C%22version%22%3A%221.4.0%2Bb2bbbfd0%22%7D%2C%22browserify%22%3A%7B%22tests%22%3Atrue%2C%22transform%22%3A%5B%5B%22babelify%22%2C%7B%22presets%22%3A%5B%22es2015%22%5D%2C%22global%22%3Atrue%7D%5D%5D%7D%2C%22web3ProviderUrl%22%3A%22https%3A%2F%2Frinkeby.infura.io%2Fv3%2Fd4f788b7a6584f7db2fc3c268d4d09e9%22%2C%22ipfs%22%3A%7B%22host%22%3A%22ipfs.kosmos.org%22%2C%22port%22%3A%225444%22%2C%22protocol%22%3A%22https%22%2C%22gatewayUrl%22%3A%22https%3A%2F%2Fipfs.kosmos.org%2Fipfs%22%7D%2C%22kreditsApmDomain%22%3A%22open.aragonpm.eth%22%2C%22exportApplicationGlobal%22%3Afalse%7D" />
<link integrity="" rel="stylesheet" href="/assets/vendor-179cf27a97ea5bbb0ce72403a401cc8c.css">
<link integrity="" rel="stylesheet" href="/assets/kredits-web-9b9ce4d483d65662223fce4307e95ab0.css">
<script>
window.addEventListener('load', function() {
window.windowLoadComplete = true;
});
</script>
</head>
<body>
<script src="/assets/vendor-80a68be62538fc452f619ea272efa683.js" integrity="sha256-1SzKk/ufxNyb/CdyXniFRUByZMpFGEz+hUTtfJVw7mw= sha512-ixkdWl+f7KrwA+YRsH92A8FSnK9ZG31nJa74FIskk2y40KHDpm6zOln2VVeApLpKdkj+FF7huYSGqJZwE7PRvA==" ></script>
<script src="/assets/kredits-web-e442d85b0b19133886e30b7076686a50.js" integrity="sha256-fXOAK653ST4p0jmRe3R2rTZHFeaMR9Y9GhzLO/y0b8Y= sha512-MLBp8nse/pEj0e3+F5xji46rftV+QXV9+xNgPqtSwvBvtPaEhg2sJe24oxLNnq3iL7IUhGze6yp2MIblEF3IYw==" ></script>
</body>
</html>
-3
View File
@@ -1,3 +0,0 @@
# http://www.robotstxt.org
User-agent: *
Disallow:
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -xe
# Get revision from local master
rev=`git rev-parse HEAD`
short_rev=${rev:0:7}
# Check out build branch
git checkout build-production
# Copy from build dir to root
cp -r dist/* .
# Add all files
git add --all
# Commit build files
git commit -m "Build production from $short_rev"
# Push to remote
git push 5apps build-production:master
# Push build branch to collab repo
git push origin build-production:build-production
# Go back to previous branch
git checkout -
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
PACKAGE_VERSION=$(cat package.json \
| grep version \
| head -1 \
| awk -F: '{ print $2 }' \
| sed 's/[",]//g' \
| tr -d '[[:space:]]')
echo $PACKAGE_VERSION > dist/VERSION
+10 -22
View File
@@ -1,25 +1,13 @@
/*jshint node:true*/
module.exports = { module.exports = {
test_page: 'tests/index.html?hidepassed', "framework": "qunit",
disable_watching: true, "test_page": "tests/index.html?hidepassed",
launch_in_ci: [ "disable_watching": true,
'Chrome' "launch_in_ci": [
"PhantomJS"
], ],
launch_in_dev: [ "launch_in_dev": [
'Chrome' "PhantomJS",
], "Chrome"
browser_args: { ]
Chrome: {
ci: [
// --no-sandbox is needed when running Chrome inside a container
process.env.CI ? '--no-sandbox' : null,
'--headless',
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-software-rasterizer',
'--mute-audio',
'--remote-debugging-port=0',
'--window-size=1440,900'
].filter(Boolean)
}
}
}; };

Some files were not shown because too many files have changed in this diff Show More