mirror of
https://github.com/mempool/mempool.git
synced 2025-04-21 22:16:05 +02:00
Expand lightning dashboard widgets & improve responsiveness
This commit is contained in:
parent
ce7a007b62
commit
ca2c5d3628
@ -34,9 +34,11 @@
|
||||
|
||||
<!-- ISP pie chart -->
|
||||
<div class="col" style="margin-bottom: 1.47rem">
|
||||
<div class="card graph-card">
|
||||
<div class="card">
|
||||
<div class="card-body pl-2 pr-2">
|
||||
<app-nodes-per-isp-chart [widget]="true"></app-nodes-per-isp-chart>
|
||||
<div class="mempool-graph">
|
||||
<app-nodes-per-isp-chart [height]="graphHeight" [widget]="true"></app-nodes-per-isp-chart>
|
||||
</div>
|
||||
<div style="margin-top: 5px"><a [attr.data-cy]="'pool-distribution-view-more'" [routerLink]="['/graphs/lightning/nodes-per-isp' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -44,11 +46,13 @@
|
||||
|
||||
<!-- Network history -->
|
||||
<div class="col">
|
||||
<div class="card graph-card">
|
||||
<div class="card">
|
||||
<div class="card-body pl-2 pr-2 pt-1">
|
||||
<h5 class="card-title mt-3" i18n="lightning.network-history">Lightning Network History</h5>
|
||||
<app-lightning-statistics-chart [widget]=true></app-lightning-statistics-chart>
|
||||
<app-nodes-networks-chart [widget]=true></app-nodes-networks-chart>
|
||||
<div class="mempool-graph">
|
||||
<h5 class="card-title mt-3" i18n="lightning.network-history">Lightning Network History</h5>
|
||||
<app-lightning-statistics-chart [height]="(graphHeight / 1.7)" [widget]=true></app-lightning-statistics-chart>
|
||||
<app-nodes-networks-chart [height]="(graphHeight / 1.7)" [widget]=true></app-nodes-networks-chart>
|
||||
</div>
|
||||
<div><a [routerLink]="['/graphs/lightning/nodes-networks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,6 +20,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-mempool-graph {
|
||||
height: 330px;
|
||||
}
|
||||
|
||||
.mempool-graph, .fixed-mempool-graph {
|
||||
@media (min-width: 768px) {
|
||||
height: 345px;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
height: 432px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
color: #4a68b9;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface';
|
||||
@ -16,6 +16,7 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit {
|
||||
statistics$: Observable<INodesStatistics>;
|
||||
nodesRanking$: Observable<INodesRanking>;
|
||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||
graphHeight: number = 300;
|
||||
|
||||
constructor(
|
||||
private lightningApiService: LightningApiService,
|
||||
@ -24,6 +25,8 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.onResize();
|
||||
|
||||
this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`);
|
||||
this.seoService.setDescription($localize`:@@meta.description.lightning.dashboard:Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).`);
|
||||
|
||||
@ -34,4 +37,15 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit {
|
||||
ngAfterViewInit(): void {
|
||||
this.stateService.focusSearchInputDesktop();
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(): void {
|
||||
if (window.innerWidth >= 992) {
|
||||
this.graphHeight = 330;
|
||||
} else if (window.innerWidth >= 768) {
|
||||
this.graphHeight = 245;
|
||||
} else {
|
||||
this.graphHeight = 210;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div [class]="!widget ? 'chart' : 'chart-widget'" echarts [initOpts]="chartInitOptions" [options]="chartOptions" (chartInit)="onChartInit($event)"></div>
|
||||
<div [class]="!widget ? 'chart' : 'chart-widget'" [style]="{ height: widget ? (height + 'px') : null}" echarts [initOpts]="chartInitOptions" [options]="chartOptions" (chartInit)="onChartInit($event)"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
@ -56,7 +56,6 @@
|
||||
}
|
||||
.chart-widget {
|
||||
width: 100%;
|
||||
height: 145px;
|
||||
}
|
||||
|
||||
.pool-distribution {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { echarts, EChartsOption, LineSeriesOption } from '../../graphs/echarts';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
@ -26,7 +26,8 @@ import { isMobile } from '../../shared/common.utils';
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class NodesNetworksChartComponent implements OnInit {
|
||||
export class NodesNetworksChartComponent implements OnInit, OnChanges {
|
||||
@Input() height: number = 150;
|
||||
@Input() right: number | string = 45;
|
||||
@Input() left: number | string = 45;
|
||||
@Input() widget = false;
|
||||
@ -47,6 +48,9 @@ export class NodesNetworksChartComponent implements OnInit {
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
chartData: any;
|
||||
maxYAxis: number;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private seoService: SeoService,
|
||||
@ -71,44 +75,49 @@ export class NodesNetworksChartComponent implements OnInit {
|
||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
||||
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
||||
|
||||
this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
|
||||
.pipe(
|
||||
startWith(this.miningWindowPreference),
|
||||
switchMap((timespan) => {
|
||||
this.timespan = timespan;
|
||||
if (!this.widget && !firstRun) {
|
||||
this.storageService.setValue('lightningWindowPreference', timespan);
|
||||
}
|
||||
firstRun = false;
|
||||
this.miningWindowPreference = timespan;
|
||||
this.isLoading = true;
|
||||
return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan)
|
||||
.pipe(
|
||||
tap((response:any) => {
|
||||
const data = response.body;
|
||||
const chartData = {
|
||||
tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]),
|
||||
clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]),
|
||||
unannounced_nodes: data.map(val => [val.added * 1000, val.unannounced_nodes]),
|
||||
clearnet_tor_nodes: data.map(val => [val.added * 1000, val.clearnet_tor_nodes]),
|
||||
};
|
||||
let maxYAxis = 0;
|
||||
for (const day of data) {
|
||||
maxYAxis = Math.max(maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes);
|
||||
}
|
||||
maxYAxis = Math.ceil(maxYAxis / 3000) * 3000;
|
||||
this.prepareChartOptions(chartData, maxYAxis);
|
||||
this.isLoading = false;
|
||||
}),
|
||||
map((response) => {
|
||||
return {
|
||||
days: parseInt(response.headers.get('x-total-count'), 10),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}),
|
||||
share()
|
||||
);
|
||||
this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||
startWith(this.miningWindowPreference),
|
||||
switchMap((timespan) => {
|
||||
this.timespan = timespan;
|
||||
if (!this.widget && !firstRun) {
|
||||
this.storageService.setValue('lightningWindowPreference', timespan);
|
||||
}
|
||||
firstRun = false;
|
||||
this.miningWindowPreference = timespan;
|
||||
this.isLoading = true;
|
||||
return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan)
|
||||
.pipe(
|
||||
tap((response:any) => {
|
||||
const data = response.body;
|
||||
this.chartData = {
|
||||
tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]),
|
||||
clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]),
|
||||
unannounced_nodes: data.map(val => [val.added * 1000, val.unannounced_nodes]),
|
||||
clearnet_tor_nodes: data.map(val => [val.added * 1000, val.clearnet_tor_nodes]),
|
||||
};
|
||||
this.maxYAxis = 0;
|
||||
for (const day of data) {
|
||||
this.maxYAxis = Math.max(this.maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes);
|
||||
}
|
||||
this.maxYAxis = Math.ceil(this.maxYAxis / 3000) * 3000;
|
||||
this.prepareChartOptions(this.chartData, this.maxYAxis);
|
||||
this.isLoading = false;
|
||||
}),
|
||||
map((response) => {
|
||||
return {
|
||||
days: parseInt(response.headers.get('x-total-count'), 10),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}),
|
||||
share()
|
||||
);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.height && this.chartData && this.maxYAxis != null) {
|
||||
this.prepareChartOptions(this.chartData, this.maxYAxis);
|
||||
}
|
||||
}
|
||||
|
||||
prepareChartOptions(data, maxYAxis): void {
|
||||
@ -228,7 +237,7 @@ export class NodesNetworksChartComponent implements OnInit {
|
||||
title: title,
|
||||
animation: false,
|
||||
grid: {
|
||||
height: this.widget ? 90 : undefined,
|
||||
height: this.widget ? ((this.height || 120) - 60) : undefined,
|
||||
top: this.widget ? 20 : 40,
|
||||
bottom: this.widget ? 0 : 70,
|
||||
right: (isMobile() && this.widget) ? 35 : this.right,
|
||||
|
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
|
||||
<div *ngIf="!indexingInProgress else indexing" [class]="!widget ? '' : 'pb-0'" class="container pb-lg-0">
|
||||
<div [class]="widget ? 'chart-widget' : 'chart'" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
<div [class]="widget ? 'chart-widget' : 'chart'" [style]="{ height: widget ? (height + 'px') : null}" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
(chartInit)="onChartInit($event)">
|
||||
</div>
|
||||
|
||||
|
@ -18,6 +18,7 @@ import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pi
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class NodesPerISPChartComponent implements OnInit {
|
||||
@Input() height: number = 300;
|
||||
@Input() widget: boolean = false;
|
||||
|
||||
isLoading = true;
|
||||
|
@ -42,7 +42,7 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div [class]="!widget ? 'chart' : 'chart-widget'" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
<div [class]="!widget ? 'chart' : 'chart-widget'" [style]="{ height: widget ? (height + 'px') : null}" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
(chartInit)="onChartInit($event)"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
|
@ -56,7 +56,6 @@
|
||||
}
|
||||
.chart-widget {
|
||||
width: 100%;
|
||||
height: 145px;
|
||||
}
|
||||
|
||||
.pool-distribution {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
|
||||
import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { echarts, EChartsOption } from '../../graphs/echarts';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, combineLatest, fromEvent } from 'rxjs';
|
||||
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { formatNumber } from '@angular/common';
|
||||
@ -25,7 +25,8 @@ import { isMobile } from '../../shared/common.utils';
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class LightningStatisticsChartComponent implements OnInit {
|
||||
export class LightningStatisticsChartComponent implements OnInit, OnChanges {
|
||||
@Input() height: number = 150;
|
||||
@Input() right: number | string = 45;
|
||||
@Input() left: number | string = 45;
|
||||
@Input() widget = false;
|
||||
@ -37,6 +38,7 @@ export class LightningStatisticsChartComponent implements OnInit {
|
||||
chartInitOptions = {
|
||||
renderer: 'svg',
|
||||
};
|
||||
chartData: any;
|
||||
|
||||
@HostBinding('attr.dir') dir = 'ltr';
|
||||
|
||||
@ -70,36 +72,42 @@ export class LightningStatisticsChartComponent implements OnInit {
|
||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
||||
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
||||
|
||||
this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
|
||||
.pipe(
|
||||
startWith(this.miningWindowPreference),
|
||||
switchMap((timespan) => {
|
||||
this.timespan = timespan;
|
||||
if (!this.widget && !firstRun) {
|
||||
this.storageService.setValue('lightningWindowPreference', timespan);
|
||||
}
|
||||
firstRun = false;
|
||||
this.miningWindowPreference = timespan;
|
||||
this.isLoading = true;
|
||||
return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan)
|
||||
.pipe(
|
||||
tap((response:any) => {
|
||||
const data = response.body;
|
||||
this.prepareChartOptions({
|
||||
channel_count: data.map(val => [val.added * 1000, val.channel_count]),
|
||||
capacity: data.map(val => [val.added * 1000, val.total_capacity]),
|
||||
});
|
||||
this.isLoading = false;
|
||||
}),
|
||||
map((response) => {
|
||||
return {
|
||||
days: parseInt(response.headers.get('x-total-count'), 10),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}),
|
||||
share(),
|
||||
);
|
||||
this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||
startWith(this.miningWindowPreference),
|
||||
switchMap((timespan) => {
|
||||
this.timespan = timespan;
|
||||
if (!this.widget && !firstRun) {
|
||||
this.storageService.setValue('lightningWindowPreference', timespan);
|
||||
}
|
||||
firstRun = false;
|
||||
this.miningWindowPreference = timespan;
|
||||
this.isLoading = true;
|
||||
return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan)
|
||||
.pipe(
|
||||
tap((response:any) => {
|
||||
const data = response.body;
|
||||
this.chartData = {
|
||||
channel_count: data.map(val => [val.added * 1000, val.channel_count]),
|
||||
capacity: data.map(val => [val.added * 1000, val.total_capacity]),
|
||||
};
|
||||
this.prepareChartOptions(this.chartData);
|
||||
this.isLoading = false;
|
||||
}),
|
||||
map((response) => {
|
||||
return {
|
||||
days: parseInt(response.headers.get('x-total-count'), 10),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}),
|
||||
share(),
|
||||
);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.height && this.chartData) {
|
||||
this.prepareChartOptions(this.chartData);
|
||||
}
|
||||
}
|
||||
|
||||
prepareChartOptions(data): void {
|
||||
@ -138,7 +146,7 @@ export class LightningStatisticsChartComponent implements OnInit {
|
||||
]),
|
||||
],
|
||||
grid: {
|
||||
height: this.widget ? 90 : undefined,
|
||||
height: this.widget ? ((this.height || 120) - 60) : undefined,
|
||||
top: this.widget ? 20 : 40,
|
||||
bottom: this.widget ? 0 : 70,
|
||||
right: (isMobile() && this.widget) ? 35 : this.right,
|
||||
|
Loading…
x
Reference in New Issue
Block a user