From 2ec75b485f60bf81079ca1cd82102b17814781e5 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 13 Aug 2018 12:02:23 +0200 Subject: [PATCH 1/4] Implement pollUpdate transaction action --- src/action/transaction.js | 17 +++++++++++++++-- test/unit/action/transaction.spec.js | 24 ++++++++++++++++++++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/action/transaction.js b/src/action/transaction.js index 213ad6e21..2d1365ab7 100644 --- a/src/action/transaction.js +++ b/src/action/transaction.js @@ -3,6 +3,7 @@ * call the corresponding GRPC apis for listing transactions. */ +import { RETRY_DELAY } from '../config'; import * as log from './log'; import { parseDate, parseSat, toHex, toHash } from '../helper'; @@ -21,7 +22,6 @@ class TransactionAction { */ init() { this._nav.goTransactions(); - this.update(); } /** @@ -33,7 +33,6 @@ class TransactionAction { select({ item }) { this._store.selectedTransaction = item; this._nav.goTransactionDetail(); - this.update(); } /** @@ -51,6 +50,20 @@ class TransactionAction { ]); } + /** + * Poll the update api to update the transactions and wallet balances. + * @return {Promise} + */ + async pollUpdate() { + try { + clearTimeout(this.tPollUpdate); + this.tPollUpdate = setTimeout(() => this.pollUpdate(), RETRY_DELAY); + await this.update(); + } catch (err) { + log.error('Polling transaction update failed', err); + } + } + /** * List the on-chain transactions by calling the respective grpc api and updating * the transactions array in the global store. diff --git a/test/unit/action/transaction.spec.js b/test/unit/action/transaction.spec.js index da0664f36..7c77aec25 100644 --- a/test/unit/action/transaction.spec.js +++ b/test/unit/action/transaction.spec.js @@ -4,6 +4,7 @@ import TransactionAction from '../../../src/action/transaction'; import WalletAction from '../../../src/action/wallet'; import NavAction from '../../../src/action/nav'; import * as logger from '../../../src/action/log'; +import { nap } from '../../../src/helper'; describe('Action Transactions Unit Tests', () => { let store; @@ -25,24 +26,21 @@ describe('Action Transactions Unit Tests', () => { }); afterEach(() => { + clearTimeout(transaction.tPollUpdate); sandbox.restore(); }); describe('init()', () => { it('should refresh and navigate to list', () => { - sandbox.stub(transaction, 'update'); transaction.init(); - expect(transaction.update, 'was called once'); expect(nav.goTransactions, 'was called once'); }); }); describe('select()', () => { it('should set selectedTransaction', () => { - sandbox.stub(transaction, 'update'); transaction.select({ item: 'some-transaction' }); expect(store.selectedTransaction, 'to equal', 'some-transaction'); - expect(transaction.update, 'was called once'); expect(nav.goTransactionDetail, 'was called once'); }); }); @@ -56,6 +54,24 @@ describe('Action Transactions Unit Tests', () => { }); }); + describe('pollUpdate()', () => { + it('should poll update api several times', async () => { + sandbox.stub(transaction, 'update'); + await transaction.pollUpdate(); + await nap(30); + expect(transaction.update.callCount, 'to be greater than', 1); + expect(logger.error, 'was not called'); + }); + + it('should handle error in update', async () => { + sandbox.stub(transaction, 'update').rejects(new Error('Boom!')); + await transaction.pollUpdate(); + await nap(30); + expect(transaction.update.callCount, 'to be greater than', 1); + expect(logger.error.callCount, 'to be greater than', 1); + }); + }); + describe('getTransactions()', () => { it('should set unconfirmed transaction in store', async () => { grpc.sendCommand.withArgs('getTransactions').resolves({ From 58bacdcb80706432c929f6bab3d6459a34542b05 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 13 Aug 2018 12:03:31 +0200 Subject: [PATCH 2/4] Invoke transaction.pollUpdate() in action index.js --- src/action/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/action/index.js b/src/action/index.js index f8d6cb54b..97e152723 100644 --- a/src/action/index.js +++ b/src/action/index.js @@ -98,7 +98,5 @@ observe(store, 'lndReady', () => { info.getInfo(); wallet.update(); channel.update(); - transaction.update(); - transaction.subscribeTransactions(); - transaction.subscribeInvoices(); + transaction.pollUpdate(); }); From b2cb3cf65530d0e9d12f2f9dd09a0cb57c8fb6eb Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 13 Aug 2018 12:04:44 +0200 Subject: [PATCH 3/4] Remove redundant transaction.update() from other actions --- src/action/index.js | 11 ++--------- src/action/invoice.js | 4 +--- src/action/payment.js | 5 +---- stories/screen.js | 11 ++--------- test/integration/action/action-integration.spec.js | 8 ++++---- test/unit/action/invoice.spec.js | 13 +------------ test/unit/action/payment.spec.js | 9 +-------- 7 files changed, 12 insertions(+), 49 deletions(-) diff --git a/src/action/index.js b/src/action/index.js index 97e152723..32cd75f5e 100644 --- a/src/action/index.js +++ b/src/action/index.js @@ -38,15 +38,8 @@ export const wallet = new WalletAction(store, grpc, db, nav, notify); export const info = new InfoAction(store, grpc, nav, notify); export const channel = new ChannelAction(store, grpc, nav, notify); export const transaction = new TransactionAction(store, grpc, wallet, nav); -export const invoice = new InvoiceAction( - store, - grpc, - transaction, - nav, - notify, - Clipboard -); -export const payment = new PaymentAction(store, grpc, transaction, nav, notify); +export const invoice = new InvoiceAction(store, grpc, nav, notify, Clipboard); +export const payment = new PaymentAction(store, grpc, nav, notify); export const setting = new SettingAction(store, wallet, db, ipc); payment.listenForUrl(ipc); // enable incoming url handler diff --git a/src/action/invoice.js b/src/action/invoice.js index 3a4cf0396..a6e908607 100644 --- a/src/action/invoice.js +++ b/src/action/invoice.js @@ -7,10 +7,9 @@ import { PREFIX_URI } from '../config'; import { toSatoshis } from '../helper'; class InvoiceAction { - constructor(store, grpc, transaction, nav, notification, clipboard) { + constructor(store, grpc, nav, notification, clipboard) { this._store = store; this._grpc = grpc; - this._transaction = transaction; this._nav = nav; this._notification = notification; this._clipboard = clipboard; @@ -70,7 +69,6 @@ class InvoiceAction { } catch (err) { this._notification.display({ msg: 'Creating invoice failed!', err }); } - await this._transaction.update(); } /** diff --git a/src/action/payment.js b/src/action/payment.js index f5f2f79fc..0ac8b27bd 100644 --- a/src/action/payment.js +++ b/src/action/payment.js @@ -15,10 +15,9 @@ import { import * as log from './log'; class PaymentAction { - constructor(store, grpc, transaction, nav, notification) { + constructor(store, grpc, nav, notification) { this._store = store; this._grpc = grpc; - this._transaction = transaction; this._nav = nav; this._notification = notification; } @@ -161,7 +160,6 @@ class PaymentAction { } catch (err) { this._notification.display({ msg: 'Sending transaction failed!', err }); } - await this._transaction.update(); } /** @@ -192,7 +190,6 @@ class PaymentAction { this._nav.goPayLightningConfirm(); this._notification.display({ msg: 'Lightning payment failed!', err }); } - await this._transaction.update(); } } diff --git a/stories/screen.js b/stories/screen.js index 8e056d506..a583c13bb 100644 --- a/stories/screen.js +++ b/stories/screen.js @@ -67,16 +67,9 @@ sinon.stub(wallet, 'checkPassword'); sinon.stub(wallet, 'getExchangeRate'); const transaction = new TransactionAction(store, grpc, wallet, nav); sinon.stub(transaction, 'update'); -const invoice = new InvoiceAction( - store, - grpc, - transaction, - nav, - notify, - Clipboard -); +const invoice = new InvoiceAction(store, grpc, nav, notify, Clipboard); sinon.stub(invoice, 'generateUri'); -const payment = new PaymentAction(store, grpc, transaction, nav, notify); +const payment = new PaymentAction(store, grpc, nav, notify); sinon.stub(payment, 'checkType'); sinon.stub(payment, 'payBitcoin'); sinon.stub(payment, 'payLightning'); diff --git a/test/integration/action/action-integration.spec.js b/test/integration/action/action-integration.spec.js index 5d0822bf4..0d391322e 100644 --- a/test/integration/action/action-integration.spec.js +++ b/test/integration/action/action-integration.spec.js @@ -153,8 +153,8 @@ describe('Action Integration Tests', function() { wallet1 = new WalletAction(store1, grpc1, db1, nav1, notify1); channels1 = new ChannelAction(store1, grpc1, nav1, notify1); transactions1 = new TransactionAction(store1, grpc1, wallet1, nav1); - invoice1 = new InvoiceAction(store1, grpc1, transactions1, nav1, notify1); - payments1 = new PaymentAction(store1, grpc1, transactions1, nav1, notify1); + invoice1 = new InvoiceAction(store1, grpc1, nav1, notify1); + payments1 = new PaymentAction(store1, grpc1, nav1, notify1); db2 = sinon.createStubInstance(AppStorage); nav2 = sinon.createStubInstance(NavAction); @@ -165,8 +165,8 @@ describe('Action Integration Tests', function() { wallet2 = new WalletAction(store2, grpc2, db2, nav2, notify2); channels2 = new ChannelAction(store2, grpc2, nav2, notify2); transactions2 = new TransactionAction(store2, grpc2, wallet2, nav2); - invoice2 = new InvoiceAction(store2, grpc2, transactions2, nav2, notify2); - payments2 = new PaymentAction(store2, grpc2, transactions2, nav2, notify2); + invoice2 = new InvoiceAction(store2, grpc2, nav2, notify2); + payments2 = new PaymentAction(store2, grpc2, nav2, notify2); }); after(async () => { diff --git a/test/unit/action/invoice.spec.js b/test/unit/action/invoice.spec.js index af9074159..f19018cc9 100644 --- a/test/unit/action/invoice.spec.js +++ b/test/unit/action/invoice.spec.js @@ -2,7 +2,6 @@ import { Store } from '../../../src/store'; import NavAction from '../../../src/action/nav'; import GrpcAction from '../../../src/action/grpc'; import InvoiceAction from '../../../src/action/invoice'; -import TransactionAction from '../../../src/action/transaction'; import NotificationAction from '../../../src/action/notification'; describe('Action Invoice Unit Tests', () => { @@ -10,7 +9,6 @@ describe('Action Invoice Unit Tests', () => { let nav; let grpc; let invoice; - let transaction; let notification; let clipboard; @@ -22,15 +20,7 @@ describe('Action Invoice Unit Tests', () => { grpc = sinon.createStubInstance(GrpcAction); notification = sinon.createStubInstance(NotificationAction); clipboard = { setString: sinon.stub() }; - transaction = sinon.createStubInstance(TransactionAction); - invoice = new InvoiceAction( - store, - grpc, - transaction, - nav, - notification, - clipboard - ); + invoice = new InvoiceAction(store, grpc, nav, notification, clipboard); }); describe('init()', () => { @@ -78,7 +68,6 @@ describe('Action Invoice Unit Tests', () => { expect(store.invoice.encoded, 'to equal', 'some-request'); expect(store.invoice.uri, 'to equal', 'lightning:some-request'); expect(nav.goInvoiceQR, 'was called once'); - expect(transaction.update, 'was called once'); }); it('should display notification on error', async () => { diff --git a/test/unit/action/payment.spec.js b/test/unit/action/payment.spec.js index bdd81f4d6..3631b8fd1 100644 --- a/test/unit/action/payment.spec.js +++ b/test/unit/action/payment.spec.js @@ -3,7 +3,6 @@ import { Store } from '../../../src/store'; import IpcAction from '../../../src/action/ipc'; import GrpcAction from '../../../src/action/grpc'; import PaymentAction from '../../../src/action/payment'; -import TransactionAction from '../../../src/action/transaction'; import NotificationAction from '../../../src/action/notification'; import NavAction from '../../../src/action/nav'; import * as logger from '../../../src/action/log'; @@ -13,7 +12,6 @@ describe('Action Payments Unit Tests', () => { let store; let sandbox; let grpc; - let transaction; let payment; let nav; let notification; @@ -27,8 +25,7 @@ describe('Action Payments Unit Tests', () => { grpc = sinon.createStubInstance(GrpcAction); notification = sinon.createStubInstance(NotificationAction); nav = sinon.createStubInstance(NavAction); - transaction = sinon.createStubInstance(TransactionAction); - payment = new PaymentAction(store, grpc, transaction, nav, notification); + payment = new PaymentAction(store, grpc, nav, notification); }); afterEach(() => { @@ -199,14 +196,12 @@ describe('Action Payments Unit Tests', () => { }); expect(nav.goPayBitcoinDone, 'was called once'); expect(notification.display, 'was not called'); - expect(transaction.update, 'was called once'); }); it('should display notification on error', async () => { grpc.sendCommand.withArgs('sendCoins').rejects(); await payment.payBitcoin(); expect(notification.display, 'was called once'); - expect(transaction.update, 'was called once'); }); }); @@ -237,7 +232,6 @@ describe('Action Payments Unit Tests', () => { expect(nav.goWait, 'was called once'); expect(nav.goPayLightningDone, 'was called once'); expect(notification.display, 'was not called'); - expect(transaction.update, 'was called once'); }); it('should display notification on error', async () => { @@ -245,7 +239,6 @@ describe('Action Payments Unit Tests', () => { await payment.payLightning({ invoice: 'some-payment' }); expect(nav.goPayLightningConfirm, 'was called once'); expect(notification.display, 'was called once'); - expect(transaction.update, 'was called once'); }); }); }); From 5765ce4c0890eb103374aef588f7c2945dd4754c Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 13 Aug 2018 12:05:08 +0200 Subject: [PATCH 4/4] Increase polling rate from 3 to 1 seconds --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index bcad843a9..3487f2790 100644 --- a/src/config.js +++ b/src/config.js @@ -2,7 +2,7 @@ * @fileOverview this file is used to hardcode default settings for the app. */ -module.exports.RETRY_DELAY = 3000; +module.exports.RETRY_DELAY = 1000; module.exports.LND_INIT_DELAY = 5000; module.exports.NOTIFICATION_DELAY = 5000; module.exports.RATE_DELAY = 15 * 60 * 1000;