Merge pull request #199 from 67P/chore/rename_recipient_for_reimbursements
Rename contributorId to recipientId
This commit was merged in pull request #199.
This commit is contained in:
@@ -5,23 +5,24 @@
|
|||||||
This README outlines the details of collaborating on this Ember application.
|
This README outlines the details of collaborating on this Ember application.
|
||||||
A short introduction of this app could easily go here.
|
A short introduction of this app could easily go here.
|
||||||
|
|
||||||
## Prerequisites
|
## Development
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
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)
|
||||||
* [Ember CLI](https://ember-cli.com/)
|
* [Ember CLI](https://ember-cli.com/)
|
||||||
* [Google Chrome](https://google.com/chrome/)
|
* [Google Chrome](https://google.com/chrome/) (only for running tests)
|
||||||
|
|
||||||
## Installation
|
### Installation
|
||||||
|
|
||||||
* `git clone git@github.com:67P/kredits-web.git` this repository
|
* `git clone git@github.com:67P/kredits-web.git` this repository
|
||||||
* `cd kredits-web`
|
* `cd kredits-web`
|
||||||
* `npm install`
|
* `npm install`
|
||||||
|
|
||||||
## Running / Development
|
### Building/running for development
|
||||||
|
|
||||||
|
|
||||||
* `ember serve` - by default kredits-web connects to the Ethreum Kovan network
|
* `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).
|
||||||
@@ -29,11 +30,11 @@ You will need the following things properly installed on your computer.
|
|||||||
|
|
||||||
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.
|
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
|
||||||
|
|
||||||
Make use of the many generators for code, try `ember help generate` for more details
|
Make use of the many generators for code, try `ember help generate` for more details
|
||||||
|
|
||||||
### Running Tests
|
### Running tests
|
||||||
|
|
||||||
* `ember test`
|
* `ember test`
|
||||||
* `ember test --server`
|
* `ember test --server`
|
||||||
@@ -55,17 +56,15 @@ _(You need collaborator permissions on the 5apps Deploy project.)_
|
|||||||
|
|
||||||
`npm run deploy`
|
`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
|
The smart contracts and their JavaScript wrapper library are developed in the
|
||||||
[kredits-contracts](https://github.com/67P/kredits-contracts) repo/package.
|
[kredits-contracts](https://github.com/67P/kredits-contracts) repo/package.
|
||||||
|
|
||||||
You can run `kredits-web` on your machine, against a local, simulated Ethereum
|
You can run `kredits-web` on your machine, against a local, simulated
|
||||||
network, provided e.g. by [ganache](http://truffleframework.com/ganache/).
|
blockchain. [kredits-contracts](https://github.com/67P/kredits-contracts)
|
||||||
|
contains all the tools to start and set up such a simulated network, as well as
|
||||||
[kredits-contracts](https://github.com/67P/kredits-contracts) holds all the tools
|
to deploy the Kredits smart contracts to it.
|
||||||
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:
|
These are the basic steps to get up and running:
|
||||||
|
|
||||||
@@ -78,31 +77,33 @@ Run a local IPFS deamon.
|
|||||||
|
|
||||||
#### 2. kredits-contracts
|
#### 2. kredits-contracts
|
||||||
|
|
||||||
Get your local Ethereum development node running. (See [kredits-contracts README](https://github.com/67P/kredits-contracts)
|
Run a local devchain with test data. (See [kredits-contracts
|
||||||
for details.
|
README](https://github.com/67P/kredits-contracts) for details.
|
||||||
|
|
||||||
* Clone [kredits-contracts](https://github.com/67P/kredits-contracts)
|
* Clone [kredits-contracts](https://github.com/67P/kredits-contracts)
|
||||||
* `npm install`
|
* `npm install`
|
||||||
* `npm run devchain` - runs a local development chain
|
* `npm run devchain` - runs a local development chain
|
||||||
* `npm run bootstrap` - bootstrap runs deploys all the contracts
|
* `npm run bootstrap` - deploys all contracts and seeds test data
|
||||||
* `npm link` - make the `kredits-contracts` module linkable as `kredits-contracts` on your machine (currently broken :( )
|
* `npm link` - makes the `kredits-contracts` module linkable as `kredits-contracts` on your machine
|
||||||
|
|
||||||
#### 3. kredits-web
|
#### 3. kredits-web
|
||||||
|
|
||||||
With IPFS and Ethereum/ganache running, you can now start this Ember app.
|
With IPFS and the local devchain running, you can now link the contracts and
|
||||||
|
start the Ember app:
|
||||||
|
|
||||||
* `npm link kredits-contracts` - link the local `kredits-contracts` package
|
* `npm link kredits-contracts` - links the local `kredits-contracts` package (has to be done again after every `npm install`)
|
||||||
* `npm run start:local` - WEB3_PROVIDER_URL=http://localhost:8545 must be set for local settings
|
* `npm run start:local` - runs the Ember app with WEB3_PROVIDER_URL=http://localhost:8545 set
|
||||||
|
|
||||||
#### 4. Metamask network
|
#### 4. Metamask network
|
||||||
|
|
||||||
Switch the network in Metamask to "Custom RPC" with the RPC URL `http://localhost:8545`.
|
If you want to interact with the local contracts via a Web3 wallet, switch the
|
||||||
|
network to a "Custom RPC" one, with the RPC URL `http://localhost:8545`.
|
||||||
|
|
||||||
#### IPFS
|
#### IPFS
|
||||||
|
|
||||||
Install IPFS with your favorite package manager and run
|
If you haven't configured your IPFS node for CORS yet, you can do so by running
|
||||||
|
the following commands:
|
||||||
|
|
||||||
ipfs init (on initial installation)
|
|
||||||
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["localhost:4200"]'
|
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-Methods '["PUT", "GET", "POST"]'
|
||||||
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'
|
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'
|
||||||
|
|||||||
@@ -9,7 +9,15 @@ export default Component.extend({
|
|||||||
kredits: service(),
|
kredits: service(),
|
||||||
|
|
||||||
attributes: null,
|
attributes: null,
|
||||||
contributors: alias('kredits.contributorsSorted'),
|
|
||||||
|
contributors: computed('kredits.contributorsSorted.[]', function() {
|
||||||
|
return this.kredits.contributorsSorted.map(c => {
|
||||||
|
return {
|
||||||
|
id: c.id.toString(),
|
||||||
|
name: c.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
isValidContributor: notEmpty('contributorId'),
|
isValidContributor: notEmpty('contributorId'),
|
||||||
isValidKind: notEmpty('kind'),
|
isValidKind: notEmpty('kind'),
|
||||||
@@ -57,6 +65,8 @@ export default Component.extend({
|
|||||||
|
|
||||||
const attributes = this.getProperties(Object.keys(this.attributes));
|
const attributes = this.getProperties(Object.keys(this.attributes));
|
||||||
|
|
||||||
|
attributes.contributorId = parseInt(this.contributorId);
|
||||||
|
|
||||||
let dateInput = (attributes.date instanceof Array) ?
|
let dateInput = (attributes.date instanceof Array) ?
|
||||||
attributes.date[0] : attributes.date;
|
attributes.date[0] : attributes.date;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<select required onchange={{action (mut this.contributorId) value="target.value"}}>
|
<select required onchange={{action (mut this.contributorId) value="target.value"}}>
|
||||||
<option value="" selected disabled hidden></option>
|
<option value="" selected disabled hidden></option>
|
||||||
{{#each @contributors as |contributor|}}
|
{{#each this.contributors as |contributor|}}
|
||||||
<option value={{contributor.id}} selected={{eq this.contributorId contributor.id}}>{{contributor.name}}</option>
|
<option value={{contributor.id}} selected={{eq this.contributorId contributor.id}}>{{contributor.name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default class AddReimbursementComponent extends Component {
|
|||||||
|
|
||||||
@alias('kredits.contributorsSorted') contributors;
|
@alias('kredits.contributorsSorted') contributors;
|
||||||
|
|
||||||
@tracked contributorId = null;
|
@tracked recipientId = null;
|
||||||
@tracked title = '';
|
@tracked title = '';
|
||||||
@tracked total = '0';
|
@tracked total = '0';
|
||||||
@tracked expenses = A([]);
|
@tracked expenses = A([]);
|
||||||
@@ -86,7 +86,7 @@ export default class AddReimbursementComponent extends Component {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
updateContributor(event) {
|
updateContributor(event) {
|
||||||
this.contributorId = event.target.value;
|
this.recipientId = event.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@@ -115,15 +115,15 @@ export default class AddReimbursementComponent extends Component {
|
|||||||
@action
|
@action
|
||||||
submit (e) {
|
submit (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.kredits.currentUser) { window.alert('You need to connect your Ethereum account first.'); return false }
|
if (!this.kredits.currentUser) { window.alert('You need to connect your RSK account first.'); return false }
|
||||||
if (!this.kredits.currentUserIsCore) { window.alert('Only core contributors can submit reimbursements.'); return false }
|
if (!this.kredits.currentUserIsCore) { window.alert('Only core contributors can submit reimbursements.'); return false }
|
||||||
|
|
||||||
const contributor = this.contributors.findBy('id', this.contributorId);
|
const contributor = this.contributors.findBy('id', parseInt(this.recipientId));
|
||||||
|
|
||||||
const attributes = {
|
const attributes = {
|
||||||
amount: parseInt(parseFloat(this.total) * 100000000), // convert to sats
|
amount: parseInt(parseFloat(this.total) * 100000000), // convert to sats
|
||||||
token: config.tokens['WBTC'],
|
token: config.tokens['BTC'],
|
||||||
contributorId: parseInt(this.contributorId),
|
recipientId: parseInt(this.recipientId),
|
||||||
title: `Expenses covered by ${contributor.name}`,
|
title: `Expenses covered by ${contributor.name}`,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
url: this.url,
|
url: this.url,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<fieldset class="horizontal thirds total-amounts">
|
<fieldset class="horizontal thirds total-amounts">
|
||||||
<label>
|
<label>
|
||||||
<p class="label">Total amount (WBTC):</p>
|
<p class="label">Total amount (BTC):</p>
|
||||||
<p>
|
<p>
|
||||||
<Input @type="text"
|
<Input @type="text"
|
||||||
@placeholder="0.0015"
|
@placeholder="0.0015"
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
{{#each this.balances as |balance|}}
|
{{#each this.balances as |balance|}}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{balance.token.symbol}}</th>
|
<th>{{balance.token.symbol}}</th>
|
||||||
<td class="amount">{{fmt-crypto-currency balance.balance balance.token.symbol}}</td>
|
<td class="amount">{{balance.confirmed_balance}}</td>
|
||||||
<td class="fiat-amount">~{{balance.balanceUsd}} USD</td>
|
<td class="fiat-amount">~{{balance.balanceUSD}} USD</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -24,10 +24,9 @@ export default Component.extend({
|
|||||||
contributors: sort('kredits.contributors', 'contributorsSorting'),
|
contributors: sort('kredits.contributors', 'contributorsSorting'),
|
||||||
|
|
||||||
contributorsActive: computed('contributors.[]', 'contributions', function() {
|
contributorsActive: computed('contributors.[]', 'contributions', function() {
|
||||||
const activeIds = new Set(this.contributions.mapBy('contributorId')
|
const activeIds = new Set(this.contributions.mapBy('contributorId'));
|
||||||
.map(id => id.toString()));
|
|
||||||
|
|
||||||
return this.contributors.filter(c => activeIds.has(c.id.toString()));
|
return this.contributors.filter(c => activeIds.has(c.id));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
contributionKinds: computed('contributions.[]', function() {
|
contributionKinds: computed('contributions.[]', function() {
|
||||||
@@ -42,7 +41,7 @@ export default Component.extend({
|
|||||||
c.amount <= 500) { included = false; }
|
c.amount <= 500) { included = false; }
|
||||||
|
|
||||||
if (isPresent(this.contributorId) &&
|
if (isPresent(this.contributorId) &&
|
||||||
c.contributorId.toString() !== this.contributorId.toString()) { included = false; }
|
c.contributorId !== parseInt(this.contributorId)) { included = false; }
|
||||||
|
|
||||||
if (isPresent(this.contributionKind) &&
|
if (isPresent(this.contributionKind) &&
|
||||||
c.kind !== this.contributionKind) { included = false; }
|
c.kind !== this.contributionKind) { included = false; }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="token-amount">
|
<p class="token-amount">
|
||||||
<span class="amount">
|
<span class="amount">
|
||||||
{{sats-to-btc reimbursement.amount}}</span> <span class="symbol">WBTC</span>
|
{{sats-to-btc reimbursement.amount}}</span> <span class="symbol">BTC</span>
|
||||||
</p>
|
</p>
|
||||||
<ul class="expense-list">
|
<ul class="expense-list">
|
||||||
{{#each reimbursement.expenses as |expense|}}
|
{{#each reimbursement.expenses as |expense|}}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ export default helper(function fmtCryptoCurrency(params/*, hash*/) {
|
|||||||
const code = params[1];
|
const code = params[1];
|
||||||
|
|
||||||
switch(code) {
|
switch(code) {
|
||||||
case 'ETH':
|
case 'RBTC':
|
||||||
fmtAmount = amount / 1000000000000000000;
|
fmtAmount = amount / 1000000000000000000;
|
||||||
break;
|
break;
|
||||||
case 'WBTC':
|
case 'BTC':
|
||||||
fmtAmount = amount / 100000000;
|
fmtAmount = amount / 100000000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { isEmpty, isPresent } from '@ember/utils';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
export default EmberObject.extend({
|
export default EmberObject.extend({
|
||||||
|
|
||||||
// Contract
|
// Contract
|
||||||
id: null,
|
id: null,
|
||||||
contributorId: null,
|
contributorId: null,
|
||||||
@@ -48,5 +47,4 @@ export default EmberObject.extend({
|
|||||||
serialize () {
|
serialize () {
|
||||||
return JSON.stringify(this);
|
return JSON.stringify(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,5 +22,4 @@ export default EmberObject.extend({
|
|||||||
serialize () {
|
serialize () {
|
||||||
return JSON.stringify(this);
|
return JSON.stringify(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,19 +6,15 @@ export default Route.extend({
|
|||||||
kredits: service(),
|
kredits: service(),
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
const contribution = this.kredits.contributions.findBy('id', parseInt(params.id));
|
return this.kredits.contributions.findBy('id', parseInt(params.id));
|
||||||
contribution.contributorId = contribution.contributorId.toString();
|
|
||||||
|
|
||||||
return contribution;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController (controller, model) {
|
setupController (controller, model) {
|
||||||
this._super(controller, model);
|
this._super(controller, model);
|
||||||
|
|
||||||
controller.set('attributes', model.getProperties([
|
controller.set('attributes', model.getProperties([
|
||||||
'kind', 'amount', 'description', 'url', 'details'
|
'contributorId', 'kind', 'amount', 'description', 'url', 'details'
|
||||||
]));
|
]));
|
||||||
controller.set('attributes.contributorId', model.contributorId.toString());
|
|
||||||
controller.set('attributes.date', model.jsDate);
|
controller.set('attributes.date', model.jsDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,38 @@
|
|||||||
import Service from '@ember/service';
|
import Service, { inject as service } from '@ember/service';
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { A } from '@ember/array';
|
import { A } from '@ember/array';
|
||||||
import { task } from 'ember-concurrency-decorators';
|
import { task } from 'ember-concurrency-decorators';
|
||||||
import config from 'kredits-web/config/environment';
|
import config from 'kredits-web/config/environment';
|
||||||
|
|
||||||
const txServiceBaseUrl = `${config.gnosisSafe.txServiceHost}/api/v1/safes/${config.gnosisSafe.address}`;
|
|
||||||
|
|
||||||
export default class CommunityFundsService extends Service {
|
export default class CommunityFundsService extends Service {
|
||||||
|
@service exchangeRates;
|
||||||
|
|
||||||
@tracked balancesLoaded = false;
|
@tracked balancesLoaded = false;
|
||||||
@tracked balances = A([]);
|
@tracked balances = A([]);
|
||||||
|
|
||||||
@task
|
@task
|
||||||
*fetchBalances () {
|
*fetchBalances () {
|
||||||
const uri = `${txServiceBaseUrl}/balances/usd/`;
|
yield fetch(config.btcBalanceAPI).then(res => res.json())
|
||||||
|
.then(res => {
|
||||||
yield fetch(uri).then(res => res.json())
|
return this.processBalances(res);
|
||||||
.then(res => this.processBalances(res))
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(`[community-funds] Fetching balances failed:`);
|
console.log(`[community-funds] Fetching balances failed:`);
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
processBalances (res) {
|
async processBalances (res) {
|
||||||
for (const balance of res) {
|
await this.exchangeRates.fetchRates();
|
||||||
// Format and round the approximate USD value
|
// Format and round the approximate USD value
|
||||||
const lang = navigator.language || navigator.userLanguage;
|
const lang = navigator.language || navigator.userLanguage;
|
||||||
balance.balanceUsd = Math.round(balance.balanceUsd).toLocaleString(lang);
|
const balanceUSD = res.confirmed_balance * this.exchangeRates.btcusd;
|
||||||
|
res.balanceUSD = Math.round(balanceUSD).toLocaleString(lang);
|
||||||
|
|
||||||
if (balance.token) {
|
this.balances.pushObject({
|
||||||
// ERC20 token, has all meta data
|
...res,
|
||||||
this.balances.pushObject(balance);
|
...{ token: { name: 'BTC', symbol: 'BTC'} }
|
||||||
} else {
|
});
|
||||||
// ETH, missing meta data
|
|
||||||
this.balances.pushObject({
|
|
||||||
...balance,
|
|
||||||
...{ token: { name: 'Ether', symbol: 'ETH'} }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.balancesLoaded = true;
|
this.balancesLoaded = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const bitstampBaseUrl = `${config.corsProxy}https://www.bitstamp.net/api/v2`;
|
|||||||
async function fetchFromBitstamp(currencyPair) {
|
async function fetchFromBitstamp(currencyPair) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${bitstampBaseUrl}/ticker/${currencyPair}/`).then(r => r.json());
|
const res = await fetch(`${bitstampBaseUrl}/ticker/${currencyPair}/`).then(r => r.json());
|
||||||
return res.vwap; // Last 24 hours volume weighted average price
|
return parseFloat(res.vwap); // Last 24 hours volume weighted average price
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error('Could not fetch exchange rate from Bitstamp:', e);
|
console.error('Could not fetch exchange rate from Bitstamp:', e);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+15
-13
@@ -160,12 +160,12 @@ export default Service.extend({
|
|||||||
kreditsByContributor: computed('contributionsUnconfirmed.@each.vetoed', 'contributors.[]', function() {
|
kreditsByContributor: computed('contributionsUnconfirmed.@each.vetoed', 'contributors.[]', function() {
|
||||||
const contributionsUnconfirmed = this.contributionsUnconfirmed.filterBy('vetoed', false);
|
const contributionsUnconfirmed = this.contributionsUnconfirmed.filterBy('vetoed', false);
|
||||||
const contributionsGrouped = groupBy(contributionsUnconfirmed, 'contributorId');
|
const contributionsGrouped = groupBy(contributionsUnconfirmed, 'contributorId');
|
||||||
const contributorsWithUnconfirmed = contributionsGrouped.map(c => c.value.toString());
|
const contributorsWithUnconfirmed = contributionsGrouped.map(c => c.value);
|
||||||
const contributorsWithOnlyConfirmed = this.contributors.reject(c => contributorsWithUnconfirmed.includes(c.id))
|
const contributorsWithOnlyConfirmed = this.contributors.reject(c => contributorsWithUnconfirmed.includes(c.id))
|
||||||
|
|
||||||
const kreditsByContributor = contributionsGrouped.map(c => {
|
const kreditsByContributor = contributionsGrouped.map(c => {
|
||||||
const amountUnconfirmed = c.items.mapBy('amount').reduce((a, b) => a + b);
|
const amountUnconfirmed = c.items.mapBy('amount').reduce((a, b) => a + b);
|
||||||
const contributor = this.contributors.findBy('id', c.value.toString());
|
const contributor = this.contributors.findBy('id', c.value);
|
||||||
|
|
||||||
return EmberObject.create({
|
return EmberObject.create({
|
||||||
contributor: contributor,
|
contributor: contributor,
|
||||||
@@ -275,7 +275,7 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
loadContributorFromData(data) {
|
loadContributorFromData (data) {
|
||||||
const contributor = Contributor.create(processContributorData(data));
|
const contributor = Contributor.create(processContributorData(data));
|
||||||
const loadedContributor = this.contributors.findBy('id', contributor.id);
|
const loadedContributor = this.contributors.findBy('id', contributor.id);
|
||||||
if (loadedContributor) { this.contributors.removeObject(loadedContributor); }
|
if (loadedContributor) { this.contributors.removeObject(loadedContributor); }
|
||||||
@@ -285,7 +285,7 @@ export default Service.extend({
|
|||||||
|
|
||||||
async cacheLoadedContributors () {
|
async cacheLoadedContributors () {
|
||||||
for (const c of this.contributors) {
|
for (const c of this.contributors) {
|
||||||
await this.browserCache.contributors.setItem(c.id, c.serialize());
|
await this.browserCache.contributors.setItem(c.id.toString(), c.serialize());
|
||||||
}
|
}
|
||||||
console.debug(`[kredits] Cached ${this.contributors.length} contributors in browser storage`);
|
console.debug(`[kredits] Cached ${this.contributors.length} contributors in browser storage`);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -340,7 +340,7 @@ export default Service.extend({
|
|||||||
|
|
||||||
loadContributionFromData(data) {
|
loadContributionFromData(data) {
|
||||||
const contribution = Contribution.create(processContributionData(data));
|
const contribution = Contribution.create(processContributionData(data));
|
||||||
contribution.set('contributor', this.contributors.findBy('id', data.contributorId.toString()));
|
contribution.set('contributor', this.contributors.findBy('id', data.contributorId));
|
||||||
const loadedContribution = this.contributions.findBy('id', contribution.id);
|
const loadedContribution = this.contributions.findBy('id', contribution.id);
|
||||||
if (loadedContribution) { this.contributions.removeObject(loadedContribution); }
|
if (loadedContribution) { this.contributions.removeObject(loadedContribution); }
|
||||||
this.contributions.pushObject(contribution);
|
this.contributions.pushObject(contribution);
|
||||||
@@ -349,7 +349,7 @@ export default Service.extend({
|
|||||||
|
|
||||||
async cacheLoadedContributions () {
|
async cacheLoadedContributions () {
|
||||||
for (const c of this.contributions) {
|
for (const c of this.contributions) {
|
||||||
await this.browserCache.contributions.setItem(c.id, c.serialize());
|
await this.browserCache.contributions.setItem(c.id.toString(), c.serialize());
|
||||||
}
|
}
|
||||||
console.debug(`[kredits] Cached ${this.contributions.length} contributions in browser storage`);
|
console.debug(`[kredits] Cached ${this.contributions.length} contributions in browser storage`);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -575,7 +575,7 @@ export default Service.extend({
|
|||||||
|
|
||||||
loadReimbursementFromData(data) {
|
loadReimbursementFromData(data) {
|
||||||
const obj = Reimbursement.create(processReimbursementData(data));
|
const obj = Reimbursement.create(processReimbursementData(data));
|
||||||
obj.set('contributor', this.contributors.findBy('id', data.contributorId.toString()));
|
obj.set('contributor', this.contributors.findBy('id', data.recipientId));
|
||||||
this.removeObjectFromCollectionIfLoaded('reimbursements', obj.id);
|
this.removeObjectFromCollectionIfLoaded('reimbursements', obj.id);
|
||||||
this.reimbursements.pushObject(obj);
|
this.reimbursements.pushObject(obj);
|
||||||
return obj;
|
return obj;
|
||||||
@@ -589,7 +589,7 @@ export default Service.extend({
|
|||||||
console.debug('[kredits] add reimbursement response', data);
|
console.debug('[kredits] add reimbursement response', data);
|
||||||
const reimbursement = Reimbursement.create(attributes);
|
const reimbursement = Reimbursement.create(attributes);
|
||||||
reimbursement.setProperties({
|
reimbursement.setProperties({
|
||||||
contributor: this.contributors.findBy('id', attributes.contributorId.toString()),
|
contributor: this.contributors.findBy('id', attributes.recipientId),
|
||||||
pendingTx: data,
|
pendingTx: data,
|
||||||
confirmedAt: this.currentBlock + 40320
|
confirmedAt: this.currentBlock + 40320
|
||||||
});
|
});
|
||||||
@@ -645,13 +645,15 @@ export default Service.extend({
|
|||||||
const contributorData = await this.kredits.Contributor.getById(contributorId);
|
const contributorData = await this.kredits.Contributor.getById(contributorId);
|
||||||
const newContributor = Contributor.create(contributorData);
|
const newContributor = Contributor.create(contributorData);
|
||||||
|
|
||||||
const oldContributor = this.contributors.findBy('id', contributorId.toString());
|
// TODO check for actual differences in the contributor data first
|
||||||
|
|
||||||
|
const oldContributor = this.contributors.findBy('id', contributorId);
|
||||||
if (oldContributor) {
|
if (oldContributor) {
|
||||||
console.debug('[kredits] old contributor', oldContributor);
|
// console.debug('[kredits] cached contributor', oldContributor);
|
||||||
this.contributors.removeObject(oldContributor);
|
this.contributors.removeObject(oldContributor);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug('[kredits] new contributor', newContributor);
|
// console.debug('[kredits] incoming contributor data', newContributor);
|
||||||
this.contributors.pushObject(newContributor);
|
this.contributors.pushObject(newContributor);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -660,13 +662,13 @@ export default Service.extend({
|
|||||||
|
|
||||||
const pendingContribution = this.contributions.find(c => {
|
const pendingContribution = this.contributions.find(c => {
|
||||||
return (c.id === null) &&
|
return (c.id === null) &&
|
||||||
(c.contributorId.toString() === contributorId.toString()) &&
|
(c.contributorId === contributorId) &&
|
||||||
(c.amount.toString() === amount.toString());
|
(c.amount.toString() === amount.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (pendingContribution) {
|
if (pendingContribution) {
|
||||||
const attributes = await this.kredits.Contribution.getById(id);
|
const attributes = await this.kredits.Contribution.getById(id);
|
||||||
attributes.contributor = this.contributors.findBy('id', attributes.contributorId.toString());
|
attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
|
||||||
const newContribution = Contribution.create(attributes);
|
const newContribution = Contribution.create(attributes);
|
||||||
this.contributions.addObject(newContribution);
|
this.contributions.addObject(newContribution);
|
||||||
this.contributions.removeObject(pendingContribution);
|
this.contributions.removeObject(pendingContribution);
|
||||||
|
|||||||
@@ -21,15 +21,16 @@ section#funds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
|
font-size: 1.5rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding-right: 1.2rem;
|
padding-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
&.amount {
|
&.amount {
|
||||||
font-size: 1.5rem;
|
font-size: 2rem;
|
||||||
padding-right: 1.2rem;
|
padding-right: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ export default function processContributionData(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const otherProperties = [
|
const otherProperties = [
|
||||||
'id', 'contributorId', 'amount', 'vetoed',
|
'id', 'contributorId', 'amount', 'vetoed', 'ipfsHash', 'kind',
|
||||||
'ipfsHash', 'kind', 'description', 'details',
|
'description', 'details', 'url', 'date', 'time', 'pendingTx'
|
||||||
'url', 'date', 'time', 'pendingTx'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
otherProperties.forEach(prop => {
|
otherProperties.forEach(prop => {
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
export default function processContributorData(data) {
|
export default function processContributorData(data) {
|
||||||
const processed = {
|
const processed = {
|
||||||
id: data.id.toString(),
|
|
||||||
balance: data.balanceInt,
|
balance: data.balanceInt,
|
||||||
totalKreditsEarned: data.totalKreditsEarned,
|
totalKreditsEarned: data.totalKreditsEarned,
|
||||||
contributionsCount: data.contributionsCount?.toNumber()
|
contributionsCount: data.contributionsCount?.toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherProperties = [
|
const otherProperties = [
|
||||||
'account', 'accounts', 'ipfsHash', 'isCore', 'kind', 'name', 'url',
|
'id', 'account', 'accounts', 'ipfsHash', 'isCore', 'kind', 'name', 'url',
|
||||||
'github_username', 'github_uid', 'wiki_username', 'zoom_display_name'
|
'github_username', 'github_uid', 'wiki_username', 'zoom_display_name'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default function processReimbursementData(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const otherProperties = [
|
const otherProperties = [
|
||||||
'id', 'contributorId', 'token', 'vetoed', 'ipfsHash',
|
'id', 'recipientId', 'token', 'vetoed', 'ipfsHash',
|
||||||
'expenses', 'pendingTx'
|
'expenses', 'pendingTx'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -39,13 +39,12 @@ module.exports = function(environment) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
tokens: {
|
tokens: {
|
||||||
'WBTC': '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'
|
// TODO this is still the WBTC address, since contracts currently
|
||||||
|
// requires a token address for reimbursements
|
||||||
|
'BTC': '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'
|
||||||
},
|
},
|
||||||
|
|
||||||
gnosisSafe: {
|
btcBalanceAPI: 'https://api.kosmos.org/kredits/onchain_btc_balance',
|
||||||
txServiceHost: 'https://safe-transaction.mainnet.gnosis.io',
|
|
||||||
address: '0x9CC29b8373FF92B01C1f09F31B5DD862350c167E'
|
|
||||||
},
|
|
||||||
|
|
||||||
corsProxy: 'https://cors.5apps.com/?uri='
|
corsProxy: 'https://cors.5apps.com/?uri='
|
||||||
};
|
};
|
||||||
@@ -67,8 +66,6 @@ module.exports = function(environment) {
|
|||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
gatewayUrl: 'http://localhost:8080/ipfs'
|
gatewayUrl: 'http://localhost:8080/ipfs'
|
||||||
};
|
};
|
||||||
|
|
||||||
ENV.corsProxy = 'https://cors-anywhere.herokuapp.com/';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (environment === 'test') {
|
if (environment === 'test') {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -18,7 +18,7 @@ const data = [
|
|||||||
|
|
||||||
data.forEach(attrs => {
|
data.forEach(attrs => {
|
||||||
const c = Model.create(processContributionData(attrs));
|
const c = Model.create(processContributionData(attrs));
|
||||||
c.set('contributor', contributors.findBy('id', attrs.contributorId.toString()));
|
c.set('contributor', contributors.findBy('id', attrs.contributorId));
|
||||||
items.push(c);
|
items.push(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Vendored
+3
-3
@@ -4,9 +4,9 @@ import processContributorData from 'kredits-web/utils/process-contributor-data';
|
|||||||
const contributors = [];
|
const contributors = [];
|
||||||
|
|
||||||
const data = [
|
const data = [
|
||||||
{ id: '1', name: 'Bumi', totalKreditsEarned: 11500, github_uid: 318 },
|
{ id: 1, name: 'Bumi', totalKreditsEarned: 11500, github_uid: 318 },
|
||||||
{ id: '2', name: 'Râu Cao', totalKreditsEarned: 3000, github_uid: 842 },
|
{ id: 2, name: 'Râu Cao', totalKreditsEarned: 3000, github_uid: 842 },
|
||||||
{ id: '3', name: 'Manuel', totalKreditsEarned: 0, github_uid: 54812 }
|
{ id: 3, name: 'Manuel', totalKreditsEarned: 0, github_uid: 54812 }
|
||||||
];
|
];
|
||||||
|
|
||||||
data.forEach(attrs => {
|
data.forEach(attrs => {
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ module('Integration | Component | contribution-list', function(hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it renders filtered contributions', async function(assert) {
|
test('it renders filtered contributions', async function(assert) {
|
||||||
let service = this.owner.lookup('service:kredits');
|
let kredits = this.owner.lookup('service:kredits');
|
||||||
service.set('contributors', contributors);
|
kredits.set('contributors', contributors);
|
||||||
|
|
||||||
|
debugger;
|
||||||
|
|
||||||
this.set('fixtures', contributions);
|
this.set('fixtures', contributions);
|
||||||
await render(hbs`{{contribution-list contributions=fixtures showQuickFilter=true}}`);
|
await render(hbs`{{contribution-list contributions=fixtures showQuickFilter=true}}`);
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ module('Integration | Component | user-avatar', function(hooks) {
|
|||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test('it renders', async function(assert) {
|
test('it renders', async function(assert) {
|
||||||
this.set('bumi', contributors.findBy('id', '1'));
|
this.set('bumi', contributors.findBy('id', 1));
|
||||||
await render(hbs`{{user-avatar contributor=bumi}}`);
|
await render(hbs`{{user-avatar contributor=bumi}}`);
|
||||||
|
|
||||||
assert.dom(this.element).hasText('');
|
assert.dom(this.element).hasText('');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default image source URL', async function(assert) {
|
test('default image source URL', async function(assert) {
|
||||||
this.set('bumi', contributors.findBy('id', '1'));
|
this.set('bumi', contributors.findBy('id', 1));
|
||||||
await render(hbs`{{user-avatar contributor=bumi}}`);
|
await render(hbs`{{user-avatar contributor=bumi}}`);
|
||||||
|
|
||||||
assert.equal(this.element.querySelector('img').src,
|
assert.equal(this.element.querySelector('img').src,
|
||||||
@@ -23,7 +23,7 @@ module('Integration | Component | user-avatar', function(hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('size-specific image source URLs', async function(assert) {
|
test('size-specific image source URLs', async function(assert) {
|
||||||
this.set('bumi', contributors.findBy('id', '1'));
|
this.set('bumi', contributors.findBy('id', 1));
|
||||||
this.set('size', 'medium');
|
this.set('size', 'medium');
|
||||||
|
|
||||||
await render(hbs`{{user-avatar contributor=bumi size=size}}`);
|
await render(hbs`{{user-avatar contributor=bumi size=size}}`);
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ import { hbs } from 'ember-cli-htmlbars';
|
|||||||
module('Integration | Helper | fmt-crypto-currency', function(hooks) {
|
module('Integration | Helper | fmt-crypto-currency', function(hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test('it converts Wei to ETH', async function(assert) {
|
test('it converts Wei to RBTC', async function(assert) {
|
||||||
this.set('balanceETH', '500000000000000000');
|
this.set('balanceRBTC', '500000000000000000');
|
||||||
await render(hbs`{{fmt-crypto-currency balanceETH "ETH"}}`);
|
await render(hbs`{{fmt-crypto-currency balanceRBTC "RBTC"}}`);
|
||||||
assert.equal(this.element.textContent.trim(), '0.5');
|
assert.equal(this.element.textContent.trim(), '0.5');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it converts Satoshis to (W)BTC', async function(assert) {
|
test('it converts Satoshis to BTC', async function(assert) {
|
||||||
this.set('balanceWBTC', '117214976');
|
this.set('balanceBTC', '117214976');
|
||||||
await render(hbs`{{fmt-crypto-currency balanceWBTC "WBTC"}}`);
|
await render(hbs`{{fmt-crypto-currency balanceBTC "BTC"}}`);
|
||||||
assert.equal(this.element.textContent.trim(), '1.17214976');
|
assert.equal(this.element.textContent.trim(), '1.17214976');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module('Unit | Utility | process-contributor-data', function() {
|
|||||||
|
|
||||||
test('formats the data correctly', function(assert) {
|
test('formats the data correctly', function(assert) {
|
||||||
// TODO use integers everywhere for IDs
|
// TODO use integers everywhere for IDs
|
||||||
assert.ok(typeof result.id === 'string');
|
assert.ok(typeof result.id === 'number');
|
||||||
assert.ok(typeof result.balance === 'number');
|
assert.ok(typeof result.balance === 'number');
|
||||||
assert.ok(typeof result.totalKreditsEarned === 'number');
|
assert.ok(typeof result.totalKreditsEarned === 'number');
|
||||||
assert.ok(typeof result.contributionsCount === 'number');
|
assert.ok(typeof result.contributionsCount === 'number');
|
||||||
|
|||||||
Reference in New Issue
Block a user