mirror of
https://github.com/mempool/mempool.git
synced 2025-03-13 11:36:07 +01:00
Merge pull request #810 from mempool/simon/liquid-lbtc-dashboard-widget
L-BTC in circulation dashboard widgete
This commit is contained in:
commit
78b488466e
7 changed files with 192 additions and 11 deletions
|
@ -37,6 +37,7 @@ import { IncomingTransactionsGraphComponent } from './components/incoming-transa
|
|||
import { TimeSpanComponent } from './components/time-span/time-span.component';
|
||||
import { SeoService } from './services/seo.service';
|
||||
import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.component';
|
||||
import { LbtcPegsGraphComponent } from './components/lbtc-pegs-graph/lbtc-pegs-graph.component';
|
||||
import { AssetComponent } from './components/asset/asset.component';
|
||||
import { AssetsComponent } from './assets/assets.component';
|
||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||
|
@ -84,6 +85,7 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
|
|||
FeeDistributionGraphComponent,
|
||||
IncomingTransactionsGraphComponent,
|
||||
MempoolGraphComponent,
|
||||
LbtcPegsGraphComponent,
|
||||
AssetComponent,
|
||||
AssetsComponent,
|
||||
MinerComponent,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<div class="echarts" echarts [initOpts]="pegsChartInitOption" [options]="pegsChartOptions"></div>
|
|
@ -0,0 +1,137 @@
|
|||
import { Component, OnInit, Inject, LOCALE_ID, ChangeDetectionStrategy, Output, EventEmitter, Input, OnChanges } from '@angular/core';
|
||||
import { formatDate, formatNumber } from '@angular/common';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-lbtc-pegs-graph',
|
||||
templateUrl: './lbtc-pegs-graph.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class LbtcPegsGraphComponent implements OnChanges {
|
||||
@Input() data: any;
|
||||
pegsChartOptions: EChartsOption;
|
||||
|
||||
height: number | string = '200';
|
||||
right: number | string = '10';
|
||||
top: number | string = '20';
|
||||
left: number | string = '50';
|
||||
template: ('widget' | 'advanced') = 'widget';
|
||||
|
||||
pegsChartOption: EChartsOption = {};
|
||||
pegsChartInitOption = {
|
||||
renderer: 'svg'
|
||||
};
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
) { }
|
||||
|
||||
ngOnChanges() {
|
||||
this.pegsChartOptions = this.createChartOptions(this.data.series, this.data.labels);
|
||||
}
|
||||
|
||||
createChartOptions(series: number[], labels: string[]): EChartsOption {
|
||||
return {
|
||||
grid: {
|
||||
height: this.height,
|
||||
right: this.right,
|
||||
top: this.top,
|
||||
left: this.left,
|
||||
},
|
||||
animation: false,
|
||||
dataZoom: [{
|
||||
type: 'inside',
|
||||
realtime: true,
|
||||
zoomOnMouseWheel: (this.template === 'advanced') ? true : false,
|
||||
maxSpan: 100,
|
||||
minSpan: 10,
|
||||
}, {
|
||||
show: (this.template === 'advanced') ? true : false,
|
||||
type: 'slider',
|
||||
brushSelect: false,
|
||||
realtime: true,
|
||||
selectedDataBackground: {
|
||||
lineStyle: {
|
||||
color: '#fff',
|
||||
opacity: 0.45,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0,
|
||||
}
|
||||
}
|
||||
}],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: (pos, params, el, elRect, size) => {
|
||||
const obj = { top: -20 };
|
||||
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80;
|
||||
return obj;
|
||||
},
|
||||
extraCssText: `width: ${(this.template === 'widget') ? '125px' : '135px'};
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;`,
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
},
|
||||
formatter: (params: any) => {
|
||||
const colorSpan = (color: string) => `<span class="indicator" style="background-color: #116761;"></span>`;
|
||||
let itemFormatted = '<div class="title">' + params[0].axisValue + '</div>';
|
||||
params.map((item: any, index: number) => {
|
||||
if (index < 26) {
|
||||
itemFormatted += `<div class="item">
|
||||
<div class="indicator-container">${colorSpan(item.color)}</div>
|
||||
<div class="grow"></div>
|
||||
<div class="value">${formatNumber(item.value, this.locale, '1.2-2')} <span class="symbol">L-BTC</span></div>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
return `<div class="tx-wrapper-tooltip-chart ${(this.template === 'advanced') ? 'tx-wrapper-tooltip-chart-advanced' : ''}">${itemFormatted}</div>`;
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
axisLabel: {
|
||||
align: 'center',
|
||||
fontSize: 11,
|
||||
lineHeight: 12
|
||||
},
|
||||
data: labels.map((value: any) => `${formatDate(value, 'MMM\ny', this.locale)}`),
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
fontSize: 11,
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
opacity: 0.25,
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: series,
|
||||
type: 'line',
|
||||
stack: 'total',
|
||||
smooth: false,
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.2,
|
||||
color: '#116761',
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: '#116761',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ng-container *ngTemplateOutlet="mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? lbtcPegs : mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,16 +44,21 @@
|
|||
<div class="card graph-card">
|
||||
<div class="card-body pl-0">
|
||||
<div style="padding-left: 1.25rem;">
|
||||
<ng-container *ngTemplateOutlet="mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? lbtcPegs : mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
|
||||
<hr>
|
||||
</div>
|
||||
<div class="mempool-graph" *ngIf="(mempoolStats$ | async) as mempoolStats; else loadingSpinner">
|
||||
<app-mempool-graph
|
||||
[template]="'widget'"
|
||||
[limitFee]="150"
|
||||
[data]="mempoolStats.mempool"
|
||||
></app-mempool-graph>
|
||||
<div class="mempool-graph" *ngIf="stateService.network === 'liquid'; else mempoolGraph">
|
||||
<app-lbtc-pegs-graph [data]="liquidPegsMonth$ | async"></app-lbtc-pegs-graph>
|
||||
</div>
|
||||
<ng-template #mempoolGraph>
|
||||
<div class="mempool-graph" *ngIf="(mempoolStats$ | async) as mempoolStats; else loadingSpinner">
|
||||
<app-mempool-graph
|
||||
[template]="'widget'"
|
||||
[limitFee]="150"
|
||||
[data]="mempoolStats.mempool"
|
||||
></app-mempool-graph>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -192,6 +197,17 @@
|
|||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #lbtcPegs let-mempoolInfoData>
|
||||
<div class="mempool-info-data">
|
||||
<div class="item">
|
||||
<h5 class="card-title" i18n="dashboard.lbtc-pegs-in-circulation">L-BTC in circulation</h5>
|
||||
<ng-container *ngIf="(liquidPegsMonth$ | async) as liquidPegsMonth; else loadingTransactions">
|
||||
<p class="card-text">{{ liquidPegsMonth.series.slice(-1)[0] | number: '1.2-2' }} <span>L-BTC</span></p>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #txPerSecond let-mempoolInfoData>
|
||||
<h5 class="card-title" i18n="dashboard.incoming-transactions">Incoming transactions</h5>
|
||||
<ng-template [ngIf]="(isLoadingWebSocket$ | async) === false && mempoolInfoData.value" [ngIfElse]="loadingTransactions">
|
||||
|
@ -207,7 +223,6 @@
|
|||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<ng-template #difficultyEpoch>
|
||||
<div class="main-title" i18n="dashboard.difficulty-adjustment">Difficulty Adjustment</div>
|
||||
<div class="card-wrapper">
|
||||
|
|
|
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@
|
|||
import { combineLatest, merge, Observable, of, timer } from 'rxjs';
|
||||
import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators';
|
||||
import { Block } from '../interfaces/electrs.interface';
|
||||
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||
import { LiquidPegs, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||
import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
|
||||
import { ApiService } from '../services/api.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
|
@ -63,6 +63,7 @@ export class DashboardComponent implements OnInit {
|
|||
mempoolStats$: Observable<MempoolStatsData>;
|
||||
transactionsWeightPerSecondOptions: any;
|
||||
isLoadingWebSocket$: Observable<boolean>;
|
||||
liquidPegsMonth$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
|
@ -261,6 +262,22 @@ export class DashboardComponent implements OnInit {
|
|||
}),
|
||||
share(),
|
||||
);
|
||||
|
||||
if (this.stateService.network === 'liquid') {
|
||||
this.liquidPegsMonth$ = this.apiService.listLiquidPegsMonth$()
|
||||
.pipe(
|
||||
map((pegs) => {
|
||||
const labels = pegs.map(stats => stats.date);
|
||||
const series = pegs.map(stats => parseFloat(stats.amount) / 100000000);
|
||||
series.reduce((prev, curr, i) => series[i] = prev + curr, 0);
|
||||
return {
|
||||
series,
|
||||
labels
|
||||
};
|
||||
}),
|
||||
share(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) {
|
||||
|
|
|
@ -47,3 +47,8 @@ export interface AddressInformation {
|
|||
confidential_key?: string; // (string) Elements only
|
||||
unconfidential?: string; // (string) Elements only
|
||||
}
|
||||
|
||||
export interface LiquidPegs {
|
||||
amount: string;
|
||||
date: string;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation } from '../interfaces/node-api.interface';
|
||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation, LiquidPegs } from '../interfaces/node-api.interface';
|
||||
import { Observable } from 'rxjs';
|
||||
import { StateService } from './state.service';
|
||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||
|
@ -100,4 +100,8 @@ export class ApiService {
|
|||
validateAddress$(address: string): Observable<AddressInformation> {
|
||||
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
|
||||
}
|
||||
|
||||
listLiquidPegsMonth$(): Observable<LiquidPegs[]> {
|
||||
return this.httpClient.get<LiquidPegs[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue