mirror of
https://github.com/mempool/mempool.git
synced 2024-11-20 10:21:52 +01:00
Refactord blockchain is rendering, block arrow location propagation and keynavigation.
This commit is contained in:
parent
69827843c9
commit
78e41fc3d3
@ -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: [],
|
||||
|
@ -1,9 +1,5 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<div style="position: relative;">
|
||||
<app-blockchain position="top" [markHeight]="blockHeight"></app-blockchain>
|
||||
</div>
|
||||
|
||||
<div class="title-block">
|
||||
<h1>Block <ng-template [ngIf]="blockHeight"><a [routerLink]="['/block/', blockHash]">{{ blockHeight }}</a></ng-template></h1>
|
||||
</div>
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="text-center" class="blockchain-wrapper">
|
||||
<div class="position-container">
|
||||
<app-mempool-blocks [markIndex]="markMempoolBlockIndex" [txFeePerVSize]="markHeight ? 0 : txFeePerVSize"></app-mempool-blocks>
|
||||
<app-blockchain-blocks [markHeight]="markHeight"></app-blockchain-blocks>
|
||||
<app-mempool-blocks></app-mempool-blocks>
|
||||
<app-blockchain-blocks></app-blockchain-blocks>
|
||||
|
||||
<div id="divider" *ngIf="!isLoading; else loadingTmpl"></div>
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -1,3 +1,7 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<hr>
|
||||
|
||||
<table class="table table-borderless" [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
|
||||
<thead>
|
||||
<th style="width: 15%;">Height</th>
|
||||
@ -30,3 +34,5 @@
|
||||
</ng-template>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
@ -1,9 +1,5 @@
|
||||
<div class="container-xl" *ngIf="mempoolBlock">
|
||||
|
||||
<div style="position: relative;">
|
||||
<app-blockchain position="top" [markMempoolBlockIndex]="mempoolBlockIndex"></app-blockchain>
|
||||
</div>
|
||||
|
||||
<div class="title-block">
|
||||
<h1>Mempool block</h1>
|
||||
</div>
|
||||
|
@ -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({});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -1,9 +1,5 @@
|
||||
<div class="container-xl">
|
||||
<div style="position: relative;">
|
||||
<app-blockchain></app-blockchain>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<app-blockchain></app-blockchain>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<app-latest-blocks></app-latest-blocks>
|
||||
|
||||
</div>
|
||||
<router-outlet></router-outlet>
|
||||
|
@ -1,11 +1,5 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<div style="position: relative;">
|
||||
<app-blockchain position="top" [txFeePerVSize]="tx?.fee / (tx?.weight / 4)" [markHeight]="tx?.status?.block_height"></app-blockchain>
|
||||
</div>
|
||||
|
||||
<div class="transaction-content">
|
||||
|
||||
<div class="title-block">
|
||||
<h1 class="float-md-left">Transaction</h1>
|
||||
|
||||
@ -181,6 +175,5 @@
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
@ -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({});
|
||||
}
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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<boolean>(false);
|
||||
connectionState$ = new BehaviorSubject<0 | 1 | 2>(2);
|
||||
|
||||
markBlock$ = new Subject<MarkBlockState>();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user