Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
8c5c6b7c6b
|
|||
|
77f95a096a
|
|||
|
4bd6bc8269
|
@@ -0,0 +1,31 @@
|
|||||||
|
import Component from '@glimmer/component';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import config from 'kredits-web/config/environment';
|
||||||
|
|
||||||
|
export default class ReimbursementItemComponent extends Component {
|
||||||
|
@service kredits;
|
||||||
|
|
||||||
|
get ipfsGatewayUrl () {
|
||||||
|
return config.ipfs.gatewayUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isConfirmed () {
|
||||||
|
return (this.args.reimbursement.confirmedAt - this.kredits.currentBlock) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isUnconfirmed () {
|
||||||
|
return !this.isConfirmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
get showVetoButton () {
|
||||||
|
return this.isUnconfirmed && this.kredits.currentUserIsCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
veto (id) {
|
||||||
|
this.kredits.vetoReimbursement(id).then(transaction => {
|
||||||
|
console.debug('[controllers:budget] Veto submitted to chain: '+transaction.hash);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<li data-reimbursement-id={{@reimbursement.id}}
|
||||||
|
class="{{item-status @reimbursement}}">
|
||||||
|
<p class="meta">
|
||||||
|
<span class="recipient">
|
||||||
|
<UserAvatar @contributor={{@reimbursement.contributor}} />
|
||||||
|
</span>
|
||||||
|
<span class="title">
|
||||||
|
Expenses covered by {{@reimbursement.contributor.name}}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="token-amount">
|
||||||
|
<span class="amount">
|
||||||
|
{{sats-to-btc @reimbursement.amount}}</span> <span class="symbol">BTC</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ExpenseList @expenses={{@reimbursement.expenses}} />
|
||||||
|
|
||||||
|
<div class="meta">
|
||||||
|
<p class="confirmation-eta">
|
||||||
|
<ConfirmedIn @confirmedAtBlock={{@reimbursement.confirmedAt}} />
|
||||||
|
</p>
|
||||||
|
<p class="actions">
|
||||||
|
<a href="{{this.ipfsGatewayUrl}}/{{@reimbursement.ipfsHash}}"
|
||||||
|
class="button small" target="_blank" rel="noopener noreferrer">
|
||||||
|
Inspect IPFS data
|
||||||
|
</a>
|
||||||
|
{{#if this.showVetoButton}}
|
||||||
|
<button {{on "click" (fn this.veto @reimbursement.id)}}
|
||||||
|
disabled={{@reimbursement.vetoed}}
|
||||||
|
class="button small danger" type="button">veto</button>
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
@@ -1,23 +1,10 @@
|
|||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { sort } from '@ember/object/computed';
|
import { sort } from '@ember/object/computed';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import config from 'kredits-web/config/environment';
|
|
||||||
|
|
||||||
export default class ReimbursementListComponent extends Component {
|
export default class ReimbursementListComponent extends Component {
|
||||||
@service kredits;
|
@service kredits;
|
||||||
|
|
||||||
itemSorting = Object.freeze(['pendingStatus:asc', 'id:desc']);
|
itemSorting = Object.freeze(['pendingStatus:asc', 'id:desc']);
|
||||||
@sort('args.items', 'itemSorting') itemsSorted;
|
@sort('args.items', 'itemSorting') itemsSorted;
|
||||||
|
|
||||||
get ipfsGatewayUrl () {
|
|
||||||
return config.ipfs.gatewayUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
veto (id) {
|
|
||||||
this.kredits.vetoReimbursement(id).then(transaction => {
|
|
||||||
console.debug('[controllers:budget] Veto submitted to chain: '+transaction.hash);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,5 @@
|
|||||||
<ul class="item-list spaced reimbursement-list {{if @loading 'loading'}}">
|
<ul class="item-list spaced reimbursement-list {{if @loading 'loading'}}">
|
||||||
{{#each this.itemsSorted as |reimbursement|}}
|
{{#each this.itemsSorted as |item|}}
|
||||||
<li data-reimbursement-id={{reimbursement.id}}
|
<ReimbursementItem @reimbursement={{item}} />
|
||||||
class="{{item-status reimbursement}}">
|
|
||||||
<p class="meta">
|
|
||||||
<span class="recipient">
|
|
||||||
<UserAvatar @contributor={{reimbursement.contributor}} />
|
|
||||||
</span>
|
|
||||||
<span class="title">
|
|
||||||
Expenses covered by {{reimbursement.contributor.name}}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p class="token-amount">
|
|
||||||
<span class="amount">
|
|
||||||
{{sats-to-btc reimbursement.amount}}</span> <span class="symbol">BTC</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ExpenseList @expenses={{reimbursement.expenses}} />
|
|
||||||
|
|
||||||
<div class="meta">
|
|
||||||
<p class="confirmation-eta">
|
|
||||||
<ConfirmedIn @confirmedAtBlock={{reimbursement.confirmedAt}} />
|
|
||||||
</p>
|
|
||||||
<p class="actions">
|
|
||||||
<a href="{{this.ipfsGatewayUrl}}/{{reimbursement.ipfsHash}}"
|
|
||||||
class="button small" target="_blank" rel="noopener noreferrer">
|
|
||||||
Inspect IPFS data
|
|
||||||
</a>
|
|
||||||
{{#if this.kredits.currentUserIsCore}}
|
|
||||||
<button {{on "click" (fn this.veto reimbursement.id)}}
|
|
||||||
disabled={{reimbursement.vetoed}}
|
|
||||||
class="button small danger" type="button">veto</button>
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -30,5 +30,8 @@ export default class BudgetRoute extends Route {
|
|||||||
}
|
}
|
||||||
schedule('afterRender', this.kredits.fetchMissingReimbursements,
|
schedule('afterRender', this.kredits.fetchMissingReimbursements,
|
||||||
this.kredits.fetchMissingReimbursements.perform);
|
this.kredits.fetchMissingReimbursements.perform);
|
||||||
|
|
||||||
|
schedule('afterRender', this.kredits.syncReimbursementEvents,
|
||||||
|
this.kredits.syncReimbursementEvents.perform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -780,5 +780,32 @@ export default Service.extend({
|
|||||||
this.contributors
|
this.contributors
|
||||||
.findBy('address', to)
|
.findBy('address', to)
|
||||||
.incrementProperty('balance', value);
|
.incrementProperty('balance', value);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
syncReimbursementEvents: task(function * () {
|
||||||
|
yield this.fetchEvents(
|
||||||
|
'Reimbursement', // contract
|
||||||
|
'ReimbursementAdded', // event
|
||||||
|
0,
|
||||||
|
// this.kredits.currentBlock - (2*60*24*14), // from block
|
||||||
|
this.currentBlock // to block
|
||||||
|
);
|
||||||
|
}).group('syncTaskGroup'),
|
||||||
|
|
||||||
|
async fetchEvents(contractName, eventName, fromBlock, toBlock) {
|
||||||
|
const contract = this.kredits[contractName].contract;
|
||||||
|
const eventFilter = contract.filters[eventName]();
|
||||||
|
const filterOptions = { fromBlock, toBlock };
|
||||||
|
|
||||||
|
console.debug(contract, eventFilter, filterOptions);
|
||||||
|
contract.queryFilter(eventFilter)
|
||||||
|
.then(events => {
|
||||||
|
events.forEach(event => {
|
||||||
|
console.debug("Event:", event.args.creator, event.args.key, event.args.value);
|
||||||
|
});
|
||||||
|
}).catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ module.exports = function(environment) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
corsProxy: 'https://cors.5apps.com/?uri='
|
corsProxy: 'https://cors.5apps.com/?uri=',
|
||||||
|
|
||||||
|
hideVetoedEntriesAfterBlocks: 5760 // 48 hours
|
||||||
};
|
};
|
||||||
|
|
||||||
if (environment === 'development') {
|
if (environment === 'development') {
|
||||||
|
|||||||
Reference in New Issue
Block a user