diff --git a/backend/src/api/statistics/statistics-api.ts b/backend/src/api/statistics/statistics-api.ts index 9df12d704..ecc0222c1 100644 --- a/backend/src/api/statistics/statistics-api.ts +++ b/backend/src/api/statistics/statistics-api.ts @@ -171,6 +171,7 @@ class StatisticsApi { private getQueryForDaysAvg(div: number, interval: string) { return `SELECT UNIX_TIMESTAMP(added) as added, + CAST(avg(unconfirmed_transactions) as DOUBLE) as unconfirmed_transactions, CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, CAST(avg(vsize_1) as DOUBLE) as vsize_1, CAST(avg(vsize_2) as DOUBLE) as vsize_2, @@ -401,6 +402,7 @@ class StatisticsApi { return statistic.map((s) => { return { added: s.added, + count: s.unconfirmed_transactions, vbytes_per_second: s.vbytes_per_second, mempool_byte_weight: s.mempool_byte_weight, total_fee: s.total_fee, diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 6c9795c89..d31044be9 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core'; import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; import { WuBytesPipe } from '../../shared/pipes/bytes-pipe/wubytes.pipe'; +import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; import { formatNumber } from '@angular/common'; import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; import { StateService } from '../../services/state.service'; @@ -26,6 +27,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { @Input() data: any[]; @Input() filterSize = 100000; @Input() limitFilterFee = 1; + @Input() hideCount: boolean = false; @Input() height: number | string = 200; @Input() top: number | string = 20; @Input() right: number | string = 10; @@ -50,10 +52,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges { inverted: boolean; chartInstance: any = undefined; weightMode: boolean = false; + isWidget: boolean = false; + showCount: boolean = true; constructor( private vbytesPipe: VbytesPipe, private wubytesPipe: WuBytesPipe, + private amountShortenerPipe: AmountShortenerPipe, private stateService: StateService, private storageService: StorageService, @Inject(LOCALE_ID) private locale: string, @@ -62,12 +67,16 @@ export class MempoolGraphComponent implements OnInit, OnChanges { ngOnInit(): void { this.isLoading = true; this.inverted = this.storageService.getValue('inverted-graph') === 'true'; + this.isWidget = this.template === 'widget'; + this.showCount = !this.isWidget && !this.hideCount; } - ngOnChanges() { + ngOnChanges(changes) { if (!this.data) { return; } + this.isWidget = this.template === 'widget'; + this.showCount = !this.isWidget && !this.hideCount; this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([])); this.mountFeeChart(); @@ -96,10 +105,12 @@ export class MempoolGraphComponent implements OnInit, OnChanges { mempoolStats.reverse(); const labels = mempoolStats.map(stats => stats.added); const finalArrayVByte = this.generateArray(mempoolStats); + const finalArrayCount = this.generateCountArray(mempoolStats); return { labels: labels, - series: finalArrayVByte + series: finalArrayVByte, + countSeries: finalArrayCount, }; } @@ -124,9 +135,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges { return finalArray; } + generateCountArray(mempoolStats: OptimizedMempoolStats[]) { + return mempoolStats.map(stats => [stats.added * 1000, stats.count]); + } + mountFeeChart() { this.orderLevels(); - const { series } = this.mempoolVsizeFeesData; + const { series, countSeries } = this.mempoolVsizeFeesData; const seriesGraph = []; const newColors = []; @@ -178,6 +193,29 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }); } } + if (this.showCount) { + newColors.push('white'); + seriesGraph.push({ + zlevel: 1, + yAxisIndex: 1, + name: 'count', + type: 'line', + stack: 'count', + smooth: false, + markPoint: false, + lineStyle: { + width: 2, + opacity: 1, + }, + symbol: 'none', + silent: true, + areaStyle: { + color: null, + opacity: 0, + }, + data: countSeries, + }); + } this.mempoolVsizeFeesOptions = { series: this.inverted ? [...seriesGraph].reverse() : seriesGraph, @@ -201,7 +239,11 @@ export class MempoolGraphComponent implements OnInit, OnChanges { label: { formatter: (params: any) => { if (params.axisDimension === 'y') { - return this.vbytesPipe.transform(params.value, 2, 'vB', 'MvB', true) + if (params.axisIndex === 0) { + return this.vbytesPipe.transform(params.value, 2, 'vB', 'MvB', true); + } else { + return this.amountShortenerPipe.transform(params.value, 2, undefined, true); + } } else { return formatterXAxis(this.locale, this.windowPreference, params.value); } @@ -214,7 +256,11 @@ export class MempoolGraphComponent implements OnInit, OnChanges { const itemFormatted = []; let totalParcial = 0; let progressPercentageText = ''; - const items = this.inverted ? [...params].reverse() : params; + let countItem; + let items = this.inverted ? [...params].reverse() : params; + if (items[items.length - 1].seriesName === 'count') { + countItem = items.pop(); + } items.map((item: any, index: number) => { totalParcial += item.value[1]; const progressPercentage = (item.value[1] / totalValue) * 100; @@ -276,6 +322,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { `); }); const classActive = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : ''; + const titleCount = $localize`Count`; const titleRange = $localize`Range`; const titleSize = $localize`:@@7faaaa08f56427999f3be41df1093ce4089bbd75:Size`; const titleSum = $localize`Sum`; @@ -286,6 +333,25 @@ export class MempoolGraphComponent implements OnInit, OnChanges { ${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)} + ` + + (this.showCount && countItem ? ` +
+ + + ${titleCount} + + | ++ ${this.amountShortenerPipe.transform(countItem.value[1], 2, undefined, true)} + | +