From ca1ccf6d930449c6a4dd10d6f693ff43ffade9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 30 Dec 2022 22:25:50 +0700 Subject: [PATCH 1/7] Formatting --- app/components/budget-balances/component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/budget-balances/component.js b/app/components/budget-balances/component.js index 7ae86bd..39b6aa0 100644 --- a/app/components/budget-balances/component.js +++ b/app/components/budget-balances/component.js @@ -3,7 +3,7 @@ import { inject as service } from '@ember/service'; import { alias } from '@ember/object/computed'; export default class BudgetBalancesComponent extends Component { - @service communityFunds + @service communityFunds; @alias('communityFunds.balances') balances; get loading () { From c7eb81450cdd0f26a2626aa6416f1d3f374cac2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 30 Dec 2022 22:26:32 +0700 Subject: [PATCH 2/7] Allow to veto reimbursements --- .../reimbursement-list/component.js | 11 +++++++ .../reimbursement-list/template.hbs | 15 ++++++++-- app/services/kredits.js | 29 ++++++++++++++----- .../components/_reimbursement-list.scss | 5 ++++ 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/app/components/reimbursement-list/component.js b/app/components/reimbursement-list/component.js index 75a5770..cd04233 100644 --- a/app/components/reimbursement-list/component.js +++ b/app/components/reimbursement-list/component.js @@ -1,7 +1,18 @@ import Component from '@glimmer/component'; import { sort } from '@ember/object/computed'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; export default class ReimbursementListComponent extends Component { + @service kredits; + itemSorting = Object.freeze(['pendingStatus:asc', 'id:desc']); @sort('args.items', 'itemSorting') itemsSorted; + + @action + veto (id) { + this.kredits.vetoReimbursement(id).then(transaction => { + console.debug('[controllers:budget] Veto submitted to chain: '+transaction.hash); + }); + } } diff --git a/app/components/reimbursement-list/template.hbs b/app/components/reimbursement-list/template.hbs index a04f230..368bc13 100644 --- a/app/components/reimbursement-list/template.hbs +++ b/app/components/reimbursement-list/template.hbs @@ -3,8 +3,12 @@
  • - - Expenses covered by {{reimbursement.contributor.name}} + + Expenses covered by {{reimbursement.contributor.name}} + + + +

    @@ -26,6 +30,13 @@

  • {{/each}} + {{#if this.kredits.currentUserIsCore}} +

    + +

    + {{/if}} {{/each}} \ No newline at end of file diff --git a/app/services/kredits.js b/app/services/kredits.js index f47954d..35f5fb9 100644 --- a/app/services/kredits.js +++ b/app/services/kredits.js @@ -617,6 +617,18 @@ export default Service.extend({ yield this.fetchMissingObjects.perform('Reimbursement'); }).group('syncTaskGroup'), + vetoReimbursement (id) { + console.debug('[kredits] veto against reimbursement', id); + const reimbursement = this.reimbursements.findBy('id', id); + + return this.kredits.Reimbursement.functions.veto(id, { gasLimit: 300000 }) + .then(data => { + console.debug('[kredits] veto response', data); + reimbursement.set('pendingTx', data); + return data; + }); + }, + // // Contract events // @@ -676,7 +688,7 @@ export default Service.extend({ }, async handleContributionVetoed (contributionId) { - console.debug('[kredits] ContributionVetoed event received for ', contributionId); + console.debug('[kredits] ContributionVetoed event received for #', contributionId); const c = this.contributions.findBy('id', contributionId); if (c) { @@ -709,14 +721,15 @@ export default Service.extend({ // // TODO test when reimbursement txs are successful and veto is implemented // - handleReimbursementVetoed (id) { - console.debug('[kredits] ReimbursementVetoed received for ', id); - const reimbursement = this.reimbursements.findBy('id', id); - console.debug('[kredits] reimbursement', this.reimbursement); + async handleReimbursementVetoed (id) { + console.debug(`[kredits] ReimbursementVetoed received for #${id}`); + const r = this.reimbursements.findBy('id', id); + console.debug('[kredits] reimbursement', r); - if (reimbursement) { - reimbursement.set('vetoed', true); - reimbursement.set('pendingTx', null); + if (r) { + r.set('vetoed', true); + r.set('pendingTx', null); + await this.browserCache.reimbursements.setItem(r.id.toString(), r.serialize()); } }, diff --git a/app/styles/components/_reimbursement-list.scss b/app/styles/components/_reimbursement-list.scss index 3c22894..dfbb5d8 100644 --- a/app/styles/components/_reimbursement-list.scss +++ b/app/styles/components/_reimbursement-list.scss @@ -7,6 +7,11 @@ ul.reimbursement-list { grid-row-gap: 0.5rem; padding-top: 1.6rem; + &.vetoed { + text-decoration: line-through; + opacity: 0.6; + } + .token-amount { text-align: right; From 0b0dea095f7cc42206627951995d10e6cb9be53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 31 Dec 2022 12:20:27 +0700 Subject: [PATCH 3/7] Reject empty tag strings --- app/components/add-expense-item/component.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/components/add-expense-item/component.js b/app/components/add-expense-item/component.js index 72790e6..a2022ba 100644 --- a/app/components/add-expense-item/component.js +++ b/app/components/add-expense-item/component.js @@ -81,7 +81,9 @@ export default class AddExpenseItemComponent extends Component { } if (isPresent(this.tags)) { - expense.tags = this.tags.split(',').map(t => t.trim()); + expense.tags = this.tags.split(',') + .map(t => t.trim()) + .filter(t => t.length > 0); } this.args.addExpenseItem(expense); From e543708b42d9cee0de843a4e3fcaebfadde81b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 31 Dec 2022 12:20:59 +0700 Subject: [PATCH 4/7] Add IPFS inspect button to reimbursements --- app/components/reimbursement-list/component.js | 5 +++++ app/components/reimbursement-list/template.hbs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/app/components/reimbursement-list/component.js b/app/components/reimbursement-list/component.js index cd04233..79c260d 100644 --- a/app/components/reimbursement-list/component.js +++ b/app/components/reimbursement-list/component.js @@ -2,6 +2,7 @@ import Component from '@glimmer/component'; import { sort } from '@ember/object/computed'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; +import config from 'kredits-web/config/environment'; export default class ReimbursementListComponent extends Component { @service kredits; @@ -9,6 +10,10 @@ export default class ReimbursementListComponent extends Component { itemSorting = Object.freeze(['pendingStatus:asc', 'id:desc']); @sort('args.items', 'itemSorting') itemsSorted; + get ipfsGatewayUrl () { + return config.ipfs.gatewayUrl; + } + @action veto (id) { this.kredits.vetoReimbursement(id).then(transaction => { diff --git a/app/components/reimbursement-list/template.hbs b/app/components/reimbursement-list/template.hbs index 368bc13..6e85150 100644 --- a/app/components/reimbursement-list/template.hbs +++ b/app/components/reimbursement-list/template.hbs @@ -32,6 +32,10 @@ {{#if this.kredits.currentUserIsCore}}

    + + Inspect IPFS data + From 2082b51c5b33a13f83aa3b8e3ba8bbe11ae33243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 31 Dec 2022 15:05:36 +0700 Subject: [PATCH 5/7] Add expenses from file Allow to upload a JSON file containing a list of expense items --- app/components/add-reimbursement/component.js | 18 +++++++++++++++++- app/components/add-reimbursement/template.hbs | 6 ++++++ app/utils/read-file-content.js | 8 ++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 app/utils/read-file-content.js diff --git a/app/components/add-reimbursement/component.js b/app/components/add-reimbursement/component.js index fd00dea..2bc100b 100644 --- a/app/components/add-reimbursement/component.js +++ b/app/components/add-reimbursement/component.js @@ -6,6 +6,7 @@ import { action } from '@ember/object'; import { A } from '@ember/array'; import { scheduleOnce } from '@ember/runloop'; import isValidAmount from 'kredits-web/utils/is-valid-amount'; +import readFileContent from 'kredits-web/utils/read-file-content'; import config from 'kredits-web/config/environment'; export default class AddReimbursementComponent extends Component { @@ -85,7 +86,22 @@ export default class AddReimbursementComponent extends Component { } @action - updateContributor(event) { + async addExpensesFromFile (evt) { + const content = await readFileContent(evt.target.files[0]); + const expenses = JSON.parse(content); + + if (expenses instanceof Array) { + for (const item of expenses) { + this.addExpenseItem(item); + } + } else { + console.warn("Expenses in file must be a list of items:"); + console.debug(content); + } + } + + @action + updateContributor (event) { this.recipientId = event.target.value; } diff --git a/app/components/add-reimbursement/template.hbs b/app/components/add-reimbursement/template.hbs index 6c1c06a..43235b3 100644 --- a/app/components/add-reimbursement/template.hbs +++ b/app/components/add-reimbursement/template.hbs @@ -87,3 +87,9 @@ {{/if}} +

    +

    Add expense items from file

    + +
    diff --git a/app/utils/read-file-content.js b/app/utils/read-file-content.js new file mode 100644 index 0000000..0413399 --- /dev/null +++ b/app/utils/read-file-content.js @@ -0,0 +1,8 @@ +export default function (file) { + const reader = new FileReader(); + return new Promise((resolve, reject) => { + reader.onload = event => resolve(event.target.result) + reader.onerror = error => reject(error) + reader.readAsText(file) + }) +} From a582f41fde0ea5a3f9b8278c22133f83cc2c8576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 31 Dec 2022 15:07:02 +0700 Subject: [PATCH 6/7] Cache reimbursement data on incoming events --- app/services/kredits.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app/services/kredits.js b/app/services/kredits.js index 35f5fb9..dc62d47 100644 --- a/app/services/kredits.js +++ b/app/services/kredits.js @@ -699,9 +699,6 @@ export default Service.extend({ } }, - // - // TODO test when reimbursement txs are successful - // async handleReimbursementAdded (id, addedByAccount, amount) { console.debug('[kredits] ReimbursementAdded event received', { id, addedByAccount, amount }); @@ -715,12 +712,10 @@ export default Service.extend({ } const data = await this.kredits.Reimbursement.getById(id); - this.loadReimbursementFromData(data); + const r = this.loadReimbursementFromData(data); + this.browserCache.reimbursements.setItem(r.id.toString(), r.serialize()); }, - // - // TODO test when reimbursement txs are successful and veto is implemented - // async handleReimbursementVetoed (id) { console.debug(`[kredits] ReimbursementVetoed received for #${id}`); const r = this.reimbursements.findBy('id', id); @@ -729,7 +724,7 @@ export default Service.extend({ if (r) { r.set('vetoed', true); r.set('pendingTx', null); - await this.browserCache.reimbursements.setItem(r.id.toString(), r.serialize()); + this.browserCache.reimbursements.setItem(r.id.toString(), r.serialize()); } }, From d159fca8166ee3cb956e2b018a30e4317b5d6809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Mon, 9 Jan 2023 11:31:31 +0800 Subject: [PATCH 7/7] Add todo note to async action --- app/components/add-reimbursement/component.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/components/add-reimbursement/component.js b/app/components/add-reimbursement/component.js index 2bc100b..5fc0397 100644 --- a/app/components/add-reimbursement/component.js +++ b/app/components/add-reimbursement/component.js @@ -85,6 +85,8 @@ export default class AddReimbursementComponent extends Component { this.total = btcAmount.toFixed(8); } + // TODO use ember-concurrency here + // https://github.com/67P/kredits-web/pull/209#discussion_r1064234421 @action async addExpensesFromFile (evt) { const content = await readFileContent(evt.target.files[0]);