diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 5f0b1da37..db0206921 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -9,6 +9,7 @@ import { AboutComponent } from './components/about/about.component'; import { TelevisionComponent } from './components/television/television.component'; import { StatisticsComponent } from './components/statistics/statistics.component'; import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component'; +import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component'; const routes: Routes = [ { @@ -18,6 +19,24 @@ const routes: Routes = [ { path: '', component: StartComponent, + children: [ + { + path: '', + component: LatestBlocksComponent + }, + { + path: 'tx/:id', + component: TransactionComponent + }, + { + path: 'block/:id', + component: BlockComponent + }, + { + path: 'mempool-block/:id', + component: MempoolBlockComponent + }, + ], }, { path: 'graphs', @@ -27,21 +46,6 @@ const routes: Routes = [ path: 'contributors', component: AboutComponent, }, - { - path: 'tx/:id', - children: [], - component: TransactionComponent - }, - { - path: 'block/:id', - children: [], - component: BlockComponent - }, - { - path: 'mempool-block/:id', - children: [], - component: MempoolBlockComponent - }, { path: 'address/:id', children: [], diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 3a240198f..0b4449bbf 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -1,9 +1,5 @@
-
- -
-

Block {{ blockHeight }}

diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index d415dab34..2b2a1637f 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -1,9 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { ElectrsApiService } from '../../services/electrs-api.service'; -import { switchMap } from 'rxjs/operators'; +import { switchMap, tap, debounceTime } from 'rxjs/operators'; import { Block, Transaction, Vout } from '../../interfaces/electrs.interface'; -import { of } from 'rxjs'; +import { of, Observable } from 'rxjs'; import { StateService } from '../../services/state.service'; import { WebsocketService } from 'src/app/services/websocket.service'; @@ -12,7 +12,7 @@ import { WebsocketService } from 'src/app/services/websocket.service'; templateUrl: './block.component.html', styleUrls: ['./block.component.scss'] }) -export class BlockComponent implements OnInit { +export class BlockComponent implements OnInit, OnDestroy { block: Block; blockHeight: number; blockHash: string; @@ -34,7 +34,8 @@ export class BlockComponent implements OnInit { ngOnInit() { this.websocketService.want(['blocks', 'stats', 'mempool-blocks']); - this.route.paramMap.pipe( + this.route.paramMap + .pipe( switchMap((params: ParamMap) => { const blockHash: string = params.get('id') || ''; this.error = undefined; @@ -54,17 +55,28 @@ export class BlockComponent implements OnInit { this.isLoadingBlock = true; return this.electrsApiService.getBlock$(blockHash); } - }) + }), + tap((block: Block) => { + this.block = block; + this.blockHeight = block.height; + this.isLoadingBlock = false; + this.setBlockSubsidy(); + if (block.reward) { + this.fees = block.reward / 100000000 - this.blockSubsidy; + } + this.stateService.markBlock$.next({ blockHeight: this.blockHeight }); + this.isLoadingTransactions = true; + this.transactions = null; + }), + debounceTime(250), + switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id)) ) - .subscribe((block: Block) => { - this.block = block; - this.blockHeight = block.height; - this.isLoadingBlock = false; - this.setBlockSubsidy(); - if (block.reward) { - this.fees = block.reward / 100000000 - this.blockSubsidy; + .subscribe((transactions: Transaction[]) => { + if (this.fees === undefined) { + this.fees = transactions[0].vout.reduce((acc: number, curr: Vout) => acc + curr.value, 0) / 100000000 - this.blockSubsidy; } - this.getBlockTransactions(block.id); + this.transactions = transactions; + this.isLoadingTransactions = false; }, (error) => { this.error = error; @@ -75,6 +87,10 @@ export class BlockComponent implements OnInit { .subscribe((block) => this.latestBlock = block); } + ngOnDestroy() { + this.stateService.markBlock$.next({}); + } + setBlockSubsidy() { this.blockSubsidy = 50; let halvenings = Math.floor(this.block.height / 210000); @@ -84,19 +100,6 @@ export class BlockComponent implements OnInit { } } - getBlockTransactions(hash: string) { - this.isLoadingTransactions = true; - this.transactions = null; - this.electrsApiService.getBlockTransactions$(hash) - .subscribe((transactions: any) => { - if (this.fees === undefined) { - this.fees = transactions[0].vout.reduce((acc: number, curr: Vout) => acc + curr.value, 0) / 100000000 - this.blockSubsidy; - } - this.transactions = transactions; - this.isLoadingTransactions = false; - }); - } - loadMore() { if (this.isLoadingTransactions || !this.transactions.length || this.transactions.length === this.block.tx_count) { return; diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 8dfbd6c88..1e63e9c53 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, Input, OnChanges, HostListener } from '@angular/core'; +import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; import { Subscription } from 'rxjs'; import { Block } from 'src/app/interfaces/electrs.interface'; import { StateService } from 'src/app/services/state.service'; @@ -9,10 +9,9 @@ import { Router } from '@angular/router'; templateUrl: './blockchain-blocks.component.html', styleUrls: ['./blockchain-blocks.component.scss'] }) -export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { - @Input() markHeight = 0; - +export class BlockchainBlocksComponent implements OnInit, OnDestroy { blocks: Block[] = []; + markHeight: number; blocksSubscription: Subscription; interval: any; @@ -37,10 +36,15 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.moveArrowToPosition(true); }); - } - ngOnChanges() { - this.moveArrowToPosition(false); + this.stateService.markBlock$ + .subscribe((state) => { + this.markHeight = undefined; + if (state.blockHeight) { + this.markHeight = state.blockHeight; + } + this.moveArrowToPosition(false); + }); } ngOnDestroy() { diff --git a/frontend/src/app/components/blockchain/blockchain.component.html b/frontend/src/app/components/blockchain/blockchain.component.html index 239013883..d8d7c3882 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.html +++ b/frontend/src/app/components/blockchain/blockchain.component.html @@ -1,7 +1,7 @@
- - + +
diff --git a/frontend/src/app/components/blockchain/blockchain.component.ts b/frontend/src/app/components/blockchain/blockchain.component.ts index 4660ab70f..80901c0bc 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.ts +++ b/frontend/src/app/components/blockchain/blockchain.component.ts @@ -1,8 +1,7 @@ -import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; import { StateService } from 'src/app/services/state.service'; -import { Router } from '@angular/router'; @Component({ selector: 'app-blockchain', @@ -10,12 +9,6 @@ import { Router } from '@angular/router'; styleUrls: ['./blockchain.component.scss'] }) export class BlockchainComponent implements OnInit, OnDestroy { - @Input() position: 'middle' | 'top' = 'middle'; - @Input() markHeight: number; - @Input() txFeePerVSize: number; - @Input() markMempoolBlockIndex = -1; - - txTrackingSubscription: Subscription; blocksSubscription: Subscription; txTrackingLoading = false; @@ -24,7 +17,6 @@ export class BlockchainComponent implements OnInit, OnDestroy { constructor( private stateService: StateService, - private router: Router, ) {} ngOnInit() { 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 7061f8dda..ffdbb79ca 100644 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.html +++ b/frontend/src/app/components/latest-blocks/latest-blocks.component.html @@ -1,3 +1,7 @@ +
+ +
+ @@ -30,3 +34,5 @@
Height
+ +
\ No newline at end of file diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.html b/frontend/src/app/components/mempool-block/mempool-block.component.html index 4840065d0..4c2a66937 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.html +++ b/frontend/src/app/components/mempool-block/mempool-block.component.html @@ -1,9 +1,5 @@
-
- -
-

Mempool block

diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.ts b/frontend/src/app/components/mempool-block/mempool-block.component.ts index 869ba0db0..bf299165f 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.ts +++ b/frontend/src/app/components/mempool-block/mempool-block.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { StateService } from 'src/app/services/state.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { switchMap, map } from 'rxjs/operators'; @@ -10,7 +10,7 @@ import { WebsocketService } from 'src/app/services/websocket.service'; templateUrl: './mempool-block.component.html', styleUrls: ['./mempool-block.component.scss'] }) -export class MempoolBlockComponent implements OnInit { +export class MempoolBlockComponent implements OnInit, OnDestroy { mempoolBlockIndex: number; mempoolBlock: MempoolBlock; @@ -26,6 +26,7 @@ export class MempoolBlockComponent implements OnInit { this.route.paramMap.pipe( switchMap((params: ParamMap) => { this.mempoolBlockIndex = parseInt(params.get('id'), 10) || 0; + this.stateService.markBlock$.next({ mempoolBlockIndex: this.mempoolBlockIndex }); return this.stateService.mempoolBlocks$ .pipe( map((mempoolBlocks) => mempoolBlocks[this.mempoolBlockIndex]) @@ -37,4 +38,8 @@ export class MempoolBlockComponent implements OnInit { }); } + ngOnDestroy(): void { + this.stateService.markBlock$.next({}); + } + } diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index 8f159570f..7e507a31b 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, Input, OnChanges, HostListener } from '@angular/core'; +import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; import { Subscription } from 'rxjs'; import { MempoolBlock } from 'src/app/interfaces/websocket.interface'; import { StateService } from 'src/app/services/state.service'; @@ -9,7 +9,7 @@ import { Router } from '@angular/router'; templateUrl: './mempool-blocks.component.html', styleUrls: ['./mempool-blocks.component.scss'] }) -export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { +export class MempoolBlocksComponent implements OnInit, OnDestroy { mempoolBlocks: MempoolBlock[]; mempoolBlocksFull: MempoolBlock[]; mempoolBlocksSubscription: Subscription; @@ -21,8 +21,8 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { rightPosition = 0; transition = '1s'; - @Input() txFeePerVSize: number; - @Input() markIndex: number; + markIndex: number; + txFeePerVSize: number; constructor( private router: Router, @@ -36,11 +36,23 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(blocks); this.calculateTransactionPosition(); }); + + this.stateService.markBlock$ + .subscribe((state) => { + this.markIndex = undefined; + this.txFeePerVSize = undefined; + if (state.mempoolBlockIndex !== undefined) { + this.markIndex = state.mempoolBlockIndex; + } + if (state.txFeePerVSize) { + this.txFeePerVSize = state.txFeePerVSize; + } + this.calculateTransactionPosition(); + }); } @HostListener('window:resize', ['$event']) onResize() { - console.log('onResize'); if (this.mempoolBlocks.length) { this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(JSON.stringify(this.mempoolBlocksFull))); } @@ -48,29 +60,27 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { @HostListener('document:keydown', ['$event']) handleKeyboardEvents(event: KeyboardEvent) { - if (this.markIndex === -1) { - return; - } - if (event.key === 'ArrowRight') { - if (this.mempoolBlocks[this.markIndex - 1]) { - this.router.navigate(['/mempool-block/', this.markIndex - 1]); - } else { - this.stateService.blocks$ - .subscribe((block) => { - if (this.stateService.latestBlockHeight === block.height) { - this.router.navigate(['/block/', block.id], { state: { data: { block } }}); - } - }); + setTimeout(() => { + if (this.markIndex === undefined) { + return; } - } else if (event.key === 'ArrowLeft') { - if (this.mempoolBlocks[this.markIndex + 1]) { - this.router.navigate(['/mempool-block/', this.markIndex + 1]); + if (event.key === 'ArrowRight') { + if (this.mempoolBlocks[this.markIndex - 1]) { + this.router.navigate(['/mempool-block/', this.markIndex - 1]); + } else { + this.stateService.blocks$ + .subscribe((block) => { + if (this.stateService.latestBlockHeight === block.height) { + this.router.navigate(['/block/', block.id], { state: { data: { block } }}); + } + }); + } + } else if (event.key === 'ArrowLeft') { + if (this.mempoolBlocks[this.markIndex + 1]) { + this.router.navigate(['/mempool-block/', this.markIndex + 1]); + } } - } - } - - ngOnChanges() { - this.calculateTransactionPosition(); + }); } ngOnDestroy() { diff --git a/frontend/src/app/components/start/start.component.html b/frontend/src/app/components/start/start.component.html index 62b0dca21..7d0da80cc 100644 --- a/frontend/src/app/components/start/start.component.html +++ b/frontend/src/app/components/start/start.component.html @@ -1,9 +1,5 @@ -
-
- -
+
+ +
-
- - -
\ No newline at end of file + diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 9d46bc76e..0c0b3912e 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -1,11 +1,5 @@
-
- -
- -
-

Transaction

@@ -181,6 +175,5 @@
-

diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index bb4fe639a..bbc27768c 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -65,6 +65,11 @@ export class TransactionComponent implements OnInit, OnDestroy { } else { this.findBlockAndSetFeeRating(); } + if (this.tx.status.confirmed) { + this.stateService.markBlock$.next({ blockHeight: tx.status.block_height }); + } else { + this.stateService.markBlock$.next({ txFeePerVSize: tx.fee / (tx.weight / 4) }); + } }, (error) => { this.error = error; @@ -82,6 +87,7 @@ export class TransactionComponent implements OnInit, OnDestroy { block_hash: block.id, block_time: block.timestamp, }; + this.stateService.markBlock$.next({ blockHeight: block.height }); this.audioService.playSound('magic'); this.findBlockAndSetFeeRating(); }); @@ -121,5 +127,6 @@ export class TransactionComponent implements OnInit, OnDestroy { ngOnDestroy() { this.websocketService.startTrackTransaction('stop'); + this.stateService.markBlock$.next({}); } } diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index ebf8188ca..56b7c90c9 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -1,8 +1,3 @@ - -export interface BlockTransaction { - f: number; -} - export interface OptimizedMempoolStats { id: number; added: string; @@ -13,7 +8,3 @@ export interface OptimizedMempoolStats { mempool_byte_weight: number; vsizes: number[] | string[]; } - -interface FeeData { - vsize: { [ fee: string ]: number }; -} diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 265434943..98a602ffd 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -4,6 +4,12 @@ import { Block, Transaction } from '../interfaces/electrs.interface'; import { MempoolBlock, MemPoolState } from '../interfaces/websocket.interface'; import { OptimizedMempoolStats } from '../interfaces/node-api.interface'; +interface MarkBlockState { + blockHeight?: number; + mempoolBlockIndex?: number; + txFeePerVSize?: number; +} + @Injectable({ providedIn: 'root' }) @@ -21,4 +27,6 @@ export class StateService { viewFiat$ = new BehaviorSubject(false); connectionState$ = new BehaviorSubject<0 | 1 | 2>(2); + + markBlock$ = new Subject(); }