From 6a5871769420e8a3a60c84fb70603b444b877e78 Mon Sep 17 00:00:00 2001 From: softsimon Date: Fri, 8 Jan 2021 21:44:36 +0700 Subject: [PATCH] Loading progressbar for loading address, block transactions and blocks --- backend/src/api/bitcoin/electrum-api.ts | 12 ++++++++---- backend/src/routes.ts | 12 +++++++++++- .../app/components/address/address.component.html | 8 ++++++++ .../src/app/components/address/address.component.ts | 13 ++++++++++--- .../src/app/components/block/block.component.html | 7 +++++++ .../src/app/components/block/block.component.ts | 11 +++++++++-- .../latest-blocks/latest-blocks.component.html | 11 ++++++++++- .../latest-blocks/latest-blocks.component.ts | 7 +++++++ 8 files changed, 70 insertions(+), 11 deletions(-) diff --git a/backend/src/api/bitcoin/electrum-api.ts b/backend/src/api/bitcoin/electrum-api.ts index b8bcc90d7..f63516d8a 100644 --- a/backend/src/api/bitcoin/electrum-api.ts +++ b/backend/src/api/bitcoin/electrum-api.ts @@ -10,6 +10,7 @@ import logger from '../../logger'; import * as ElectrumClient from '@mempool/electrum-client'; import * as sha256 from 'crypto-js/sha256'; import * as hexEnc from 'crypto-js/enc-hex'; +import loadingIndicators from '../loading-indicators'; class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi { private electrumClient: any; @@ -121,6 +122,8 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi { } try { + loadingIndicators.setProgress('address-' + address, 0); + const transactions: IEsploraApi.Transaction[] = []; const history = await this.$getScriptHashHistory(addressInfo.scriptPubKey); history.reverse(); @@ -132,16 +135,17 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi { startingIndex = pos + 1; } } + const endIndex = Math.min(startingIndex + 10, history.length); - for (let i = startingIndex; i < Math.min(startingIndex + 10, history.length); i++) { + for (let i = startingIndex; i < endIndex; i++) { const tx = await this.$getRawTransaction(history[i].tx_hash, false, true); - if (tx) { - transactions.push(tx); - } + transactions.push(tx); + loadingIndicators.setProgress('address-' + address, (i + 1) / endIndex * 100); } return transactions; } catch (e) { + loadingIndicators.setProgress('address-' + address, 100); if (e === 'failed to get confirmed status') { e = 'The number of transactions on this address exceeds the Electrum server limit'; } diff --git a/backend/src/routes.ts b/backend/src/routes.ts index d770aea8d..390d11ddd 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -16,6 +16,7 @@ import logger from './logger'; import bitcoinApi from './api/bitcoin/bitcoin-api-factory'; import transactionUtils from './api/transaction-utils'; import blocks from './api/blocks'; +import loadingIndicators from './api/loading-indicators'; class Routes { private cache: { [date: string]: OptimizedStatistic[] } = { @@ -553,6 +554,8 @@ class Routes { public async getBlocks(req: Request, res: Response) { try { + loadingIndicators.setProgress('blocks', 0); + const returnBlocks: IEsploraApi.Block[] = []; const fromHeight = parseInt(req.params.height, 10) || blocks.getCurrentBlockHeight(); @@ -576,28 +579,35 @@ class Routes { returnBlocks.push(block); nextHash = block.previousblockhash; } + loadingIndicators.setProgress('blocks', i / 10 * 100); } res.json(returnBlocks); } catch (e) { + loadingIndicators.setProgress('blocks', 100); res.status(500).send(e.message || e); } } public async getBlockTransactions(req: Request, res: Response) { try { + loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0); + const txIds = await bitcoinApi.$getTxIdsForBlock(req.params.hash); const transactions: TransactionExtended[] = []; const startingIndex = Math.max(0, parseInt(req.params.index, 10)); - for (let i = startingIndex; i < Math.min(startingIndex + 10, txIds.length); i++) { + const endIndex = Math.min(startingIndex + 10, txIds.length); + for (let i = startingIndex; i < endIndex; i++) { const transaction = await transactionUtils.$getTransactionExtended(txIds[i], false, true); if (transaction) { transactions.push(transaction); + loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100); } } res.json(transactions); } catch (e) { + loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100); res.status(500).send(e.message || e); } } diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html index e5a0ffd0a..49280f471 100644 --- a/frontend/src/app/components/address/address.component.html +++ b/frontend/src/app/components/address/address.component.html @@ -67,6 +67,14 @@ + + +
+
+
+
+
+ diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index f1d21d3b9..601d422d6 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -1,13 +1,13 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { ElectrsApiService } from '../../services/electrs-api.service'; -import { switchMap, filter, catchError } from 'rxjs/operators'; +import { switchMap, filter, catchError, map, tap } from 'rxjs/operators'; import { Address, Transaction } from '../../interfaces/electrs.interface'; import { WebsocketService } from 'src/app/services/websocket.service'; import { StateService } from 'src/app/services/state.service'; import { AudioService } from 'src/app/services/audio.service'; import { ApiService } from 'src/app/services/api.service'; -import { of, merge, Subscription } from 'rxjs'; +import { of, merge, Subscription, Observable } from 'rxjs'; import { SeoService } from 'src/app/services/seo.service'; @Component({ @@ -25,6 +25,7 @@ export class AddressComponent implements OnInit, OnDestroy { isLoadingTransactions = true; error: any; mainSubscription: Subscription; + addressLoadingStatus$: Observable; totalConfirmedTxCount = 0; loadedConfirmedTxCount = 0; @@ -48,7 +49,13 @@ export class AddressComponent implements OnInit, OnDestroy { ngOnInit() { this.stateService.networkChanged$.subscribe((network) => this.network = network); - this.websocketService.want(['blocks', 'mempool-blocks']); + this.websocketService.want(['blocks']); + + this.addressLoadingStatus$ = this.route.paramMap + .pipe( + switchMap(() => this.stateService.loadingIndicators$), + map((indicators) => indicators['address-' + this.addressString] !== undefined ? indicators['address-' + this.addressString] : 0) + ); this.mainSubscription = this.route.paramMap .pipe( diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 8b0f852c9..85daa10bd 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -112,6 +112,13 @@ + + +
+
+
+
+
diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 3c9b3bf08..99b66de77 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -2,9 +2,9 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { Location } from '@angular/common'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { ElectrsApiService } from '../../services/electrs-api.service'; -import { switchMap, tap, debounceTime, catchError } from 'rxjs/operators'; +import { switchMap, tap, debounceTime, catchError, map } from 'rxjs/operators'; import { Block, Transaction, Vout } from '../../interfaces/electrs.interface'; -import { of, Subscription } from 'rxjs'; +import { Observable, of, Subscription } from 'rxjs'; import { StateService } from '../../services/state.service'; import { SeoService } from 'src/app/services/seo.service'; import { WebsocketService } from 'src/app/services/websocket.service'; @@ -31,6 +31,7 @@ export class BlockComponent implements OnInit, OnDestroy { coinbaseTx: Transaction; page = 1; itemsPerPage: number; + txsLoadingStatus$: Observable; constructor( private route: ActivatedRoute, @@ -48,6 +49,12 @@ export class BlockComponent implements OnInit, OnDestroy { this.network = this.stateService.network; this.itemsPerPage = this.stateService.env.ELECTRS_ITEMS_PER_PAGE; + this.txsLoadingStatus$ = this.route.paramMap + .pipe( + switchMap(() => this.stateService.loadingIndicators$), + map((indicators) => indicators['blocktxs-' + this.blockHash] !== undefined ? indicators['blocktxs-' + this.blockHash] : 0) + ); + this.subscription = this.route.paramMap .pipe( switchMap((params: ParamMap) => { diff --git a/frontend/src/app/components/latest-blocks/latest-blocks.component.html b/frontend/src/app/components/latest-blocks/latest-blocks.component.html index 5c8853158..882442ec4 100644 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.html +++ b/frontend/src/app/components/latest-blocks/latest-blocks.component.html @@ -33,7 +33,16 @@ - + + + +
+
+
+ + +
+ diff --git a/frontend/src/app/components/latest-blocks/latest-blocks.component.ts b/frontend/src/app/components/latest-blocks/latest-blocks.component.ts index ccc898355..eb8b8236d 100644 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.ts +++ b/frontend/src/app/components/latest-blocks/latest-blocks.component.ts @@ -5,6 +5,7 @@ import { Block } from '../../interfaces/electrs.interface'; import { Subscription, Observable, merge, of } from 'rxjs'; import { SeoService } from '../../services/seo.service'; import { WebsocketService } from 'src/app/services/websocket.service'; +import { map } from 'rxjs/operators'; @Component({ selector: 'app-latest-blocks', @@ -19,6 +20,7 @@ export class LatestBlocksComponent implements OnInit, OnDestroy { blockSubscription: Subscription; isLoading = true; interval: any; + blocksLoadingStatus$: Observable; latestBlockHeight: number; @@ -39,6 +41,11 @@ export class LatestBlocksComponent implements OnInit, OnDestroy { this.network$ = merge(of(''), this.stateService.networkChanged$); + this.blocksLoadingStatus$ = this.stateService.loadingIndicators$ + .pipe( + map((indicators) => indicators['blocks'] !== undefined ? indicators['blocks'] : 0) + ); + this.blockSubscription = this.stateService.blocks$ .subscribe(([block]) => { if (block === null || !this.blocks.length) {