Merge branch 'master' into simon/block-transactions-error-catcher

This commit is contained in:
wiz
2022-05-25 19:22:31 +09:00
committed by GitHub
30 changed files with 332 additions and 521 deletions

View File

@@ -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';
@@ -36,19 +35,20 @@ let routes: Routes = [
component: MasterPageComponent,
children: [
{
path: 'tx/push',
component: PushTransactionComponent,
path: 'mining/blocks',
redirectTo: 'blocks',
pathMatch: 'full'
},
{
path: 'blocks',
component: LatestBlocksComponent,
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'mining/blocks',
path: 'blocks',
component: BlocksList,
},
{
@@ -115,6 +115,11 @@ let routes: Routes = [
{
path: 'signet',
children: [
{
path: 'mining/blocks',
redirectTo: 'blocks',
pathMatch: 'full'
},
{
path: '',
pathMatch: 'full',
@@ -128,16 +133,12 @@ let routes: Routes = [
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'blocks',
component: LatestBlocksComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'mining/blocks',
path: 'blocks',
component: BlocksList,
},
{
@@ -211,19 +212,20 @@ let routes: Routes = [
component: MasterPageComponent,
children: [
{
path: 'tx/push',
component: PushTransactionComponent,
path: 'mining/blocks',
redirectTo: 'blocks',
pathMatch: 'full'
},
{
path: 'blocks',
component: LatestBlocksComponent,
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'mining/blocks',
path: 'blocks',
component: BlocksList,
},
{
@@ -321,16 +323,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 +427,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,
},
{

View File

@@ -250,6 +250,10 @@
<img class="image" src="/resources/profile/marina.svg" />
<span>Marina</span>
</a>
<a href="https://github.com/bitcoin-wallet/bitcoin-wallet/" target="_blank" title="Bitcoin Wallet (Schildbach)">
<img class="image" src="/resources/profile/schildbach.svg" />
<span>Schildbach</span>
</a>
</div>
</div>

View File

@@ -89,13 +89,13 @@ export class BlockFeeRatesGraphComponent implements OnInit {
};
for (const rate of data.blockFeeRates) {
const timestamp = rate.timestamp * 1000;
seriesData['Min'].push([timestamp, rate.avg_fee_0, rate.avg_height]);
seriesData['10th'].push([timestamp, rate.avg_fee_10, rate.avg_height]);
seriesData['25th'].push([timestamp, rate.avg_fee_25, rate.avg_height]);
seriesData['Median'].push([timestamp, rate.avg_fee_50, rate.avg_height]);
seriesData['75th'].push([timestamp, rate.avg_fee_75, rate.avg_height]);
seriesData['90th'].push([timestamp, rate.avg_fee_90, rate.avg_height]);
seriesData['Max'].push([timestamp, rate.avg_fee_100, rate.avg_height]);
seriesData['Min'].push([timestamp, rate.avgFee_0, rate.avgHeight]);
seriesData['10th'].push([timestamp, rate.avgFee_10, rate.avgHeight]);
seriesData['25th'].push([timestamp, rate.avgFee_25, rate.avgHeight]);
seriesData['Median'].push([timestamp, rate.avgFee_50, rate.avgHeight]);
seriesData['75th'].push([timestamp, rate.avgFee_75, rate.avgHeight]);
seriesData['90th'].push([timestamp, rate.avgFee_90, rate.avgHeight]);
seriesData['Max'].push([timestamp, rate.avgFee_100, rate.avgHeight]);
}
// Prepare chart

View File

@@ -71,7 +71,7 @@ export class BlockFeesGraphComponent implements OnInit {
.pipe(
tap((response) => {
this.prepareChartOptions({
blockFees: response.body.map(val => [val.timestamp * 1000, val.avg_fees / 100000000]),
blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000]),
});
this.isLoading = false;
}),

View File

@@ -69,7 +69,7 @@ export class BlockRewardsGraphComponent implements OnInit {
.pipe(
tap((response) => {
this.prepareChartOptions({
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avg_rewards / 100000000]),
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000]),
});
this.isLoading = false;
}),

View File

@@ -83,8 +83,8 @@ export class BlockSizesWeightsGraphComponent implements OnInit {
tap((response) => {
const data = response.body;
this.prepareChartOptions({
sizes: data.sizes.map(val => [val.timestamp * 1000, val.avg_size / 1000000, val.avg_height]),
weights: data.weights.map(val => [val.timestamp * 1000, val.avg_weight / 1000000, val.avg_height]),
sizes: data.sizes.map(val => [val.timestamp * 1000, val.avgSize / 1000000, val.avgHeight]),
weights: data.weights.map(val => [val.timestamp * 1000, val.avgWeight / 1000000, val.avgHeight]),
});
this.isLoading = false;
}),

View File

@@ -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" style="min-height: 335px" [ngClass]="{'widget': widget, 'full-height': !widget, 'legacy': !indexingAvailable}">
<h1 *ngIf="!widget" class="float-left" i18n="master-page.blocks">Blocks</h1>
<div class="clearfix"></div>
@@ -8,22 +8,22 @@
<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" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" i18n="latest-blocks.height">Height</th>
<th *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" 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" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">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" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">TXs</th>
<th *ngIf="!indexingAvailable" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">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">
<td class="text-left" [class]="widget ? 'widget' : ''">
<a [routerLink]="['/block' | relativeUrl, block.height]">{{ block.height
}}</a>
<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" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<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 +36,19 @@
<td class="timestamp" *ngIf="!widget">
&lrm;{{ 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" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<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" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
{{ 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>
@@ -60,29 +60,29 @@
<ng-template #skeleton>
<tbody>
<tr *ngFor="let item of skeletonLines">
<td class="height text-left" [class]="widget ? 'widget' : ''">
<td class="height text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<span class="skeleton-loader" style="max-width: 75px"></span>
</td>
<td class="pool text-left" [class]="widget ? 'widget' : ''">
<td *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<img width="1" height="25" style="opacity: 0">
<span class="skeleton-loader" style="max-width: 125px"></span>
</td>
<td class="timestamp" *ngIf="!widget">
<td class="timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 150px"></span>
</td>
<td class="mined" *ngIf="!widget">
<td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 125px"></span>
</td>
<td class="reward text-right" [class]="widget ? 'widget' : ''">
<td *ngIf="indexingAvailable" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<span class="skeleton-loader" style="max-width: 75px"></span>
</td>
<td class="fees text-right" *ngIf="!widget">
<td *ngIf="indexingAvailable && !widget" class="fees text-right" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 75px"></span>
</td>
<td class="txs text-right" [class]="widget ? 'widget' : ''">
<td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<span class="skeleton-loader" style="max-width: 75px"></span>
</td>
<td class="size" *ngIf="!widget">
<td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader"></span>
</td>
</tr>

View File

@@ -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 {

View File

@@ -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))))
)
})
),
@@ -81,9 +87,11 @@ export class BlocksList implements OnInit {
return blocks[0];
}
this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1;
// @ts-ignore: Need to add an extra field for the template
blocks[1][0].extras.pool.logo = `./resources/mining-pools/` +
blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
if (this.stateService.env.MINING_DASHBOARD) {
// @ts-ignore: Need to add an extra field for the template
blocks[1][0].extras.pool.logo = `./resources/mining-pools/` +
blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
}
acc.unshift(blocks[1][0]);
acc = acc.slice(0, this.widget ? 6 : 15);
return acc;

View File

@@ -1,4 +1,4 @@
<div style="min-height: 295px">
<div style="min-height: 335px">
<table class="table latest-adjustments">
<thead>
<tr>

View File

@@ -1,7 +1,7 @@
<div class="mb-3 d-flex menu" style="padding: 0px 35px;">
<div *ngIf="stateService.env.MINING_DASHBOARD" class="mb-3 d-flex menu" style="padding: 0px 35px;">
<a routerLinkActive="active" class="btn btn-primary w-50 mr-1"
[routerLink]="['/graphs/mempool' | relativeUrl]">Mempool</a>
<div ngbDropdown *ngIf="stateService.env.MINING_DASHBOARD" class="w-50">
<div ngbDropdown class="w-50">
<button class="btn btn-primary w-100" id="dropdownBasic1" ngbDropdownToggle i18n="mining">Mining</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/mining/pools' | relativeUrl]"

View File

@@ -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">&lrm;{{ 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>

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -51,7 +51,7 @@
<div class="card-body">
<h5 class="card-title" i18n="dashboard.latest-blocks">Latest blocks</h5>
<app-blocks-list [widget]=true></app-blocks-list>
<div><a [routerLink]="['/mining/blocks' | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div>
<div><a [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div>
</div>
</div>
</div>

View File

@@ -136,7 +136,7 @@
</tr>
</tbody>
</table>
<div class=""><a href="" [routerLink]="[(stateService.env.MINING_DASHBOARD ? '/mining/blocks' : '/blocks') | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div>
<div class=""><a href="" [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div>
</div>
</div>
</div>

View File

@@ -198,9 +198,7 @@
display: none;
text-align: right;
width: 20%;
@media (min-width: 376px) {
display: table-cell;
}
display: table-cell;
}
.table-cell-size {
display: none;

View File

@@ -3834,34 +3834,34 @@ export const restApiDocsData = [
curl: [`1w`],
response: `[
{
"avg_height": 735644,
"avgHeight": 735644,
"timestamp": 1652119111,
"avg_fees": 24212890
"avgFees": 24212890
},
{
"avg_height": 735646,
"avgHeight": 735646,
"timestamp": 1652120252,
"avg_fees": 21655996
"avgFees": 21655996
},
{
"avg_height": 735648,
"avgHeight": 735648,
"timestamp": 1652121214,
"avg_fees": 20678859
"avgFees": 20678859
},
{
"avg_height": 735649,
"avgHeight": 735649,
"timestamp": 1652121757,
"avg_fees": 21020140
"avgFees": 21020140
},
{
"avg_height": 735650,
"avgHeight": 735650,
"timestamp": 1652122367,
"avg_fees": 23064200
"avgFees": 23064200
},
{
"avg_height": 735652,
"avgHeight": 735652,
"timestamp": 1652122893,
"avg_fees": 17620723
"avgFees": 17620723
},
...
]`
@@ -3872,14 +3872,14 @@ export const restApiDocsData = [
curl: [`1w`],
response: `[
{
"avg_height": 2224253,
"avgHeight": 2224253,
"timestamp": 1652346420,
"avg_fees": 211686
"avgFees": 211686
},
{
"avg_height": 2224254,
"avgHeight": 2224254,
"timestamp": 1652346850,
"avg_fees": 2565952
"avgFees": 2565952
},
...
]`
@@ -3890,14 +3890,14 @@ export const restApiDocsData = [
curl: [`1w`],
response: `[
{
"avg_height": 89978,
"avgHeight": 89978,
"timestamp": 1652346573,
"avg_fees": 1071
"avgFees": 1071
},
{
"avg_height": 89979,
"avgHeight": 89979,
"timestamp": 1652346970,
"avg_fees": 1224
"avgFees": 1224
},
...
]`
@@ -3933,29 +3933,29 @@ export const restApiDocsData = [
curl: [`1d`],
response: `[
{
"avg_height": 599992,
"avgHeight": 599992,
"timestamp": 1571438412,
"avg_rewards": 1260530933
"avgRewards": 1260530933
},
{
"avg_height": 600000,
"avgHeight": 600000,
"timestamp": 1571443398,
"avg_rewards": 1264314538
"avgRewards": 1264314538
},
{
"avg_height": 725441,
"avgHeight": 725441,
"timestamp": 1646139035,
"avg_rewards": 637067563
"avgRewards": 637067563
},
{
"avg_height": 725585,
"avgHeight": 725585,
"timestamp": 1646222444,
"avg_rewards": 646519104
"avgRewards": 646519104
},
{
"avg_height": 725727,
"avgHeight": 725727,
"timestamp": 1646308374,
"avg_rewards": 638709605
"avgRewards": 638709605
},
...
]`
@@ -3966,14 +3966,14 @@ export const restApiDocsData = [
curl: [`1d`],
response: `[
{
"avg_height": 12,
"avgHeight": 12,
"timestamp": 1296689648,
"avg_rewards": 5000000000
"avgRewards": 5000000000
},
{
"avg_height": 269,
"avgHeight": 269,
"timestamp": 1296717674,
"avg_rewards": 5000091820
"avgRewards": 5000091820
},
...
]`
@@ -3984,14 +3984,14 @@ export const restApiDocsData = [
curl: [`1d`],
response: `[
{
"avg_height": 183,
"avgHeight": 183,
"timestamp": 1598962247,
"avg_rewards": 5000000000
"avgRewards": 5000000000
},
{
"avg_height": 576,
"avgHeight": 576,
"timestamp": 1599047892,
"avg_rewards": 5000000000
"avgRewards": 5000000000
},
...
]`
@@ -4029,37 +4029,37 @@ export const restApiDocsData = [
"oldestIndexedBlockTimestamp": 1571434851,
"blockFeeRates": [
{
"avg_height": 732152,
"avgHeight": 732152,
"timestamp": 1650132959,
"avg_fee_0": 1,
"avg_fee_10": 2,
"avg_fee_25": 2,
"avg_fee_50": 3,
"avg_fee_75": 4,
"avg_fee_90": 8,
"avg_fee_100": 393
"avgFee_0": 1,
"avgFee_10": 2,
"avgFee_25": 2,
"avgFee_50": 3,
"avgFee_75": 4,
"avgFee_90": 8,
"avgFee_100": 393
},
{
"avg_height": 732158,
"avgHeight": 732158,
"timestamp": 1650134432,
"avg_fee_0": 1,
"avg_fee_10": 1,
"avg_fee_25": 2,
"avg_fee_50": 4,
"avg_fee_75": 6,
"avg_fee_90": 10,
"avg_fee_100": 240
"avgFee_0": 1,
"avgFee_10": 1,
"avgFee_25": 2,
"avgFee_50": 4,
"avgFee_75": 6,
"avgFee_90": 10,
"avgFee_100": 240
},
{
"avg_height": 732161,
"avgHeight": 732161,
"timestamp": 1650135818,
"avg_fee_0": 1,
"avg_fee_10": 1,
"avg_fee_25": 1,
"avg_fee_50": 2,
"avg_fee_75": 5,
"avg_fee_90": 8,
"avg_fee_100": 251
"avgFee_0": 1,
"avgFee_10": 1,
"avgFee_25": 1,
"avgFee_50": 2,
"avgFee_75": 5,
"avgFee_90": 8,
"avgFee_100": 251
},
...
]
@@ -4073,26 +4073,26 @@ export const restApiDocsData = [
"oldestIndexedBlockTimestamp": 1296688602,
"blockFeeRates": [
{
"avg_height": 2196306,
"avgHeight": 2196306,
"timestamp": 1650360168,
"avg_fee_0": 1,
"avg_fee_10": 1,
"avg_fee_25": 1,
"avg_fee_50": 1,
"avg_fee_75": 2,
"avg_fee_90": 28,
"avg_fee_100": 2644
"avgFee_0": 1,
"avgFee_10": 1,
"avgFee_25": 1,
"avgFee_50": 1,
"avgFee_75": 2,
"avgFee_90": 28,
"avgFee_100": 2644
},
{
"avg_height": 2196308,
"avgHeight": 2196308,
"timestamp": 1650361209,
"avg_fee_0": 1,
"avg_fee_10": 1,
"avg_fee_25": 1,
"avg_fee_50": 4,
"avg_fee_75": 12,
"avg_fee_90": 65,
"avg_fee_100": 102
"avgFee_0": 1,
"avgFee_10": 1,
"avgFee_25": 1,
"avgFee_50": 4,
"avgFee_75": 12,
"avgFee_90": 65,
"avgFee_100": 102
},
...
]
@@ -4106,26 +4106,26 @@ export const restApiDocsData = [
"oldestIndexedBlockTimestamp": 1598918400,
"blockFeeRates": [
{
"avg_height": 86620,
"avgHeight": 86620,
"timestamp": 1650360010,
"avg_fee_0": 1,
"avg_fee_10": 1,
"avg_fee_25": 1,
"avg_fee_50": 1,
"avg_fee_75": 1,
"avg_fee_90": 1,
"avg_fee_100": 1
"avgFee_0": 1,
"avgFee_10": 1,
"avgFee_25": 1,
"avgFee_50": 1,
"avgFee_75": 1,
"avgFee_90": 1,
"avgFee_100": 1
},
{
"avg_height": 86623,
"avgHeight": 86623,
"timestamp": 1650361330,
"avg_fee_0": 1,
"avg_fee_10": 1,
"avg_fee_25": 1,
"avg_fee_50": 1,
"avg_fee_75": 1,
"avg_fee_90": 1,
"avg_fee_100": 1
"avgFee_0": 1,
"avgFee_10": 1,
"avgFee_25": 1,
"avgFee_50": 1,
"avgFee_75": 1,
"avgFee_90": 1,
"avgFee_100": 1
},
...
]
@@ -4163,47 +4163,47 @@ export const restApiDocsData = [
response: `{
"sizes": [
{
"avg_height": 576650,
"avgHeight": 576650,
"timestamp": 1558212081,
"avg_size": 1271404
"avgSize": 1271404
},
{
"avg_height": 576715,
"avgHeight": 576715,
"timestamp": 1558246272,
"avg_size": 1105893
"avgSize": 1105893
},
{
"avg_height": 576797,
"avgHeight": 576797,
"timestamp": 1558289379,
"avg_size": 1141071
"avgSize": 1141071
},
{
"avg_height": 576885,
"avgHeight": 576885,
"timestamp": 1558330184,
"avg_size": 1108166
"avgSize": 1108166
},
...
],
"weights": [
{
"avg_height": 576650,
"avgHeight": 576650,
"timestamp": 1558212081,
"avg_weight": 3994002
"avgWeight": 3994002
},
{
"avg_height": 576715,
"avgHeight": 576715,
"timestamp": 1558246272,
"avg_weight": 3756312
"avgWeight": 3756312
},
{
"avg_height": 576797,
"avgHeight": 576797,
"timestamp": 1558289379,
"avg_weight": 3719625
"avgWeight": 3719625
},
{
"avg_height": 576885,
"avgHeight": 576885,
"timestamp": 1558330184,
"avg_weight": 3631381
"avgWeight": 3631381
},
...
]
@@ -4216,27 +4216,27 @@ export const restApiDocsData = [
response: `{
"sizes": [
{
"avg_height": 1517188,
"avgHeight": 1517188,
"timestamp": 1558262730,
"avg_size": 25089
"avgSize": 25089
},
{
"avg_height": 1517275,
"avgHeight": 1517275,
"timestamp": 1558290933,
"avg_size": 21679
"avgSize": 21679
},
...
],
"weights": [
{
"avg_height": 1517188,
"avgHeight": 1517188,
"timestamp": 1558262730,
"avg_weight": 74921
"avgWeight": 74921
},
{
"avg_height": 1517275,
"avgHeight": 1517275,
"timestamp": 1558290933,
"avg_weight": 65164
"avgWeight": 65164
},
...
]
@@ -4249,27 +4249,27 @@ export const restApiDocsData = [
response: `{
"sizes": [
{
"avg_height": 83,
"avgHeight": 83,
"timestamp": 1598937527,
"avg_size": 329
"avgSize": 329
},
{
"avg_height": 266,
"avgHeight": 266,
"timestamp": 1598982991,
"avg_size": 330
"avgSize": 330
},
...
],
"weights": [
{
"avg_height": 83,
"avgHeight": 83,
"timestamp": 1598937527,
"avg_weight": 1209
"avgWeight": 1209
},
{
"avg_height": 266,
"avgHeight": 266,
"timestamp": 1598982991,
"avg_weight": 1212
"avgWeight": 1212
},
...
]

View File

@@ -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}` : ``)
);
}

View File

@@ -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,

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="-0.015 -0.015 1.03 1.03" preserveAspectRatio="xMidYMid"><defs><filter id="b"><feGaussianBlur in="SourceAlpha" result="blur-out" stdDeviation=".5"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter><filter id="c"><feGaussianBlur in="SourceAlpha" result="blur-out" stdDeviation="1.3"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter><linearGradient id="a" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" style="stop-color:#f9b949"/><stop offset="100%" style="stop-color:#f7931a"/></linearGradient></defs><path d="M63.036 39.741c-4.274 17.143-21.637 27.576-38.782 23.301C7.116 58.768-3.317 41.404.959 24.262 5.23 7.117 22.594-3.317 39.734.957c17.144 4.274 27.576 21.64 23.302 38.784z" style="fill:url(#a)" filter="url(#b)" transform="scale(.01563)"/><path d="M46.1 27.441c.638-4.258-2.604-6.547-7.037-8.074l1.438-5.768-3.511-.875-1.4 5.616c-.923-.23-1.871-.447-2.813-.662l1.41-5.653-3.51-.875-1.438 5.766c-.764-.174-1.514-.346-2.242-.527l.004-.018-4.842-1.209-.934 3.75s2.605.597 2.55.634c1.422.355 1.679 1.296 1.636 2.042l-1.638 6.571c.098.025.225.061.365.117l-.371-.092-2.296 9.205c-.174.432-.615 1.08-1.61.834.036.051-2.551-.637-2.551-.637l-1.743 4.019 4.569 1.139c.85.213 1.683.436 2.503.646l-1.453 5.834 3.507.875 1.439-5.772c.958.26 1.888.5 2.798.726l-1.434 5.745 3.51.875 1.454-5.823c5.987 1.133 10.489.676 12.384-4.739 1.527-4.36-.076-6.875-3.226-8.515 2.294-.529 4.022-2.038 4.483-5.155zM38.08 38.69c-1.085 4.36-8.426 2.003-10.806 1.412l1.928-7.729c2.38.594 10.012 1.77 8.878 6.317zm1.086-11.312c-.99 3.966-7.1 1.951-9.082 1.457l1.748-7.01c1.982.494 8.365 1.416 7.334 5.553z" style="fill:#fff" filter="url(#c)" transform="scale(.01563)"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB