From df0f244bd1a60f5b17031f0dea193be85c12aae1 Mon Sep 17 00:00:00 2001 From: natsoni Date: Tue, 23 Jul 2024 12:18:12 +0200 Subject: [PATCH 1/2] Prevent never ending loop of calls to transactionTimes --- .../transaction/transaction.component.ts | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index f73a0b225..24d90b269 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -11,7 +11,9 @@ import { tap, map, retry, - startWith + startWith, + repeat, + take } from 'rxjs/operators'; import { Transaction } from '../../interfaces/electrs.interface'; import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest, BehaviorSubject } from 'rxjs'; @@ -76,6 +78,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { transactionTime = -1; subscription: Subscription; fetchCpfpSubscription: Subscription; + transactionTimesSubscription: Subscription; fetchRbfSubscription: Subscription; fetchCachedTxSubscription: Subscription; fetchAccelerationSubscription: Subscription; @@ -106,6 +109,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { showCpfpDetails = false; miningStats: MiningStats; fetchCpfp$ = new Subject(); + transactionTimes$ = new Subject(); fetchRbfHistory$ = new Subject(); fetchCachedTx$ = new Subject(); fetchAcceleration$ = new Subject(); @@ -572,7 +576,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { if (tx.firstSeen) { this.transactionTime = tx.firstSeen; } else { - this.getTransactionTime(); + this.transactionTimes$.next(tx.txid); } } else { this.fetchAcceleration$.next(tx.status.block_height); @@ -730,6 +734,25 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { ); }) ) + + this.transactionTimesSubscription = this.transactionTimes$.pipe( + tap(() => { + this.isLoadingFirstSeen = true; + }), + switchMap((txid) => this.apiService.getTransactionTimes$([txid]).pipe( + retry({ count: 2, delay: 2000 }), + // Try again until we either get a valid response, or the transaction is confirmed + repeat({ delay: 2000 }), + filter((transactionTimes) => transactionTimes?.length && transactionTimes[0] > 0 && !this.tx.status?.confirmed), + take(1), + )), + ) + .subscribe((transactionTimes) => { + this.isLoadingFirstSeen = false; + if (transactionTimes?.length && transactionTimes[0]) { + this.transactionTime = transactionTimes[0]; + } + }); } ngAfterViewInit(): void { @@ -763,28 +786,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { return of(false); } - getTransactionTime() { - this.isLoadingFirstSeen = true; - this.apiService - .getTransactionTimes$([this.tx.txid]) - .pipe( - retry({ count: 2, delay: 2000 }), - catchError(() => { - this.isLoadingFirstSeen = false; - return throwError(() => new Error('')); - }) - ) - .subscribe((transactionTimes) => { - if (transactionTimes?.length && transactionTimes[0]) { - this.transactionTime = transactionTimes[0]; - } else { - setTimeout(() => { - this.getTransactionTime(); - }, 2000); - } - }); - } - setCpfpInfo(cpfpInfo: CpfpInfo): void { if (!cpfpInfo || !this.tx) { this.cpfpInfo = null; @@ -1057,6 +1058,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { ngOnDestroy() { this.subscription.unsubscribe(); this.fetchCpfpSubscription.unsubscribe(); + this.transactionTimesSubscription.unsubscribe(); this.fetchRbfSubscription.unsubscribe(); this.fetchCachedTxSubscription.unsubscribe(); this.fetchAccelerationSubscription.unsubscribe(); From ca4b1943a83cda3511c392d3ba79d8e87614b754 Mon Sep 17 00:00:00 2001 From: softsimon Date: Thu, 25 Jul 2024 03:35:17 -0500 Subject: [PATCH 2/2] moving code block --- .../transaction/transaction.component.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 24d90b269..0d3754e0b 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -229,6 +229,25 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.latestBlock = blocks[0]; }); + this.transactionTimesSubscription = this.transactionTimes$.pipe( + tap(() => { + this.isLoadingFirstSeen = true; + }), + switchMap((txid) => this.apiService.getTransactionTimes$([txid]).pipe( + retry({ count: 2, delay: 2000 }), + // Try again until we either get a valid response, or the transaction is confirmed + repeat({ delay: 2000 }), + filter((transactionTimes) => transactionTimes?.length && transactionTimes[0] > 0 && !this.tx.status?.confirmed), + take(1), + )), + ) + .subscribe((transactionTimes) => { + this.isLoadingFirstSeen = false; + if (transactionTimes?.length && transactionTimes[0]) { + this.transactionTime = transactionTimes[0]; + } + }); + this.fetchCpfpSubscription = this.fetchCpfp$ .pipe( switchMap((txId) => @@ -733,26 +752,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.accelerationPositions, ); }) - ) - - this.transactionTimesSubscription = this.transactionTimes$.pipe( - tap(() => { - this.isLoadingFirstSeen = true; - }), - switchMap((txid) => this.apiService.getTransactionTimes$([txid]).pipe( - retry({ count: 2, delay: 2000 }), - // Try again until we either get a valid response, or the transaction is confirmed - repeat({ delay: 2000 }), - filter((transactionTimes) => transactionTimes?.length && transactionTimes[0] > 0 && !this.tx.status?.confirmed), - take(1), - )), - ) - .subscribe((transactionTimes) => { - this.isLoadingFirstSeen = false; - if (transactionTimes?.length && transactionTimes[0]) { - this.transactionTime = transactionTimes[0]; - } - }); + ); } ngAfterViewInit(): void {