mirror of
https://github.com/mempool/mempool.git
synced 2025-02-23 14:40:38 +01:00
Merge legacy and mining /blocks components and APIs
This commit is contained in:
parent
b4beb29f31
commit
37b7ea6702
12 changed files with 63 additions and 309 deletions
|
@ -426,10 +426,7 @@ class Blocks {
|
|||
return blockExtended;
|
||||
}
|
||||
|
||||
public async $getBlocksExtras(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> {
|
||||
// Note - This API is breaking if indexing is not available. For now it is okay because we only
|
||||
// use it for the mining pages, and mining pages should not be available if indexing is turned off.
|
||||
// I'll need to fix it before we refactor the block(s) related pages
|
||||
public async $getBlocks(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> {
|
||||
try {
|
||||
let currentHeight = fromHeight !== undefined ? fromHeight : this.getCurrentBlockHeight();
|
||||
const returnBlocks: BlockExtended[] = [];
|
||||
|
|
|
@ -27,7 +27,6 @@ import icons from './api/liquid/icons';
|
|||
import { Common } from './api/common';
|
||||
import mining from './api/mining';
|
||||
import HashratesRepository from './repositories/HashratesRepository';
|
||||
import BlocksRepository from './repositories/BlocksRepository';
|
||||
import poolsUpdater from './tasks/pools-updater';
|
||||
|
||||
class Server {
|
||||
|
@ -337,8 +336,8 @@ class Server {
|
|||
}
|
||||
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras', routes.getBlocksExtras)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock);
|
||||
|
||||
if (config.MEMPOOL.BACKEND !== 'esplora') {
|
||||
|
@ -352,8 +351,6 @@ class Server {
|
|||
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', routes.getBlockHeader)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/height', routes.getBlockTipHeight)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs', routes.getBlockTransactions)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs/:index', routes.getBlockTransactions)
|
||||
|
|
|
@ -720,53 +720,16 @@ class Routes {
|
|||
}
|
||||
}
|
||||
|
||||
public async getBlocksExtras(req: Request, res: Response) {
|
||||
public async getBlocks(req: Request, res: Response) {
|
||||
try {
|
||||
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(await blocks.$getBlocksExtras(height, 15));
|
||||
res.json(await blocks.$getBlocks(height, 15));
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Check if block height exist in local cache to skip the hash lookup
|
||||
const blockByHeight = blocks.getBlocks().find((b) => b.height === fromHeight);
|
||||
let startFromHash: string | null = null;
|
||||
if (blockByHeight) {
|
||||
startFromHash = blockByHeight.id;
|
||||
} else {
|
||||
startFromHash = await bitcoinApi.$getBlockHash(fromHeight);
|
||||
}
|
||||
|
||||
let nextHash = startFromHash;
|
||||
for (let i = 0; i < 10 && nextHash; i++) {
|
||||
const localBlock = blocks.getBlocks().find((b) => b.id === nextHash);
|
||||
if (localBlock) {
|
||||
returnBlocks.push(localBlock);
|
||||
nextHash = localBlock.previousblockhash;
|
||||
} else {
|
||||
const block = await bitcoinApi.$getBlock(nextHash);
|
||||
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 instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
public async getBlockTransactions(req: Request, res: Response) {
|
||||
try {
|
||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0);
|
||||
|
|
|
@ -7,7 +7,6 @@ import { AddressComponent } from './components/address/address.component';
|
|||
import { MasterPageComponent } from './components/master-page/master-page.component';
|
||||
import { AboutComponent } from './components/about/about.component';
|
||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
|
||||
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
|
||||
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
|
||||
|
@ -39,16 +38,12 @@ let routes: Routes = [
|
|||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
|
@ -128,16 +123,12 @@ let routes: Routes = [
|
|||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
|
@ -214,16 +205,12 @@ let routes: Routes = [
|
|||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
|
@ -321,16 +308,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
|||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
|
@ -429,16 +412,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
|||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<app-indexing-progress *ngIf="!widget"></app-indexing-progress>
|
||||
|
||||
<div class="container-xl" [class]="widget ? 'widget' : 'full-height'">
|
||||
<div class="container-xl" [class]="widget ? 'widget' : 'full-height'" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<h1 *ngIf="!widget" class="float-left" i18n="master-page.blocks">Blocks</h1>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
@ -8,14 +8,15 @@
|
|||
<div style="min-height: 295px">
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<th class="height text-left" [class]="widget ? 'widget' : ''" i18n="latest-blocks.height">Height</th>
|
||||
<th class="pool text-left" [class]="widget ? 'widget' : ''" i18n="mining.pool-name">Pool</th>
|
||||
<th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget">Timestamp</th>
|
||||
<th class="mined" i18n="latest-blocks.mined" *ngIf="!widget">Mined</th>
|
||||
<th class="reward text-right" i18n="latest-blocks.reward" [class]="widget ? 'widget' : ''">Reward</th>
|
||||
<th class="fees text-right" i18n="latest-blocks.fees" *ngIf="!widget">Fees</th>
|
||||
<th class="txs text-right" i18n="dashboard.txs" [class]="widget ? 'widget' : ''">TXs</th>
|
||||
<th class="size" i18n="latest-blocks.size" *ngIf="!widget">Size</th>
|
||||
<th class="height text-left" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'" i18n="latest-blocks.height">Height</th>
|
||||
<th *ngIf="indexingAvailable" class="pool text-left" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'" i18n="mining.pool-name">Pool</th>
|
||||
<th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Timestamp</th>
|
||||
<th class="mined" i18n="latest-blocks.mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Mined</th>
|
||||
<th *ngIf="indexingAvailable" class="reward text-right" i18n="latest-blocks.reward" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'">Reward</th>
|
||||
<th *ngIf="indexingAvailable && !widget" class="fees text-right" i18n="latest-blocks.fees" [class]="indexingAvailable ? '' : 'legacy'">Fees</th>
|
||||
<th *ngIf="indexingAvailable" class="txs text-right" i18n="dashboard.txs" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'">TXs</th>
|
||||
<th *ngIf="!indexingAvailable" class="txs text-right" i18n="dashboard.txs" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'">Transactions</th>
|
||||
<th class="size" i18n="latest-blocks.size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Size</th>
|
||||
</thead>
|
||||
<tbody *ngIf="blocks$ | async as blocks; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
|
||||
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
||||
|
@ -23,7 +24,7 @@
|
|||
<a [routerLink]="['/block' | relativeUrl, block.height]">{{ block.height
|
||||
}}</a>
|
||||
</td>
|
||||
<td class="pool text-left" [class]="widget ? 'widget' : ''">
|
||||
<td *ngIf="indexingAvailable" class="pool text-left" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<div class="tooltip-custom">
|
||||
<a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]">
|
||||
<img width="22" height="22" src="{{ block.extras.pool['logo'] }}"
|
||||
|
@ -36,19 +37,19 @@
|
|||
<td class="timestamp" *ngIf="!widget">
|
||||
‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||
</td>
|
||||
<td class="mined" *ngIf="!widget">
|
||||
<td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>
|
||||
</td>
|
||||
<td class="reward text-right" [class]="widget ? 'widget' : ''">
|
||||
<td *ngIf="indexingAvailable" class="reward text-right" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<app-amount [satoshis]="block.extras.reward" [noFiat]="true" digitsInfo="1.2-2"></app-amount>
|
||||
</td>
|
||||
<td class="fees text-right" *ngIf="!widget">
|
||||
<td *ngIf="indexingAvailable && !widget" class="fees text-right" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<app-amount [satoshis]="block.extras.totalFees" [noFiat]="true" digitsInfo="1.2-2"></app-amount>
|
||||
</td>
|
||||
<td class="txs text-right" [class]="widget ? 'widget' : ''">
|
||||
<td class="txs text-right" [class]="widget ? 'widget' : ''" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
{{ block.tx_count | number }}
|
||||
</td>
|
||||
<td class="size" *ngIf="!widget">
|
||||
<td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-mempool" role="progressbar"
|
||||
[ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div>
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
padding-left: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
.container-xl.legacy {
|
||||
max-width: 1140px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 100%;
|
||||
|
@ -58,6 +61,9 @@ tr, td, th {
|
|||
width: 10%;
|
||||
}
|
||||
}
|
||||
.height.legacy {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
width: 18%;
|
||||
|
@ -65,6 +71,9 @@ tr, td, th {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.timestamp.legacy {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.mined {
|
||||
width: 13%;
|
||||
|
@ -72,6 +81,12 @@ tr, td, th {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.mined.legacy {
|
||||
width: 15%;
|
||||
@media (max-width: 576px) {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.txs {
|
||||
padding-right: 40px;
|
||||
|
@ -88,6 +103,10 @@ tr, td, th {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.txs.legacy {
|
||||
padding-right: 80px;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.fees {
|
||||
width: 10%;
|
||||
|
@ -126,6 +145,12 @@ tr, td, th {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.size.legacy {
|
||||
width: 20%;
|
||||
@media (max-width: 576px) {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.tooltip-custom {
|
||||
|
|
|
@ -17,6 +17,7 @@ export class BlocksList implements OnInit {
|
|||
|
||||
blocks$: Observable<any[]> = undefined;
|
||||
|
||||
indexingAvailable = false;
|
||||
isLoading = true;
|
||||
fromBlockHeight = undefined;
|
||||
paginationMaxSize: number;
|
||||
|
@ -35,6 +36,9 @@ export class BlocksList implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' &&
|
||||
this.stateService.env.MINING_DASHBOARD === true);
|
||||
|
||||
if (!this.widget) {
|
||||
this.websocketService.want(['blocks']);
|
||||
}
|
||||
|
@ -55,17 +59,19 @@ export class BlocksList implements OnInit {
|
|||
this.isLoading = false;
|
||||
}),
|
||||
map(blocks => {
|
||||
for (const block of blocks) {
|
||||
// @ts-ignore: Need to add an extra field for the template
|
||||
block.extras.pool.logo = `./resources/mining-pools/` +
|
||||
block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
|
||||
if (this.indexingAvailable) {
|
||||
for (const block of blocks) {
|
||||
// @ts-ignore: Need to add an extra field for the template
|
||||
block.extras.pool.logo = `./resources/mining-pools/` +
|
||||
block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
|
||||
}
|
||||
}
|
||||
if (this.widget) {
|
||||
return blocks.slice(0, 6);
|
||||
}
|
||||
return blocks;
|
||||
}),
|
||||
retryWhen(errors => errors.pipe(delayWhen(() => timer(1000))))
|
||||
retryWhen(errors => errors.pipe(delayWhen(() => timer(10000))))
|
||||
)
|
||||
})
|
||||
),
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
<div class="container-xl">
|
||||
<h1 class="float-left" i18n="master-page.blocks">Blocks</h1>
|
||||
<br>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
|
||||
<thead>
|
||||
<th style="width: 15%;" i18n="latest-blocks.height">Height</th>
|
||||
<th class="d-none d-md-block" style="width: 20%;" i18n="latest-blocks.timestamp">Timestamp</th>
|
||||
<th style="width: 20%;" i18n="latest-blocks.mined">Mined</th>
|
||||
<th class="d-none d-lg-block" style="width: 15%;" i18n="latest-blocks.transactions">Transactions</th>
|
||||
<th style="width: 20%;" i18n="latest-blocks.size">Size</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></td>
|
||||
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-mempool {{ network$ | async }}" role="progressbar" [ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div>
|
||||
<div class="progress-text" [innerHTML]="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-container *ngIf="(blocksLoadingStatus$ | async) as blocksLoadingStatus">
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<div class="progress progress-dark">
|
||||
<div class="progress-bar progress-darklight" role="progressbar" [ngStyle]="{'width': blocksLoadingStatus + '%' }"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ng-template [ngIf]="error">
|
||||
<div class="text-center">
|
||||
<span>Error loading blocks</span>
|
||||
<br>
|
||||
<i>{{ error.error }}</i>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
</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,140 +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 '../../services/seo.service';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@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>;
|
||||
error: any;
|
||||
blocks: any[] = [];
|
||||
blockSubscription: Subscription;
|
||||
isLoading = true;
|
||||
interval: any;
|
||||
blocksLoadingStatus$: Observable<number>;
|
||||
|
||||
latestBlockHeight: number;
|
||||
|
||||
heightOfPageUntilBlocks = 150;
|
||||
heightOfBlocksTableChunk = 470;
|
||||
|
||||
constructor(
|
||||
private electrsApiService: ElectrsApiService,
|
||||
public stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
private websocketService: WebsocketService,
|
||||
private cd: ChangeDetectorRef,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`);
|
||||
this.websocketService.want(['blocks']);
|
||||
|
||||
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) {
|
||||
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.error = undefined;
|
||||
|
||||
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();
|
||||
},
|
||||
(error) => {
|
||||
console.log(error);
|
||||
this.error = error;
|
||||
this.isLoading = false;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
loadMore(chunks = 0) {
|
||||
if (this.isLoading) {
|
||||
return;
|
||||
}
|
||||
const height = this.blocks[this.blocks.length - 1].height - 1;
|
||||
if (height < 0) {
|
||||
return;
|
||||
}
|
||||
this.isLoading = true;
|
||||
this.electrsApiService.listBlocks$(height)
|
||||
.subscribe((blocks) => {
|
||||
this.blocks = this.blocks.concat(blocks);
|
||||
this.isLoading = false;
|
||||
this.error = undefined;
|
||||
|
||||
const chunksLeft = chunks - 1;
|
||||
if (chunksLeft > 0) {
|
||||
this.loadMore(chunksLeft);
|
||||
}
|
||||
this.cd.markForCheck();
|
||||
},
|
||||
(error) => {
|
||||
console.log(error);
|
||||
this.error = error;
|
||||
this.isLoading = false;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
trackByBlock(index: number, block: Block) {
|
||||
return block.height;
|
||||
}
|
||||
}
|
|
@ -149,7 +149,7 @@ export class ApiService {
|
|||
|
||||
getBlocks$(from: number): Observable<BlockExtended[]> {
|
||||
return this.httpClient.get<BlockExtended[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks-extras` +
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks` +
|
||||
(from !== undefined ? `/${from}` : ``)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ import { TransactionsListComponent } from '../components/transactions-list/trans
|
|||
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 { AddressLabelsComponent } from '../components/address-labels/address-labels.component';
|
||||
import { FooterComponent } from '../components/footer/footer.component';
|
||||
import { TimeSpanComponent } from '../components/time-span/time-span.component';
|
||||
|
@ -113,7 +112,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
|
|||
BlockComponent,
|
||||
TransactionsListComponent,
|
||||
AddressComponent,
|
||||
LatestBlocksComponent,
|
||||
SearchFormComponent,
|
||||
TimeSpanComponent,
|
||||
AddressLabelsComponent,
|
||||
|
@ -208,7 +206,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
|
|||
BlockComponent,
|
||||
TransactionsListComponent,
|
||||
AddressComponent,
|
||||
LatestBlocksComponent,
|
||||
SearchFormComponent,
|
||||
TimeSpanComponent,
|
||||
AddressLabelsComponent,
|
||||
|
|
Loading…
Add table
Reference in a new issue