Merge branch 'master' into natsoni/fix-broken-blocks-skeleton
This commit is contained in:
commit
a51d82bcfb
26
backend/package-lock.json
generated
26
backend/package-lock.json
generated
@ -17,7 +17,7 @@
|
|||||||
"crypto-js": "~4.2.0",
|
"crypto-js": "~4.2.0",
|
||||||
"express": "~4.18.2",
|
"express": "~4.18.2",
|
||||||
"maxmind": "~4.3.11",
|
"maxmind": "~4.3.11",
|
||||||
"mysql2": "~3.7.0",
|
"mysql2": "~3.9.1",
|
||||||
"redis": "^4.6.6",
|
"redis": "^4.6.6",
|
||||||
"rust-gbt": "file:./rust-gbt",
|
"rust-gbt": "file:./rust-gbt",
|
||||||
"socks-proxy-agent": "~7.0.0",
|
"socks-proxy-agent": "~7.0.0",
|
||||||
@ -3673,9 +3673,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@ -6110,9 +6110,9 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"node_modules/mysql2": {
|
"node_modules/mysql2": {
|
||||||
"version": "3.7.0",
|
"version": "3.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.1.tgz",
|
||||||
"integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==",
|
"integrity": "sha512-3njoWAAhGBYy0tWBabqUQcLtczZUxrmmtc2vszQUekg3kTJyZ5/IeLC3Fo04u6y6Iy5Sba7pIIa2P/gs8D3ZeQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"denque": "^2.1.0",
|
"denque": "^2.1.0",
|
||||||
"generate-function": "^2.3.1",
|
"generate-function": "^2.3.1",
|
||||||
@ -10440,9 +10440,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw=="
|
||||||
},
|
},
|
||||||
"form-data": {
|
"form-data": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@ -12230,9 +12230,9 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"mysql2": {
|
"mysql2": {
|
||||||
"version": "3.7.0",
|
"version": "3.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.1.tgz",
|
||||||
"integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==",
|
"integrity": "sha512-3njoWAAhGBYy0tWBabqUQcLtczZUxrmmtc2vszQUekg3kTJyZ5/IeLC3Fo04u6y6Iy5Sba7pIIa2P/gs8D3ZeQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"denque": "^2.1.0",
|
"denque": "^2.1.0",
|
||||||
"generate-function": "^2.3.1",
|
"generate-function": "^2.3.1",
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
"crypto-js": "~4.2.0",
|
"crypto-js": "~4.2.0",
|
||||||
"express": "~4.18.2",
|
"express": "~4.18.2",
|
||||||
"maxmind": "~4.3.11",
|
"maxmind": "~4.3.11",
|
||||||
"mysql2": "~3.7.0",
|
"mysql2": "~3.9.1",
|
||||||
"rust-gbt": "file:./rust-gbt",
|
"rust-gbt": "file:./rust-gbt",
|
||||||
"redis": "^4.6.6",
|
"redis": "^4.6.6",
|
||||||
"socks-proxy-agent": "~7.0.0",
|
"socks-proxy-agent": "~7.0.0",
|
||||||
|
@ -27,12 +27,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="widget">
|
|
||||||
<div class="item">
|
|
||||||
<h5 class="card-title" i18n="acceleration.total-bid-boost">Total Bid Boost</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div [class.chart]="!widget" [class.chart-widget]="widget" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
<div [class.chart]="!widget" [class.chart-widget]="widget" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||||
(chartInit)="onChartInit($event)">
|
(chartInit)="onChartInit($event)">
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,11 +53,6 @@
|
|||||||
padding-bottom: 55px;
|
padding-bottom: 55px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.chart-widget {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-height: 290px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { EChartsOption, graphic } from 'echarts';
|
import { EChartsOption, graphic } from 'echarts';
|
||||||
import { Observable, Subscription, combineLatest } from 'rxjs';
|
import { Observable, Subscription, combineLatest, fromEvent } from 'rxjs';
|
||||||
import { map, max, startWith, switchMap, tap } from 'rxjs/operators';
|
import { map, max, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { ApiService } from '../../../services/api.service';
|
import { ApiService } from '../../../services/api.service';
|
||||||
import { SeoService } from '../../../services/seo.service';
|
import { SeoService } from '../../../services/seo.service';
|
||||||
@ -28,6 +28,7 @@ import { Acceleration } from '../../../interfaces/node-api.interface';
|
|||||||
})
|
})
|
||||||
export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||||
@Input() widget: boolean = false;
|
@Input() widget: boolean = false;
|
||||||
|
@Input() height: number | string = '200';
|
||||||
@Input() right: number | string = 45;
|
@Input() right: number | string = 45;
|
||||||
@Input() left: number | string = 75;
|
@Input() left: number | string = 75;
|
||||||
@Input() accelerations$: Observable<Acceleration[]>;
|
@Input() accelerations$: Observable<Acceleration[]>;
|
||||||
@ -74,6 +75,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
|||||||
this.statsObservable$ = combineLatest([
|
this.statsObservable$ = combineLatest([
|
||||||
(this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })),
|
(this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })),
|
||||||
this.apiService.getHistoricalBlockFees$(this.miningWindowPreference),
|
this.apiService.getHistoricalBlockFees$(this.miningWindowPreference),
|
||||||
|
fromEvent(window, 'resize').pipe(startWith(null)),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
tap(([accelerations, blockFeesResponse]) => {
|
tap(([accelerations, blockFeesResponse]) => {
|
||||||
this.prepareChartOptions(accelerations, blockFeesResponse.body);
|
this.prepareChartOptions(accelerations, blockFeesResponse.body);
|
||||||
@ -173,6 +175,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
|||||||
],
|
],
|
||||||
animation: false,
|
animation: false,
|
||||||
grid: {
|
grid: {
|
||||||
|
height: this.height,
|
||||||
right: this.right,
|
right: this.right,
|
||||||
left: this.left,
|
left: this.left,
|
||||||
bottom: this.widget ? 30 : 80,
|
bottom: this.widget ? 30 : 80,
|
||||||
|
@ -37,6 +37,11 @@
|
|||||||
<div class="col" style="margin-bottom: 1.47rem">
|
<div class="col" style="margin-bottom: 1.47rem">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
|
<div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
|
||||||
|
<a class="title-link" href="" [routerLink]="['/mempool-block/0' | relativeUrl]">
|
||||||
|
<h5 class="card-title d-inline" i18n="dashboard.mempool-goggles-accelerations">Mempool Goggles: Accelerations</h5>
|
||||||
|
<span> </span>
|
||||||
|
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: #4a68b9"></fa-icon>
|
||||||
|
</a>
|
||||||
<div class="mempool-block-wrapper">
|
<div class="mempool-block-wrapper">
|
||||||
<app-mempool-block-overview [index]="0" [overrideColors]="getAcceleratorColor"></app-mempool-block-overview>
|
<app-mempool-block-overview [index]="0" [overrideColors]="getAcceleratorColor"></app-mempool-block-overview>
|
||||||
</div>
|
</div>
|
||||||
@ -48,7 +53,15 @@
|
|||||||
<div class="col" style="margin-bottom: 1.47rem">
|
<div class="col" style="margin-bottom: 1.47rem">
|
||||||
<div class="card graph-card">
|
<div class="card graph-card">
|
||||||
<div class="card-body pl-2 pr-2">
|
<div class="card-body pl-2 pr-2">
|
||||||
<app-acceleration-fees-graph [attr.data-cy]="'acceleration-fees'" [widget]=true [accelerations$]="accelerations$"></app-acceleration-fees-graph>
|
<h5 class="card-title" i18n="acceleration.total-bid-boost">Total Bid Boost</h5>
|
||||||
|
<div class="mempool-graph">
|
||||||
|
<app-acceleration-fees-graph
|
||||||
|
[height]="graphHeight"
|
||||||
|
[attr.data-cy]="'acceleration-fees'"
|
||||||
|
[widget]=true
|
||||||
|
[accelerations$]="accelerations$"
|
||||||
|
></app-acceleration-fees-graph>
|
||||||
|
</div>
|
||||||
<div class="mt-1"><a [attr.data-cy]="'acceleration-fees-view-more'" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
<div class="mt-1"><a [attr.data-cy]="'acceleration-fees-view-more'" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +17,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mempool-graph {
|
||||||
|
height: 295px;
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 325px;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #4a68b9;
|
color: #4a68b9;
|
||||||
@ -135,7 +145,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
height: 385px;
|
@media (min-width: 768px) {
|
||||||
|
height: 420px;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
height: 510px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.list-card {
|
.list-card {
|
||||||
height: 410px;
|
height: 410px;
|
||||||
@ -145,7 +160,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mempool-block-wrapper {
|
.mempool-block-wrapper {
|
||||||
max-height: 380px;
|
max-height: 430px;
|
||||||
max-width: 380px;
|
max-width: 430px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
max-height: 344px;
|
||||||
|
max-width: 344px;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
max-height: 430px;
|
||||||
|
max-width: 430px;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
|
||||||
import { SeoService } from '../../../services/seo.service';
|
import { SeoService } from '../../../services/seo.service';
|
||||||
import { WebsocketService } from '../../../services/websocket.service';
|
import { WebsocketService } from '../../../services/websocket.service';
|
||||||
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
||||||
@ -30,6 +30,8 @@ export class AcceleratorDashboardComponent implements OnInit {
|
|||||||
minedAccelerations$: Observable<Acceleration[]>;
|
minedAccelerations$: Observable<Acceleration[]>;
|
||||||
loadingBlocks: boolean = true;
|
loadingBlocks: boolean = true;
|
||||||
|
|
||||||
|
graphHeight: number = 300;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
@ -40,6 +42,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.onResize();
|
||||||
this.websocketService.want(['blocks', 'mempool-blocks', 'stats']);
|
this.websocketService.want(['blocks', 'mempool-blocks', 'stats']);
|
||||||
|
|
||||||
this.pendingAccelerations$ = interval(30000).pipe(
|
this.pendingAccelerations$ = interval(30000).pipe(
|
||||||
@ -121,4 +124,15 @@ export class AcceleratorDashboardComponent implements OnInit {
|
|||||||
return normalColors[feeLevelIndex] || normalColors[mempoolFeeColors.length - 1];
|
return normalColors[feeLevelIndex] || normalColors[mempoolFeeColors.length - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ export class AddressLabelsComponent implements OnChanges {
|
|||||||
|
|
||||||
handleVin() {
|
handleVin() {
|
||||||
if (this.vin.inner_witnessscript_asm) {
|
if (this.vin.inner_witnessscript_asm) {
|
||||||
if (this.vin.inner_witnessscript_asm.indexOf('OP_DEPTH OP_PUSHNUM_12 OP_EQUAL OP_IF OP_PUSHNUM_11') === 0) {
|
if (this.vin.inner_witnessscript_asm.indexOf('OP_DEPTH OP_PUSHNUM_12 OP_EQUAL OP_IF OP_PUSHNUM_11') === 0 || this.vin.inner_witnessscript_asm.indexOf('OP_PUSHNUM_15 OP_CHECKMULTISIG OP_IFDUP OP_NOTIF OP_PUSHBYTES_2') === 1259) {
|
||||||
if (this.vin.witness.length > 11) {
|
if (this.vin.witness.length > 11) {
|
||||||
this.label = 'Liquid Peg Out';
|
this.label = 'Liquid Peg Out';
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,8 +31,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
addressLoadingStatus$: Observable<number>;
|
addressLoadingStatus$: Observable<number>;
|
||||||
addressInfo: null | AddressInformation = null;
|
addressInfo: null | AddressInformation = null;
|
||||||
|
|
||||||
totalConfirmedTxCount = 0;
|
fullyLoaded = false;
|
||||||
loadedConfirmedTxCount = 0;
|
|
||||||
txCount = 0;
|
txCount = 0;
|
||||||
received = 0;
|
received = 0;
|
||||||
sent = 0;
|
sent = 0;
|
||||||
@ -66,7 +65,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
switchMap((params: ParamMap) => {
|
switchMap((params: ParamMap) => {
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
this.isLoadingAddress = true;
|
this.isLoadingAddress = true;
|
||||||
this.loadedConfirmedTxCount = 0;
|
this.fullyLoaded = false;
|
||||||
this.address = null;
|
this.address = null;
|
||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
this.transactions = null;
|
this.transactions = null;
|
||||||
@ -128,7 +127,6 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
this.tempTransactions = transactions;
|
this.tempTransactions = transactions;
|
||||||
if (transactions.length) {
|
if (transactions.length) {
|
||||||
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
||||||
this.loadedConfirmedTxCount += transactions.filter((tx) => tx.status.confirmed).length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTxs: string[] = [];
|
const fetchTxs: string[] = [];
|
||||||
@ -191,8 +189,6 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
this.audioService.playSound('magic');
|
this.audioService.playSound('magic');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.totalConfirmedTxCount++;
|
|
||||||
this.loadedConfirmedTxCount++;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,16 +248,19 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
if (this.isLoadingTransactions || !this.totalConfirmedTxCount || this.loadedConfirmedTxCount >= this.totalConfirmedTxCount) {
|
if (this.isLoadingTransactions || this.fullyLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
this.retryLoadMore = false;
|
this.retryLoadMore = false;
|
||||||
this.electrsApiService.getAddressTransactions$(this.address.address, this.lastTransactionTxId)
|
this.electrsApiService.getAddressTransactions$(this.address.address, this.lastTransactionTxId)
|
||||||
.subscribe((transactions: Transaction[]) => {
|
.subscribe((transactions: Transaction[]) => {
|
||||||
|
if (transactions && transactions.length) {
|
||||||
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
||||||
this.loadedConfirmedTxCount += transactions.length;
|
|
||||||
this.transactions = this.transactions.concat(transactions);
|
this.transactions = this.transactions.concat(transactions);
|
||||||
|
} else {
|
||||||
|
this.fullyLoaded = true;
|
||||||
|
}
|
||||||
this.isLoadingTransactions = false;
|
this.isLoadingTransactions = false;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
@ -278,7 +277,6 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
this.received = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum;
|
this.received = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum;
|
||||||
this.sent = this.address.chain_stats.spent_txo_sum + this.address.mempool_stats.spent_txo_sum;
|
this.sent = this.address.chain_stats.spent_txo_sum + this.address.mempool_stats.spent_txo_sum;
|
||||||
this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count;
|
this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count;
|
||||||
this.totalConfirmedTxCount = this.address.chain_stats.tx_count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
@ -42,6 +42,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
|||||||
@Input() showFilters: boolean = false;
|
@Input() showFilters: boolean = false;
|
||||||
@Input() excludeFilters: string[] = [];
|
@Input() excludeFilters: string[] = [];
|
||||||
@Input() filterFlags: bigint | null = null;
|
@Input() filterFlags: bigint | null = null;
|
||||||
|
@Input() filterMode: 'and' | 'or' = 'and';
|
||||||
@Input() blockConversion: Price;
|
@Input() blockConversion: Price;
|
||||||
@Input() overrideColors: ((tx: TxView) => Color) | null = null;
|
@Input() overrideColors: ((tx: TxView) => Color) | null = null;
|
||||||
@Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>();
|
@Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>();
|
||||||
@ -113,7 +114,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
|||||||
if (changes.overrideColor && this.scene) {
|
if (changes.overrideColor && this.scene) {
|
||||||
this.scene.setColorFunction(this.overrideColors);
|
this.scene.setColorFunction(this.overrideColors);
|
||||||
}
|
}
|
||||||
if ((changes.filterFlags || changes.showFilters)) {
|
if ((changes.filterFlags || changes.showFilters || changes.filterMode)) {
|
||||||
this.setFilterFlags();
|
this.setFilterFlags();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,8 +122,8 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
|||||||
setFilterFlags(flags?: bigint | null): void {
|
setFilterFlags(flags?: bigint | null): void {
|
||||||
this.activeFilterFlags = this.filterFlags || flags || null;
|
this.activeFilterFlags = this.filterFlags || flags || null;
|
||||||
if (this.scene) {
|
if (this.scene) {
|
||||||
if (flags != null) {
|
if (this.activeFilterFlags != null) {
|
||||||
this.scene.setColorFunction(this.getFilterColorFunction(flags));
|
this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags));
|
||||||
} else {
|
} else {
|
||||||
this.scene.setColorFunction(this.overrideColors);
|
this.scene.setColorFunction(this.overrideColors);
|
||||||
}
|
}
|
||||||
@ -523,7 +524,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
|||||||
|
|
||||||
getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) {
|
getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) {
|
||||||
return (tx: TxView) => {
|
return (tx: TxView) => {
|
||||||
if ((tx.bigintFlags & flags) === flags) {
|
if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (tx.bigintFlags & flags) > 0n)) {
|
||||||
return defaultColorFunction(tx);
|
return defaultColorFunction(tx);
|
||||||
} else {
|
} else {
|
||||||
return defaultColorFunction(
|
return defaultColorFunction(
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class]="!widget ? 'chart' : 'chart-widget'" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
<div [class]="!widget ? 'chart' : 'chart-widget'" [style]="{ height: widget ? ((height + 20) + 'px') : null}" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||||
(chartInit)="onChartInit($event)">
|
(chartInit)="onChartInit($event)">
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
|
@ -57,8 +57,6 @@
|
|||||||
}
|
}
|
||||||
.chart-widget {
|
.chart-widget {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
height: 240px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pool-distribution {
|
.pool-distribution {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
|
||||||
import { echarts, EChartsOption } from '../../graphs/echarts';
|
import { echarts, EChartsOption } from '../../graphs/echarts';
|
||||||
import { merge, Observable, of } from 'rxjs';
|
import { combineLatest, fromEvent, merge, Observable, of } from 'rxjs';
|
||||||
import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators';
|
import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
@ -31,6 +31,7 @@ import { seoDescriptionNetwork } from '../../shared/common.utils';
|
|||||||
export class HashrateChartComponent implements OnInit {
|
export class HashrateChartComponent implements OnInit {
|
||||||
@Input() tableOnly = false;
|
@Input() tableOnly = false;
|
||||||
@Input() widget = false;
|
@Input() widget = false;
|
||||||
|
@Input() height: number = 300;
|
||||||
@Input() right: number | string = 45;
|
@Input() right: number | string = 45;
|
||||||
@Input() left: number | string = 75;
|
@Input() left: number | string = 75;
|
||||||
|
|
||||||
@ -86,7 +87,8 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.hashrateObservable$ = merge(
|
this.hashrateObservable$ = combineLatest(
|
||||||
|
merge(
|
||||||
this.radioGroupForm.get('dateSpan').valueChanges
|
this.radioGroupForm.get('dateSpan').valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
startWith(this.radioGroupForm.controls.dateSpan.value),
|
startWith(this.radioGroupForm.controls.dateSpan.value),
|
||||||
@ -107,7 +109,10 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
return this.apiService.getHistoricalHashrate$(this.timespan);
|
return this.apiService.getHistoricalHashrate$(this.timespan);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
),
|
||||||
|
fromEvent(window, 'resize').pipe(startWith(null)),
|
||||||
).pipe(
|
).pipe(
|
||||||
|
map(([response, _]) => response),
|
||||||
tap((response: any) => {
|
tap((response: any) => {
|
||||||
const data = response.body;
|
const data = response.body;
|
||||||
|
|
||||||
@ -221,6 +226,7 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
grid: {
|
grid: {
|
||||||
|
height: (this.widget && this.height) ? this.height - 30 : undefined,
|
||||||
top: this.widget ? 20 : 40,
|
top: this.widget ? 20 : 40,
|
||||||
bottom: this.widget ? 30 : 70,
|
bottom: this.widget ? 30 : 70,
|
||||||
right: this.right,
|
right: this.right,
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
<app-block-overview-graph
|
<app-block-overview-graph
|
||||||
#blockGraph
|
#blockGraph
|
||||||
[isLoading]="isLoading$ | async"
|
[isLoading]="isLoading$ | async"
|
||||||
[resolution]="86"
|
[resolution]="resolution"
|
||||||
[blockLimit]="stateService.blockVSize"
|
[blockLimit]="stateService.blockVSize"
|
||||||
[orientation]="timeLtr ? 'right' : 'left'"
|
[orientation]="timeLtr ? 'right' : 'left'"
|
||||||
[flip]="true"
|
[flip]="true"
|
||||||
[showFilters]="showFilters"
|
[showFilters]="showFilters"
|
||||||
|
[filterFlags]="filterFlags"
|
||||||
|
[filterMode]="filterMode"
|
||||||
[overrideColors]="overrideColors"
|
[overrideColors]="overrideColors"
|
||||||
(txClickEvent)="onTxClick($event)"
|
(txClickEvent)="onTxClick($event)"
|
||||||
></app-block-overview-graph>
|
></app-block-overview-graph>
|
||||||
|
@ -18,8 +18,11 @@ import TxView from '../block-overview-graph/tx-view';
|
|||||||
})
|
})
|
||||||
export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
|
export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
|
||||||
@Input() index: number;
|
@Input() index: number;
|
||||||
|
@Input() resolution = 86;
|
||||||
@Input() showFilters: boolean = false;
|
@Input() showFilters: boolean = false;
|
||||||
@Input() overrideColors: ((tx: TxView) => Color) | null = null;
|
@Input() overrideColors: ((tx: TxView) => Color) | null = null;
|
||||||
|
@Input() filterFlags: bigint | undefined = undefined;
|
||||||
|
@Input() filterMode: 'and' | 'or' = 'and';
|
||||||
@Output() txPreviewEvent = new EventEmitter<TransactionStripped | void>();
|
@Output() txPreviewEvent = new EventEmitter<TransactionStripped | void>();
|
||||||
|
|
||||||
@ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent;
|
@ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent;
|
||||||
|
@ -26,9 +26,11 @@
|
|||||||
|
|
||||||
<!-- pool distribution -->
|
<!-- pool distribution -->
|
||||||
<div class="col" style="margin-bottom: 1.47rem">
|
<div class="col" style="margin-bottom: 1.47rem">
|
||||||
<div class="card graph-card">
|
<div class="card">
|
||||||
<div class="card-body pl-2 pr-2">
|
<div class="card-body pl-2 pr-2">
|
||||||
<app-pool-ranking [attr.data-cy]="'pool-distribution'" [widget]=true></app-pool-ranking>
|
<div class="mempool-graph">
|
||||||
|
<app-pool-ranking [height]="graphHeight" [attr.data-cy]="'pool-distribution'" [widget]=true></app-pool-ranking>
|
||||||
|
</div>
|
||||||
<div class="mt-1"><a [attr.data-cy]="'pool-distribution-view-more'" [routerLink]="['/graphs/mining/pools' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
<div class="mt-1"><a [attr.data-cy]="'pool-distribution-view-more'" [routerLink]="['/graphs/mining/pools' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -38,7 +40,9 @@
|
|||||||
<div class="col" style="margin-bottom: 1.47rem">
|
<div class="col" style="margin-bottom: 1.47rem">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
|
<div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
|
||||||
<app-hashrate-chart [attr.data-cy]="'hashrate-graph'" [widget]="true"></app-hashrate-chart>
|
<div class="fixed-mempool-graph">
|
||||||
|
<app-hashrate-chart [height]="graphHeight" [attr.data-cy]="'hashrate-graph'" [widget]="true"></app-hashrate-chart>
|
||||||
|
</div>
|
||||||
<div class="mt-1"><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" fragment="1y" i18n="dashboard.view-more">View more »</a></div>
|
<div class="mt-1"><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" fragment="1y" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +17,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fixed-mempool-graph {
|
||||||
|
height: 330px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mempool-graph, .fixed-mempool-graph {
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 345px;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
height: 472px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #4a68b9;
|
color: #4a68b9;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AfterViewInit, ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
@ -11,6 +11,8 @@ import { EventType, NavigationStart, Router } from '@angular/router';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class MiningDashboardComponent implements OnInit, AfterViewInit {
|
export class MiningDashboardComponent implements OnInit, AfterViewInit {
|
||||||
|
graphHeight = 300;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
@ -22,6 +24,7 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.onResize();
|
||||||
this.websocketService.want(['blocks', 'mempool-blocks', 'stats']);
|
this.websocketService.want(['blocks', 'mempool-blocks', 'stats']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,4 +38,15 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize(): void {
|
||||||
|
if (window.innerWidth >= 992) {
|
||||||
|
this.graphHeight = 340;
|
||||||
|
} else if (window.innerWidth >= 768) {
|
||||||
|
this.graphHeight = 245;
|
||||||
|
} else {
|
||||||
|
this.graphHeight = 240;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class]="!widget ? '' : 'pb-0'" class="container pb-lg-0">
|
<div [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)">
|
(chartInit)="onChartInit($event)">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -28,7 +28,9 @@
|
|||||||
.chart-widget {
|
.chart-widget {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
height: 240px;
|
@media (max-width: 767px) {
|
||||||
|
max-height: 240px;
|
||||||
|
}
|
||||||
@media (max-width: 485px) {
|
@media (max-width: 485px) {
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import { isMobile } from '../../shared/common.utils';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class PoolRankingComponent implements OnInit {
|
export class PoolRankingComponent implements OnInit {
|
||||||
|
@Input() height: number = 300;
|
||||||
@Input() widget = false;
|
@Input() widget = false;
|
||||||
|
|
||||||
miningWindowPreference: string;
|
miningWindowPreference: string;
|
||||||
|
@ -16,23 +16,34 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card graph-card">
|
<div class="card graph-card">
|
||||||
<div class="card-body pl-0">
|
<div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
|
||||||
|
<ng-template [ngIf]="(network$ | async) !== 'liquid'" [ngIfElse]="liquidPegs">
|
||||||
|
<a class="title-link mb-0" style="margin-top: -2px" href="" [routerLink]="['/mempool-block/0' | relativeUrl]">
|
||||||
|
<h5 class="card-title d-inline"><span i18n="dashboard.mempool-goggles">Mempool Goggles</span>: {{ goggleCycle[goggleIndex].name }}</h5>
|
||||||
|
<span> </span>
|
||||||
|
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: #4a68b9"></fa-icon>
|
||||||
|
</a>
|
||||||
|
<div class="quick-filter">
|
||||||
|
<div class="btn-group btn-group-toggle">
|
||||||
|
<label class="btn btn-primary btn-xs" [class.active]="filter.index === goggleIndex" *ngFor="let filter of goggleCycle">
|
||||||
|
<input type="radio" [value]="'3m'" fragment="3m" (click)="goggleIndex = filter.index" [attr.data-cy]="'3m'"> {{ filter.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mempool-block-wrapper">
|
||||||
|
<app-mempool-block-overview
|
||||||
|
[index]="0"
|
||||||
|
[resolution]="goggleResolution"
|
||||||
|
[filterFlags]="goggleCycle[goggleIndex].flag"
|
||||||
|
filterMode="or"
|
||||||
|
></app-mempool-block-overview>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #liquidPegs>
|
||||||
<div style="padding-left: 1.25rem;">
|
<div style="padding-left: 1.25rem;">
|
||||||
<ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? lbtcPegs : mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
|
<ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? lbtcPegs : mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
|
||||||
<hr>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
<ng-template [ngIf]="(network$ | async) !== 'liquid'" [ngIfElse]="liquidPegs">
|
|
||||||
<ng-container *ngIf="{ value: (mempoolStats$ | async) } as mempoolStats">
|
|
||||||
<div class="mempool-graph">
|
|
||||||
<app-mempool-graph
|
|
||||||
[template]="'widget'"
|
|
||||||
[data]="mempoolStats.value?.mempool"
|
|
||||||
[windowPreferenceOverride]="'2h'"
|
|
||||||
></app-mempool-graph>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template #liquidPegs>
|
|
||||||
<app-lbtc-pegs-graph [data]="fullHistory$ | async"></app-lbtc-pegs-graph>
|
<app-lbtc-pegs-graph [data]="fullHistory$ | async"></app-lbtc-pegs-graph>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
@ -41,9 +52,21 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card graph-card">
|
<div class="card graph-card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? mempoolTable : txPerSecond; context: { $implicit: mempoolInfoData }"></ng-container>
|
<ng-container *ngTemplateOutlet="mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
|
||||||
|
<ng-container *ngIf="stateService.network !== 'liquid'">
|
||||||
|
<h5 class="card-title mt-3" i18n="dashboard.incoming-transactions">Incoming Transactions</h5>
|
||||||
|
<div class="mempool-graph" *ngIf="{ value: (mempoolStats$ | async) } as mempoolStats">
|
||||||
|
<app-incoming-transactions-graph
|
||||||
|
[height]="incomingGraphHeight"
|
||||||
|
[left]="50"
|
||||||
|
[right]="20"
|
||||||
|
[data]="mempoolStats.value?.weightPerSecond"
|
||||||
|
[windowPreferenceOverride]="'2h'"
|
||||||
|
></app-incoming-transactions-graph>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<div class="mempool-graph" *ngIf="stateService.network === 'liquid'">
|
||||||
<hr>
|
<hr>
|
||||||
<div class="mempool-graph" *ngIf="stateService.network === 'liquid'; else mempoolGraph">
|
|
||||||
<table class="table table-borderless table-striped" *ngIf="(featuredAssets$ | async) as featuredAssets else loadingAssetsTable">
|
<table class="table table-borderless table-striped" *ngIf="(featuredAssets$ | async) as featuredAssets else loadingAssetsTable">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let group of featuredAssets">
|
<tr *ngFor="let group of featuredAssets">
|
||||||
@ -60,15 +83,6 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<ng-template #mempoolGraph>
|
|
||||||
<div class="mempool-graph" *ngIf="{ value: (mempoolStats$ | async) } as mempoolStats">
|
|
||||||
<app-incoming-transactions-graph
|
|
||||||
[left]="50"
|
|
||||||
[data]="mempoolStats.value?.weightPerSecond"
|
|
||||||
[windowPreferenceOverride]="'2h'"
|
|
||||||
></app-incoming-transactions-graph>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -287,19 +301,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</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">
|
|
||||||
<span *ngIf="(mempoolLoadingStatus$ | async) !== 100; else inSync">
|
|
||||||
<span class="badge badge-pill badge-warning"><ng-container i18n="dashboard.backend-is-synchronizing">Backend is synchronizing</ng-container> ({{ mempoolLoadingStatus$ | async }}%)</span>
|
|
||||||
</span>
|
|
||||||
<ng-template #inSync>
|
|
||||||
<div class="progress inc-tx-progress-bar">
|
|
||||||
<div class="progress-bar {{ mempoolInfoData.value.progressColor }}" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth}"> </div>
|
|
||||||
<div *only-vsize class="progress-text">‎{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-container i18n="shared.vbytes-per-second|vB/s">vB/s</ng-container></div>
|
|
||||||
<div *only-weight class="progress-text">‎{{ mempoolInfoData.value.vBytesPerSecond * 4 | ceil | number }} <ng-container i18n="shared.weight-per-second|WU/s">WU/s</ng-container></div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</ng-template>
|
|
||||||
</ng-template>
|
|
||||||
|
@ -44,8 +44,11 @@
|
|||||||
|
|
||||||
.graph-card {
|
.graph-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 415px;
|
||||||
|
}
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
height: 385px;
|
height: 510px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +261,12 @@
|
|||||||
|
|
||||||
.mempool-graph {
|
.mempool-graph {
|
||||||
height: 255px;
|
height: 255px;
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 285px;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
height: 370px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.loadingGraphs{
|
.loadingGraphs{
|
||||||
height: 250px;
|
height: 250px;
|
||||||
@ -364,3 +373,39 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mempool-block-wrapper {
|
||||||
|
max-height: 410px;
|
||||||
|
max-width: 410px;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
max-height: 344px;
|
||||||
|
max-width: 344px;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
max-height: 410px;
|
||||||
|
max-width: 410px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.goggle-badge {
|
||||||
|
margin: 6px 5px 8px;
|
||||||
|
background: none;
|
||||||
|
border: solid 2px #105fb0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: #105fb0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-xs {
|
||||||
|
padding: 0.35rem 0.5rem;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-filter {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { combineLatest, EMPTY, merge, Observable, of, Subject, Subscription, timer } from 'rxjs';
|
import { combineLatest, EMPTY, merge, Observable, of, Subject, Subscription, timer } from 'rxjs';
|
||||||
import { catchError, delayWhen, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators';
|
import { catchError, delayWhen, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators';
|
||||||
import { AuditStatus, BlockExtended, CurrentPegs, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
import { AuditStatus, BlockExtended, CurrentPegs, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||||
@ -54,12 +54,23 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
currentReserves$: Observable<CurrentPegs>;
|
currentReserves$: Observable<CurrentPegs>;
|
||||||
fullHistory$: Observable<any>;
|
fullHistory$: Observable<any>;
|
||||||
isLoad: boolean = true;
|
isLoad: boolean = true;
|
||||||
|
mempoolInfoSubscription: Subscription;
|
||||||
currencySubscription: Subscription;
|
currencySubscription: Subscription;
|
||||||
currency: string;
|
currency: string;
|
||||||
|
incomingGraphHeight: number = 300;
|
||||||
private lastPegBlockUpdate: number = 0;
|
private lastPegBlockUpdate: number = 0;
|
||||||
private lastPegAmount: string = '';
|
private lastPegAmount: string = '';
|
||||||
private lastReservesBlockUpdate: number = 0;
|
private lastReservesBlockUpdate: number = 0;
|
||||||
|
|
||||||
|
goggleResolution = 82;
|
||||||
|
goggleCycle = [
|
||||||
|
{ index: 0, name: 'All' },
|
||||||
|
{ index: 1, name: 'Consolidations', flag: 0b00000010_00000000_00000000_00000000_00000000n },
|
||||||
|
{ index: 2, name: 'Coinjoin', flag: 0b00000001_00000000_00000000_00000000_00000000n },
|
||||||
|
{ index: 3, name: '💩', flag: 0b00000100_00000000_00000000_00000000n | 0b00000010_00000000_00000000_00000000n | 0b00000001_00000000_00000000_00000000n },
|
||||||
|
];
|
||||||
|
goggleIndex = 0; // Math.floor(Math.random() * this.goggleCycle.length);
|
||||||
|
|
||||||
private destroy$ = new Subject();
|
private destroy$ = new Subject();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -74,6 +85,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
this.mempoolInfoSubscription.unsubscribe();
|
||||||
this.currencySubscription.unsubscribe();
|
this.currencySubscription.unsubscribe();
|
||||||
this.websocketService.stopTrackRbfSummary();
|
this.websocketService.stopTrackRbfSummary();
|
||||||
this.destroy$.next(1);
|
this.destroy$.next(1);
|
||||||
@ -81,6 +93,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.onResize();
|
||||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||||
this.seoService.resetTitle();
|
this.seoService.resetTitle();
|
||||||
this.seoService.resetDescription();
|
this.seoService.resetDescription();
|
||||||
@ -95,8 +108,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
this.mempoolInfoData$ = combineLatest([
|
this.mempoolInfoData$ = combineLatest([
|
||||||
this.stateService.mempoolInfo$,
|
this.stateService.mempoolInfo$,
|
||||||
this.stateService.vbytesPerSecond$
|
this.stateService.vbytesPerSecond$
|
||||||
])
|
]).pipe(
|
||||||
.pipe(
|
|
||||||
map(([mempoolInfo, vbytesPerSecond]) => {
|
map(([mempoolInfo, vbytesPerSecond]) => {
|
||||||
const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100);
|
const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100);
|
||||||
|
|
||||||
@ -126,6 +138,8 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.mempoolInfoSubscription = this.mempoolInfoData$.subscribe();
|
||||||
|
|
||||||
this.mempoolBlocksData$ = this.stateService.mempoolBlocks$
|
this.mempoolBlocksData$ = this.stateService.mempoolBlocks$
|
||||||
.pipe(
|
.pipe(
|
||||||
map((mempoolBlocks) => {
|
map((mempoolBlocks) => {
|
||||||
@ -347,4 +361,18 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
trackByBlock(index: number, block: BlockExtended) {
|
trackByBlock(index: number, block: BlockExtended) {
|
||||||
return block.height;
|
return block.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize(): void {
|
||||||
|
if (window.innerWidth >= 992) {
|
||||||
|
this.incomingGraphHeight = 300;
|
||||||
|
this.goggleResolution = 82;
|
||||||
|
} else if (window.innerWidth >= 768) {
|
||||||
|
this.incomingGraphHeight = 215;
|
||||||
|
this.goggleResolution = 80;
|
||||||
|
} else {
|
||||||
|
this.incomingGraphHeight = 180;
|
||||||
|
this.goggleResolution = 86;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,11 @@
|
|||||||
|
|
||||||
<!-- ISP pie chart -->
|
<!-- ISP pie chart -->
|
||||||
<div class="col" style="margin-bottom: 1.47rem">
|
<div class="col" style="margin-bottom: 1.47rem">
|
||||||
<div class="card graph-card">
|
<div class="card">
|
||||||
<div class="card-body pl-2 pr-2">
|
<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 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>
|
||||||
</div>
|
</div>
|
||||||
@ -44,11 +46,13 @@
|
|||||||
|
|
||||||
<!-- Network history -->
|
<!-- Network history -->
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card graph-card">
|
<div class="card">
|
||||||
<div class="card-body pl-2 pr-2 pt-1">
|
<div class="card-body pl-2 pr-2 pt-1">
|
||||||
|
<div class="mempool-graph">
|
||||||
<h5 class="card-title mt-3" i18n="lightning.network-history">Lightning Network History</h5>
|
<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-lightning-statistics-chart [height]="(graphHeight / 1.7)" [widget]=true></app-lightning-statistics-chart>
|
||||||
<app-nodes-networks-chart [widget]=true></app-nodes-networks-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><a [routerLink]="['/graphs/lightning/nodes-networks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</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: 442px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #4a68b9;
|
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 { Observable } from 'rxjs';
|
||||||
import { share } from 'rxjs/operators';
|
import { share } from 'rxjs/operators';
|
||||||
import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface';
|
import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface';
|
||||||
@ -16,6 +16,7 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit {
|
|||||||
statistics$: Observable<INodesStatistics>;
|
statistics$: Observable<INodesStatistics>;
|
||||||
nodesRanking$: Observable<INodesRanking>;
|
nodesRanking$: Observable<INodesRanking>;
|
||||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||||
|
graphHeight: number = 300;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private lightningApiService: LightningApiService,
|
private lightningApiService: LightningApiService,
|
||||||
@ -24,6 +25,8 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.onResize();
|
||||||
|
|
||||||
this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`);
|
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).`);
|
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 {
|
ngAfterViewInit(): void {
|
||||||
this.stateService.focusSearchInputDesktop();
|
this.stateService.focusSearchInputDesktop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize(): void {
|
||||||
|
if (window.innerWidth >= 992) {
|
||||||
|
this.graphHeight = 340;
|
||||||
|
} else if (window.innerWidth >= 768) {
|
||||||
|
this.graphHeight = 245;
|
||||||
|
} else {
|
||||||
|
this.graphHeight = 210;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</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="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
}
|
}
|
||||||
.chart-widget {
|
.chart-widget {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 145px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pool-distribution {
|
.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 { echarts, EChartsOption, LineSeriesOption } from '../../graphs/echarts';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
@ -26,7 +26,8 @@ import { isMobile } from '../../shared/common.utils';
|
|||||||
`],
|
`],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class NodesNetworksChartComponent implements OnInit {
|
export class NodesNetworksChartComponent implements OnInit, OnChanges {
|
||||||
|
@Input() height: number = 150;
|
||||||
@Input() right: number | string = 45;
|
@Input() right: number | string = 45;
|
||||||
@Input() left: number | string = 45;
|
@Input() left: number | string = 45;
|
||||||
@Input() widget = false;
|
@Input() widget = false;
|
||||||
@ -47,6 +48,9 @@ export class NodesNetworksChartComponent implements OnInit {
|
|||||||
timespan = '';
|
timespan = '';
|
||||||
chartInstance: any = undefined;
|
chartInstance: any = undefined;
|
||||||
|
|
||||||
|
chartData: any;
|
||||||
|
maxYAxis: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(LOCALE_ID) public locale: string,
|
@Inject(LOCALE_ID) public locale: string,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
@ -71,8 +75,7 @@ export class NodesNetworksChartComponent implements OnInit {
|
|||||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
||||||
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
||||||
|
|
||||||
this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
|
this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||||
.pipe(
|
|
||||||
startWith(this.miningWindowPreference),
|
startWith(this.miningWindowPreference),
|
||||||
switchMap((timespan) => {
|
switchMap((timespan) => {
|
||||||
this.timespan = timespan;
|
this.timespan = timespan;
|
||||||
@ -86,18 +89,18 @@ export class NodesNetworksChartComponent implements OnInit {
|
|||||||
.pipe(
|
.pipe(
|
||||||
tap((response:any) => {
|
tap((response:any) => {
|
||||||
const data = response.body;
|
const data = response.body;
|
||||||
const chartData = {
|
this.chartData = {
|
||||||
tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]),
|
tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]),
|
||||||
clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]),
|
clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]),
|
||||||
unannounced_nodes: data.map(val => [val.added * 1000, val.unannounced_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]),
|
clearnet_tor_nodes: data.map(val => [val.added * 1000, val.clearnet_tor_nodes]),
|
||||||
};
|
};
|
||||||
let maxYAxis = 0;
|
this.maxYAxis = 0;
|
||||||
for (const day of data) {
|
for (const day of data) {
|
||||||
maxYAxis = Math.max(maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes);
|
this.maxYAxis = Math.max(this.maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes);
|
||||||
}
|
}
|
||||||
maxYAxis = Math.ceil(maxYAxis / 3000) * 3000;
|
this.maxYAxis = Math.ceil(this.maxYAxis / 3000) * 3000;
|
||||||
this.prepareChartOptions(chartData, maxYAxis);
|
this.prepareChartOptions(this.chartData, this.maxYAxis);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}),
|
}),
|
||||||
map((response) => {
|
map((response) => {
|
||||||
@ -111,6 +114,12 @@ export class NodesNetworksChartComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes.height && this.chartData && this.maxYAxis != null) {
|
||||||
|
this.prepareChartOptions(this.chartData, this.maxYAxis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prepareChartOptions(data, maxYAxis): void {
|
prepareChartOptions(data, maxYAxis): void {
|
||||||
let title: object;
|
let title: object;
|
||||||
if (!this.widget && data.tor_nodes.length === 0) {
|
if (!this.widget && data.tor_nodes.length === 0) {
|
||||||
@ -228,7 +237,7 @@ export class NodesNetworksChartComponent implements OnInit {
|
|||||||
title: title,
|
title: title,
|
||||||
animation: false,
|
animation: false,
|
||||||
grid: {
|
grid: {
|
||||||
height: this.widget ? 90 : undefined,
|
height: this.widget ? ((this.height || 120) - 60) : undefined,
|
||||||
top: this.widget ? 20 : 40,
|
top: this.widget ? 20 : 40,
|
||||||
bottom: this.widget ? 0 : 70,
|
bottom: this.widget ? 0 : 70,
|
||||||
right: (isMobile() && this.widget) ? 35 : this.right,
|
right: (isMobile() && this.widget) ? 35 : this.right,
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="!indexingInProgress else indexing" [class]="!widget ? '' : 'pb-0'" class="container pb-lg-0">
|
<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)">
|
(chartInit)="onChartInit($event)">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pi
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class NodesPerISPChartComponent implements OnInit {
|
export class NodesPerISPChartComponent implements OnInit {
|
||||||
|
@Input() height: number = 300;
|
||||||
@Input() widget: boolean = false;
|
@Input() widget: boolean = false;
|
||||||
|
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</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>
|
(chartInit)="onChartInit($event)"></div>
|
||||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
}
|
}
|
||||||
.chart-widget {
|
.chart-widget {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 145px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pool-distribution {
|
.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 { 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 { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { formatNumber } from '@angular/common';
|
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() right: number | string = 45;
|
||||||
@Input() left: number | string = 45;
|
@Input() left: number | string = 45;
|
||||||
@Input() widget = false;
|
@Input() widget = false;
|
||||||
@ -37,6 +38,7 @@ export class LightningStatisticsChartComponent implements OnInit {
|
|||||||
chartInitOptions = {
|
chartInitOptions = {
|
||||||
renderer: 'svg',
|
renderer: 'svg',
|
||||||
};
|
};
|
||||||
|
chartData: any;
|
||||||
|
|
||||||
@HostBinding('attr.dir') dir = 'ltr';
|
@HostBinding('attr.dir') dir = 'ltr';
|
||||||
|
|
||||||
@ -70,8 +72,7 @@ export class LightningStatisticsChartComponent implements OnInit {
|
|||||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
||||||
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
||||||
|
|
||||||
this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
|
this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||||
.pipe(
|
|
||||||
startWith(this.miningWindowPreference),
|
startWith(this.miningWindowPreference),
|
||||||
switchMap((timespan) => {
|
switchMap((timespan) => {
|
||||||
this.timespan = timespan;
|
this.timespan = timespan;
|
||||||
@ -85,10 +86,11 @@ export class LightningStatisticsChartComponent implements OnInit {
|
|||||||
.pipe(
|
.pipe(
|
||||||
tap((response:any) => {
|
tap((response:any) => {
|
||||||
const data = response.body;
|
const data = response.body;
|
||||||
this.prepareChartOptions({
|
this.chartData = {
|
||||||
channel_count: data.map(val => [val.added * 1000, val.channel_count]),
|
channel_count: data.map(val => [val.added * 1000, val.channel_count]),
|
||||||
capacity: data.map(val => [val.added * 1000, val.total_capacity]),
|
capacity: data.map(val => [val.added * 1000, val.total_capacity]),
|
||||||
});
|
};
|
||||||
|
this.prepareChartOptions(this.chartData);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}),
|
}),
|
||||||
map((response) => {
|
map((response) => {
|
||||||
@ -102,6 +104,12 @@ export class LightningStatisticsChartComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes.height && this.chartData) {
|
||||||
|
this.prepareChartOptions(this.chartData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prepareChartOptions(data): void {
|
prepareChartOptions(data): void {
|
||||||
let title: object;
|
let title: object;
|
||||||
if (!this.widget && data.channel_count.length === 0) {
|
if (!this.widget && data.channel_count.length === 0) {
|
||||||
@ -138,7 +146,7 @@ export class LightningStatisticsChartComponent implements OnInit {
|
|||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
grid: {
|
grid: {
|
||||||
height: this.widget ? 90 : undefined,
|
height: this.widget ? ((this.height || 120) - 60) : undefined,
|
||||||
top: this.widget ? 20 : 40,
|
top: this.widget ? 20 : 40,
|
||||||
bottom: this.widget ? 0 : 70,
|
bottom: this.widget ? 0 : 70,
|
||||||
right: (isMobile() && this.widget) ? 35 : this.right,
|
right: (isMobile() && this.widget) ? 35 : this.right,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user