mirror of
https://github.com/mempool/mempool.git
synced 2025-01-18 05:12:35 +01:00
Replacing footer and latest blocks with a stats dashboard.
This commit is contained in:
parent
8146939f0f
commit
6c1d28a9ac
@ -7,6 +7,7 @@ import { Common } from './common';
|
||||
class Blocks {
|
||||
private blocks: Block[] = [];
|
||||
private currentBlockHeight = 0;
|
||||
private lastDifficultyAdjustmentTime = 0;
|
||||
private newBlockCallback: ((block: Block, txIds: string[], transactions: TransactionExtended[]) => void) | undefined;
|
||||
|
||||
constructor() { }
|
||||
@ -38,6 +39,13 @@ class Blocks {
|
||||
this.currentBlockHeight = blockHeightTip - config.INITIAL_BLOCK_AMOUNT;
|
||||
}
|
||||
|
||||
if (!this.lastDifficultyAdjustmentTime) {
|
||||
const heightDiff = blockHeightTip % 2016;
|
||||
const blockHash = await bitcoinApi.getBlockHash(blockHeightTip - heightDiff);
|
||||
const block = await bitcoinApi.getBlock(blockHash);
|
||||
this.lastDifficultyAdjustmentTime = block.timestamp;
|
||||
}
|
||||
|
||||
while (this.currentBlockHeight < blockHeightTip) {
|
||||
if (this.currentBlockHeight === 0) {
|
||||
this.currentBlockHeight = blockHeightTip;
|
||||
@ -78,6 +86,10 @@ class Blocks {
|
||||
block.medianFee = transactions.length > 1 ? Common.median(transactions.map((tx) => tx.feePerVsize)) : 0;
|
||||
block.feeRange = transactions.length > 1 ? Common.getFeesInRange(transactions.slice(0, transactions.length - 1), 8) : [0, 0];
|
||||
|
||||
if (block.height % 2016 === 0) {
|
||||
this.lastDifficultyAdjustmentTime = block.timestamp;
|
||||
}
|
||||
|
||||
this.blocks.push(block);
|
||||
if (this.blocks.length > config.KEEP_BLOCK_AMOUNT) {
|
||||
this.blocks.shift();
|
||||
@ -93,6 +105,10 @@ class Blocks {
|
||||
}
|
||||
}
|
||||
|
||||
public getLastDifficultyAdjustmentTime(): number {
|
||||
return this.lastDifficultyAdjustmentTime;
|
||||
}
|
||||
|
||||
private stripCoinbaseTransaction(tx: TransactionExtended): TransactionMinerInfo {
|
||||
return {
|
||||
vin: [{
|
||||
|
@ -84,6 +84,7 @@ class WebsocketHandler {
|
||||
client.send(JSON.stringify({
|
||||
'mempoolInfo': memPool.getMempoolInfo(),
|
||||
'vBytesPerSecond': memPool.getVBytesPerSecond(),
|
||||
'lastDifficultyAdjustment': blocks.getLastDifficultyAdjustmentTime(),
|
||||
'blocks': _blocks.slice(Math.max(_blocks.length - config.INITIAL_BLOCK_AMOUNT, 0)),
|
||||
'conversions': fiatConversion.getTickers()['BTCUSD'],
|
||||
'mempool-blocks': mempoolBlocks.getMempoolBlocks(),
|
||||
@ -270,6 +271,7 @@ class WebsocketHandler {
|
||||
const response = {
|
||||
'block': block,
|
||||
'mempoolInfo': memPool.getMempoolInfo(),
|
||||
'lastDifficultyAdjustment': blocks.getLastDifficultyAdjustmentTime(),
|
||||
};
|
||||
|
||||
if (mBlocks && client['want-mempool-blocks']) {
|
||||
|
@ -9,10 +9,10 @@ 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';
|
||||
import { AssetComponent } from './components/asset/asset.component';
|
||||
import { AssetsComponent } from './assets/assets.component';
|
||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -25,7 +25,7 @@ const routes: Routes = [
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: LatestBlocksComponent
|
||||
component: DashboardComponent,
|
||||
},
|
||||
{
|
||||
path: 'tx/:id',
|
||||
@ -69,7 +69,7 @@ const routes: Routes = [
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: LatestBlocksComponent
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path: 'tx/:id',
|
||||
@ -134,7 +134,7 @@ const routes: Routes = [
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: LatestBlocksComponent
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path: 'tx/:id',
|
||||
|
@ -16,7 +16,6 @@ import { StateService } from './services/state.service';
|
||||
import { BlockComponent } from './components/block/block.component';
|
||||
import { AddressComponent } from './components/address/address.component';
|
||||
import { SearchFormComponent } from './components/search-form/search-form.component';
|
||||
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
|
||||
import { WebsocketService } from './services/websocket.service';
|
||||
import { AddressLabelsComponent } from './components/address-labels/address-labels.component';
|
||||
import { MempoolBlocksComponent } from './components/mempool-blocks/mempool-blocks.component';
|
||||
@ -41,6 +40,7 @@ import { MinerComponent } from './components/miner/miner.component';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -58,7 +58,6 @@ import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||
AddressComponent,
|
||||
AmountComponent,
|
||||
SearchFormComponent,
|
||||
LatestBlocksComponent,
|
||||
TimespanComponent,
|
||||
AddressLabelsComponent,
|
||||
MempoolBlocksComponent,
|
||||
@ -72,6 +71,7 @@ import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||
MinerComponent,
|
||||
StatusViewComponent,
|
||||
FeesBoxComponent,
|
||||
DashboardComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -48,7 +48,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
|
||||
ngOnInit() {
|
||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||
this.websocketService.want(['blocks', 'stats', 'mempool-blocks']);
|
||||
this.websocketService.want(['blocks', 'mempool-blocks']);
|
||||
|
||||
this.mainSubscription = this.route.paramMap
|
||||
.pipe(
|
||||
|
@ -53,7 +53,7 @@ export class AssetComponent implements OnInit, OnDestroy {
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.websocketService.want(['blocks', 'stats', 'mempool-blocks']);
|
||||
this.websocketService.want(['blocks', 'mempool-blocks']);
|
||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||
|
||||
this.mainSubscription = this.route.paramMap
|
||||
|
@ -1,39 +0,0 @@
|
||||
<app-fees-box *ngIf="(network$ | async) === ''" class="d-block mr-2 ml-2 mb-4"></app-fees-box>
|
||||
|
||||
<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>
|
||||
<th class="d-none d-md-block" style="width: 20%;">Timestamp</th>
|
||||
<th style="width: 20%;">Mined</th>
|
||||
<th class="d-none d-lg-block" style="width: 15%;">Transactions</th>
|
||||
<th style="width: 20%;">Filled</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
||||
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
|
||||
<td class="d-none d-md-block">{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago</td>
|
||||
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
|
||||
<td>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar progress-mempool {{ network$ | async }}" role="progressbar" [ngStyle]="{'width': (block.weight / 4000000)*100 + '%' }"></div>
|
||||
<div class="progress-text">{{ block.size | bytes: 2 }}</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<ng-template [ngIf]="isLoading">
|
||||
<tr *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td class="d-none d-md-block"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td class="d-none d-lg-block"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
@ -1,14 +0,0 @@
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.d-md-block {
|
||||
display: table-cell !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.d-lg-block {
|
||||
display: table-cell !important;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LatestBlocksComponent } from './latest-blocks.component';
|
||||
|
||||
describe('LatestBlocksComponent', () => {
|
||||
let component: LatestBlocksComponent;
|
||||
let fixture: ComponentFixture<LatestBlocksComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LatestBlocksComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LatestBlocksComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,111 +0,0 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { ElectrsApiService } from '../../services/electrs-api.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Block } from '../../interfaces/electrs.interface';
|
||||
import { Subscription, Observable, merge, of } from 'rxjs';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-latest-blocks',
|
||||
templateUrl: './latest-blocks.component.html',
|
||||
styleUrls: ['./latest-blocks.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class LatestBlocksComponent implements OnInit, OnDestroy {
|
||||
network$: Observable<string>;
|
||||
|
||||
blocks: any[] = [];
|
||||
blockSubscription: Subscription;
|
||||
isLoading = true;
|
||||
interval: any;
|
||||
|
||||
latestBlockHeight: number;
|
||||
|
||||
heightOfPageUntilBlocks = 430;
|
||||
heightOfBlocksTableChunk = 470;
|
||||
|
||||
constructor(
|
||||
private electrsApiService: ElectrsApiService,
|
||||
private stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
private cd: ChangeDetectorRef,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.seoService.resetTitle();
|
||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||
|
||||
this.blockSubscription = this.stateService.blocks$
|
||||
.subscribe(([block]) => {
|
||||
if (block === null || !this.blocks.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.latestBlockHeight = block.height;
|
||||
|
||||
if (block.height === this.blocks[0].height) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are out of sync, reload the blocks instead
|
||||
if (block.height > this.blocks[0].height + 1) {
|
||||
this.loadInitialBlocks();
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.height <= this.blocks[0].height) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.blocks.pop();
|
||||
this.blocks.unshift(block);
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
|
||||
this.loadInitialBlocks();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
this.blockSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
loadInitialBlocks() {
|
||||
this.electrsApiService.listBlocks$()
|
||||
.subscribe((blocks) => {
|
||||
this.blocks = blocks;
|
||||
this.isLoading = false;
|
||||
|
||||
this.latestBlockHeight = blocks[0].height;
|
||||
|
||||
const spaceForBlocks = window.innerHeight - this.heightOfPageUntilBlocks;
|
||||
const chunks = Math.ceil(spaceForBlocks / this.heightOfBlocksTableChunk) - 1;
|
||||
if (chunks > 0) {
|
||||
this.loadMore(chunks);
|
||||
}
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
loadMore(chunks = 0) {
|
||||
if (this.isLoading) {
|
||||
return;
|
||||
}
|
||||
this.isLoading = true;
|
||||
this.electrsApiService.listBlocks$(this.blocks[this.blocks.length - 1].height - 1)
|
||||
.subscribe((blocks) => {
|
||||
this.blocks = this.blocks.concat(blocks);
|
||||
this.isLoading = false;
|
||||
|
||||
const chunksLeft = chunks - 1;
|
||||
if (chunksLeft > 0) {
|
||||
this.loadMore(chunksLeft);
|
||||
}
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
trackByBlock(index: number, block: Block) {
|
||||
return block.height;
|
||||
}
|
||||
}
|
@ -66,10 +66,4 @@
|
||||
|
||||
<br>
|
||||
|
||||
<ng-container *ngIf="network.val !== 'bisq'">
|
||||
<br><br>
|
||||
|
||||
<app-footer></app-footer>
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
@ -108,10 +108,10 @@ export class StatisticsComponent implements OnInit {
|
||||
switchMap(() => {
|
||||
this.spinnerLoading = true;
|
||||
if (this.radioGroupForm.controls.dateSpan.value === '2h') {
|
||||
this.websocketService.want(['blocks', 'stats', 'live-2h-chart']);
|
||||
this.websocketService.want(['blocks', 'live-2h-chart']);
|
||||
return this.apiService.list2HStatistics$();
|
||||
}
|
||||
this.websocketService.want(['blocks', 'stats']);
|
||||
this.websocketService.want(['blocks']);
|
||||
if (this.radioGroupForm.controls.dateSpan.value === '24h') {
|
||||
return this.apiService.list24HStatistics$();
|
||||
}
|
||||
|
51
frontend/src/app/dashboard/dashboard.component.html
Normal file
51
frontend/src/app/dashboard/dashboard.component.html
Normal file
@ -0,0 +1,51 @@
|
||||
<app-fees-box *ngIf="(network$ | async) === ''" class="d-block mr-2 ml-2 mb-5"></app-fees-box>
|
||||
|
||||
<div class="container-xl">
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2" *ngIf="mempoolInfoData$ | async as mempoolInfoData">
|
||||
<div class="col mb-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mempoolSize">Mempool size</h5>
|
||||
<p class="card-text" *ngIf="(mempoolBlocksData$ | async) as mempoolBlocksData">{{ mempoolBlocksData.size | bytes }} ({{ mempoolBlocksData.blocks }} block<span [hidden]="mempoolBlocksData.blocks <= 1">s</span>)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mb-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title unconfirmedTx">Unconfirmed transactions</h5>
|
||||
<p class="card-text">{{ mempoolInfoData.memPoolInfo.size | number }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mb-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title txWeightPerSecond">Tx weight per second</h5>
|
||||
<span *ngIf="mempoolInfoData.vBytesPerSecond === 0; else inSync">
|
||||
<span class="badge badge-pill badge-warning">Backend is synchronizing</span>
|
||||
</span>
|
||||
<ng-template #inSync>
|
||||
<div class="progress sub-text">
|
||||
<div class="progress-bar {{ mempoolInfoData.progressClass }}" style="padding: 4px;" role="progressbar" [ngStyle]="{'width': mempoolInfoData.progressWidth}">{{ mempoolInfoData.vBytesPerSecond | ceil | number }} vB/s</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mb-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title txPerSecond">Difficulty Epoch</h5>
|
||||
<div class="progress" *ngIf="(difficultyEpoch$ | async) as epochData">
|
||||
<div class="progress-bar" role="progressbar" style="width: 15%; background-color: #105fb0" [ngStyle]="{'width': epochData.base}"></div>
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: 0%" [ngStyle]="{'width': epochData.green}"></div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: 1%; background-color: #f14d80;" [ngStyle]="{'width': epochData.red}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
36
frontend/src/app/dashboard/dashboard.component.scss
Normal file
36
frontend/src/app/dashboard/dashboard.component.scss
Normal file
@ -0,0 +1,36 @@
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
}
|
||||
|
||||
.txWeightPerSecond {
|
||||
color: #4a9ff4;
|
||||
}
|
||||
|
||||
.mempoolSize {
|
||||
color: #4a68b9;
|
||||
}
|
||||
|
||||
.txPerSecond {
|
||||
color: #f4bb4a;;
|
||||
}
|
||||
|
||||
.unconfirmedTx {
|
||||
color: #f14d80;
|
||||
}
|
||||
|
||||
.info-block {
|
||||
float: left;
|
||||
width: 350px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
display: inline-flex;
|
||||
width: 250px;
|
||||
background-color: #2d3348;
|
||||
height: 1.1rem;
|
||||
}
|
||||
|
||||
.bg-warning {
|
||||
background-color: #b58800 !important;
|
||||
}
|
114
frontend/src/app/dashboard/dashboard.component.ts
Normal file
114
frontend/src/app/dashboard/dashboard.component.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { combineLatest, merge, Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { MempoolInfo } from '../interfaces/websocket.interface';
|
||||
import { StateService } from '../services/state.service';
|
||||
|
||||
interface MempoolBlocksData {
|
||||
blocks: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
interface EpochProgress {
|
||||
base: string;
|
||||
green: string;
|
||||
red: string;
|
||||
}
|
||||
|
||||
interface MempoolInfoData {
|
||||
memPoolInfo: MempoolInfo;
|
||||
vBytesPerSecond: number;
|
||||
progressWidth: string;
|
||||
progressClass: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
network$: Observable<string>;
|
||||
mempoolBlocksData$: Observable<MempoolBlocksData>;
|
||||
latestBlockHeight$: Observable<number>;
|
||||
mempoolInfoData$: Observable<MempoolInfoData>;
|
||||
difficultyEpoch$: Observable<EpochProgress>;
|
||||
vBytesPerSecondLimit = 1667;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||
|
||||
this.mempoolInfoData$ = combineLatest([
|
||||
this.stateService.mempoolInfo$,
|
||||
this.stateService.vbytesPerSecond$
|
||||
])
|
||||
.pipe(
|
||||
map(([mempoolInfo, vbytesPerSecond]) => {
|
||||
const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100);
|
||||
|
||||
let progressClass = 'bg-danger';
|
||||
if (percent <= 75) {
|
||||
progressClass = 'bg-success';
|
||||
} else if (percent <= 99) {
|
||||
progressClass = 'bg-warning';
|
||||
}
|
||||
|
||||
return {
|
||||
memPoolInfo: mempoolInfo,
|
||||
vBytesPerSecond: vbytesPerSecond,
|
||||
progressWidth: percent + '%',
|
||||
progressClass: progressClass,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.difficultyEpoch$ = combineLatest([
|
||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||
this.stateService.lastDifficultyAdjustment$
|
||||
])
|
||||
.pipe(
|
||||
map(([block, DATime]) => {
|
||||
const now = new Date().getTime() / 1000;
|
||||
const diff = now - DATime;
|
||||
const blocksInEpoch = block.height % 2016;
|
||||
const estimatedBlocks = Math.round(diff / 60 / 10);
|
||||
|
||||
let base = 0;
|
||||
let green = 0;
|
||||
let red = 0;
|
||||
|
||||
if (blocksInEpoch >= estimatedBlocks) {
|
||||
base = estimatedBlocks / 2016 * 100;
|
||||
green = (blocksInEpoch - estimatedBlocks) / 2016 * 100;
|
||||
} else {
|
||||
base = blocksInEpoch / 2016 * 100;
|
||||
red = (estimatedBlocks - blocksInEpoch) / 2016 * 100;
|
||||
}
|
||||
|
||||
return {
|
||||
base: base + '%',
|
||||
green: green + '%',
|
||||
red: red + '%',
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.mempoolBlocksData$ = this.stateService.mempoolBlocks$
|
||||
.pipe(
|
||||
map((mempoolBlocks) => {
|
||||
const size = mempoolBlocks.map((m) => m.blockSize).reduce((a, b) => a + b, 0);
|
||||
const vsize = mempoolBlocks.map((m) => m.blockVSize).reduce((a, b) => a + b, 0);
|
||||
|
||||
return {
|
||||
size: size,
|
||||
blocks: Math.ceil(vsize / 1000000)
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ export interface WebsocketResponse {
|
||||
historicalDate?: string;
|
||||
mempoolInfo?: MempoolInfo;
|
||||
vBytesPerSecond?: number;
|
||||
lastDifficultyAdjustment?: number;
|
||||
action?: string;
|
||||
data?: string[];
|
||||
tx?: Transaction;
|
||||
@ -31,8 +32,4 @@ export interface MempoolBlock {
|
||||
export interface MempoolInfo {
|
||||
size: number;
|
||||
bytes: number;
|
||||
usage?: number;
|
||||
maxmempool?: number;
|
||||
mempoolminfee?: number;
|
||||
minrelaytxfee?: number;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ export class StateService {
|
||||
blockTransactions$ = new Subject<Transaction>();
|
||||
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
||||
vbytesPerSecond$ = new ReplaySubject<number>(1);
|
||||
lastDifficultyAdjustment$ = new ReplaySubject<number>(1);
|
||||
gitCommit$ = new ReplaySubject<string>(1);
|
||||
|
||||
live2Chart$ = new Subject<OptimizedMempoolStats>();
|
||||
|
@ -141,6 +141,10 @@ export class WebsocketService {
|
||||
this.stateService.vbytesPerSecond$.next(response.vBytesPerSecond);
|
||||
}
|
||||
|
||||
if (response.lastDifficultyAdjustment !== undefined) {
|
||||
this.stateService.lastDifficultyAdjustment$.next(response.lastDifficultyAdjustment);
|
||||
}
|
||||
|
||||
if (response['git-commit']) {
|
||||
this.stateService.gitCommit$.next(response['git-commit']);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user